@@ -25,30 +25,36 @@ ifneq ($(HAIKU),true) | |||||
ifneq ($(HURD),true) | ifneq ($(HURD),true) | ||||
ifneq ($(LINUX),true) | ifneq ($(LINUX),true) | ||||
ifneq ($(MACOS),true) | ifneq ($(MACOS),true) | ||||
ifneq ($(WASM),true) | |||||
ifneq ($(WINDOWS),true) | ifneq ($(WINDOWS),true) | ||||
ifneq (,$(findstring bsd,$(TARGET_MACHINE))) | ifneq (,$(findstring bsd,$(TARGET_MACHINE))) | ||||
BSD=true | |||||
BSD = true | |||||
else ifneq (,$(findstring haiku,$(TARGET_MACHINE))) | else ifneq (,$(findstring haiku,$(TARGET_MACHINE))) | ||||
HAIKU=true | |||||
HAIKU = true | |||||
else ifneq (,$(findstring linux,$(TARGET_MACHINE))) | else ifneq (,$(findstring linux,$(TARGET_MACHINE))) | ||||
LINUX=true | |||||
LINUX = true | |||||
else ifneq (,$(findstring gnu,$(TARGET_MACHINE))) | else ifneq (,$(findstring gnu,$(TARGET_MACHINE))) | ||||
HURD=true | |||||
HURD = true | |||||
else ifneq (,$(findstring apple,$(TARGET_MACHINE))) | else ifneq (,$(findstring apple,$(TARGET_MACHINE))) | ||||
MACOS=true | |||||
MACOS = true | |||||
else ifneq (,$(findstring mingw,$(TARGET_MACHINE))) | else ifneq (,$(findstring mingw,$(TARGET_MACHINE))) | ||||
WINDOWS=true | |||||
WINDOWS = true | |||||
else ifneq (,$(findstring msys,$(TARGET_MACHINE))) | |||||
WINDOWS = true | |||||
else ifneq (,$(findstring wasm,$(TARGET_MACHINE))) | |||||
WASM = true | |||||
else ifneq (,$(findstring windows,$(TARGET_MACHINE))) | else ifneq (,$(findstring windows,$(TARGET_MACHINE))) | ||||
WINDOWS=true | |||||
WINDOWS = true | |||||
endif | endif | ||||
endif | |||||
endif | |||||
endif | |||||
endif | |||||
endif | |||||
endif | |||||
endif # WINDOWS | |||||
endif # WASM | |||||
endif # MACOS | |||||
endif # LINUX | |||||
endif # HURD | |||||
endif # HAIKU | |||||
endif # BSD | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Auto-detect the processor | # Auto-detect the processor | ||||
@@ -56,81 +62,105 @@ endif | |||||
TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) | TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) | ||||
ifneq (,$(filter i%86,$(TARGET_PROCESSOR))) | ifneq (,$(filter i%86,$(TARGET_PROCESSOR))) | ||||
CPU_I386=true | |||||
CPU_I386_OR_X86_64=true | |||||
CPU_I386 = true | |||||
CPU_I386_OR_X86_64 = true | |||||
endif | |||||
ifneq (,$(filter wasm32,$(TARGET_PROCESSOR))) | |||||
CPU_I386 = true | |||||
CPU_I386_OR_X86_64 = true | |||||
endif | endif | ||||
ifneq (,$(filter x86_64,$(TARGET_PROCESSOR))) | ifneq (,$(filter x86_64,$(TARGET_PROCESSOR))) | ||||
CPU_X86_64=true | |||||
CPU_I386_OR_X86_64=true | |||||
CPU_X86_64 = true | |||||
CPU_I386_OR_X86_64 = true | |||||
endif | endif | ||||
ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) | ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) | ||||
CPU_ARM=true | |||||
CPU_ARM_OR_AARCH64=true | |||||
CPU_ARM = true | |||||
CPU_ARM_OR_AARCH64 = true | |||||
endif | endif | ||||
ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) | ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) | ||||
CPU_ARM64=true | |||||
CPU_ARM_OR_AARCH64=true | |||||
CPU_ARM64 = true | |||||
CPU_ARM_OR_AARCH64 = true | |||||
endif | endif | ||||
ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) | ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) | ||||
CPU_AARCH64=true | |||||
CPU_ARM_OR_AARCH64=true | |||||
CPU_AARCH64 = true | |||||
CPU_ARM_OR_AARCH64 = true | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set PKG_CONFIG (can be overridden by environment variable) | # Set PKG_CONFIG (can be overridden by environment variable) | ||||
ifeq ($(WINDOWS),true) | |||||
ifeq ($(WASM),true) | |||||
# Skip on wasm by default | |||||
PKG_CONFIG ?= false | |||||
else ifeq ($(WINDOWS),true) | |||||
# Build statically on Windows by default | # Build statically on Windows by default | ||||
PKG_CONFIG ?= pkg-config --static | PKG_CONFIG ?= pkg-config --static | ||||
else | else | ||||
PKG_CONFIG ?= pkg-config | PKG_CONFIG ?= pkg-config | ||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set cross compiling flag | |||||
ifeq ($(WASM),true) | |||||
CROSS_COMPILING = true | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set LINUX_OR_MACOS | # Set LINUX_OR_MACOS | ||||
ifeq ($(LINUX),true) | ifeq ($(LINUX),true) | ||||
LINUX_OR_MACOS=true | |||||
LINUX_OR_MACOS = true | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
LINUX_OR_MACOS=true | |||||
LINUX_OR_MACOS = true | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set MACOS_OR_WINDOWS and HAIKU_OR_MACOS_OR_WINDOWS | |||||
# Set MACOS_OR_WINDOWS, MACOS_OR_WASM_OR_WINDOWS, HAIKU_OR_MACOS_OR_WINDOWS and HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS | |||||
ifeq ($(HAIKU),true) | ifeq ($(HAIKU),true) | ||||
HAIKU_OR_MACOS_OR_WINDOWS=true | |||||
HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true | |||||
HAIKU_OR_MACOS_OR_WINDOWS = true | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
MACOS_OR_WINDOWS=true | |||||
HAIKU_OR_MACOS_OR_WINDOWS=true | |||||
HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true | |||||
HAIKU_OR_MACOS_OR_WINDOWS = true | |||||
MACOS_OR_WASM_OR_WINDOWS = true | |||||
MACOS_OR_WINDOWS = true | |||||
endif | |||||
ifeq ($(WASM),true) | |||||
HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true | |||||
MACOS_OR_WASM_OR_WINDOWS = true | |||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
MACOS_OR_WINDOWS=true | |||||
HAIKU_OR_MACOS_OR_WINDOWS=true | |||||
HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true | |||||
HAIKU_OR_MACOS_OR_WINDOWS = true | |||||
MACOS_OR_WASM_OR_WINDOWS = true | |||||
MACOS_OR_WINDOWS = true | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set UNIX | # Set UNIX | ||||
ifeq ($(BSD),true) | ifeq ($(BSD),true) | ||||
UNIX=true | |||||
UNIX = true | |||||
endif | endif | ||||
ifeq ($(HURD),true) | ifeq ($(HURD),true) | ||||
UNIX=true | |||||
UNIX = true | |||||
endif | endif | ||||
ifeq ($(LINUX),true) | ifeq ($(LINUX),true) | ||||
UNIX=true | |||||
UNIX = true | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
UNIX=true | |||||
UNIX = true | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -140,7 +170,12 @@ BASE_FLAGS = -Wall -Wextra -pipe -MD -MP | |||||
BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections | BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections | ||||
ifeq ($(CPU_I386_OR_X86_64),true) | ifeq ($(CPU_I386_OR_X86_64),true) | ||||
BASE_OPTS += -mtune=generic -msse -msse2 -mfpmath=sse | |||||
BASE_OPTS += -mtune=generic | |||||
ifeq ($(WASM),true) | |||||
BASE_OPTS += -msse -msse2 -msse3 -msimd128 | |||||
else | |||||
BASE_OPTS += -msse -msse2 -mfpmath=sse | |||||
endif | |||||
endif | endif | ||||
ifeq ($(CPU_ARM),true) | ifeq ($(CPU_ARM),true) | ||||
@@ -150,19 +185,26 @@ endif | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
# MacOS linker flags | # MacOS linker flags | ||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs | |||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip,-dead_strip_dylibs | |||||
ifneq ($(SKIP_STRIPPING),true) | ifneq ($(SKIP_STRIPPING),true) | ||||
LINK_OPTS += -Wl,-x | LINK_OPTS += -Wl,-x | ||||
endif | endif | ||||
else | else | ||||
# Common linker flags | # Common linker flags | ||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed | |||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections | |||||
ifneq ($(WASM),true) | |||||
LINK_OPTS += -Wl,--as-needed | |||||
ifneq ($(SKIP_STRIPPING),true) | ifneq ($(SKIP_STRIPPING),true) | ||||
LINK_OPTS += -Wl,--strip-all | LINK_OPTS += -Wl,--strip-all | ||||
endif | endif | ||||
endif | endif | ||||
endif | |||||
ifeq ($(SKIP_STRIPPING),true) | ifeq ($(SKIP_STRIPPING),true) | ||||
BASE_FLAGS += -g | BASE_FLAGS += -g | ||||
endif | endif | ||||
@@ -172,9 +214,15 @@ ifeq ($(NOOPT),true) | |||||
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | ||||
endif | endif | ||||
ifneq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
ifneq ($(BSD),true) | |||||
BASE_FLAGS += -fno-gnu-unique | |||||
endif | |||||
endif | |||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
# Assume we want posix | # Assume we want posix | ||||
BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS | |||||
BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS=1 -D__USE_MINGW_ANSI_STDIO=1 | |||||
# Needed for windows, see https://github.com/falkTX/Carla/issues/855 | # Needed for windows, see https://github.com/falkTX/Carla/issues/855 | ||||
BASE_FLAGS += -mstackrealign | BASE_FLAGS += -mstackrealign | ||||
else | else | ||||
@@ -185,29 +233,45 @@ endif | |||||
ifeq ($(DEBUG),true) | ifeq ($(DEBUG),true) | ||||
BASE_FLAGS += -DDEBUG -O0 -g | BASE_FLAGS += -DDEBUG -O0 -g | ||||
LINK_OPTS = | LINK_OPTS = | ||||
ifeq ($(WASM),true) | |||||
LINK_OPTS += -sASSERTIONS=1 | |||||
endif | |||||
else | else | ||||
BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | ||||
CXXFLAGS += -fvisibility-inlines-hidden | CXXFLAGS += -fvisibility-inlines-hidden | ||||
endif | endif | ||||
ifeq ($(STATIC_BUILD),true) | |||||
BASE_FLAGS += -DSTATIC_BUILD | |||||
# LINK_OPTS += -static | |||||
endif | |||||
ifeq ($(WITH_LTO),true) | ifeq ($(WITH_LTO),true) | ||||
BASE_FLAGS += -fno-strict-aliasing -flto | BASE_FLAGS += -fno-strict-aliasing -flto | ||||
LINK_FLAGS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch | |||||
LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch | |||||
endif | endif | ||||
BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) | BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) | ||||
BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++11 $(CXXFLAGS) | BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++11 $(CXXFLAGS) | ||||
LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) | LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) | ||||
ifneq ($(MACOS),true) | |||||
ifeq ($(WASM),true) | |||||
# Special flag for emscripten | |||||
LINK_FLAGS += -sLLD_REPORT_UNDEFINED | |||||
else ifneq ($(MACOS),true) | |||||
# Not available on MacOS | # Not available on MacOS | ||||
LINK_FLAGS += -Wl,--no-undefined | |||||
LINK_FLAGS += -Wl,--no-undefined | |||||
endif | endif | ||||
ifeq ($(MACOS_OLD),true) | ifeq ($(MACOS_OLD),true) | ||||
BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) -DHAVE_CPP11_SUPPORT=0 | BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) -DHAVE_CPP11_SUPPORT=0 | ||||
endif | endif | ||||
ifeq ($(WASM_EXCEPTIONS),true) | |||||
BUILD_CXX_FLAGS += -fexceptions | |||||
LINK_FLAGS += -fexceptions | |||||
endif | |||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
# Always build statically on windows | # Always build statically on windows | ||||
LINK_FLAGS += -static -static-libgcc -static-libstdc++ | LINK_FLAGS += -static -static-libgcc -static-libstdc++ | ||||
@@ -241,46 +305,47 @@ endif | |||||
HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | ||||
# Vulkan is not supported yet | |||||
# HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) | |||||
ifeq ($(MACOS_OR_WINDOWS),true) | |||||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
HAVE_OPENGL = true | HAVE_OPENGL = true | ||||
else | else | ||||
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | |||||
ifneq ($(HAIKU),true) | |||||
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | |||||
HAVE_DBUS = $(shell $(PKG_CONFIG) --exists dbus-1 && echo true) | HAVE_DBUS = $(shell $(PKG_CONFIG) --exists dbus-1 && echo true) | ||||
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | ||||
HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) | HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) | ||||
HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) | HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) | ||||
HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) | HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) | ||||
endif | endif | ||||
endif | |||||
# Vulkan is not supported yet | |||||
# HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Check for optional libraries | # Check for optional libraries | ||||
HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | ||||
ifeq ($(SKIP_RTAUDIO_FALLBACK),true) | |||||
CXXFLAGS += -DDPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK | |||||
else | |||||
ifneq ($(SKIP_NATIVE_AUDIO_FALLBACK),true) | |||||
ifneq ($(SKIP_RTAUDIO_FALLBACK),true) | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
HAVE_RTAUDIO = true | HAVE_RTAUDIO = true | ||||
else ifeq ($(WINDOWS),true) | else ifeq ($(WINDOWS),true) | ||||
HAVE_RTAUDIO = true | HAVE_RTAUDIO = true | ||||
else ifneq ($(HAIKU),true) | |||||
else | |||||
HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true) | HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true) | ||||
HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true) | HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true) | ||||
HAVE_SDL2 = $(shell $(PKG_CONFIG) --exists sdl2 && echo true) | |||||
ifeq ($(HAVE_ALSA),true) | ifeq ($(HAVE_ALSA),true) | ||||
HAVE_RTAUDIO = true | HAVE_RTAUDIO = true | ||||
else ifeq ($(HAVE_PULSEAUDIO),true) | else ifeq ($(HAVE_PULSEAUDIO),true) | ||||
HAVE_RTAUDIO = true | HAVE_RTAUDIO = true | ||||
endif | endif | ||||
endif | |||||
endif | endif | ||||
endif | endif | ||||
# backwards compat | |||||
# backwards compat, always available/enabled | |||||
HAVE_JACK = true | HAVE_JACK = true | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -288,18 +353,13 @@ HAVE_JACK = true | |||||
ifeq ($(HAIKU),true) | ifeq ($(HAIKU),true) | ||||
DGL_SYSTEM_LIBS += -lbe | DGL_SYSTEM_LIBS += -lbe | ||||
endif | |||||
ifeq ($(MACOS),true) | |||||
else ifeq ($(MACOS),true) | |||||
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo | DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo | ||||
endif | |||||
ifeq ($(WINDOWS),true) | |||||
else ifeq ($(WASM),true) | |||||
else ifeq ($(WINDOWS),true) | |||||
DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | ||||
# -lole32 | # -lole32 | ||||
endif | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
else | |||||
ifeq ($(HAVE_DBUS),true) | ifeq ($(HAVE_DBUS),true) | ||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | ||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | ||||
@@ -346,18 +406,18 @@ DGL_FLAGS += -DHAVE_OPENGL | |||||
ifeq ($(HAIKU),true) | ifeq ($(HAIKU),true) | ||||
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl) | OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl) | ||||
OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl) | OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl) | ||||
endif | |||||
ifeq ($(MACOS),true) | |||||
else ifeq ($(MACOS),true) | |||||
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations | OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations | ||||
OPENGL_LIBS = -framework OpenGL | OPENGL_LIBS = -framework OpenGL | ||||
else ifeq ($(WASM),true) | |||||
ifneq ($(USE_GLES2),true) | |||||
ifneq ($(USE_GLES3),true) | |||||
OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0 | |||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | |||||
OPENGL_LIBS = -lopengl32 | |||||
endif | endif | ||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
else ifeq ($(WINDOWS),true) | |||||
OPENGL_LIBS = -lopengl32 | |||||
else | |||||
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11) | OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11) | ||||
OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl x11) | OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl x11) | ||||
endif | endif | ||||
@@ -369,7 +429,7 @@ endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set Stub specific stuff | # Set Stub specific stuff | ||||
ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
HAVE_STUB = true | HAVE_STUB = true | ||||
else | else | ||||
HAVE_STUB = $(HAVE_X11) | HAVE_STUB = $(HAVE_X11) | ||||
@@ -409,21 +469,40 @@ PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple) | |||||
PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) | PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) | ||||
endif | endif | ||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
ifeq ($(HAVE_SDL2),true) | |||||
SDL2_FLAGS = $(shell $(PKG_CONFIG) --cflags sdl2) | |||||
SDL2_LIBS = $(shell $(PKG_CONFIG) --libs sdl2) | |||||
endif | |||||
ifeq ($(HAVE_JACK),true) | |||||
ifeq ($(STATIC_BUILD),true) | |||||
JACK_FLAGS = $(shell $(PKG_CONFIG) --cflags jack) | |||||
JACK_LIBS = $(shell $(PKG_CONFIG) --libs jack) | |||||
endif | |||||
endif | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | |||||
SHARED_MEMORY_LIBS = -lrt | SHARED_MEMORY_LIBS = -lrt | ||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Backwards-compatible HAVE_DGL | # Backwards-compatible HAVE_DGL | ||||
ifeq ($(MACOS_OR_WINDOWS),true) | |||||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else ifeq ($(HAVE_OPENGL),true) | else ifeq ($(HAVE_OPENGL),true) | ||||
ifeq ($(HAIKU),true) | |||||
HAVE_DGL = true | |||||
else | |||||
HAVE_DGL = $(HAVE_X11) | HAVE_DGL = $(HAVE_X11) | ||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Namespace flags | |||||
ifneq ($(DISTRHO_NAMESPACE),) | |||||
BUILD_CXX_FLAGS += -DDISTRHO_NAMESPACE=$(DISTRHO_NAMESPACE) | |||||
endif | |||||
ifneq ($(DGL_NAMESPACE),) | |||||
BUILD_CXX_FLAGS += -DDGL_NAMESPACE=$(DGL_NAMESPACE) | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -441,6 +520,18 @@ ifeq ($(FILE_BROWSER_DISABLED),true) | |||||
BUILD_CXX_FLAGS += -DDGL_FILE_BROWSER_DISABLED | BUILD_CXX_FLAGS += -DDGL_FILE_BROWSER_DISABLED | ||||
endif | endif | ||||
ifneq ($(WINDOWS_ICON_ID),) | |||||
BUILD_CXX_FLAGS += -DDGL_WINDOWS_ICON_ID=$(WINDOWS_ICON_ID) | |||||
endif | |||||
ifeq ($(USE_GLES2),true) | |||||
BUILD_CXX_FLAGS += -DDGL_USE_GLES -DDGL_USE_GLES2 | |||||
endif | |||||
ifeq ($(USE_GLES3),true) | |||||
BUILD_CXX_FLAGS += -DDGL_USE_GLES -DDGL_USE_GLES3 | |||||
endif | |||||
ifeq ($(USE_OPENGL3),true) | ifeq ($(USE_OPENGL3),true) | ||||
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 | BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 | ||||
endif | endif | ||||
@@ -457,25 +548,26 @@ ifeq ($(USE_RGBA),true) | |||||
BUILD_CXX_FLAGS += -DDGL_USE_RGBA | BUILD_CXX_FLAGS += -DDGL_USE_RGBA | ||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set app extension | # Set app extension | ||||
ifeq ($(WINDOWS),true) | |||||
ifeq ($(WASM),true) | |||||
APP_EXT = .html | |||||
else ifeq ($(WINDOWS),true) | |||||
APP_EXT = .exe | APP_EXT = .exe | ||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set shared lib extension | # Set shared lib extension | ||||
LIB_EXT = .so | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
LIB_EXT = .dylib | LIB_EXT = .dylib | ||||
endif | |||||
ifeq ($(WINDOWS),true) | |||||
else ifeq ($(WASM),true) | |||||
LIB_EXT = .wasm | |||||
else ifeq ($(WINDOWS),true) | |||||
LIB_EXT = .dll | LIB_EXT = .dll | ||||
else | |||||
LIB_EXT = .so | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -483,6 +575,8 @@ endif | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
SHARED = -dynamiclib | SHARED = -dynamiclib | ||||
else ifeq ($(WASM),true) | |||||
SHARED = -sSIDE_MODULE=2 | |||||
else | else | ||||
SHARED = -shared | SHARED = -shared | ||||
endif | endif | ||||
@@ -490,8 +584,12 @@ endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Handle the verbosity switch | # Handle the verbosity switch | ||||
ifeq ($(VERBOSE),true) | |||||
SILENT = | SILENT = | ||||
ifeq ($(VERBOSE),1) | |||||
else ifeq ($(VERBOSE),y) | |||||
else ifeq ($(VERBOSE),yes) | |||||
else ifeq ($(VERBOSE),true) | |||||
else | else | ||||
SILENT = @ | SILENT = @ | ||||
endif | endif | ||||
@@ -520,9 +618,12 @@ features: | |||||
$(call print_available,HURD) | $(call print_available,HURD) | ||||
$(call print_available,LINUX) | $(call print_available,LINUX) | ||||
$(call print_available,MACOS) | $(call print_available,MACOS) | ||||
$(call print_available,WASM) | |||||
$(call print_available,WINDOWS) | $(call print_available,WINDOWS) | ||||
$(call print_available,HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS) | |||||
$(call print_available,HAIKU_OR_MACOS_OR_WINDOWS) | $(call print_available,HAIKU_OR_MACOS_OR_WINDOWS) | ||||
$(call print_available,LINUX_OR_MACOS) | $(call print_available,LINUX_OR_MACOS) | ||||
$(call print_available,MACOS_OR_WASM_OR_WINDOWS) | |||||
$(call print_available,MACOS_OR_WINDOWS) | $(call print_available,MACOS_OR_WINDOWS) | ||||
$(call print_available,UNIX) | $(call print_available,UNIX) | ||||
@echo === Detected features | @echo === Detected features | ||||
@@ -534,6 +635,7 @@ features: | |||||
$(call print_available,HAVE_OPENGL) | $(call print_available,HAVE_OPENGL) | ||||
$(call print_available,HAVE_PULSEAUDIO) | $(call print_available,HAVE_PULSEAUDIO) | ||||
$(call print_available,HAVE_RTAUDIO) | $(call print_available,HAVE_RTAUDIO) | ||||
$(call print_available,HAVE_SDL2) | |||||
$(call print_available,HAVE_STUB) | $(call print_available,HAVE_STUB) | ||||
$(call print_available,HAVE_VULKAN) | $(call print_available,HAVE_VULKAN) | ||||
$(call print_available,HAVE_X11) | $(call print_available,HAVE_X11) | ||||
@@ -38,6 +38,10 @@ ifeq ($(HAVE_ALSA),true) | |||||
BASE_FLAGS += -DHAVE_ALSA | BASE_FLAGS += -DHAVE_ALSA | ||||
endif | endif | ||||
ifeq ($(HAVE_JACK),true) | |||||
BASE_FLAGS += -DHAVE_JACK | |||||
endif | |||||
ifeq ($(HAVE_LIBLO),true) | ifeq ($(HAVE_LIBLO),true) | ||||
BASE_FLAGS += -DHAVE_LIBLO | BASE_FLAGS += -DHAVE_LIBLO | ||||
endif | endif | ||||
@@ -46,6 +50,36 @@ ifeq ($(HAVE_PULSEAUDIO),true) | |||||
BASE_FLAGS += -DHAVE_PULSEAUDIO | BASE_FLAGS += -DHAVE_PULSEAUDIO | ||||
endif | endif | ||||
ifeq ($(HAVE_RTAUDIO),true) | |||||
BASE_FLAGS += -DHAVE_RTAUDIO | |||||
endif | |||||
ifeq ($(HAVE_SDL2),true) | |||||
BASE_FLAGS += -DHAVE_SDL2 | |||||
endif | |||||
# always needed | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | |||||
ifneq ($(STATIC_BUILD),true) | |||||
LINK_FLAGS += -ldl | |||||
endif | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# JACK/Standalone setup | |||||
ifeq ($(WASM),true) | |||||
JACK_FLAGS += -sUSE_SDL=2 | |||||
JACK_LIBS += -sUSE_SDL=2 | |||||
JACK_LIBS += -sMAIN_MODULE -ldl | |||||
ifneq ($(FILE_BROWSER_DISABLED),true) | |||||
JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap | |||||
endif | |||||
else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
JACK_LIBS += -framework CoreAudio -framework CoreFoundation | JACK_LIBS += -framework CoreAudio -framework CoreFoundation | ||||
else ifeq ($(WINDOWS),true) | else ifeq ($(WINDOWS),true) | ||||
@@ -54,26 +88,25 @@ JACK_LIBS += -lole32 -lwinmm | |||||
JACK_LIBS += -ldsound | JACK_LIBS += -ldsound | ||||
# WASAPI | # WASAPI | ||||
# JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid | # JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid | ||||
else ifneq ($(HAIKU),true) | |||||
ifeq ($(HAVE_ALSA),true) | |||||
JACK_FLAGS += $(ALSA_FLAGS) | |||||
JACK_LIBS += $(ALSA_LIBS) | |||||
endif | |||||
ifeq ($(HAVE_PULSEAUDIO),true) | |||||
else ifeq ($(HAVE_PULSEAUDIO),true) | |||||
JACK_FLAGS += $(PULSEAUDIO_FLAGS) | JACK_FLAGS += $(PULSEAUDIO_FLAGS) | ||||
JACK_LIBS += $(PULSEAUDIO_LIBS) | JACK_LIBS += $(PULSEAUDIO_LIBS) | ||||
else ifeq ($(HAVE_ALSA),true) | |||||
JACK_FLAGS += $(ALSA_FLAGS) | |||||
JACK_LIBS += $(ALSA_LIBS) | |||||
endif | endif | ||||
ifeq ($(HAVE_RTAUDIO),true) | ifeq ($(HAVE_RTAUDIO),true) | ||||
ifneq ($(HAIKU),true) | |||||
JACK_LIBS += -lpthread | JACK_LIBS += -lpthread | ||||
endif # !HAIKU | |||||
endif | |||||
endif | endif | ||||
# backwards compat | |||||
BASE_FLAGS += -DHAVE_JACK | |||||
ifeq ($(HAVE_SDL2),true) | |||||
JACK_FLAGS += $(SDL2_FLAGS) | |||||
JACK_LIBS += $(SDL2_LIBS) | |||||
endif | |||||
# always needed | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
LINK_FLAGS += -ldl | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -103,13 +136,17 @@ endif | |||||
ifeq ($(LINUX),true) | ifeq ($(LINUX),true) | ||||
VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so | VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so | ||||
endif | |||||
ifeq ($(MACOS),true) | |||||
else ifeq ($(MACOS),true) | |||||
VST3_CONTENTS = $(NAME).vst3/Contents | VST3_CONTENTS = $(NAME).vst3/Contents | ||||
VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) | VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) | ||||
else ifeq ($(WASM),true) | |||||
VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3 | |||||
else ifeq ($(WINDOWS),true) | |||||
ifeq ($(CPU_I386),true) | |||||
VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 | |||||
else ifeq ($(CPU_X86_64),true) | |||||
VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 | |||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | |||||
VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-win/$(NAME).vst3 | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -127,6 +164,7 @@ ifneq ($(VST3_FILENAME),) | |||||
vst3 = $(TARGET_DIR)/$(VST3_FILENAME) | vst3 = $(TARGET_DIR)/$(VST3_FILENAME) | ||||
endif | endif | ||||
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) | shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) | ||||
static = $(TARGET_DIR)/$(NAME).a | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist | vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist | ||||
@@ -149,6 +187,15 @@ SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp | |||||
SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.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_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.exp | ||||
SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp | SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp | ||||
else ifeq ($(WASM),true) | |||||
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" | |||||
SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_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_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']" | |||||
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']" | |||||
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" | |||||
else ifeq ($(WINDOWS),true) | else ifeq ($(WINDOWS),true) | ||||
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def | SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def | ||||
SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def | SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def | ||||
@@ -314,7 +361,11 @@ $(BUILD_DIR)/%.mm.o: %.mm | |||||
clean: | clean: | ||||
rm -rf $(BUILD_DIR) | rm -rf $(BUILD_DIR) | ||||
rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 | |||||
rm -rf $(TARGET_DIR)/$(NAME) | |||||
rm -rf $(TARGET_DIR)/$(NAME)-* | |||||
rm -rf $(TARGET_DIR)/$(NAME).lv2 | |||||
rm -rf $(TARGET_DIR)/$(NAME).vst | |||||
rm -rf $(TARGET_DIR)/$(NAME).vst3 | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# DGL | # DGL | ||||
@@ -336,8 +387,6 @@ $(DPF_PATH)/build/libdgl-vulkan.a: | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
AS_PUGL_NAMESPACE = $(subst -,_,$(1)) | |||||
$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) | $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoPluginMain.cpp ($*)" | @echo "Compiling DistrhoPluginMain.cpp ($*)" | ||||
@@ -351,7 +400,7 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTR | |||||
$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) | $(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUI_macOS.mm ($*)" | @echo "Compiling DistrhoUI_macOS.mm ($*)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$(call AS_PUGL_NAMESPACE,$*) -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | |||||
$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) | $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@@ -361,7 +410,7 @@ $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain | |||||
$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) | $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUIMain.cpp (DSSI)" | @echo "Compiling DistrhoUIMain.cpp (DSSI)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI $(LIBLO_FLAGS) -c -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# JACK | # JACK | ||||
@@ -423,12 +472,12 @@ endif | |||||
$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin library for $(NAME)" | @echo "Creating LV2 plugin library for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ | |||||
$(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin UI for $(NAME)" | @echo "Creating LV2 plugin UI for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# VST2 | # VST2 | ||||
@@ -472,6 +521,21 @@ endif | |||||
@echo "Creating shared library for $(NAME)" | @echo "Creating shared library for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ | $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ | ||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Static | |||||
static: $(static) | |||||
ifeq ($(HAVE_DGL),true) | |||||
$(static): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.o $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.o | |||||
else | |||||
$(static): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.o | |||||
endif | |||||
-@mkdir -p $(shell dirname $@) | |||||
@echo "Creating static library for $(NAME)" | |||||
$(SILENT)rm -f $@ | |||||
$(SILENT)$(AR) crs $@ $^ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# macOS files | # macOS files | ||||
@@ -501,6 +565,7 @@ endif | |||||
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | -include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d | -include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoPluginMain_SHARED.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_JACK.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d | ||||
@@ -508,5 +573,6 @@ endif | |||||
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- |
@@ -142,7 +142,7 @@ function(dpf_add_plugin NAME) | |||||
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | ||||
target_link_libraries("${NAME}-ui" PRIVATE "dl") | target_link_libraries("${NAME}-ui" PRIVATE "dl") | ||||
endif() | endif() | ||||
# add the files containing Objective-C classes, recompiled under namespace | |||||
# add the files containing Objective-C classes | |||||
dpf__add_plugin_specific_ui_sources("${NAME}-ui") | dpf__add_plugin_specific_ui_sources("${NAME}-ui") | ||||
else() | else() | ||||
add_library("${NAME}-ui" INTERFACE) | add_library("${NAME}-ui" INTERFACE) | ||||
@@ -468,9 +468,9 @@ function(dpf__add_dgl_cairo) | |||||
if(NOT APPLE) | if(NOT APPLE) | ||||
target_sources(dgl-cairo PRIVATE | target_sources(dgl-cairo PRIVATE | ||||
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp") | "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") | ||||
else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm | |||||
#target_sources(dgl-opengl PRIVATE | |||||
# "${DPF_ROOT_DIR}/dgl/src/pugl.mm") | |||||
else() | |||||
target_sources(dgl-opengl PRIVATE | |||||
"${DPF_ROOT_DIR}/dgl/src/pugl.mm") | |||||
endif() | endif() | ||||
target_include_directories(dgl-cairo PUBLIC | target_include_directories(dgl-cairo PUBLIC | ||||
"${DPF_ROOT_DIR}/dgl") | "${DPF_ROOT_DIR}/dgl") | ||||
@@ -530,9 +530,9 @@ function(dpf__add_dgl_opengl) | |||||
if(NOT APPLE) | if(NOT APPLE) | ||||
target_sources(dgl-opengl PRIVATE | target_sources(dgl-opengl PRIVATE | ||||
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp") | "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") | ||||
else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm | |||||
#target_sources(dgl-opengl PRIVATE | |||||
# "${DPF_ROOT_DIR}/dgl/src/pugl.mm") | |||||
else() | |||||
target_sources(dgl-opengl PRIVATE | |||||
"${DPF_ROOT_DIR}/dgl/src/pugl.mm") | |||||
endif() | endif() | ||||
target_include_directories(dgl-opengl PUBLIC | target_include_directories(dgl-opengl PUBLIC | ||||
"${DPF_ROOT_DIR}/dgl") | "${DPF_ROOT_DIR}/dgl") | ||||
@@ -556,19 +556,12 @@ endfunction() | |||||
# dpf__add_plugin_specific_ui_sources | # dpf__add_plugin_specific_ui_sources | ||||
# ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||
# | # | ||||
# Compile plugin-specific UI sources into the target designated by the given | |||||
# name. There are some special considerations here: | |||||
# - On most platforms, sources can be compiled only once, as part of DGL; | |||||
# - On macOS, for any sources which define Objective-C interfaces, these must | |||||
# be recompiled for each plugin under a unique namespace. In this case, the | |||||
# name must be a plugin-specific identifier, and it will be used for computing | |||||
# the unique ID along with the project version. | |||||
# Compile system specific files, for now it is just Objective-C code | |||||
# | |||||
function(dpf__add_plugin_specific_ui_sources NAME) | function(dpf__add_plugin_specific_ui_sources NAME) | ||||
if(APPLE) | if(APPLE) | ||||
target_sources("${NAME}" PRIVATE | target_sources("${NAME}" PRIVATE | ||||
"${DPF_ROOT_DIR}/distrho/DistrhoUI_macOS.mm") | "${DPF_ROOT_DIR}/distrho/DistrhoUI_macOS.mm") | ||||
string(SHA256 _hash "${NAME}:${PROJECT_VERSION}") | |||||
target_compile_definitions("${NAME}" PUBLIC "PUGL_NAMESPACE=${_hash}") | |||||
endif() | endif() | ||||
endfunction() | endfunction() | ||||
@@ -596,22 +589,22 @@ function(dpf__add_dgl_system_libs) | |||||
target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") | target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") | target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") | ||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") | target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") | ||||
if(X11_Xcursor_FOUND) | |||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") | |||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") | |||||
endif() | |||||
if(X11_Xext_FOUND) | if(X11_Xext_FOUND) | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") | target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") | ||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") | target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") | ||||
endif() | endif() | ||||
if(X11_XSync_FOUND) | |||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") | |||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") | |||||
endif() | |||||
if(X11_Xrandr_FOUND) | if(X11_Xrandr_FOUND) | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") | target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") | ||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") | target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") | ||||
endif() | endif() | ||||
#if(X11_Xcursor_FOUND) | |||||
# target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") | |||||
# target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") | |||||
#endif() | |||||
if(X11_XSync_FOUND) | |||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") | |||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") | |||||
endif() | |||||
endif() | endif() | ||||
if(MSVC) | if(MSVC) | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -19,6 +19,12 @@ | |||||
#include "Base.hpp" | #include "Base.hpp" | ||||
#ifdef DISTRHO_NAMESPACE | |||||
START_NAMESPACE_DISTRHO | |||||
class PluginApplication; | |||||
END_NAMESPACE_DISTRHO | |||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
@@ -33,7 +39,7 @@ START_NAMESPACE_DGL | |||||
Unless stated otherwise, functions within this class are not thread-safe. | Unless stated otherwise, functions within this class are not thread-safe. | ||||
*/ | */ | ||||
class Application | |||||
class DISTRHO_API Application | |||||
{ | { | ||||
public: | public: | ||||
/** | /** | ||||
@@ -103,7 +109,7 @@ public: | |||||
void removeIdleCallback(IdleCallback* callback); | void removeIdleCallback(IdleCallback* callback); | ||||
/** | /** | ||||
Set the class name of the application. | |||||
Get the class name of the application. | |||||
This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. | This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. | ||||
It is not displayed to the user, but can be used in scripts and by window managers, | It is not displayed to the user, but can be used in scripts and by window managers, | ||||
@@ -111,13 +117,21 @@ public: | |||||
Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. | Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. | ||||
*/ | */ | ||||
const char* getClassName() const noexcept; | |||||
/** | |||||
Set the class name of the application. | |||||
@see getClassName | |||||
*/ | |||||
void setClassName(const char* name); | void setClassName(const char* name); | ||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
friend class PluginApplication; | |||||
friend class Window; | friend class Window; | ||||
#ifdef DISTRHO_NAMESPACE | |||||
friend class DISTRHO_NAMESPACE::PluginApplication; | |||||
#endif | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | ||||
}; | }; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -130,21 +130,44 @@ enum CrossingMode { | |||||
kCrossingUngrab ///< Crossing due to a grab release | kCrossingUngrab ///< Crossing due to a grab release | ||||
}; | }; | ||||
/** | |||||
A mouse button. | |||||
Mouse button numbers start from 1, and are ordered: primary, secondary, middle. | |||||
So, on a typical right-handed mouse, the button numbers are: | |||||
Left: 1 | |||||
Right: 2 | |||||
Middle (often a wheel): 3 | |||||
Higher button numbers are reported in the same order they are represented on the system. | |||||
There is no universal standard here, but buttons 4 and 5 are typically a pair of buttons or a rocker, | |||||
which are usually bound to "back" and "forward" operations. | |||||
Note that these numbers may differ from those used on the underlying | |||||
platform, since they are manipulated to provide a consistent portable API. | |||||
*/ | |||||
enum MouseButton { | |||||
kMouseButtonLeft = 1, | |||||
kMouseButtonRight, | |||||
kMouseButtonMiddle, | |||||
}; | |||||
/** | /** | ||||
A mouse cursor type. | A mouse cursor type. | ||||
This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. | This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. | ||||
*/ | */ | ||||
enum MouseCursor { | enum MouseCursor { | ||||
kMouseCursorArrow, ///< Default pointing arrow | |||||
kMouseCursorCaret, ///< Caret (I-Beam) for text entry | |||||
kMouseCursorCrosshair, ///< Cross-hair | |||||
kMouseCursorHand, ///< Hand with a pointing finger | |||||
kMouseCursorNotAllowed, ///< Operation not allowed | |||||
kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize | |||||
kMouseCursorUpDown, ///< Up/down arrow for vertical resize | |||||
kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize | |||||
kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize | |||||
kMouseCursorArrow, ///< Default pointing arrow | |||||
kMouseCursorCaret, ///< Caret (I-Beam) for text entry | |||||
kMouseCursorCrosshair, ///< Cross-hair | |||||
kMouseCursorHand, ///< Hand with a pointing finger | |||||
kMouseCursorNotAllowed, ///< Operation not allowed | |||||
kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize | |||||
kMouseCursorUpDown, ///< Up/down arrow for vertical resize | |||||
kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize | |||||
kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize | |||||
}; | }; | ||||
/** | /** | ||||
@@ -155,11 +178,29 @@ enum MouseCursor { | |||||
while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads. | while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads. | ||||
*/ | */ | ||||
enum ScrollDirection { | enum ScrollDirection { | ||||
kScrollUp, ///< Scroll up | |||||
kScrollDown, ///< Scroll down | |||||
kScrollLeft, ///< Scroll left | |||||
kScrollRight, ///< Scroll right | |||||
kScrollSmooth ///< Smooth scroll in any direction | |||||
kScrollUp, ///< Scroll up | |||||
kScrollDown, ///< Scroll down | |||||
kScrollLeft, ///< Scroll left | |||||
kScrollRight, ///< Scroll right | |||||
kScrollSmooth ///< Smooth scroll in any direction | |||||
}; | |||||
/** | |||||
A clipboard data offer. | |||||
@see Window::onClipboardDataOffer | |||||
*/ | |||||
struct ClipboardDataOffer { | |||||
/** | |||||
The id of this data offer. | |||||
@note The value 0 is reserved for null/invalid. | |||||
*/ | |||||
uint32_t id; | |||||
/** | |||||
The type of this data offer. | |||||
Usually a MIME type, but may also be another platform-specific type identifier. | |||||
*/ | |||||
const char* type; | |||||
}; | }; | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -20,7 +20,7 @@ | |||||
#include "ImageBase.hpp" | #include "ImageBase.hpp" | ||||
#include "ImageBaseWidgets.hpp" | #include "ImageBaseWidgets.hpp" | ||||
#include <cairo/cairo.h> | |||||
#include <cairo.h> | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -151,7 +151,7 @@ public: | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
virtual ~CairoBaseWidget() {} | |||||
~CairoBaseWidget() override {} | |||||
protected: | protected: | ||||
/** | /** | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -14,17 +14,15 @@ | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include "tests.hpp" | |||||
#ifndef DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||||
#define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
#include "Base.hpp" | |||||
int main() | |||||
{ | |||||
USE_NAMESPACE_DGL; | |||||
START_NAMESPACE_DGL | |||||
// TODO | |||||
#include "../distrho/extra/FileBrowserDialogImpl.hpp" | |||||
return 0; | |||||
} | |||||
END_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
#endif // DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED |
@@ -13,10 +13,10 @@ BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-un | |||||
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include | BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include | ||||
LINK_FLAGS += $(DGL_LIBS) | LINK_FLAGS += $(DGL_LIBS) | ||||
# TODO fix these after pugl-upstream is done | |||||
BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers | |||||
ifneq ($(MACOS),true) | |||||
BUILD_CXX_FLAGS += -Wno-narrowing | |||||
ifeq ($(MACOS),true) | |||||
BUILD_CXX_FLAGS += -Wno-deprecated-declarations | |||||
else | |||||
PUGL_EXTRA_FLAGS = -Wno-extra -Wmissing-field-initializers | |||||
endif | endif | ||||
# ifneq ($(MACOS_OLD),true) | # ifneq ($(MACOS_OLD),true) | ||||
@@ -186,51 +186,63 @@ vulkan: ../build/libdgl-vulkan.a | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/dgl/pugl.cpp.o: src/pugl.cpp | |||||
-@mkdir -p ../build/dgl | |||||
@echo "Compiling $<" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -o $@ | |||||
../build/dgl/pugl.mm.o: src/pugl.mm | |||||
-@mkdir -p ../build/dgl | |||||
@echo "Compiling $<" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
../build/dgl/%.cpp.cairo.o: src/%.cpp | ../build/dgl/%.cpp.cairo.o: src/%.cpp | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (Cairo variant)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | |||||
../build/dgl/%.mm.cairo.o: src/%.mm | ../build/dgl/%.mm.cairo.o: src/%.mm | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (Cairo variant)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/dgl/%.cpp.opengl.o: src/%.cpp | ../build/dgl/%.cpp.opengl.o: src/%.cpp | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL variant)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | |||||
../build/dgl/%.mm.opengl.o: src/%.mm | ../build/dgl/%.mm.opengl.o: src/%.mm | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL variant)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/dgl/%.cpp.opengl3.o: src/%.cpp | ../build/dgl/%.cpp.opengl3.o: src/%.cpp | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL3 variant)" | @echo "Compiling $< (OpenGL3 variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ | |||||
../build/dgl/%.mm.opengl3.o: src/%.mm | ../build/dgl/%.mm.opengl3.o: src/%.mm | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL3 variant)" | @echo "Compiling $< (OpenGL3 variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/dgl/%.cpp.vulkan.o: src/%.cpp | ../build/dgl/%.cpp.vulkan.o: src/%.cpp | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (Vulkan variant)" | @echo "Compiling $< (Vulkan variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ | |||||
../build/dgl/%.mm.vulkan.o: src/%.mm | ../build/dgl/%.mm.vulkan.o: src/%.mm | ||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (Vulkan variant)" | @echo "Compiling $< (Vulkan variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -940,7 +940,7 @@ public: | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
virtual ~NanoBaseWidget() {} | |||||
~NanoBaseWidget() override {} | |||||
protected: | protected: | ||||
/** | /** | ||||
@@ -0,0 +1,112 @@ | |||||
/* | |||||
* DISTRHO Plugin Framework (DPF) | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
* or without fee is hereby granted, provided that the above copyright notice and this | |||||
* permission notice appear in all copies. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef DGL_OPENGL_INCLUDE_HPP_INCLUDED | |||||
#define DGL_OPENGL_INCLUDE_HPP_INCLUDED | |||||
#include "../distrho/src/DistrhoDefines.h" | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Fix OpenGL includes for Windows, based on glfw code (part 1) | |||||
#undef DGL_CALLBACK_DEFINED | |||||
#undef DGL_WINGDIAPI_DEFINED | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
#ifndef APIENTRY | |||||
# define APIENTRY __stdcall | |||||
#endif // APIENTRY | |||||
/* We need WINGDIAPI defined */ | |||||
#ifndef WINGDIAPI | |||||
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) | |||||
# define WINGDIAPI __declspec(dllimport) | |||||
# elif defined(__LCC__) | |||||
# define WINGDIAPI __stdcall | |||||
# else | |||||
# define WINGDIAPI extern | |||||
# endif | |||||
# define DGL_WINGDIAPI_DEFINED | |||||
#endif // WINGDIAPI | |||||
/* Some <GL/glu.h> files also need CALLBACK defined */ | |||||
#ifndef CALLBACK | |||||
# if defined(_MSC_VER) | |||||
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) | |||||
# define CALLBACK __stdcall | |||||
# else | |||||
# define CALLBACK | |||||
# endif | |||||
# else | |||||
# define CALLBACK __stdcall | |||||
# endif | |||||
# define DGL_CALLBACK_DEFINED | |||||
#endif // CALLBACK | |||||
#endif // DISTRHO_OS_WINDOWS | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// OpenGL includes | |||||
#ifdef DISTRHO_OS_MAC | |||||
# ifdef DGL_USE_OPENGL3 | |||||
# include <OpenGL/gl3.h> | |||||
# include <OpenGL/gl3ext.h> | |||||
# else | |||||
# include <OpenGL/gl.h> | |||||
# endif | |||||
#else | |||||
# ifndef DISTRHO_OS_WINDOWS | |||||
# define GL_GLEXT_PROTOTYPES | |||||
# endif | |||||
# ifndef __GLEW_H__ | |||||
# include <GL/gl.h> | |||||
# include <GL/glext.h> | |||||
# endif | |||||
#endif | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Missing OpenGL defines | |||||
#if defined(GL_BGR_EXT) && !defined(GL_BGR) | |||||
# define GL_BGR GL_BGR_EXT | |||||
#endif | |||||
#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) | |||||
# define GL_BGRA GL_BGRA_EXT | |||||
#endif | |||||
#ifndef GL_CLAMP_TO_BORDER | |||||
# define GL_CLAMP_TO_BORDER 0x812D | |||||
#endif | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Fix OpenGL includes for Windows, based on glfw code (part 2) | |||||
#ifdef DGL_CALLBACK_DEFINED | |||||
# undef CALLBACK | |||||
# undef DGL_CALLBACK_DEFINED | |||||
#endif | |||||
#ifdef DGL_WINGDIAPI_DEFINED | |||||
# undef WINGDIAPI | |||||
# undef DGL_WINGDIAPI_DEFINED | |||||
#endif | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
#endif |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -20,96 +20,7 @@ | |||||
#include "ImageBase.hpp" | #include "ImageBase.hpp" | ||||
#include "ImageBaseWidgets.hpp" | #include "ImageBaseWidgets.hpp" | ||||
// ----------------------------------------------------------------------- | |||||
// Fix OpenGL includes for Windows, based on glfw code (part 1) | |||||
#undef DGL_CALLBACK_DEFINED | |||||
#undef DGL_WINGDIAPI_DEFINED | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
#ifndef APIENTRY | |||||
# define APIENTRY __stdcall | |||||
#endif // APIENTRY | |||||
/* We need WINGDIAPI defined */ | |||||
#ifndef WINGDIAPI | |||||
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) | |||||
# define WINGDIAPI __declspec(dllimport) | |||||
# elif defined(__LCC__) | |||||
# define WINGDIAPI __stdcall | |||||
# else | |||||
# define WINGDIAPI extern | |||||
# endif | |||||
# define DGL_WINGDIAPI_DEFINED | |||||
#endif // WINGDIAPI | |||||
/* Some <GL/glu.h> files also need CALLBACK defined */ | |||||
#ifndef CALLBACK | |||||
# if defined(_MSC_VER) | |||||
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) | |||||
# define CALLBACK __stdcall | |||||
# else | |||||
# define CALLBACK | |||||
# endif | |||||
# else | |||||
# define CALLBACK __stdcall | |||||
# endif | |||||
# define DGL_CALLBACK_DEFINED | |||||
#endif // CALLBACK | |||||
/* Most GL/glu.h variants on Windows need wchar_t */ | |||||
#include <cstddef> | |||||
#endif // DISTRHO_OS_WINDOWS | |||||
// ----------------------------------------------------------------------- | |||||
// OpenGL includes | |||||
#ifdef DISTRHO_OS_MAC | |||||
# ifdef DGL_USE_OPENGL3 | |||||
# include <OpenGL/gl3.h> | |||||
# include <OpenGL/gl3ext.h> | |||||
# else | |||||
# include <OpenGL/gl.h> | |||||
# endif | |||||
#else | |||||
# ifndef DISTRHO_OS_WINDOWS | |||||
# define GL_GLEXT_PROTOTYPES | |||||
# endif | |||||
# ifndef __GLEW_H__ | |||||
# include <GL/gl.h> | |||||
# include <GL/glext.h> | |||||
# endif | |||||
#endif | |||||
// ----------------------------------------------------------------------- | |||||
// Missing OpenGL defines | |||||
#if defined(GL_BGR_EXT) && !defined(GL_BGR) | |||||
# define GL_BGR GL_BGR_EXT | |||||
#endif | |||||
#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) | |||||
# define GL_BGRA GL_BGRA_EXT | |||||
#endif | |||||
#ifndef GL_CLAMP_TO_BORDER | |||||
# define GL_CLAMP_TO_BORDER 0x812D | |||||
#endif | |||||
// ----------------------------------------------------------------------- | |||||
// Fix OpenGL includes for Windows, based on glfw code (part 2) | |||||
#ifdef DGL_CALLBACK_DEFINED | |||||
# undef CALLBACK | |||||
# undef DGL_CALLBACK_DEFINED | |||||
#endif | |||||
#ifdef DGL_WINGDIAPI_DEFINED | |||||
# undef WINGDIAPI | |||||
# undef DGL_WINGDIAPI_DEFINED | |||||
#endif | |||||
#include "OpenGL-include.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -120,6 +31,8 @@ START_NAMESPACE_DGL | |||||
*/ | */ | ||||
struct OpenGLGraphicsContext : GraphicsContext | struct OpenGLGraphicsContext : GraphicsContext | ||||
{ | { | ||||
#ifdef DGL_USE_OPENGL3 | |||||
#endif | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -238,11 +151,11 @@ public: | |||||
// FIXME this should not be needed | // FIXME this should not be needed | ||||
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) | inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) | ||||
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); }; | |||||
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); } | |||||
inline void draw(const GraphicsContext& context) | inline void draw(const GraphicsContext& context) | ||||
{ drawAt(context, Point<int>(0, 0)); }; | |||||
{ drawAt(context, Point<int>(0, 0)); } | |||||
inline void drawAt(const GraphicsContext& context, int x, int y) | inline void drawAt(const GraphicsContext& context, int x, int y) | ||||
{ drawAt(context, Point<int>(x, y)); }; | |||||
{ drawAt(context, Point<int>(x, y)); } | |||||
/** | /** | ||||
Constructor using raw image data, specifying an OpenGL image format. | Constructor using raw image data, specifying an OpenGL image format. | ||||
@@ -305,4 +218,4 @@ typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
#endif | |||||
#endif // DGL_OPENGL_HPP_INCLUDED |
@@ -71,6 +71,7 @@ public: | |||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) | bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) | ||||
{ return Window::addIdleCallback(callback, timerFrequencyInMs); } | { return Window::addIdleCallback(callback, timerFrequencyInMs); } | ||||
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | ||||
Application& getApp() const noexcept { return Window::getApp(); } | |||||
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | ||||
double getScaleFactor() const noexcept { return Window::getScaleFactor(); } | double getScaleFactor() const noexcept { return Window::getScaleFactor(); } | ||||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | ||||
@@ -47,7 +47,7 @@ public: | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
virtual ~SubWidget(); | |||||
~SubWidget() override; | |||||
/** | /** | ||||
Check if this widget contains the point defined by @a x and @a y. | Check if this widget contains the point defined by @a x and @a y. | ||||
@@ -54,7 +54,7 @@ public: | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
virtual ~TopLevelWidget(); | |||||
~TopLevelWidget() override; | |||||
/** | /** | ||||
Get the application associated with this top-level widget's window. | Get the application associated with this top-level widget's window. | ||||
@@ -101,8 +101,8 @@ public: | |||||
void repaint(const Rectangle<uint>& rect) noexcept; | void repaint(const Rectangle<uint>& rect) noexcept; | ||||
// TODO group stuff after here, convenience functions present in Window class | // TODO group stuff after here, convenience functions present in Window class | ||||
const void* getClipboard(size_t& dataSize); | |||||
bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | ||||
const void* getClipboard(const char*& mimeType, size_t& dataSize); | |||||
bool setCursor(MouseCursor cursor); | bool setCursor(MouseCursor cursor); | ||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | ||||
bool removeIdleCallback(IdleCallback* callback); | bool removeIdleCallback(IdleCallback* callback); | ||||
@@ -56,17 +56,16 @@ public: | |||||
/** | /** | ||||
Base event data. | Base event data. | ||||
These are the fields present on all Widget events. | These are the fields present on all Widget events. | ||||
@a mod Currently active keyboard modifiers, @see Modifier. | |||||
@a mod Event flags, @see EventFlag. | |||||
@a time Event timestamp (if any). | |||||
*/ | */ | ||||
struct BaseEvent { | struct BaseEvent { | ||||
/** Currently active keyboard modifiers. @see Modifier */ | |||||
uint mod; | uint mod; | ||||
/** Event flags. @see EventFlag */ | |||||
uint flags; | uint flags; | ||||
/** Event timestamp (if any). */ | |||||
uint time; | uint time; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | ||||
/** Destuctor */ | /** Destuctor */ | ||||
virtual ~BaseEvent() noexcept {} | virtual ~BaseEvent() noexcept {} | ||||
@@ -86,17 +85,17 @@ public: | |||||
Alternatively, the raw @a keycode can be used to work directly with physical keys, | Alternatively, the raw @a keycode can be used to work directly with physical keys, | ||||
but note that this value is not portable and differs between platforms and hardware. | but note that this value is not portable and differs between platforms and hardware. | ||||
@a press True if the key was pressed, false if released. | |||||
@a key Unicode point of the key pressed. | |||||
@a keycode Raw keycode. | |||||
@see onKeyboard | @see onKeyboard | ||||
*/ | */ | ||||
struct KeyboardEvent : BaseEvent { | struct KeyboardEvent : BaseEvent { | ||||
/** True if the key was pressed, false if released. */ | |||||
bool press; | bool press; | ||||
/** Unicode point of the key pressed. */ | |||||
uint key; | uint key; | ||||
/** Raw keycode. */ | |||||
uint keycode; | uint keycode; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
KeyboardEvent() noexcept | KeyboardEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
press(false), | press(false), | ||||
@@ -112,9 +111,9 @@ public: | |||||
*/ | */ | ||||
struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { | struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { | ||||
bool press; | bool press; | ||||
Key key; | |||||
Key key; | |||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
SpecialEvent() noexcept | SpecialEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
press(false), | press(false), | ||||
@@ -131,17 +130,17 @@ public: | |||||
so there is not necessarily a direct correspondence between text events and physical key presses. | so there is not necessarily a direct correspondence between text events and physical key presses. | ||||
For example, with some input methods a sequence of several key presses will generate a single character. | For example, with some input methods a sequence of several key presses will generate a single character. | ||||
@a keycode Raw key code. | |||||
@a character Unicode character code. | |||||
@a string UTF-8 string. | |||||
@see onCharacterInput | @see onCharacterInput | ||||
*/ | */ | ||||
struct CharacterInputEvent : BaseEvent { | struct CharacterInputEvent : BaseEvent { | ||||
/** Raw key code. */ | |||||
uint keycode; | uint keycode; | ||||
/** Unicode character code. */ | |||||
uint character; | uint character; | ||||
/** UTF-8 string. */ | |||||
char string[8]; | char string[8]; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
CharacterInputEvent() noexcept | CharacterInputEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
keycode(0), | keycode(0), | ||||
@@ -155,20 +154,19 @@ public: | |||||
/** | /** | ||||
Mouse press or release event. | Mouse press or release event. | ||||
@a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). | |||||
@a press True if the button was pressed, false if released. | |||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a absolutePos The absolute coordinates of the pointer. | |||||
@see onMouse | @see onMouse | ||||
*/ | */ | ||||
struct MouseEvent : BaseEvent { | struct MouseEvent : BaseEvent { | ||||
/** The button number starting from 1. @see MouseButton */ | |||||
uint button; | uint button; | ||||
/** True if the button was pressed, false if released. */ | |||||
bool press; | bool press; | ||||
/** The widget-relative coordinates of the pointer. */ | |||||
Point<double> pos; | Point<double> pos; | ||||
/** The absolute coordinates of the pointer. */ | |||||
Point<double> absolutePos; | Point<double> absolutePos; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
MouseEvent() noexcept | MouseEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
button(0), | button(0), | ||||
@@ -179,16 +177,15 @@ public: | |||||
/** | /** | ||||
Mouse motion event. | Mouse motion event. | ||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a absolutePos The absolute coordinates of the pointer. | |||||
@see onMotion | @see onMotion | ||||
*/ | */ | ||||
struct MotionEvent : BaseEvent { | struct MotionEvent : BaseEvent { | ||||
/** The widget-relative coordinates of the pointer. */ | |||||
Point<double> pos; | Point<double> pos; | ||||
/** The absolute coordinates of the pointer. */ | |||||
Point<double> absolutePos; | Point<double> absolutePos; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
MotionEvent() noexcept | MotionEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
pos(0.0, 0.0), | pos(0.0, 0.0), | ||||
@@ -204,19 +201,19 @@ public: | |||||
Some systems and devices support finer resolution and/or higher values for fast scrolls, | Some systems and devices support finer resolution and/or higher values for fast scrolls, | ||||
so programs should handle any value gracefully. | so programs should handle any value gracefully. | ||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a absolutePos The absolute coordinates of the pointer. | |||||
@a delta The scroll distance. | |||||
@a direction The direction of the scroll or "smooth". | |||||
@see onScroll | @see onScroll | ||||
*/ | */ | ||||
struct ScrollEvent : BaseEvent { | struct ScrollEvent : BaseEvent { | ||||
/** The widget-relative coordinates of the pointer. */ | |||||
Point<double> pos; | Point<double> pos; | ||||
/** The absolute coordinates of the pointer. */ | |||||
Point<double> absolutePos; | Point<double> absolutePos; | ||||
/** The scroll distance. */ | |||||
Point<double> delta; | Point<double> delta; | ||||
/** The direction of the scroll or "smooth". */ | |||||
ScrollDirection direction; | ScrollDirection direction; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
ScrollEvent() noexcept | ScrollEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
pos(0.0, 0.0), | pos(0.0, 0.0), | ||||
@@ -227,15 +224,15 @@ public: | |||||
/** | /** | ||||
Resize event. | Resize event. | ||||
@a size The new widget size. | |||||
@a oldSize The previous size, may be null. | |||||
@see onResize | @see onResize | ||||
*/ | */ | ||||
struct ResizeEvent { | struct ResizeEvent { | ||||
/** The new widget size. */ | |||||
Size<uint> size; | Size<uint> size; | ||||
/** The previous size, can be null. */ | |||||
Size<uint> oldSize; | Size<uint> oldSize; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
ResizeEvent() noexcept | ResizeEvent() noexcept | ||||
: size(0, 0), | : size(0, 0), | ||||
oldSize(0, 0) {} | oldSize(0, 0) {} | ||||
@@ -243,15 +240,15 @@ public: | |||||
/** | /** | ||||
Widget position changed event. | Widget position changed event. | ||||
@a pos The new absolute position of the widget. | |||||
@a oldPos The previous absolute position of the widget. | |||||
@see onPositionChanged | @see onPositionChanged | ||||
*/ | */ | ||||
struct PositionChangedEvent { | struct PositionChangedEvent { | ||||
/** The new absolute position of the widget. */ | |||||
Point<int> pos; | Point<int> pos; | ||||
/** The previous absolute position of the widget. */ | |||||
Point<int> oldPos; | Point<int> oldPos; | ||||
/** Constructor */ | |||||
/** Constructor for default/null values */ | |||||
PositionChangedEvent() noexcept | PositionChangedEvent() noexcept | ||||
: pos(0, 0), | : pos(0, 0), | ||||
oldPos(0, 0) {} | oldPos(0, 0) {} | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -20,13 +20,20 @@ | |||||
#include "Geometry.hpp" | #include "Geometry.hpp" | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
# include "../distrho/extra/FileBrowserDialog.hpp" | |||||
# include "FileBrowserDialog.hpp" | |||||
#endif | |||||
#include <vector> | |||||
#ifdef DISTRHO_NAMESPACE | |||||
START_NAMESPACE_DISTRHO | |||||
class PluginWindow; | |||||
END_NAMESPACE_DISTRHO | |||||
#endif | #endif | ||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
class Application; | class Application; | ||||
class PluginWindow; | |||||
class TopLevelWidget; | class TopLevelWidget; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -52,16 +59,11 @@ class TopLevelWidget; | |||||
... | ... | ||||
*/ | */ | ||||
class Window | |||||
class DISTRHO_API Window | |||||
{ | { | ||||
struct PrivateData; | struct PrivateData; | ||||
public: | public: | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; | |||||
typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; | |||||
#endif | |||||
/** | /** | ||||
Window graphics context as a scoped struct. | Window graphics context as a scoped struct. | ||||
This class gives graphics context drawing time to a window's widgets. | This class gives graphics context drawing time to a window's widgets. | ||||
@@ -206,6 +208,41 @@ public: | |||||
*/ | */ | ||||
void setResizable(bool resizable); | void setResizable(bool resizable); | ||||
/** | |||||
Get X offset, typically 0. | |||||
*/ | |||||
int getOffsetX() const noexcept; | |||||
/** | |||||
Get Y offset, typically 0. | |||||
*/ | |||||
int getOffsetY() const noexcept; | |||||
/** | |||||
Get offset. | |||||
*/ | |||||
Point<int> getOffset() const noexcept; | |||||
/** | |||||
Set X offset. | |||||
*/ | |||||
void setOffsetX(int x); | |||||
/** | |||||
Set Y offset. | |||||
*/ | |||||
void setOffsetY(int y); | |||||
/** | |||||
Set offset using @a x and @a y values. | |||||
*/ | |||||
void setOffset(int x, int y); | |||||
/** | |||||
Set offset. | |||||
*/ | |||||
void setOffset(const Point<int>& offset); | |||||
/** | /** | ||||
Get width. | Get width. | ||||
*/ | */ | ||||
@@ -263,6 +300,19 @@ public: | |||||
*/ | */ | ||||
void setIgnoringKeyRepeat(bool ignore) noexcept; | void setIgnoringKeyRepeat(bool ignore) noexcept; | ||||
/** | |||||
Get the clipboard contents. | |||||
This gets the system clipboard contents, | |||||
which may have been set with setClipboard() or copied from another application. | |||||
Returns the clipboard contents, or null. | |||||
@note By default only "text/plain" mimetype is supported and returned. | |||||
Override onClipboardDataOffer for supporting other types. | |||||
*/ | |||||
const void* getClipboard(size_t& dataSize); | |||||
/** | /** | ||||
Set the clipboard contents. | Set the clipboard contents. | ||||
@@ -274,16 +324,6 @@ public: | |||||
*/ | */ | ||||
bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | ||||
/** | |||||
Get the clipboard contents. | |||||
This gets the system clipboard contents, | |||||
which may have been set with setClipboard() or copied from another application. | |||||
returns the clipboard contents, or null. | |||||
*/ | |||||
const void* getClipboard(const char*& mimeType, size_t& dataSize); | |||||
/** | /** | ||||
Set the mouse cursor. | Set the mouse cursor. | ||||
@@ -360,7 +400,7 @@ public: | |||||
This function does not block the event loop. | This function does not block the event loop. | ||||
*/ | */ | ||||
bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); | |||||
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); | |||||
#endif | #endif | ||||
/** | /** | ||||
@@ -402,6 +442,14 @@ public: | |||||
bool automaticallyScale = false, | bool automaticallyScale = false, | ||||
bool resizeNowIfAutoScaling = true); | bool resizeNowIfAutoScaling = true); | ||||
/** | |||||
Set the transient parent of the window. | |||||
Set this for transient children like dialogs, to have them properly associated with their parent window. | |||||
This should be not be called for embed windows, or after making the window visible. | |||||
*/ | |||||
void setTransientParent(uintptr_t transientParentWindowHandle); | |||||
/** DEPRECATED Use isIgnoringKeyRepeat(). */ | /** DEPRECATED Use isIgnoringKeyRepeat(). */ | ||||
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") | DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") | ||||
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } | inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } | ||||
@@ -415,6 +463,23 @@ public: | |||||
inline void exec(bool blockWait = false) { runAsModal(blockWait); } | inline void exec(bool blockWait = false) { runAsModal(blockWait); } | ||||
protected: | protected: | ||||
/** | |||||
Get the types available for the data in a clipboard. | |||||
Must only be called within the context of onClipboardDataOffer. | |||||
*/ | |||||
std::vector<ClipboardDataOffer> getClipboardDataOfferTypes(); | |||||
/** | |||||
A function called when clipboard has data present, possibly with several datatypes. | |||||
While handling this event, the data types can be investigated with getClipboardDataOfferTypes() to decide whether to accept the offer. | |||||
Reimplement and return a non-zero id to accept the clipboard data offer for a particular type. | |||||
Applications must ignore any type they do not recognize. | |||||
The default implementation accepts the "text/plain" mimetype. | |||||
*/ | |||||
virtual uint32_t onClipboardDataOffer(); | |||||
/** | /** | ||||
A function called when the window is attempted to be closed. | A function called when the window is attempted to be closed. | ||||
Returning true closes the window, which is the default behaviour. | Returning true closes the window, which is the default behaviour. | ||||
@@ -464,8 +529,10 @@ protected: | |||||
private: | private: | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
friend class Application; | friend class Application; | ||||
friend class PluginWindow; | |||||
friend class TopLevelWidget; | friend class TopLevelWidget; | ||||
#ifdef DISTRHO_NAMESPACE | |||||
friend class DISTRHO_NAMESPACE::PluginWindow; | |||||
#endif | |||||
/** @internal */ | /** @internal */ | ||||
explicit Window(Application& app, | explicit Window(Application& app, | ||||
@@ -477,7 +544,7 @@ private: | |||||
bool isVST3, | bool isVST3, | ||||
bool doPostInit); | bool doPostInit); | ||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -16,10 +16,21 @@ | |||||
#include "ApplicationPrivateData.hpp" | #include "ApplicationPrivateData.hpp" | ||||
#ifdef __EMSCRIPTEN__ | |||||
# include <emscripten/emscripten.h> | |||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
#ifdef __EMSCRIPTEN__ | |||||
static void app_idle(void* const app) | |||||
{ | |||||
static_cast<Application*>(app)->idle(); | |||||
} | |||||
#endif | |||||
Application::Application(const bool isStandalone) | Application::Application(const bool isStandalone) | ||||
: pData(new PrivateData(isStandalone)) {} | : pData(new PrivateData(isStandalone)) {} | ||||
@@ -37,8 +48,12 @@ void Application::exec(const uint idleTimeInMs) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | ||||
#ifdef __EMSCRIPTEN__ | |||||
emscripten_set_main_loop_arg(app_idle, this, 0, true); | |||||
#else | |||||
while (! pData->isQuitting) | while (! pData->isQuitting) | ||||
pData->idle(idleTimeInMs); | pData->idle(idleTimeInMs); | ||||
#endif | |||||
} | } | ||||
void Application::quit() | void Application::quit() | ||||
@@ -45,6 +45,13 @@ static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
const char* Application::getClassName() const noexcept | |||||
{ | |||||
return puglGetClassName(pData->world); | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
Application::PrivateData::PrivateData(const bool standalone) | Application::PrivateData::PrivateData(const bool standalone) | ||||
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | ||||
standalone ? PUGL_WORLD_THREADS : 0x0)), | standalone ? PUGL_WORLD_THREADS : 0x0)), | ||||
@@ -60,11 +67,8 @@ Application::PrivateData::PrivateData(const bool standalone) | |||||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | ||||
puglSetWorldHandle(world, this); | puglSetWorldHandle(world, this); | ||||
#ifndef __EMSCRIPTEN__ | |||||
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | ||||
#ifdef DISTRHO_OS_MAC | |||||
if (standalone) | |||||
puglMacOSActivateApp(); | |||||
#endif | #endif | ||||
} | } | ||||
@@ -22,6 +22,9 @@ | |||||
#include <list> | #include <list> | ||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
# ifndef NOMINMAX | |||||
# define NOMINMAX | |||||
# endif | |||||
# include <winsock2.h> | # include <winsock2.h> | ||||
# include <windows.h> | # include <windows.h> | ||||
typedef HANDLE d_ThreadHandle; | typedef HANDLE d_ThreadHandle; | ||||
@@ -55,9 +55,9 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | ||||
# ifdef DGL_USE_NANOVG_FBO | # ifdef DGL_USE_NANOVG_FBO | ||||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | ||||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | ||||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||||
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | ||||
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | ||||
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | ||||
@@ -82,7 +82,9 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||||
// Include NanoVG OpenGL implementation | // Include NanoVG OpenGL implementation | ||||
//#define STB_IMAGE_STATIC | //#define STB_IMAGE_STATIC | ||||
#ifdef DGL_USE_OPENGL3 | |||||
#if defined(DGL_USE_GLES2) | |||||
# define NANOVG_GLES2_IMPLEMENTATION | |||||
#elif defined(DGL_USE_OPENGL3) | |||||
# define NANOVG_GL3_IMPLEMENTATION | # define NANOVG_GL3_IMPLEMENTATION | ||||
#else | #else | ||||
# define NANOVG_GL2_IMPLEMENTATION | # define NANOVG_GL2_IMPLEMENTATION | ||||
@@ -138,6 +140,11 @@ NVGcontext* nvgCreateGL(int flags) | |||||
# define DGL_EXT(PROC, func) \ | # define DGL_EXT(PROC, func) \ | ||||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | ||||
DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | ||||
# define DGL_EXT2(PROC, func, fallback) \ | |||||
if (needsInit) { \ | |||||
func = (PROC) wglGetProcAddress ( #func ); \ | |||||
if (func == nullptr) func = (PROC) wglGetProcAddress ( #fallback ); \ | |||||
} DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | |||||
DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | ||||
DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | ||||
DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) | DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) | ||||
@@ -167,16 +174,16 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | ||||
# ifdef DGL_USE_NANOVG_FBO | # ifdef DGL_USE_NANOVG_FBO | ||||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | |||||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | |||||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | ||||
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | |||||
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | |||||
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | |||||
DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) | |||||
DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) | |||||
DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) | |||||
DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) | |||||
DGL_EXT2(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer, glBindFramebufferEXT) | |||||
DGL_EXT2(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer, glBindRenderbufferEXT) | |||||
DGL_EXT2(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers, glDeleteFramebuffersEXT) | |||||
DGL_EXT2(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers, glDeleteRenderbuffersEXT) | |||||
DGL_EXT2(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D, glFramebufferTexture2DEXT) | |||||
DGL_EXT2(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer, glFramebufferRenderbufferEXT) | |||||
DGL_EXT2(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers, glGenFramebuffersEXT) | |||||
DGL_EXT2(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers, glGenRenderbuffersEXT) | |||||
DGL_EXT2(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage, glRenderbufferStorageEXT) | |||||
# endif | # endif | ||||
# ifdef DGL_USE_OPENGL3 | # ifdef DGL_USE_OPENGL3 | ||||
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) | DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) | ||||
@@ -188,6 +195,7 @@ DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) | |||||
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | ||||
# endif | # endif | ||||
# undef DGL_EXT | # undef DGL_EXT | ||||
# undef DGL_EXT2 | |||||
needsInit = false; | needsInit = false; | ||||
# if defined(__GNUC__) && (__GNUC__ >= 9) | # if defined(__GNUC__) && (__GNUC__ >= 9) | ||||
# pragma GCC diagnostic pop | # pragma GCC diagnostic pop | ||||
@@ -314,11 +322,14 @@ NanoVG::Paint::operator NVGpaint() const noexcept | |||||
NanoVG::NanoVG(int flags) | NanoVG::NanoVG(int flags) | ||||
: fContext(nvgCreateGL(flags)), | : fContext(nvgCreateGL(flags)), | ||||
fInFrame(false), | fInFrame(false), | ||||
fIsSubWidget(false) {} | |||||
fIsSubWidget(false) | |||||
{ | |||||
DISTRHO_CUSTOM_SAFE_ASSERT("Failed to create NanoVG context, expect a black screen", fContext != nullptr); | |||||
} | |||||
NanoVG::~NanoVG() | NanoVG::~NanoVG() | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT(! fInFrame); | |||||
DISTRHO_CUSTOM_SAFE_ASSERT("Destroying NanoVG context with still active frame", ! fInFrame); | |||||
if (fContext != nullptr && ! fIsSubWidget) | if (fContext != nullptr && ! fIsSubWidget) | ||||
nvgDeleteGL(fContext); | nvgDeleteGL(fContext); | ||||
@@ -35,11 +35,23 @@ START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
#ifdef DGL_USE_OPENGL3 | |||||
#if defined(DGL_USE_GLES2) | |||||
static void notImplemented(const char* const name) | |||||
{ | |||||
// d_stderr2("GLES2 function not implemented: %s", name); | |||||
} | |||||
#elif defined(DGL_USE_GLES3) | |||||
static void notImplemented(const char* const name) | |||||
{ | |||||
d_stderr2("GLES3 function not implemented: %s", name); | |||||
} | |||||
#elif defined(DGL_USE_OPENGL3) | |||||
static void notImplemented(const char* const name) | static void notImplemented(const char* const name) | ||||
{ | { | ||||
d_stderr2("OpenGL3 function not implemented: %s", name); | d_stderr2("OpenGL3 function not implemented: %s", name); | ||||
} | } | ||||
#else | |||||
# define DGL_USE_COMPAT_OPENGL | |||||
#endif | #endif | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -47,7 +59,7 @@ static void notImplemented(const char* const name) | |||||
void Color::setFor(const GraphicsContext&, const bool includeAlpha) | void Color::setFor(const GraphicsContext&, const bool includeAlpha) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
if (includeAlpha) | if (includeAlpha) | ||||
glColor4f(red, green, blue, alpha); | glColor4f(red, green, blue, alpha); | ||||
else | else | ||||
@@ -62,7 +74,7 @@ void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Line | // Line | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
template<typename T> | template<typename T> | ||||
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | ||||
{ | { | ||||
@@ -82,7 +94,7 @@ static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||||
template<typename T> | template<typename T> | ||||
void Line<T>::draw(const GraphicsContext&, const T width) | void Line<T>::draw(const GraphicsContext&, const T width) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | ||||
glLineWidth(static_cast<GLfloat>(width)); | glLineWidth(static_cast<GLfloat>(width)); | ||||
@@ -96,7 +108,7 @@ void Line<T>::draw(const GraphicsContext&, const T width) | |||||
template<typename T> | template<typename T> | ||||
void Line<T>::draw() | void Line<T>::draw() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawLine<T>(posStart, posEnd); | drawLine<T>(posStart, posEnd); | ||||
#else | #else | ||||
notImplemented("Line::draw"); | notImplemented("Line::draw"); | ||||
@@ -113,7 +125,7 @@ template class Line<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Circle | // Circle | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
template<typename T> | template<typename T> | ||||
static void drawCircle(const Point<T>& pos, | static void drawCircle(const Point<T>& pos, | ||||
const uint numSegments, | const uint numSegments, | ||||
@@ -146,7 +158,7 @@ static void drawCircle(const Point<T>& pos, | |||||
template<typename T> | template<typename T> | ||||
void Circle<T>::draw(const GraphicsContext&) | void Circle<T>::draw(const GraphicsContext&) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | ||||
#else | #else | ||||
notImplemented("Circle::draw"); | notImplemented("Circle::draw"); | ||||
@@ -159,7 +171,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | ||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | glLineWidth(static_cast<GLfloat>(lineWidth)); | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | ||||
#else | #else | ||||
notImplemented("Circle::drawOutline"); | notImplemented("Circle::drawOutline"); | ||||
@@ -170,7 +182,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
template<typename T> | template<typename T> | ||||
void Circle<T>::draw() | void Circle<T>::draw() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | ||||
#else | #else | ||||
notImplemented("Circle::draw"); | notImplemented("Circle::draw"); | ||||
@@ -180,7 +192,7 @@ void Circle<T>::draw() | |||||
template<typename T> | template<typename T> | ||||
void Circle<T>::drawOutline() | void Circle<T>::drawOutline() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | ||||
#else | #else | ||||
notImplemented("Circle::drawOutline"); | notImplemented("Circle::drawOutline"); | ||||
@@ -197,7 +209,7 @@ template class Circle<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Triangle | // Triangle | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
template<typename T> | template<typename T> | ||||
static void drawTriangle(const Point<T>& pos1, | static void drawTriangle(const Point<T>& pos1, | ||||
const Point<T>& pos2, | const Point<T>& pos2, | ||||
@@ -221,7 +233,7 @@ static void drawTriangle(const Point<T>& pos1, | |||||
template<typename T> | template<typename T> | ||||
void Triangle<T>::draw(const GraphicsContext&) | void Triangle<T>::draw(const GraphicsContext&) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawTriangle<T>(pos1, pos2, pos3, false); | drawTriangle<T>(pos1, pos2, pos3, false); | ||||
#else | #else | ||||
notImplemented("Triangle::draw"); | notImplemented("Triangle::draw"); | ||||
@@ -234,7 +246,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | ||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | glLineWidth(static_cast<GLfloat>(lineWidth)); | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawTriangle<T>(pos1, pos2, pos3, true); | drawTriangle<T>(pos1, pos2, pos3, true); | ||||
#else | #else | ||||
notImplemented("Triangle::drawOutline"); | notImplemented("Triangle::drawOutline"); | ||||
@@ -245,7 +257,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
template<typename T> | template<typename T> | ||||
void Triangle<T>::draw() | void Triangle<T>::draw() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawTriangle<T>(pos1, pos2, pos3, false); | drawTriangle<T>(pos1, pos2, pos3, false); | ||||
#else | #else | ||||
notImplemented("Triangle::draw"); | notImplemented("Triangle::draw"); | ||||
@@ -255,7 +267,7 @@ void Triangle<T>::draw() | |||||
template<typename T> | template<typename T> | ||||
void Triangle<T>::drawOutline() | void Triangle<T>::drawOutline() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawTriangle<T>(pos1, pos2, pos3, true); | drawTriangle<T>(pos1, pos2, pos3, true); | ||||
#else | #else | ||||
notImplemented("Triangle::drawOutline"); | notImplemented("Triangle::drawOutline"); | ||||
@@ -272,7 +284,7 @@ template class Triangle<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Rectangle | // Rectangle | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
template<typename T> | template<typename T> | ||||
static void drawRectangle(const Rectangle<T>& rect, const bool outline) | static void drawRectangle(const Rectangle<T>& rect, const bool outline) | ||||
{ | { | ||||
@@ -306,7 +318,7 @@ static void drawRectangle(const Rectangle<T>& rect, const bool outline) | |||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::draw(const GraphicsContext&) | void Rectangle<T>::draw(const GraphicsContext&) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawRectangle<T>(*this, false); | drawRectangle<T>(*this, false); | ||||
#else | #else | ||||
notImplemented("Rectangle::draw"); | notImplemented("Rectangle::draw"); | ||||
@@ -319,7 +331,7 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | ||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | glLineWidth(static_cast<GLfloat>(lineWidth)); | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawRectangle<T>(*this, true); | drawRectangle<T>(*this, true); | ||||
#else | #else | ||||
notImplemented("Rectangle::drawOutline"); | notImplemented("Rectangle::drawOutline"); | ||||
@@ -330,7 +342,7 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::draw() | void Rectangle<T>::draw() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawRectangle<T>(*this, false); | drawRectangle<T>(*this, false); | ||||
#else | #else | ||||
notImplemented("Rectangle::draw"); | notImplemented("Rectangle::draw"); | ||||
@@ -340,7 +352,7 @@ void Rectangle<T>::draw() | |||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::drawOutline() | void Rectangle<T>::drawOutline() | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
drawRectangle<T>(*this, true); | drawRectangle<T>(*this, true); | ||||
#else | #else | ||||
notImplemented("Rectangle::drawOutline"); | notImplemented("Rectangle::drawOutline"); | ||||
@@ -395,14 +407,14 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, con | |||||
setupCalled = true; | setupCalled = true; | ||||
} | } | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | ||||
#endif | #endif | ||||
glEnable(GL_TEXTURE_2D); | glEnable(GL_TEXTURE_2D); | ||||
glBindTexture(GL_TEXTURE_2D, textureId); | glBindTexture(GL_TEXTURE_2D, textureId); | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
glBegin(GL_QUADS); | glBegin(GL_QUADS); | ||||
{ | { | ||||
@@ -616,21 +628,21 @@ void ImageBaseKnob<OpenGLImage>::onDisplay() | |||||
if (pData->rotationAngle != 0) | if (pData->rotationAngle != 0) | ||||
{ | { | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
glPushMatrix(); | glPushMatrix(); | ||||
#endif | #endif | ||||
const int w2 = w/2; | const int w2 = w/2; | ||||
const int h2 = h/2; | const int h2 = h/2; | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | ||||
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | ||||
#endif | #endif | ||||
Rectangle<int>(-w2, -h2, w, h).draw(context); | Rectangle<int>(-w2, -h2, w, h).draw(context); | ||||
#ifndef DGL_USE_OPENGL3 | |||||
#ifdef DGL_USE_COMPAT_OPENGL | |||||
glPopMatrix(); | glPopMatrix(); | ||||
#endif | #endif | ||||
} | } | ||||
@@ -60,14 +60,14 @@ void TopLevelWidget::setSize(const Size<uint>& size) | |||||
pData->window.setSize(size); | pData->window.setSize(size); | ||||
} | } | ||||
bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) | |||||
const void* TopLevelWidget::getClipboard(size_t& dataSize) | |||||
{ | { | ||||
return pData->window.setClipboard(mimeType, data, dataSize); | |||||
return pData->window.getClipboard(dataSize); | |||||
} | } | ||||
const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize) | |||||
bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) | |||||
{ | { | ||||
return pData->window.getClipboard(mimeType, dataSize); | |||||
return pData->window.setClipboard(mimeType, data, dataSize); | |||||
} | } | ||||
bool TopLevelWidget::setCursor(const MouseCursor cursor) | bool TopLevelWidget::setCursor(const MouseCursor cursor) | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -155,6 +155,48 @@ void Window::setResizable(const bool resizable) | |||||
pData->setResizable(resizable); | pData->setResizable(resizable); | ||||
} | } | ||||
int Window::getOffsetX() const noexcept | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||||
return puglGetFrame(pData->view).x; | |||||
} | |||||
int Window::getOffsetY() const noexcept | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||||
return puglGetFrame(pData->view).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); | |||||
} | |||||
void Window::setOffsetX(const int x) | |||||
{ | |||||
setOffset(x, getOffsetY()); | |||||
} | |||||
void Window::setOffsetY(const int y) | |||||
{ | |||||
setOffset(getOffsetX(), y); | |||||
} | |||||
void Window::setOffset(const int x, const int y) | |||||
{ | |||||
puglSetPosition(pData->view, x, y); | |||||
} | |||||
void Window::setOffset(const Point<int>& offset) | |||||
{ | |||||
setOffset(offset.getX(), offset.getY()); | |||||
} | |||||
uint Window::getWidth() const noexcept | uint Window::getWidth() const noexcept | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | ||||
@@ -247,7 +289,7 @@ void Window::setSize(uint width, uint height) | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
puglSetWindowSize(pData->view, width, height); | |||||
puglSetSizeAndDefault(pData->view, width, height); | |||||
} | } | ||||
} | } | ||||
@@ -277,18 +319,14 @@ void Window::setIgnoringKeyRepeat(const bool ignore) noexcept | |||||
puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); | puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); | ||||
} | } | ||||
bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) | |||||
const void* Window::getClipboard(size_t& dataSize) | |||||
{ | { | ||||
return puglSetClipboard(pData->view, mimeType, data, dataSize) == PUGL_SUCCESS; | |||||
return pData->getClipboard(dataSize); | |||||
} | } | ||||
const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) | |||||
bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(!pData->ignoreEvents, nullptr); | |||||
pData->ignoreEvents = true; | |||||
const void* const clipboard = puglGetClipboard(pData->view, &mimeType, &dataSize); | |||||
pData->ignoreEvents = false; | |||||
return clipboard; | |||||
return puglSetClipboard(pData->view, mimeType != nullptr ? mimeType : "text/plain", data, dataSize) == PUGL_SUCCESS; | |||||
} | } | ||||
bool Window::setCursor(const MouseCursor cursor) | bool Window::setCursor(const MouseCursor cursor) | ||||
@@ -324,7 +362,7 @@ const GraphicsContext& Window::getGraphicsContext() const noexcept | |||||
uintptr_t Window::getNativeWindowHandle() const noexcept | uintptr_t Window::getNativeWindowHandle() const noexcept | ||||
{ | { | ||||
return puglGetNativeWindow(pData->view); | |||||
return puglGetNativeView(pData->view); | |||||
} | } | ||||
double Window::getScaleFactor() const noexcept | double Window::getScaleFactor() const noexcept | ||||
@@ -358,10 +396,10 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||||
return; | return; | ||||
PuglRect prect = { | PuglRect prect = { | ||||
static_cast<double>(rect.getX()), | |||||
static_cast<double>(rect.getY()), | |||||
static_cast<double>(rect.getWidth()), | |||||
static_cast<double>(rect.getHeight()), | |||||
static_cast<PuglCoord>(rect.getX()), | |||||
static_cast<PuglCoord>(rect.getY()), | |||||
static_cast<PuglSpan>(rect.getWidth()), | |||||
static_cast<PuglSpan>(rect.getHeight()), | |||||
}; | }; | ||||
if (pData->autoScaling) | if (pData->autoScaling) | ||||
{ | { | ||||
@@ -427,6 +465,43 @@ void Window::setGeometryConstraints(uint minimumWidth, | |||||
} | } | ||||
} | } | ||||
void Window::setTransientParent(const uintptr_t transientParentWindowHandle) | |||||
{ | |||||
puglSetTransientParent(pData->view, transientParentWindowHandle); | |||||
} | |||||
std::vector<ClipboardDataOffer> Window::getClipboardDataOfferTypes() | |||||
{ | |||||
std::vector<ClipboardDataOffer> offerTypes; | |||||
if (const uint32_t numTypes = puglGetNumClipboardTypes(pData->view)) | |||||
{ | |||||
offerTypes.reserve(numTypes); | |||||
for (uint32_t i=0; i<numTypes; ++i) | |||||
{ | |||||
const ClipboardDataOffer offer = { i + 1, puglGetClipboardType(pData->view, i) }; | |||||
offerTypes.push_back(offer); | |||||
} | |||||
} | |||||
return offerTypes; | |||||
} | |||||
uint32_t Window::onClipboardDataOffer() | |||||
{ | |||||
std::vector<ClipboardDataOffer> offers(getClipboardDataOfferTypes()); | |||||
for (std::vector<ClipboardDataOffer>::iterator it=offers.begin(), end=offers.end(); it != end;++it) | |||||
{ | |||||
const ClipboardDataOffer offer = *it; | |||||
if (std::strcmp(offer.type, "text/plain") == 0) | |||||
return offer.id; | |||||
} | |||||
return 0; | |||||
} | |||||
bool Window::onClose() | bool Window::onClose() | ||||
{ | { | ||||
return true; | return true; | ||||
@@ -451,13 +526,6 @@ void Window::onFileSelected(const char*) | |||||
} | } | ||||
#endif | #endif | ||||
#if 0 | |||||
void Window::setTransientWinId(const uintptr_t winId) | |||||
{ | |||||
puglSetTransientFor(pData->view, winId); | |||||
} | |||||
#endif | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -52,39 +52,66 @@ START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
static double getDesktopScaleFactor(const PuglView* const view) | |||||
static double getScaleFactorFromParent(const PuglView* const view) | |||||
{ | { | ||||
// allow custom scale for testing | // allow custom scale for testing | ||||
if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | ||||
return std::max(1.0, std::atof(scale)); | return std::max(1.0, std::atof(scale)); | ||||
if (view != nullptr) | if (view != nullptr) | ||||
return puglGetDesktopScaleFactor(view); | |||||
return puglGetScaleFactorFromParent(view); | |||||
return 1.0; | return 1.0; | ||||
} | } | ||||
static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); | |||||
if (PuglView* const view = puglNewView(world)) | |||||
{ | |||||
puglSetTransientParent(view, puglGetNativeView(transientParentView)); | |||||
return view; | |||||
} | |||||
return nullptr; | |||||
} | |||||
static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); | |||||
if (PuglView* const view = puglNewView(world)) | |||||
{ | |||||
puglSetParentWindow(view, parentWindowHandle); | |||||
return view; | |||||
} | |||||
return nullptr; | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
Window::PrivateData::PrivateData(Application& a, Window* const s) | Window::PrivateData::PrivateData(Application& a, Window* const s) | ||||
: app(a), | : app(a), | ||||
appData(a.pData), | appData(a.pData), | ||||
self(s), | self(s), | ||||
view(puglNewView(appData->world)), | |||||
transientParentView(nullptr), | |||||
view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), | |||||
topLevelWidgets(), | topLevelWidgets(), | ||||
isClosed(true), | isClosed(true), | ||||
isVisible(false), | isVisible(false), | ||||
isEmbed(false), | isEmbed(false), | ||||
usesSizeRequest(false), | usesSizeRequest(false), | ||||
scaleFactor(getDesktopScaleFactor(view)), | |||||
scaleFactor(getScaleFactorFromParent(view)), | |||||
autoScaling(false), | autoScaling(false), | ||||
autoScaleFactor(1.0), | autoScaleFactor(1.0), | ||||
minWidth(0), | minWidth(0), | ||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
ignoreEvents(false), | |||||
waitingForClipboardData(false), | |||||
waitingForClipboardEvents(false), | |||||
clipboardTypeId(0), | |||||
filenameToRenderInto(nullptr), | filenameToRenderInto(nullptr), | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
fileBrowserHandle(nullptr), | fileBrowserHandle(nullptr), | ||||
@@ -98,8 +125,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||||
: app(a), | : app(a), | ||||
appData(a.pData), | appData(a.pData), | ||||
self(s), | self(s), | ||||
view(puglNewView(appData->world)), | |||||
transientParentView(ppData->view), | |||||
view(puglNewViewWithTransientParent(appData->world, ppData->view)), | |||||
topLevelWidgets(), | topLevelWidgets(), | ||||
isClosed(true), | isClosed(true), | ||||
isVisible(false), | isVisible(false), | ||||
@@ -112,15 +138,15 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
ignoreEvents(false), | |||||
waitingForClipboardData(false), | |||||
waitingForClipboardEvents(false), | |||||
clipboardTypeId(0), | |||||
filenameToRenderInto(nullptr), | filenameToRenderInto(nullptr), | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
fileBrowserHandle(nullptr), | fileBrowserHandle(nullptr), | ||||
#endif | #endif | ||||
modal(ppData) | modal(ppData) | ||||
{ | { | ||||
puglSetTransientFor(view, puglGetNativeWindow(transientParentView)); | |||||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | ||||
} | } | ||||
@@ -130,30 +156,28 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
: app(a), | : app(a), | ||||
appData(a.pData), | appData(a.pData), | ||||
self(s), | self(s), | ||||
view(puglNewView(appData->world)), | |||||
transientParentView(nullptr), | |||||
view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |||||
topLevelWidgets(), | topLevelWidgets(), | ||||
isClosed(parentWindowHandle == 0), | isClosed(parentWindowHandle == 0), | ||||
isVisible(parentWindowHandle != 0), | isVisible(parentWindowHandle != 0), | ||||
isEmbed(parentWindowHandle != 0), | isEmbed(parentWindowHandle != 0), | ||||
usesSizeRequest(false), | usesSizeRequest(false), | ||||
scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), | |||||
scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |||||
autoScaling(false), | autoScaling(false), | ||||
autoScaleFactor(1.0), | autoScaleFactor(1.0), | ||||
minWidth(0), | minWidth(0), | ||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
ignoreEvents(false), | |||||
waitingForClipboardData(false), | |||||
waitingForClipboardEvents(false), | |||||
clipboardTypeId(0), | |||||
filenameToRenderInto(nullptr), | filenameToRenderInto(nullptr), | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
fileBrowserHandle(nullptr), | fileBrowserHandle(nullptr), | ||||
#endif | #endif | ||||
modal() | modal() | ||||
{ | { | ||||
if (isEmbed) | |||||
puglSetParentWindow(view, parentWindowHandle); | |||||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); | initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); | ||||
} | } | ||||
@@ -164,21 +188,22 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
: app(a), | : app(a), | ||||
appData(a.pData), | appData(a.pData), | ||||
self(s), | self(s), | ||||
view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), | |||||
transientParentView(nullptr), | |||||
view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |||||
topLevelWidgets(), | topLevelWidgets(), | ||||
isClosed(parentWindowHandle == 0), | isClosed(parentWindowHandle == 0), | ||||
isVisible(parentWindowHandle != 0 && view != nullptr), | isVisible(parentWindowHandle != 0 && view != nullptr), | ||||
isEmbed(parentWindowHandle != 0), | isEmbed(parentWindowHandle != 0), | ||||
usesSizeRequest(isVST3), | usesSizeRequest(isVST3), | ||||
scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), | |||||
scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |||||
autoScaling(false), | autoScaling(false), | ||||
autoScaleFactor(1.0), | autoScaleFactor(1.0), | ||||
minWidth(0), | minWidth(0), | ||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
ignoreEvents(false), | |||||
waitingForClipboardData(false), | |||||
waitingForClipboardEvents(false), | |||||
clipboardTypeId(0), | |||||
filenameToRenderInto(nullptr), | filenameToRenderInto(nullptr), | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
fileBrowserHandle(nullptr), | fileBrowserHandle(nullptr), | ||||
@@ -230,11 +255,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||||
} | } | ||||
puglSetMatchingBackendForCurrentBuild(view); | puglSetMatchingBackendForCurrentBuild(view); | ||||
puglClearMinSize(view); | |||||
puglSetWindowSize(view, width, height); | |||||
puglSetHandle(view, this); | puglSetHandle(view, this); | ||||
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | ||||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); | puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); | ||||
#if DGL_USE_RGBA | #if DGL_USE_RGBA | ||||
@@ -243,12 +265,23 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||||
puglSetViewHint(view, PUGL_DEPTH_BITS, 16); | puglSetViewHint(view, PUGL_DEPTH_BITS, 16); | ||||
#endif | #endif | ||||
puglSetViewHint(view, PUGL_STENCIL_BITS, 8); | puglSetViewHint(view, PUGL_STENCIL_BITS, 8); | ||||
#ifdef DGL_USE_OPENGL3 | |||||
#if defined(DGL_USE_OPENGL3) || defined(DGL_USE_GLES3) | |||||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | ||||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | ||||
#elif defined(DGL_USE_GLES2) | |||||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | |||||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||||
#else | |||||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_TRUE); | |||||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||||
#endif | #endif | ||||
// PUGL_SAMPLES ?? | // PUGL_SAMPLES ?? | ||||
puglSetEventFunc(view, puglEventCallback); | puglSetEventFunc(view, puglEventCallback); | ||||
// setting default size triggers system-level calls, do it last | |||||
puglSetSizeHint(view, PUGL_DEFAULT_SIZE, width, height); | |||||
} | } | ||||
bool Window::PrivateData::initPost() | bool Window::PrivateData::initPost() | ||||
@@ -314,8 +347,8 @@ void Window::PrivateData::show() | |||||
appData->oneWindowShown(); | appData->oneWindowShown(); | ||||
// FIXME | // FIXME | ||||
PuglRect rect = puglGetFrame(view); | |||||
puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||||
// PuglRect rect = puglGetFrame(view); | |||||
// puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
puglWin32ShowCentered(view); | puglWin32ShowCentered(view); | ||||
@@ -378,11 +411,7 @@ void Window::PrivateData::focus() | |||||
if (! isEmbed) | if (! isEmbed) | ||||
puglRaiseWindow(view); | puglRaiseWindow(view); | ||||
#ifdef HAVE_X11 | |||||
puglX11GrabFocus(view); | |||||
#else | |||||
puglGrabFocus(view); | puglGrabFocus(view); | ||||
#endif | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -393,10 +422,7 @@ void Window::PrivateData::setResizable(const bool resizable) | |||||
DGL_DBG("Window setResizable called\n"); | DGL_DBG("Window setResizable called\n"); | ||||
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
puglWin32SetWindowResizable(view, resizable); | |||||
#endif | |||||
puglSetResizable(view, resizable); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -460,7 +486,7 @@ bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options) | |||||
options2.title = puglGetWindowTitle(view); | options2.title = puglGetWindowTitle(view); | ||||
fileBrowserHandle = fileBrowserCreate(isEmbed, | fileBrowserHandle = fileBrowserCreate(isEmbed, | ||||
puglGetNativeWindow(view), | |||||
puglGetNativeView(view), | |||||
autoScaling ? autoScaleFactor : scaleFactor, | autoScaling ? autoScaleFactor : scaleFactor, | ||||
options2); | options2); | ||||
@@ -588,7 +614,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh | |||||
void Window::PrivateData::onPuglExpose() | void Window::PrivateData::onPuglExpose() | ||||
{ | { | ||||
DGL_DBGp("PUGL: onPuglExpose\n"); | |||||
DGL_DBG("PUGL: onPuglExpose\n"); | |||||
puglOnDisplayPrepare(view); | puglOnDisplayPrepare(view); | ||||
@@ -744,6 +770,78 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||||
#endif | #endif | ||||
} | } | ||||
const void* Window::PrivateData::getClipboard(size_t& dataSize) | |||||
{ | |||||
clipboardTypeId = 0; | |||||
waitingForClipboardData = true, | |||||
waitingForClipboardEvents = true; | |||||
// begin clipboard dance here | |||||
if (puglPaste(view) != PUGL_SUCCESS) | |||||
{ | |||||
dataSize = 0; | |||||
waitingForClipboardEvents = false; | |||||
return nullptr; | |||||
} | |||||
#ifdef DGL_USING_X11 | |||||
// wait for type request, clipboardTypeId must be != 0 to be valid | |||||
int retry = static_cast<int>(2 / 0.03); | |||||
while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0) | |||||
{ | |||||
if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |||||
break; | |||||
} | |||||
#endif | |||||
if (clipboardTypeId == 0) | |||||
{ | |||||
dataSize = 0; | |||||
waitingForClipboardEvents = false; | |||||
return nullptr; | |||||
} | |||||
#ifdef DGL_USING_X11 | |||||
// wait for actual data (assumes offer was accepted) | |||||
retry = static_cast<int>(2 / 0.03); | |||||
while (waitingForClipboardData && --retry >= 0) | |||||
{ | |||||
if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |||||
break; | |||||
} | |||||
#endif | |||||
if (clipboardTypeId == 0) | |||||
{ | |||||
dataSize = 0; | |||||
waitingForClipboardEvents = false; | |||||
return nullptr; | |||||
} | |||||
waitingForClipboardEvents = false; | |||||
return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); | |||||
} | |||||
uint32_t Window::PrivateData::onClipboardDataOffer() | |||||
{ | |||||
DGL_DBG("onClipboardDataOffer\n"); | |||||
if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) | |||||
return clipboardTypeId; | |||||
// stop waiting for data, it was rejected | |||||
waitingForClipboardData = false; | |||||
return 0; | |||||
} | |||||
void Window::PrivateData::onClipboardData(const uint32_t typeId) | |||||
{ | |||||
if (clipboardTypeId != typeId) | |||||
clipboardTypeId = 0; | |||||
waitingForClipboardData = false; | |||||
} | |||||
#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | ||||
static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); | static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); | ||||
#endif | #endif | ||||
@@ -757,6 +855,36 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
} | } | ||||
#endif | #endif | ||||
if (pData->waitingForClipboardEvents) | |||||
{ | |||||
switch (event->type) | |||||
{ | |||||
case PUGL_UPDATE: | |||||
case PUGL_EXPOSE: | |||||
case PUGL_FOCUS_IN: | |||||
case PUGL_FOCUS_OUT: | |||||
case PUGL_KEY_PRESS: | |||||
case PUGL_KEY_RELEASE: | |||||
case PUGL_TEXT: | |||||
case PUGL_POINTER_IN: | |||||
case PUGL_POINTER_OUT: | |||||
case PUGL_BUTTON_PRESS: | |||||
case PUGL_BUTTON_RELEASE: | |||||
case PUGL_MOTION: | |||||
case PUGL_SCROLL: | |||||
case PUGL_TIMER: | |||||
case PUGL_LOOP_ENTER: | |||||
case PUGL_LOOP_LEAVE: | |||||
return PUGL_SUCCESS; | |||||
case PUGL_DATA_OFFER: | |||||
case PUGL_DATA: | |||||
break; | |||||
default: | |||||
d_stdout("Got event %d while waitingForClipboardEvents", event->type); | |||||
break; | |||||
} | |||||
} | |||||
switch (event->type) | switch (event->type) | ||||
{ | { | ||||
///< No event | ///< No event | ||||
@@ -765,10 +893,10 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< View created, a #PuglEventCreate | ///< View created, a #PuglEventCreate | ||||
case PUGL_CREATE: | case PUGL_CREATE: | ||||
#ifdef HAVE_X11 | |||||
#ifdef DGL_USING_X11 | |||||
if (! pData->isEmbed) | if (! pData->isEmbed) | ||||
puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | ||||
#endif | |||||
#endif | |||||
break; | break; | ||||
///< View destroyed, a #PuglEventDestroy | ///< View destroyed, a #PuglEventDestroy | ||||
@@ -795,8 +923,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< View must be drawn, a #PuglEventExpose | ///< View must be drawn, a #PuglEventExpose | ||||
case PUGL_EXPOSE: | case PUGL_EXPOSE: | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
// unused x, y, width, height (double) | // unused x, y, width, height (double) | ||||
pData->onPuglExpose(); | pData->onPuglExpose(); | ||||
break; | break; | ||||
@@ -810,8 +936,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
case PUGL_FOCUS_IN: | case PUGL_FOCUS_IN: | ||||
///< Keyboard focus left view, a #PuglEventFocus | ///< Keyboard focus left view, a #PuglEventFocus | ||||
case PUGL_FOCUS_OUT: | case PUGL_FOCUS_OUT: | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
pData->onPuglFocus(event->type == PUGL_FOCUS_IN, | pData->onPuglFocus(event->type == PUGL_FOCUS_IN, | ||||
static_cast<CrossingMode>(event->focus.mode)); | static_cast<CrossingMode>(event->focus.mode)); | ||||
break; | break; | ||||
@@ -821,8 +945,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Key released, a #PuglEventKey | ///< Key released, a #PuglEventKey | ||||
case PUGL_KEY_RELEASE: | case PUGL_KEY_RELEASE: | ||||
{ | { | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
// unused x, y, xRoot, yRoot (double) | // unused x, y, xRoot, yRoot (double) | ||||
Widget::KeyboardEvent ev; | Widget::KeyboardEvent ev; | ||||
ev.mod = event->key.state; | ev.mod = event->key.state; | ||||
@@ -846,8 +968,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Character entered, a #PuglEventText | ///< Character entered, a #PuglEventText | ||||
case PUGL_TEXT: | case PUGL_TEXT: | ||||
{ | { | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
// unused x, y, xRoot, yRoot (double) | // unused x, y, xRoot, yRoot (double) | ||||
Widget::CharacterInputEvent ev; | Widget::CharacterInputEvent ev; | ||||
ev.mod = event->text.state; | ev.mod = event->text.state; | ||||
@@ -872,13 +992,11 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Mouse button released, a #PuglEventButton | ///< Mouse button released, a #PuglEventButton | ||||
case PUGL_BUTTON_RELEASE: | case PUGL_BUTTON_RELEASE: | ||||
{ | { | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
Widget::MouseEvent ev; | Widget::MouseEvent ev; | ||||
ev.mod = event->button.state; | ev.mod = event->button.state; | ||||
ev.flags = event->button.flags; | ev.flags = event->button.flags; | ||||
ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5); | ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5); | ||||
ev.button = event->button.button; | |||||
ev.button = event->button.button + 1; | |||||
ev.press = event->type == PUGL_BUTTON_PRESS; | ev.press = event->type == PUGL_BUTTON_PRESS; | ||||
ev.pos = Point<double>(event->button.x, event->button.y); | ev.pos = Point<double>(event->button.x, event->button.y); | ||||
ev.absolutePos = ev.pos; | ev.absolutePos = ev.pos; | ||||
@@ -889,8 +1007,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Pointer moved, a #PuglEventMotion | ///< Pointer moved, a #PuglEventMotion | ||||
case PUGL_MOTION: | case PUGL_MOTION: | ||||
{ | { | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
Widget::MotionEvent ev; | Widget::MotionEvent ev; | ||||
ev.mod = event->motion.state; | ev.mod = event->motion.state; | ||||
ev.flags = event->motion.flags; | ev.flags = event->motion.flags; | ||||
@@ -904,8 +1020,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Scrolled, a #PuglEventScroll | ///< Scrolled, a #PuglEventScroll | ||||
case PUGL_SCROLL: | case PUGL_SCROLL: | ||||
{ | { | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
Widget::ScrollEvent ev; | Widget::ScrollEvent ev; | ||||
ev.mod = event->scroll.state; | ev.mod = event->scroll.state; | ||||
ev.flags = event->scroll.flags; | ev.flags = event->scroll.flags; | ||||
@@ -924,8 +1038,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Timer triggered, a #PuglEventTimer | ///< Timer triggered, a #PuglEventTimer | ||||
case PUGL_TIMER: | case PUGL_TIMER: | ||||
if (pData->ignoreEvents) | |||||
break; | |||||
if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id)) | if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id)) | ||||
idleCallback->idleCallback(); | idleCallback->idleCallback(); | ||||
break; | break; | ||||
@@ -937,6 +1049,17 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||||
///< Recursive loop left, a #PuglEventLoopLeave | ///< Recursive loop left, a #PuglEventLoopLeave | ||||
case PUGL_LOOP_LEAVE: | case PUGL_LOOP_LEAVE: | ||||
break; | break; | ||||
///< Data offered from clipboard, a #PuglDataOfferEvent | |||||
case PUGL_DATA_OFFER: | |||||
if (const uint32_t offerTypeId = pData->onClipboardDataOffer()) | |||||
puglAcceptOffer(view, &event->offer, offerTypeId - 1); | |||||
break; | |||||
///< Data available from clipboard, a #PuglDataEvent | |||||
case PUGL_DATA: | |||||
pData->onClipboardData(event->data.typeIndex + 1); | |||||
break; | |||||
} | } | ||||
return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -44,9 +44,6 @@ struct Window::PrivateData : IdleCallback { | |||||
/** Pugl view instance. */ | /** Pugl view instance. */ | ||||
PuglView* view; | PuglView* view; | ||||
/** Pugl view instance of the transient parent window. */ | |||||
PuglView* const transientParentView; | |||||
/** Reserved space for graphics context. */ | /** Reserved space for graphics context. */ | ||||
mutable uint8_t graphicsContext[sizeof(void*)]; | mutable uint8_t graphicsContext[sizeof(void*)]; | ||||
@@ -80,15 +77,19 @@ struct Window::PrivateData : IdleCallback { | |||||
/** Whether to ignore idle callback requests, useful for temporary windows. */ | /** Whether to ignore idle callback requests, useful for temporary windows. */ | ||||
bool ignoreIdleCallbacks; | bool ignoreIdleCallbacks; | ||||
/** Whether to ignore pugl events (except create and destroy), used for puglGetClipboard. */ | |||||
bool ignoreEvents; | |||||
/** Whether we are waiting to receive clipboard data, ignoring some events in the process. */ | |||||
bool waitingForClipboardData; | |||||
bool waitingForClipboardEvents; | |||||
/** The type id returned by the last onClipboardDataOffer call. */ | |||||
uint32_t clipboardTypeId; | |||||
/** Render to a picture file when non-null, automatically free+unset after saving. */ | /** Render to a picture file when non-null, automatically free+unset after saving. */ | ||||
char* filenameToRenderInto; | char* filenameToRenderInto; | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
/** Handle for file browser dialog operations. */ | /** Handle for file browser dialog operations. */ | ||||
FileBrowserHandle fileBrowserHandle; | |||||
DGL_NAMESPACE::FileBrowserHandle fileBrowserHandle; | |||||
#endif | #endif | ||||
/** Modal window setup. */ | /** Modal window setup. */ | ||||
@@ -165,7 +166,7 @@ struct Window::PrivateData : IdleCallback { | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
// file handling | // file handling | ||||
bool openFileBrowser(const FileBrowserOptions& options); | |||||
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options); | |||||
#endif | #endif | ||||
static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); | static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); | ||||
@@ -186,6 +187,11 @@ struct Window::PrivateData : IdleCallback { | |||||
void onPuglMotion(const Widget::MotionEvent& ev); | void onPuglMotion(const Widget::MotionEvent& ev); | ||||
void onPuglScroll(const Widget::ScrollEvent& ev); | void onPuglScroll(const Widget::ScrollEvent& ev); | ||||
// clipboard related handling | |||||
const void* getClipboard(size_t& dataSize); | |||||
uint32_t onClipboardDataOffer(); | |||||
void onClipboardData(uint32_t typeId); | |||||
// Pugl event handling entry point | // Pugl event handling entry point | ||||
static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); | static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); | ||||
@@ -967,7 +967,10 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in | |||||
int idx = fons__allocFont(stash); | int idx = fons__allocFont(stash); | ||||
if (idx == FONS_INVALID) | if (idx == FONS_INVALID) | ||||
{ | |||||
if (freeData && data) free(data); | |||||
return FONS_INVALID; | return FONS_INVALID; | ||||
} | |||||
font = stash->fonts[idx]; | font = stash->fonts[idx]; | ||||
@@ -18,6 +18,28 @@ | |||||
#ifndef NANOVG_GL_H | #ifndef NANOVG_GL_H | ||||
#define NANOVG_GL_H | #define NANOVG_GL_H | ||||
#if defined NANOVG_GL2_FORCED | |||||
# undef NANOVG_GL3 | |||||
# undef NANOVG_GLES2 | |||||
# undef NANOVG_GLES3 | |||||
# define NANOVG_GL2 1 | |||||
#elif defined NANOVG_GL3_FORCED | |||||
# undef NANOVG_GL2 | |||||
# undef NANOVG_GLES2 | |||||
# undef NANOVG_GLES3 | |||||
# define NANOVG_GL3 1 | |||||
#elif defined NANOVG_GLES2_FORCED | |||||
# undef NANOVG_GL2 | |||||
# undef NANOVG_GL3 | |||||
# undef NANOVG_GLES3 | |||||
# define NANOVG_GLES2 1 | |||||
#elif defined NANOVG_GLES3_FORCED | |||||
# undef NANOVG_GL2 | |||||
# undef NANOVG_GL3 | |||||
# undef NANOVG_GLES2 | |||||
# define NANOVG_GLES3 1 | |||||
#endif | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
extern "C" { | extern "C" { | ||||
#endif | #endif | ||||
@@ -151,6 +173,9 @@ struct GLNVGtexture { | |||||
int width, height; | int width, height; | ||||
int type; | int type; | ||||
int flags; | int flags; | ||||
#if defined NANOVG_GLES2 | |||||
unsigned char* data; | |||||
#endif | |||||
}; | }; | ||||
typedef struct GLNVGtexture GLNVGtexture; | typedef struct GLNVGtexture GLNVGtexture; | ||||
@@ -399,7 +424,12 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||||
for (i = 0; i < gl->textureContext->ntextures; i++) { | for (i = 0; i < gl->textureContext->ntextures; i++) { | ||||
if (gl->textureContext->textures[i].id == id) { | if (gl->textureContext->textures[i].id == id) { | ||||
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | ||||
{ | |||||
glDeleteTextures(1, &gl->textureContext->textures[i].tex); | glDeleteTextures(1, &gl->textureContext->textures[i].tex); | ||||
#if defined NANOVG_GLES2 | |||||
free(gl->textureContext->textures[i].data); | |||||
#endif | |||||
} | |||||
memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); | memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); | ||||
return 1; | return 1; | ||||
} | } | ||||
@@ -753,7 +783,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
} | } | ||||
// No mips. | // No mips. | ||||
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | ||||
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | |||||
printf("Mip-maps is not supported for non power-of-two textures (%d x %d)\n", w, h); | |||||
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | ||||
} | } | ||||
} | } | ||||
@@ -783,10 +813,37 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
switch (type) | switch (type) | ||||
{ | { | ||||
case NVG_TEXTURE_BGR: | case NVG_TEXTURE_BGR: | ||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); | |||||
#if NANOVG_GLES2 | |||||
// GLES2 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) | |||||
{ | |||||
tex->data[i*3+0] = data[i*3+2]; | |||||
tex->data[i*3+1] = data[i*3+1]; | |||||
tex->data[i*3+2] = data[i*3+0]; | |||||
} | |||||
data = tex->data; | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||||
#else | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); | |||||
#endif | |||||
break; | break; | ||||
case NVG_TEXTURE_BGRA: | case NVG_TEXTURE_BGRA: | ||||
#if NANOVG_GLES2 | |||||
// GLES2 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) | |||||
{ | |||||
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]; | |||||
} | |||||
data = tex->data; | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||||
#else | |||||
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_BGRA, GL_UNSIGNED_BYTE, data); | ||||
#endif | |||||
break; | break; | ||||
case NVG_TEXTURE_RGB: | 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_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | ||||
@@ -24,5 +24,6 @@ StatementMacros: | |||||
- PUGL_CONST_FUNC | - PUGL_CONST_FUNC | ||||
- PUGL_DEPRECATED_BY | - PUGL_DEPRECATED_BY | ||||
- PUGL_UNUSED | - PUGL_UNUSED | ||||
- PUGL_WARN_UNUSED_RESULT | |||||
- _Pragma | - _Pragma | ||||
... | ... |
@@ -1,15 +1,7 @@ | |||||
[ | [ | ||||
{ "include": [ "<ext/alloc_traits.h>", "private", "<string>", "public", ] }, | { "include": [ "<ext/alloc_traits.h>", "private", "<string>", "public", ] }, | ||||
{ "include": [ "<ext/alloc_traits.h>", "private", "<vector>", "public", ] }, | { "include": [ "<ext/alloc_traits.h>", "private", "<vector>", "public", ] }, | ||||
{ "symbol": [ "bool", "private", "<stdbool.h>", "public" ] }, | |||||
{ "symbol": [ "int32_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "int64_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "std::uintptr_t", "private", "<cstdint>", "public" ] }, | |||||
{ "symbol": [ "timespec", "private", "<time.h>", "public" ] }, | { "symbol": [ "timespec", "private", "<time.h>", "public" ] }, | ||||
{ "symbol": [ "timeval", "private", "<time.h>", "public" ] }, | |||||
{ "symbol": [ "uint32_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "uint64_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "uint8_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "uintptr_t", "private", "<stdint.h>", "public" ] }, | |||||
{ "symbol": [ "uintptr_t", "private", "<cstdint>", "public" ] } | |||||
{ "symbol": [ "timeval", "private", "<time.h>", "public" ] } | |||||
] | ] |
@@ -0,0 +1,16 @@ | |||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||||
Upstream-Name: pugl | |||||
Upstream-Contact: David Robillard <d@drobilla.net> | |||||
Source: https://gitlab.com/lv2/pugl | |||||
Files: **/meson.build *.md */.clang* .clang* .clant.json .editorconfig .git* .includes.imp AUTHORS doc/*.in meson_options.txt resources/Info.plist.in | |||||
Copyright: 2021 David Robillard <d@drobilla.net> | |||||
License: CC0-1.0 OR ISC | |||||
Files: doc/*.rst examples/glad/glad.c examples/glad/glad.h resources/pugl.ipe resources/pugl.png resources/pugl.svg | |||||
Copyright: 2021 David Robillard <d@drobilla.net> | |||||
License: ISC | |||||
Files: examples/glad/khrplatform.h | |||||
Copyright: 2008-2018 The Khronos Group Inc. | |||||
License: MIT |
@@ -0,0 +1,121 @@ | |||||
Creative Commons Legal Code | |||||
CC0 1.0 Universal | |||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE | |||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN | |||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS | |||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES | |||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS | |||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM | |||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED | |||||
HEREUNDER. | |||||
Statement of Purpose | |||||
The laws of most jurisdictions throughout the world automatically confer | |||||
exclusive Copyright and Related Rights (defined below) upon the creator | |||||
and subsequent owner(s) (each and all, an "owner") of an original work of | |||||
authorship and/or a database (each, a "Work"). | |||||
Certain owners wish to permanently relinquish those rights to a Work for | |||||
the purpose of contributing to a commons of creative, cultural and | |||||
scientific works ("Commons") that the public can reliably and without fear | |||||
of later claims of infringement build upon, modify, incorporate in other | |||||
works, reuse and redistribute as freely as possible in any form whatsoever | |||||
and for any purposes, including without limitation commercial purposes. | |||||
These owners may contribute to the Commons to promote the ideal of a free | |||||
culture and the further production of creative, cultural and scientific | |||||
works, or to gain reputation or greater distribution for their Work in | |||||
part through the use and efforts of others. | |||||
For these and/or other purposes and motivations, and without any | |||||
expectation of additional consideration or compensation, the person | |||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she | |||||
is an owner of Copyright and Related Rights in the Work, voluntarily | |||||
elects to apply CC0 to the Work and publicly distribute the Work under its | |||||
terms, with knowledge of his or her Copyright and Related Rights in the | |||||
Work and the meaning and intended legal effect of CC0 on those rights. | |||||
1. Copyright and Related Rights. A Work made available under CC0 may be | |||||
protected by copyright and related or neighboring rights ("Copyright and | |||||
Related Rights"). Copyright and Related Rights include, but are not | |||||
limited to, the following: | |||||
i. the right to reproduce, adapt, distribute, perform, display, | |||||
communicate, and translate a Work; | |||||
ii. moral rights retained by the original author(s) and/or performer(s); | |||||
iii. publicity and privacy rights pertaining to a person's image or | |||||
likeness depicted in a Work; | |||||
iv. rights protecting against unfair competition in regards to a Work, | |||||
subject to the limitations in paragraph 4(a), below; | |||||
v. rights protecting the extraction, dissemination, use and reuse of data | |||||
in a Work; | |||||
vi. database rights (such as those arising under Directive 96/9/EC of the | |||||
European Parliament and of the Council of 11 March 1996 on the legal | |||||
protection of databases, and under any national implementation | |||||
thereof, including any amended or successor version of such | |||||
directive); and | |||||
vii. other similar, equivalent or corresponding rights throughout the | |||||
world based on applicable law or treaty, and any national | |||||
implementations thereof. | |||||
2. Waiver. To the greatest extent permitted by, but not in contravention | |||||
of, applicable law, Affirmer hereby overtly, fully, permanently, | |||||
irrevocably and unconditionally waives, abandons, and surrenders all of | |||||
Affirmer's Copyright and Related Rights and associated claims and causes | |||||
of action, whether now known or unknown (including existing as well as | |||||
future claims and causes of action), in the Work (i) in all territories | |||||
worldwide, (ii) for the maximum duration provided by applicable law or | |||||
treaty (including future time extensions), (iii) in any current or future | |||||
medium and for any number of copies, and (iv) for any purpose whatsoever, | |||||
including without limitation commercial, advertising or promotional | |||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each | |||||
member of the public at large and to the detriment of Affirmer's heirs and | |||||
successors, fully intending that such Waiver shall not be subject to | |||||
revocation, rescission, cancellation, termination, or any other legal or | |||||
equitable action to disrupt the quiet enjoyment of the Work by the public | |||||
as contemplated by Affirmer's express Statement of Purpose. | |||||
3. Public License Fallback. Should any part of the Waiver for any reason | |||||
be judged legally invalid or ineffective under applicable law, then the | |||||
Waiver shall be preserved to the maximum extent permitted taking into | |||||
account Affirmer's express Statement of Purpose. In addition, to the | |||||
extent the Waiver is so judged Affirmer hereby grants to each affected | |||||
person a royalty-free, non transferable, non sublicensable, non exclusive, | |||||
irrevocable and unconditional license to exercise Affirmer's Copyright and | |||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the | |||||
maximum duration provided by applicable law or treaty (including future | |||||
time extensions), (iii) in any current or future medium and for any number | |||||
of copies, and (iv) for any purpose whatsoever, including without | |||||
limitation commercial, advertising or promotional purposes (the | |||||
"License"). The License shall be deemed effective as of the date CC0 was | |||||
applied by Affirmer to the Work. Should any part of the License for any | |||||
reason be judged legally invalid or ineffective under applicable law, such | |||||
partial invalidity or ineffectiveness shall not invalidate the remainder | |||||
of the License, and in such case Affirmer hereby affirms that he or she | |||||
will not (i) exercise any of his or her remaining Copyright and Related | |||||
Rights in the Work or (ii) assert any associated claims and causes of | |||||
action with respect to the Work, in either case contrary to Affirmer's | |||||
express Statement of Purpose. | |||||
4. Limitations and Disclaimers. | |||||
a. No trademark or patent rights held by Affirmer are waived, abandoned, | |||||
surrendered, licensed or otherwise affected by this document. | |||||
b. Affirmer offers the Work as-is and makes no representations or | |||||
warranties of any kind concerning the Work, express, implied, | |||||
statutory or otherwise, including without limitation warranties of | |||||
title, merchantability, fitness for a particular purpose, non | |||||
infringement, or the absence of latent or other defects, accuracy, or | |||||
the present or absence of errors, whether or not discoverable, all to | |||||
the greatest extent permissible under applicable law. | |||||
c. Affirmer disclaims responsibility for clearing rights of other persons | |||||
that may apply to the Work or any use thereof, including without | |||||
limitation any person's Copyright and Related Rights in the Work. | |||||
Further, Affirmer disclaims responsibility for obtaining any necessary | |||||
consents, permissions or other rights required for any use of the | |||||
Work. | |||||
d. Affirmer understands and acknowledges that Creative Commons is not a | |||||
party to this document and has no duty or obligation with respect to | |||||
this CC0 or use of the Work. |
@@ -0,0 +1,11 @@ | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
@@ -0,0 +1,17 @@ | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy of | |||||
this software and associated documentation files (the "Software"), to deal in | |||||
the Software without restriction, including without limitation the rights to | |||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||||
of the Software, and to permit persons to whom the Software is furnished to do | |||||
so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. |
@@ -1,12 +1,12 @@ | |||||
Pugl | Pugl | ||||
==== | ==== | ||||
Pugl (PlUgin Graphics Library) is a minimal portable API for GUIs which is | |||||
suitable for use in plugins. It works on X11, MacOS, and Windows, and | |||||
optionally supports Vulkan, OpenGL, and Cairo graphics contexts. | |||||
Pugl (PlUgin Graphics Library) is a minimal portability layer for GUIs which is | |||||
suitable for use in plugins. It works on X11, MacOS, and Windows, and includes | |||||
optional support for drawing with Vulkan, OpenGL, and Cairo. | |||||
Pugl is vaguely similar to libraries like GLUT and GLFW, but with some | |||||
distinguishing features: | |||||
Pugl is vaguely similar to libraries like GLUT and GLFW, but has different | |||||
goals and priorities: | |||||
* Minimal in scope, providing only a thin interface to isolate | * Minimal in scope, providing only a thin interface to isolate | ||||
platform-specific details from applications. | platform-specific details from applications. | ||||
@@ -16,18 +16,18 @@ distinguishing features: | |||||
* Support for embedding in native windows, for example as a plugin or | * Support for embedding in native windows, for example as a plugin or | ||||
component within a larger application that is not based on Pugl. | component within a larger application that is not based on Pugl. | ||||
* Simple and extensible event-based API that makes dispatching in application | |||||
or toolkit code easy with minimal boilerplate. | |||||
* Explicit context and no static data, so that several instances can be used | |||||
within a single program at once. | |||||
* Suitable not only for continuously rendering applications like games, but | |||||
also event-driven applications that only draw when necessary. | |||||
* Consistent event-based API that makes dispatching in application or toolkit | |||||
code easy with minimal boilerplate. | |||||
* Explicit context and no static data whatsoever, so that several instances | |||||
can be used within a single program at once. | |||||
* Suitable for both continuously rendering applications like games, and | |||||
event-driven applications that only draw when necessary. | |||||
* Small, liberally licensed Free Software implementation that is suitable for | |||||
vendoring and/or static linking. Pugl can be installed as a library, or | |||||
used by simply copying the headers into a project. | |||||
* Small, liberally licensed implementation that is suitable for vendoring | |||||
and/or static linking. Pugl can be installed as a library, or used by | |||||
simply copying the implementation into a project. | |||||
Stability | Stability | ||||
--------- | --------- | ||||
@@ -37,6 +37,10 @@ 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 | feedback, or file feature requests, so that we can ensure that the released API | ||||
is stable for as long as possible. | 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. | |||||
Documentation | Documentation | ||||
------------- | ------------- | ||||
@@ -62,37 +66,7 @@ all the tests at once via ninja: | |||||
cd build | cd build | ||||
ninja test | ninja test | ||||
The `examples` directory contains several programs that serve as both manual | |||||
tests and demonstrations: | |||||
* `pugl_embed_demo` shows a view embedded in another, and also tests | |||||
requesting attention (which happens after 5 seconds), keyboard focus | |||||
(switched by pressing tab), view moving (with the arrow keys), and view | |||||
resizing (with the arrow keys while shift is held). This program uses only | |||||
very old OpenGL and should work on any system. | |||||
* `pugl_window_demo` demonstrates multiple top-level windows. | |||||
* `pugl_shader_demo` demonstrates using more modern OpenGL (version 3 or 4) | |||||
where dynamic loading and shaders are required. It can also be used to test | |||||
performance by passing the number of rectangles to draw on the command line. | |||||
* `pugl_cairo_demo` demonstrates using Cairo on top of the native windowing | |||||
system (without OpenGL), and partial redrawing. | |||||
* `pugl_print_events` is a utility that prints all received events to the | |||||
console in a human readable format. | |||||
* `pugl_cpp_demo` is a simple cube demo that uses the C++ API. | |||||
* `pugl_vulkan_demo` is a simple example of using Vulkan in C that simply | |||||
clears the window. | |||||
* `pugl_vulkan_cpp_demo` is a more advanced Vulkan demo in C++ that draws many | |||||
animated rectangles like `pugl_shader_demo`. | |||||
All example programs support several command line options to control various | |||||
behaviours, see the output of `--help` for details. Please file an issue if | |||||
any of these programs do not work as expected on your system. | |||||
The [examples](examples) directory contains several demonstration programs that | |||||
can be used for manual testing. | |||||
-- David Robillard <d@drobilla.net> | -- David Robillard <d@drobilla.net> |
@@ -1,15 +0,0 @@ | |||||
Checks: > | |||||
*, | |||||
-*-uppercase-literal-suffix, | |||||
-altera-struct-pack-align, | |||||
-clang-diagnostic-unused-macros, | |||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic, | |||||
-cppcoreguidelines-pro-type-reinterpret-cast, | |||||
-google-runtime-references, | |||||
-hicpp-named-parameter, | |||||
-llvmlibc-*, | |||||
-modernize-use-trailing-return-type, | |||||
-readability-implicit-bool-conversion, | |||||
-readability-named-parameter, | |||||
FormatStyle: file | |||||
HeaderFilterRegex: 'pugl/.*' |
@@ -1,12 +0,0 @@ | |||||
cpp_headers = [ | |||||
'pugl/pugl.hpp', | |||||
'pugl/cairo.hpp', | |||||
'pugl/gl.hpp', | |||||
'pugl/stub.hpp', | |||||
'pugl/vulkan.hpp', | |||||
] | |||||
cpp_header_files = files(cpp_headers) | |||||
install_headers(cpp_headers, subdir: 'puglpp' + version_suffix / 'pugl') |
@@ -1,45 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef PUGL_CAIRO_HPP | |||||
#define PUGL_CAIRO_HPP | |||||
#include "pugl/cairo.h" | |||||
#include "pugl/pugl.h" | |||||
namespace pugl { | |||||
/** | |||||
@defgroup cairopp Cairo | |||||
Cairo graphics support. | |||||
@ingroup puglpp | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglCairoBackend | |||||
inline const PuglBackend* | |||||
cairoBackend() noexcept | |||||
{ | |||||
return puglCairoBackend(); | |||||
} | |||||
/** | |||||
@} | |||||
*/ | |||||
} // namespace pugl | |||||
#endif // PUGL_CAIRO_HPP |
@@ -1,70 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef PUGL_GL_HPP | |||||
#define PUGL_GL_HPP | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include "pugl/pugl.hpp" | |||||
namespace pugl { | |||||
/** | |||||
@defgroup glpp OpenGL | |||||
OpenGL graphics support. | |||||
@ingroup puglpp | |||||
@{ | |||||
*/ | |||||
/// @copydoc PuglGlFunc | |||||
using GlFunc = PuglGlFunc; | |||||
/// @copydoc puglGetProcAddress | |||||
inline GlFunc | |||||
getProcAddress(const char* name) noexcept | |||||
{ | |||||
return puglGetProcAddress(name); | |||||
} | |||||
/// @copydoc puglEnterContext | |||||
inline Status | |||||
enterContext(View& view) noexcept | |||||
{ | |||||
return static_cast<Status>(puglEnterContext(view.cobj())); | |||||
} | |||||
/// @copydoc puglLeaveContext | |||||
inline Status | |||||
leaveContext(View& view) noexcept | |||||
{ | |||||
return static_cast<Status>(puglLeaveContext(view.cobj())); | |||||
} | |||||
/// @copydoc puglGlBackend | |||||
inline const PuglBackend* | |||||
glBackend() noexcept | |||||
{ | |||||
return puglGlBackend(); | |||||
} | |||||
/** | |||||
@} | |||||
*/ | |||||
} // namespace pugl | |||||
#endif // PUGL_GL_HPP |
@@ -1,734 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef PUGL_PUGL_HPP | |||||
#define PUGL_PUGL_HPP | |||||
#include "pugl/pugl.h" | |||||
#include <cstdint> | |||||
#if defined(PUGL_HPP_THROW_FAILED_CONSTRUCTION) | |||||
# include <exception> | |||||
#elif defined(PUGL_HPP_ASSERT_CONSTRUCTION) | |||||
# include <cassert> | |||||
#endif | |||||
namespace pugl { | |||||
/** | |||||
@defgroup puglpp Pugl C++ API | |||||
Pugl C++ API wrapper. | |||||
@{ | |||||
*/ | |||||
namespace detail { | |||||
/// Free function for a C object | |||||
template<typename T> | |||||
using FreeFunc = void (*)(T*); | |||||
/// Generic C++ wrapper for a C object | |||||
template<class T, FreeFunc<T> Free> | |||||
class Wrapper | |||||
{ | |||||
public: | |||||
Wrapper(const Wrapper&) = delete; | |||||
Wrapper& operator=(const Wrapper&) = delete; | |||||
Wrapper(Wrapper&& wrapper) noexcept | |||||
: _ptr{wrapper._ptr} | |||||
{ | |||||
wrapper._ptr = nullptr; | |||||
} | |||||
Wrapper& operator=(Wrapper&& wrapper) noexcept | |||||
{ | |||||
_ptr = wrapper._ptr; | |||||
wrapper._ptr = nullptr; | |||||
return *this; | |||||
} | |||||
~Wrapper() noexcept { Free(_ptr); } | |||||
T* cobj() noexcept { return _ptr; } | |||||
const T* cobj() const noexcept { return _ptr; } | |||||
protected: | |||||
explicit Wrapper(T* ptr) noexcept | |||||
: _ptr{ptr} | |||||
{} | |||||
private: | |||||
T* _ptr; | |||||
}; | |||||
} // namespace detail | |||||
using Rect = PuglRect; ///< @copydoc PuglRect | |||||
/** | |||||
@defgroup eventspp Events | |||||
@{ | |||||
*/ | |||||
/** | |||||
A strongly-typed analogue of PuglEvent. | |||||
This is bit-for-bit identical to the corresponding PuglEvent. | |||||
@tparam t The `type` field of the corresponding PuglEvent. | |||||
@tparam Base The specific struct type of the corresponding PuglEvent. | |||||
*/ | |||||
template<PuglEventType t, class Base> | |||||
struct Event final : Base { | |||||
/// The type of the corresponding C event structure | |||||
using BaseEvent = Base; | |||||
/// The `type` field of the corresponding C event structure | |||||
static constexpr const PuglEventType type = t; | |||||
explicit Event(Base base) | |||||
: Base{base} | |||||
{} | |||||
template<class... Args> | |||||
explicit Event(const PuglEventFlags f, Args... args) | |||||
: Base{t, f, args...} | |||||
{} | |||||
}; | |||||
using Mod = PuglMod; ///< @copydoc PuglMod | |||||
using Mods = PuglMods; ///< @copydoc PuglMods | |||||
using Key = PuglKey; ///< @copydoc PuglKey | |||||
using EventType = PuglEventType; ///< @copydoc PuglEventType | |||||
using EventFlag = PuglEventFlag; ///< @copydoc PuglEventFlag | |||||
using EventFlags = PuglEventFlags; ///< @copydoc PuglEventFlags | |||||
using CrossingMode = PuglCrossingMode; ///< @copydoc PuglCrossingMode | |||||
/// @copydoc PuglCreateEvent | |||||
using CreateEvent = Event<PUGL_CREATE, PuglCreateEvent>; | |||||
/// @copydoc PuglDestroyEvent | |||||
using DestroyEvent = Event<PUGL_DESTROY, PuglDestroyEvent>; | |||||
/// @copydoc PuglConfigureEvent | |||||
using ConfigureEvent = Event<PUGL_CONFIGURE, PuglConfigureEvent>; | |||||
/// @copydoc PuglMapEvent | |||||
using MapEvent = Event<PUGL_MAP, PuglMapEvent>; | |||||
/// @copydoc PuglUnmapEvent | |||||
using UnmapEvent = Event<PUGL_UNMAP, PuglUnmapEvent>; | |||||
/// @copydoc PuglUpdateEvent | |||||
using UpdateEvent = Event<PUGL_UPDATE, PuglUpdateEvent>; | |||||
/// @copydoc PuglExposeEvent | |||||
using ExposeEvent = Event<PUGL_EXPOSE, PuglExposeEvent>; | |||||
/// @copydoc PuglCloseEvent | |||||
using CloseEvent = Event<PUGL_CLOSE, PuglCloseEvent>; | |||||
/// @copydoc PuglFocusEvent | |||||
using FocusInEvent = Event<PUGL_FOCUS_IN, PuglFocusEvent>; | |||||
/// @copydoc PuglFocusEvent | |||||
using FocusOutEvent = Event<PUGL_FOCUS_OUT, PuglFocusEvent>; | |||||
/// @copydoc PuglKeyEvent | |||||
using KeyPressEvent = Event<PUGL_KEY_PRESS, PuglKeyEvent>; | |||||
/// @copydoc PuglKeyEvent | |||||
using KeyReleaseEvent = Event<PUGL_KEY_RELEASE, PuglKeyEvent>; | |||||
/// @copydoc PuglTextEvent | |||||
using TextEvent = Event<PUGL_TEXT, PuglTextEvent>; | |||||
/// @copydoc PuglCrossingEvent | |||||
using PointerInEvent = Event<PUGL_POINTER_IN, PuglCrossingEvent>; | |||||
/// @copydoc PuglCrossingEvent | |||||
using PointerOutEvent = Event<PUGL_POINTER_OUT, PuglCrossingEvent>; | |||||
/// @copydoc PuglButtonEvent | |||||
using ButtonPressEvent = Event<PUGL_BUTTON_PRESS, PuglButtonEvent>; | |||||
/// @copydoc PuglButtonEvent | |||||
using ButtonReleaseEvent = Event<PUGL_BUTTON_RELEASE, PuglButtonEvent>; | |||||
/// @copydoc PuglMotionEvent | |||||
using MotionEvent = Event<PUGL_MOTION, PuglMotionEvent>; | |||||
/// @copydoc PuglScrollEvent | |||||
using ScrollEvent = Event<PUGL_SCROLL, PuglScrollEvent>; | |||||
/// @copydoc PuglClientEvent | |||||
using ClientEvent = Event<PUGL_CLIENT, PuglClientEvent>; | |||||
/// @copydoc PuglTimerEvent | |||||
using TimerEvent = Event<PUGL_TIMER, PuglTimerEvent>; | |||||
/// @copydoc PuglLoopEnterEvent | |||||
using LoopEnterEvent = Event<PUGL_LOOP_ENTER, PuglLoopEnterEvent>; | |||||
/// @copydoc PuglLoopLeaveEvent | |||||
using LoopLeaveEvent = Event<PUGL_LOOP_LEAVE, PuglLoopLeaveEvent>; | |||||
/** | |||||
@} | |||||
@defgroup statuspp Status | |||||
@{ | |||||
*/ | |||||
/// @copydoc PuglStatus | |||||
enum class Status { | |||||
success, ///< @copydoc PUGL_SUCCESS | |||||
failure, ///< @copydoc PUGL_FAILURE | |||||
unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR | |||||
badBackend, ///< @copydoc PUGL_BAD_BACKEND | |||||
badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION | |||||
badParameter, ///< @copydoc PUGL_BAD_PARAMETER | |||||
backendFailed, ///< @copydoc PUGL_BACKEND_FAILED | |||||
registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED | |||||
realizeFailed, ///< @copydoc PUGL_REALIZE_FAILED | |||||
setFormatFailed, ///< @copydoc PUGL_SET_FORMAT_FAILED | |||||
createContextFailed, ///< @copydoc PUGL_CREATE_CONTEXT_FAILED | |||||
unsupportedType, ///< @copydoc PUGL_UNSUPPORTED_TYPE | |||||
}; | |||||
static_assert(Status(PUGL_UNSUPPORTED_TYPE) == Status::unsupportedType, ""); | |||||
/// @copydoc puglStrerror | |||||
inline const char* | |||||
strerror(const Status status) noexcept | |||||
{ | |||||
return puglStrerror(static_cast<PuglStatus>(status)); | |||||
} | |||||
/** | |||||
@} | |||||
@defgroup worldpp World | |||||
@{ | |||||
*/ | |||||
/// @copydoc PuglWorldType | |||||
enum class WorldType { | |||||
program, ///< @copydoc PUGL_PROGRAM | |||||
module, ///< @copydoc PUGL_MODULE | |||||
}; | |||||
static_assert(WorldType(PUGL_MODULE) == WorldType::module, ""); | |||||
/// @copydoc PuglWorldFlag | |||||
enum class WorldFlag { | |||||
threads = PUGL_WORLD_THREADS, ///< @copydoc PUGL_WORLD_THREADS | |||||
}; | |||||
static_assert(WorldFlag(PUGL_WORLD_THREADS) == WorldFlag::threads, ""); | |||||
using WorldFlags = PuglWorldFlags; ///< @copydoc PuglWorldFlags | |||||
#if defined(PUGL_HPP_THROW_FAILED_CONSTRUCTION) | |||||
/// An exception thrown when construction fails | |||||
class FailedConstructionError : public std::exception | |||||
{ | |||||
public: | |||||
FailedConstructionError(const char* const msg) noexcept | |||||
: _msg{msg} | |||||
{} | |||||
virtual const char* what() const noexcept override; | |||||
private: | |||||
const char* _msg; | |||||
}; | |||||
# define PUGL_CHECK_CONSTRUCTION(cond, msg) \ | |||||
do { \ | |||||
if (!(cond)) { \ | |||||
throw FailedConstructionError(msg); \ | |||||
} \ | |||||
} while (0) | |||||
#elif defined(PUGL_HPP_ASSERT_CONSTRUCTION) | |||||
# define PUGL_CHECK_CONSTRUCTION(cond, msg) assert(cond); | |||||
#else | |||||
/** | |||||
Configurable macro for handling construction failure. | |||||
If `PUGL_HPP_THROW_FAILED_CONSTRUCTION` is defined, then this throws a | |||||
`pugl::FailedConstructionError` if construction fails. | |||||
If `PUGL_HPP_ASSERT_CONSTRUCTION` is defined, then this asserts if | |||||
construction fails. | |||||
Otherwise, this does nothing. | |||||
*/ | |||||
# define PUGL_CHECK_CONSTRUCTION(cond, msg) | |||||
#endif | |||||
/// @copydoc PuglWorld | |||||
class World : public detail::Wrapper<PuglWorld, puglFreeWorld> | |||||
{ | |||||
public: | |||||
World(const World&) = delete; | |||||
World& operator=(const World&) = delete; | |||||
World(World&&) = delete; | |||||
World& operator=(World&&) = delete; | |||||
~World() = default; | |||||
World(WorldType type, WorldFlag flag) | |||||
: Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), | |||||
static_cast<PuglWorldFlags>(flag))} | |||||
{ | |||||
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::World"); | |||||
} | |||||
World(WorldType type, WorldFlags flags) | |||||
: Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)} | |||||
{ | |||||
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::World"); | |||||
} | |||||
explicit World(WorldType type) | |||||
: World{type, WorldFlags{}} | |||||
{} | |||||
/// @copydoc puglGetNativeWorld | |||||
void* nativeWorld() noexcept { return puglGetNativeWorld(cobj()); } | |||||
/// @copydoc puglSetClassName | |||||
Status setClassName(const char* const name) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetClassName(cobj(), name)); | |||||
} | |||||
/// @copydoc puglGetTime | |||||
double time() const noexcept { return puglGetTime(cobj()); } | |||||
/// @copydoc puglUpdate | |||||
Status update(const double timeout) noexcept | |||||
{ | |||||
return static_cast<Status>(puglUpdate(cobj(), timeout)); | |||||
} | |||||
}; | |||||
/** | |||||
@} | |||||
@defgroup viewpp View | |||||
@{ | |||||
*/ | |||||
using Backend = PuglBackend; ///< @copydoc PuglBackend | |||||
using NativeView = PuglNativeView; ///< @copydoc PuglNativeView | |||||
/// @copydoc PuglViewHint | |||||
enum class ViewHint { | |||||
useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE | |||||
useDebugContext, ///< @copydoc PUGL_USE_DEBUG_CONTEXT | |||||
contextVersionMajor, ///< @copydoc PUGL_CONTEXT_VERSION_MAJOR | |||||
contextVersionMinor, ///< @copydoc PUGL_CONTEXT_VERSION_MINOR | |||||
redBits, ///< @copydoc PUGL_RED_BITS | |||||
greenBits, ///< @copydoc PUGL_GREEN_BITS | |||||
blueBits, ///< @copydoc PUGL_BLUE_BITS | |||||
alphaBits, ///< @copydoc PUGL_ALPHA_BITS | |||||
depthBits, ///< @copydoc PUGL_DEPTH_BITS | |||||
stencilBits, ///< @copydoc PUGL_STENCIL_BITS | |||||
samples, ///< @copydoc PUGL_SAMPLES | |||||
doubleBuffer, ///< @copydoc PUGL_DOUBLE_BUFFER | |||||
swapInterval, ///< @copydoc PUGL_SWAP_INTERVAL | |||||
resizable, ///< @copydoc PUGL_RESIZABLE | |||||
ignoreKeyRepeat, ///< @copydoc PUGL_IGNORE_KEY_REPEAT | |||||
refreshRate, ///< @copydoc PUGL_REFRESH_RATE | |||||
}; | |||||
static_assert(ViewHint(PUGL_REFRESH_RATE) == ViewHint::refreshRate, ""); | |||||
using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue | |||||
/// @copydoc PuglCursor | |||||
enum class Cursor { | |||||
arrow, ///< @copydoc PUGL_CURSOR_ARROW | |||||
caret, ///< @copydoc PUGL_CURSOR_CARET | |||||
crosshair, ///< @copydoc PUGL_CURSOR_CROSSHAIR | |||||
hand, ///< @copydoc PUGL_CURSOR_HAND | |||||
no, ///< @copydoc PUGL_CURSOR_NO | |||||
leftRight, ///< @copydoc PUGL_CURSOR_LEFT_RIGHT | |||||
upDown, ///< @copydoc PUGL_CURSOR_UP_DOWN | |||||
}; | |||||
static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, ""); | |||||
/// @copydoc PuglView | |||||
class View : protected detail::Wrapper<PuglView, puglFreeView> | |||||
{ | |||||
public: | |||||
/** | |||||
@name Setup | |||||
Methods for creating and destroying a view. | |||||
@{ | |||||
*/ | |||||
explicit View(World& world) | |||||
: Wrapper{puglNewView(world.cobj())} | |||||
, _world(world) | |||||
{ | |||||
PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::View"); | |||||
} | |||||
const World& world() const noexcept { return _world; } | |||||
World& world() noexcept { return _world; } | |||||
/** | |||||
Set the object that will be called to handle events. | |||||
This is a type-safe wrapper for the C functions puglSetHandle() and | |||||
puglSetEventFunc() that will automatically dispatch events to the | |||||
`onEvent` method of `handler` that takes the appropriate event type. | |||||
The handler must have such a method defined for every event type, but if | |||||
the handler is the view itself, a `using` declaration can be used to | |||||
"inherit" the default implementation to avoid having to define every | |||||
method. For example: | |||||
@code | |||||
class MyView : public pugl::View | |||||
{ | |||||
public: | |||||
explicit MyView(pugl::World& world) | |||||
: pugl::View{world} | |||||
{ | |||||
setEventHandler(*this); | |||||
} | |||||
using pugl::View::onEvent; | |||||
pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; | |||||
}; | |||||
@endcode | |||||
This facility is just a convenience, applications may use the C API | |||||
directly to set a handle and event function to set up a different | |||||
approach for event handling. | |||||
*/ | |||||
template<class Handler> | |||||
Status setEventHandler(Handler& handler) | |||||
{ | |||||
puglSetHandle(cobj(), &handler); | |||||
return static_cast<Status>(puglSetEventFunc(cobj(), eventFunc<Handler>)); | |||||
} | |||||
/// @copydoc puglSetBackend | |||||
Status setBackend(const PuglBackend* backend) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetBackend(cobj(), backend)); | |||||
} | |||||
/// @copydoc puglSetViewHint | |||||
Status setHint(ViewHint hint, int value) noexcept | |||||
{ | |||||
return static_cast<Status>( | |||||
puglSetViewHint(cobj(), static_cast<PuglViewHint>(hint), value)); | |||||
} | |||||
/// @copydoc puglGetViewHint | |||||
int getHint(ViewHint hint) noexcept | |||||
{ | |||||
return puglGetViewHint(cobj(), static_cast<PuglViewHint>(hint)); | |||||
} | |||||
/** | |||||
@} | |||||
@name Frame | |||||
Methods for working with the position and size of a view. | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglGetFrame | |||||
Rect frame() const noexcept { return puglGetFrame(cobj()); } | |||||
/// @copydoc puglSetFrame | |||||
Status setFrame(Rect frame) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetFrame(cobj(), frame)); | |||||
} | |||||
/// @copydoc puglSetDefaultSize | |||||
Status setDefaultSize(int width, int height) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetDefaultSize(cobj(), width, height)); | |||||
} | |||||
/// @copydoc puglSetMinSize | |||||
Status setMinSize(int width, int height) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetMinSize(cobj(), width, height)); | |||||
} | |||||
/// @copydoc puglSetMaxSize | |||||
Status setMaxSize(int width, int height) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetMaxSize(cobj(), width, height)); | |||||
} | |||||
/// @copydoc puglSetAspectRatio | |||||
Status setAspectRatio(int minX, int minY, int maxX, int maxY) noexcept | |||||
{ | |||||
return static_cast<Status>( | |||||
puglSetAspectRatio(cobj(), minX, minY, maxX, maxY)); | |||||
} | |||||
/** | |||||
@} | |||||
@name Windows | |||||
Methods for working with top-level windows. | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglSetWindowTitle | |||||
Status setWindowTitle(const char* title) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetWindowTitle(cobj(), title)); | |||||
} | |||||
/// @copydoc puglSetParentWindow | |||||
Status setParentWindow(NativeView parent) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetParentWindow(cobj(), parent)); | |||||
} | |||||
/// @copydoc puglSetTransientFor | |||||
Status setTransientFor(NativeView parent) noexcept | |||||
{ | |||||
return static_cast<Status>(puglSetTransientFor(cobj(), parent)); | |||||
} | |||||
/// @copydoc puglRealize | |||||
Status realize() noexcept { return static_cast<Status>(puglRealize(cobj())); } | |||||
/// @copydoc puglShow | |||||
Status show() noexcept { return static_cast<Status>(puglShow(cobj())); } | |||||
/// @copydoc puglHide | |||||
Status hide() noexcept { return static_cast<Status>(puglHide(cobj())); } | |||||
/// @copydoc puglGetVisible | |||||
bool visible() const noexcept { return puglGetVisible(cobj()); } | |||||
/// @copydoc puglGetNativeWindow | |||||
NativeView nativeWindow() noexcept { return puglGetNativeWindow(cobj()); } | |||||
/** | |||||
@} | |||||
@name Graphics | |||||
Methods for working with the graphics context and scheduling | |||||
redisplays. | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglGetContext | |||||
void* context() noexcept { return puglGetContext(cobj()); } | |||||
/// @copydoc puglPostRedisplay | |||||
Status postRedisplay() noexcept | |||||
{ | |||||
return static_cast<Status>(puglPostRedisplay(cobj())); | |||||
} | |||||
/// @copydoc puglPostRedisplayRect | |||||
Status postRedisplayRect(const Rect rect) noexcept | |||||
{ | |||||
return static_cast<Status>(puglPostRedisplayRect(cobj(), rect)); | |||||
} | |||||
/** | |||||
@} | |||||
@name Interaction | |||||
Methods for interacting with the user and window system. | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglGrabFocus | |||||
Status grabFocus() noexcept | |||||
{ | |||||
return static_cast<Status>(puglGrabFocus(cobj())); | |||||
} | |||||
/// @copydoc puglHasFocus | |||||
bool hasFocus() const noexcept { return puglHasFocus(cobj()); } | |||||
/// @copydoc puglSetCursor | |||||
Status setCursor(const Cursor cursor) noexcept | |||||
{ | |||||
return static_cast<Status>( | |||||
puglSetCursor(cobj(), static_cast<PuglCursor>(cursor))); | |||||
} | |||||
/// @copydoc puglRequestAttention | |||||
Status requestAttention() noexcept | |||||
{ | |||||
return static_cast<Status>(puglRequestAttention(cobj())); | |||||
} | |||||
/** | |||||
Activate a repeating timer event. | |||||
This starts a timer which will send a timer event to `view` every | |||||
`timeout` seconds. This can be used to perform some action in a view at a | |||||
regular interval with relatively low frequency. Note that the frequency | |||||
of timer events may be limited by how often update() is called. | |||||
If the given timer already exists, it is replaced. | |||||
@param id The identifier for this timer. This is an application-specific | |||||
ID that should be a low number, typically the value of a constant or `enum` | |||||
that starts from 0. There is a platform-specific limit to the number of | |||||
supported timers, and overhead associated with each, so applications should | |||||
create only a few timers and perform several tasks in one if necessary. | |||||
@param timeout The period, in seconds, of this timer. This is not | |||||
guaranteed to have a resolution better than 10ms (the maximum timer | |||||
resolution on Windows) and may be rounded up if it is too short. On X11 | |||||
and MacOS, a resolution of about 1ms can usually be relied on. | |||||
@return #PUGL_FAILURE if timers are not supported by the system, | |||||
#PUGL_UNKNOWN_ERROR if setting the timer failed. | |||||
*/ | |||||
Status startTimer(const uintptr_t id, const double timeout) noexcept | |||||
{ | |||||
return static_cast<Status>(puglStartTimer(cobj(), id, timeout)); | |||||
} | |||||
/** | |||||
Stop an active timer. | |||||
@param id The ID previously passed to startTimer(). | |||||
@return #PUGL_FAILURE if timers are not supported by this system, | |||||
#PUGL_UNKNOWN_ERROR if stopping the timer failed. | |||||
*/ | |||||
Status stopTimer(const uintptr_t id) noexcept | |||||
{ | |||||
return static_cast<Status>(puglStopTimer(cobj(), id)); | |||||
} | |||||
template<PuglEventType t, class Base> | |||||
Status sendEvent(const Event<t, Base>& event) noexcept | |||||
{ | |||||
PuglEvent cEvent{{t, 0}}; | |||||
*reinterpret_cast<Base*>(&cEvent) = event; | |||||
return static_cast<Status>(puglSendEvent(cobj(), &cEvent)); | |||||
} | |||||
/** | |||||
@} | |||||
*/ | |||||
PuglView* cobj() noexcept { return Wrapper::cobj(); } | |||||
const PuglView* cobj() const noexcept { return Wrapper::cobj(); } | |||||
private: | |||||
template<class Target> | |||||
static Status dispatch(Target& target, const PuglEvent* event) | |||||
{ | |||||
switch (event->type) { | |||||
case PUGL_NOTHING: | |||||
return Status::success; | |||||
case PUGL_CREATE: | |||||
return target.onEvent(CreateEvent{event->any}); | |||||
case PUGL_DESTROY: | |||||
return target.onEvent(DestroyEvent{event->any}); | |||||
case PUGL_CONFIGURE: | |||||
return target.onEvent(ConfigureEvent{event->configure}); | |||||
case PUGL_MAP: | |||||
return target.onEvent(MapEvent{event->any}); | |||||
case PUGL_UNMAP: | |||||
return target.onEvent(UnmapEvent{event->any}); | |||||
case PUGL_UPDATE: | |||||
return target.onEvent(UpdateEvent{event->any}); | |||||
case PUGL_EXPOSE: | |||||
return target.onEvent(ExposeEvent{event->expose}); | |||||
case PUGL_CLOSE: | |||||
return target.onEvent(CloseEvent{event->any}); | |||||
case PUGL_FOCUS_IN: | |||||
return target.onEvent(FocusInEvent{event->focus}); | |||||
case PUGL_FOCUS_OUT: | |||||
return target.onEvent(FocusOutEvent{event->focus}); | |||||
case PUGL_KEY_PRESS: | |||||
return target.onEvent(KeyPressEvent{event->key}); | |||||
case PUGL_KEY_RELEASE: | |||||
return target.onEvent(KeyReleaseEvent{event->key}); | |||||
case PUGL_TEXT: | |||||
return target.onEvent(TextEvent{event->text}); | |||||
case PUGL_POINTER_IN: | |||||
return target.onEvent(PointerInEvent{event->crossing}); | |||||
case PUGL_POINTER_OUT: | |||||
return target.onEvent(PointerOutEvent{event->crossing}); | |||||
case PUGL_BUTTON_PRESS: | |||||
return target.onEvent(ButtonPressEvent{event->button}); | |||||
case PUGL_BUTTON_RELEASE: | |||||
return target.onEvent(ButtonReleaseEvent{event->button}); | |||||
case PUGL_MOTION: | |||||
return target.onEvent(MotionEvent{event->motion}); | |||||
case PUGL_SCROLL: | |||||
return target.onEvent(ScrollEvent{event->scroll}); | |||||
case PUGL_CLIENT: | |||||
return target.onEvent(ClientEvent{event->client}); | |||||
case PUGL_TIMER: | |||||
return target.onEvent(TimerEvent{event->timer}); | |||||
case PUGL_LOOP_ENTER: | |||||
return target.onEvent(LoopEnterEvent{event->any}); | |||||
case PUGL_LOOP_LEAVE: | |||||
return target.onEvent(LoopLeaveEvent{event->any}); | |||||
} | |||||
return Status::failure; | |||||
} | |||||
template<class Target> | |||||
static PuglStatus eventFunc(PuglView* view, const PuglEvent* event) noexcept | |||||
{ | |||||
auto* target = static_cast<Target*>(puglGetHandle(view)); | |||||
#ifdef __cpp_exceptions | |||||
try { | |||||
return static_cast<PuglStatus>(dispatch(*target, event)); | |||||
} catch (...) { | |||||
return PUGL_UNKNOWN_ERROR; | |||||
} | |||||
#else | |||||
return static_cast<PuglStatus>(pugl::dispatch(*target, event)); | |||||
#endif | |||||
} | |||||
World& _world; | |||||
}; | |||||
/** | |||||
@} | |||||
@} | |||||
*/ | |||||
} // namespace pugl | |||||
#endif // PUGL_PUGL_HPP |
@@ -1,45 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef PUGL_STUB_HPP | |||||
#define PUGL_STUB_HPP | |||||
#include "pugl/pugl.h" | |||||
#include "pugl/stub.h" | |||||
namespace pugl { | |||||
/** | |||||
@defgroup stubpp Stub | |||||
Stub graphics support. | |||||
@ingroup puglpp | |||||
@{ | |||||
*/ | |||||
/// @copydoc puglStubBackend | |||||
inline const PuglBackend* | |||||
stubBackend() noexcept | |||||
{ | |||||
return puglStubBackend(); | |||||
} | |||||
/** | |||||
@} | |||||
*/ | |||||
} // namespace pugl | |||||
#endif // PUGL_STUB_HPP |
@@ -1,168 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/* | |||||
Note that this header includes Vulkan headers, so if you are writing a | |||||
program or plugin that dynamically loads vulkan, you should first define | |||||
`VK_NO_PROTOTYPES` before including it. | |||||
*/ | |||||
#ifndef PUGL_VULKAN_HPP | |||||
#define PUGL_VULKAN_HPP | |||||
#include "pugl/pugl.h" | |||||
#include "pugl/pugl.hpp" | |||||
#include "pugl/vulkan.h" | |||||
#include <vulkan/vulkan_core.h> | |||||
#include <cstdint> | |||||
namespace pugl { | |||||
/** | |||||
@defgroup vulkanpp Vulkan | |||||
Vulkan graphics support. | |||||
Note that the Pugl C++ wrapper does not use vulkan-hpp because it is a | |||||
heavyweight dependency which not everyone uses, and its design is not very | |||||
friendly to dynamic loading in plugins anyway. However, if you do use | |||||
vulkan-hpp smart handles, it is relatively straightforward to wrap the | |||||
result of createSurface() manually. | |||||
@ingroup puglpp | |||||
@{ | |||||
*/ | |||||
/// @copydoc PuglVulkanLoader | |||||
class VulkanLoader final | |||||
: public detail::Wrapper<PuglVulkanLoader, puglFreeVulkanLoader> | |||||
{ | |||||
public: | |||||
/** | |||||
Create a new dynamic loader for Vulkan functions. | |||||
This dynamically loads the Vulkan library and gets the load functions | |||||
from it. | |||||
Note that this constructor does not throw exceptions, though failure is | |||||
possible. To check if the Vulkan library failed to load, test this | |||||
loader, which is explicitly convertible to `bool`. It is safe to use a | |||||
failed loader, but the accessors will always return null. | |||||
*/ | |||||
explicit VulkanLoader(World& world) noexcept | |||||
: Wrapper{puglNewVulkanLoader(world.cobj())} | |||||
{} | |||||
/** | |||||
Return the `vkGetInstanceProcAddr` function. | |||||
@return Null if the Vulkan library failed to load, or does not contain | |||||
this function (which is unlikely and indicates a broken system). | |||||
*/ | |||||
PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc() const noexcept | |||||
{ | |||||
return cobj() ? puglGetInstanceProcAddrFunc(cobj()) : nullptr; | |||||
} | |||||
/** | |||||
Return the `vkGetDeviceProcAddr` function. | |||||
@return Null if the Vulkan library failed to load, or does not contain | |||||
this function (which is unlikely and indicates a broken system). | |||||
*/ | |||||
PFN_vkGetDeviceProcAddr getDeviceProcAddrFunc() const noexcept | |||||
{ | |||||
return cobj() ? puglGetDeviceProcAddrFunc(cobj()) : nullptr; | |||||
} | |||||
/// Return true if this loader is valid to use | |||||
explicit operator bool() const noexcept { return cobj(); } | |||||
}; | |||||
/** | |||||
A simple wrapper for an array of static C strings. | |||||
This provides a minimal API that supports iteration, like `std::vector`, but | |||||
avoids allocation, exceptions, and a dependency on the C++ standard library. | |||||
*/ | |||||
class StaticStringArray final | |||||
{ | |||||
public: | |||||
using value_type = const char*; | |||||
using const_iterator = const char* const*; | |||||
using size_type = uint32_t; | |||||
StaticStringArray(const char* const* strings, const uint32_t size) noexcept | |||||
: _strings{strings} | |||||
, _size{size} | |||||
{} | |||||
const char* const* begin() const noexcept { return _strings; } | |||||
const char* const* end() const noexcept { return _strings + _size; } | |||||
const char* const* data() const noexcept { return _strings; } | |||||
uint32_t size() const noexcept { return _size; } | |||||
private: | |||||
const char* const* _strings; | |||||
uint32_t _size; | |||||
}; | |||||
/** | |||||
Return the Vulkan instance extensions required to draw to a PuglView. | |||||
If successful, the returned array always contains "VK_KHR_surface", along | |||||
with whatever other platform-specific extensions are required. | |||||
@return An array of extension name strings. | |||||
*/ | |||||
inline StaticStringArray | |||||
getInstanceExtensions() noexcept | |||||
{ | |||||
uint32_t count = 0; | |||||
const char* const* const extensions = puglGetInstanceExtensions(&count); | |||||
return StaticStringArray{extensions, count}; | |||||
} | |||||
/// @copydoc puglCreateSurface | |||||
inline VkResult | |||||
createSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||||
View& view, | |||||
VkInstance instance, | |||||
const VkAllocationCallbacks* const allocator, | |||||
VkSurfaceKHR* const surface) noexcept | |||||
{ | |||||
const VkResult r = puglCreateSurface( | |||||
vkGetInstanceProcAddr, view.cobj(), instance, allocator, surface); | |||||
return (!r && !surface) ? VK_ERROR_INITIALIZATION_FAILED : r; | |||||
} | |||||
/// @copydoc puglVulkanBackend | |||||
inline const PuglBackend* | |||||
vulkanBackend() noexcept | |||||
{ | |||||
return puglVulkanBackend(); | |||||
} | |||||
/** | |||||
@} | |||||
*/ | |||||
} // namespace pugl | |||||
#endif // PUGL_VULKAN_HPP |
@@ -1,7 +0,0 @@ | |||||
subdir('include') | |||||
pkg.generate(name: 'Pugl++', | |||||
filebase: 'puglpp-@0@'.format(major_version), | |||||
subdirs: ['puglpp-@0@'.format(major_version)], | |||||
version: meson.project_version(), | |||||
description: 'Pugl GUI library C++ bindings') |
@@ -1,2 +0,0 @@ | |||||
configure_file(copy: true, input: '../../resources/pugl.svg', output: 'pugl.svg') | |||||
@@ -1,32 +0,0 @@ | |||||
PROJECT_NAME = Pugl | |||||
PROJECT_BRIEF = "A minimal portable API for embeddable GUIs" | |||||
QUIET = YES | |||||
WARN_AS_ERROR = YES | |||||
WARN_IF_UNDOCUMENTED = NO | |||||
WARN_NO_PARAMDOC = NO | |||||
JAVADOC_AUTOBRIEF = YES | |||||
FULL_PATH_NAMES = NO | |||||
CASE_SENSE_NAMES = YES | |||||
HIDE_IN_BODY_DOCS = YES | |||||
REFERENCES_LINK_SOURCE = NO | |||||
GENERATE_HTML = NO | |||||
GENERATE_LATEX = NO | |||||
GENERATE_XML = YES | |||||
XML_PROGRAMLISTING = NO | |||||
SHOW_FILES = NO | |||||
MACRO_EXPANSION = YES | |||||
PREDEFINED = PUGL_API \ | |||||
PUGL_DISABLE_DEPRECATED \ | |||||
PUGL_CONST_API= \ | |||||
PUGL_CONST_FUNC= | |||||
RECURSIVE = YES | |||||
STRIP_FROM_PATH = @PUGL_SRCDIR@ | |||||
INPUT = @PUGL_SRCDIR@/include | |||||
OUTPUT_DIRECTORY = @DOX_OUTPUT@ |
@@ -1,5 +0,0 @@ | |||||
c_pugl_rst = custom_target( | |||||
'C API ReST Documentation', | |||||
command: [dox_to_sphinx, '-f', '@INPUT0@', 'doc/c/api'], | |||||
input: [c_index_xml] + c_rst_files, | |||||
output: 'pugl.rst') |
@@ -1,101 +0,0 @@ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
###################### | |||||
Driving the Event Loop | |||||
###################### | |||||
Pugl does not contain any threads or other event loop "magic". | |||||
For flexibility, the event loop is driven explicitly by repeatedly calling :func:`puglUpdate`, | |||||
which processes events from the window system and dispatches them to views when necessary. | |||||
The exact use of :func:`puglUpdate` depends on the application. | |||||
Plugins should call it with a ``timeout`` of 0 in a callback driven by the host. | |||||
This avoids blocking the main loop, | |||||
since other plugins and the host itself need to run as well. | |||||
A program can use whatever timeout is appropriate: | |||||
event-driven applications may wait forever by using a ``timeout`` of -1, | |||||
while those that draw continuously may use a significant fraction of the frame period | |||||
(with enough time left over to render). | |||||
********* | |||||
Redrawing | |||||
********* | |||||
Occasional redrawing can be requested by calling :func:`puglPostRedisplay` or :func:`puglPostRedisplayRect`. | |||||
After these are called, | |||||
a :struct:`PuglExposeEvent` will be dispatched on the next call to :func:`puglUpdate`. | |||||
For continuous redrawing, | |||||
call :func:`puglPostRedisplay` while handling a :struct:`PuglUpdateEvent` event. | |||||
This event is sent just before views are redrawn, | |||||
so it can be used as a hook to expand the update region right before the view is exposed. | |||||
Anything else that needs to be done every frame can be handled similarly. | |||||
***************** | |||||
Event Dispatching | |||||
***************** | |||||
Ideally, pending events are dispatched during a call to :func:`puglUpdate`, | |||||
directly within the scope of that call. | |||||
Unfortunately, this is not universally true due to differences between platforms. | |||||
MacOS | |||||
===== | |||||
On MacOS, drawing is handled specially and not by the normal event queue mechanism. | |||||
This means that configure and expose events, | |||||
and possibly others, | |||||
may be dispatched to a view outside the scope of a :func:`puglUpdate` call. | |||||
In general, you can not rely on coherent event dispatching semantics on MacOS: | |||||
the operating system can call into application code at "random" times, | |||||
and these calls may result in Pugl events being dispatched. | |||||
An application that follows the Pugl guidelines should work fine, | |||||
but there is one significant inconsistency you may encounter on MacOS: | |||||
posting a redisplay will not wake up a blocked :func:`puglUpdate` call. | |||||
Windows | |||||
======= | |||||
On Windows, the application has relatively tight control over the event loop, | |||||
so events are typically dispatched explicitly by :func:`puglUpdate`. | |||||
Drawing is handled by events, | |||||
so posting a redisplay will wake up a blocked :func:`puglUpdate` call. | |||||
However, it is possible for the system to dispatch events at other times. | |||||
So, | |||||
it is possible for events to be dispatched outside the scope of a :func:`puglUpdate` call, | |||||
but this does not happen in normal circumstances and can largely be ignored. | |||||
X11 | |||||
=== | |||||
On X11, the application strictly controls event dispatching, | |||||
and there is no way for the system to call into application code at surprising times. | |||||
So, all events are dispatched in the scope of a :func:`puglUpdate` call. | |||||
********************* | |||||
Recursive Event Loops | |||||
********************* | |||||
On Windows and MacOS, | |||||
the event loop is stalled while the user is resizing the window or, | |||||
on Windows, | |||||
has displayed the window menu. | |||||
This means that :func:`puglUpdate` will block until the resize is finished, | |||||
or the menu is closed. | |||||
Pugl dispatches :struct:`PuglLoopEnterEvent` and :struct:`PuglLoopLeaveEvent` events to notify the application of this situation. | |||||
If you want to continuously redraw during resizing on these platforms, | |||||
you can schedule a timer with :func:`puglStartTimer` when the recursive loop is entered, | |||||
and post redisplays when handling the :struct:`PuglTimerEvent`. | |||||
Be sure to remove the timer with :func:`puglStopTimer` when the recursive loop is finished. | |||||
On X11, there are no recursive event loops, | |||||
and everything works as usual while the user is resizing the window. | |||||
There is nothing special about a "live resize" on X11, | |||||
and the above loop events will never be dispatched. | |||||
@@ -1,84 +0,0 @@ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
*************** | |||||
Handling Events | |||||
*************** | |||||
Events are sent to a view when it has received user input, | |||||
must be drawn, or in other situations that may need to be handled such as resizing. | |||||
Events are sent to the event handler as a :union:`PuglEvent` union. | |||||
The ``type`` field defines the type of the event and which field of the union is active. | |||||
The application must handle at least :enumerator:`PUGL_CONFIGURE <PuglEventType.PUGL_CONFIGURE>` | |||||
and :enumerator:`PUGL_EXPOSE <PuglEventType.PUGL_EXPOSE>` to draw anything, | |||||
but there are many other :enum:`event types <PuglEventType>`. | |||||
For example, a basic event handler might look something like this: | |||||
.. code-block:: c | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
MyApp* app = (MyApp*)puglGetHandle(view); | |||||
switch (event->type) { | |||||
case PUGL_CREATE: | |||||
return setupGraphics(app); | |||||
case PUGL_DESTROY: | |||||
return teardownGraphics(app); | |||||
case PUGL_CONFIGURE: | |||||
return resize(app, event->configure.width, event->configure.height); | |||||
case PUGL_EXPOSE: | |||||
return draw(app, view); | |||||
case PUGL_CLOSE: | |||||
return quit(app); | |||||
case PUGL_BUTTON_PRESS: | |||||
return onButtonPress(app, view, event->button); | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
Using the Graphics Context | |||||
========================== | |||||
Drawing | |||||
------- | |||||
Note that Pugl uses a different drawing model than many libraries, | |||||
particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_. | |||||
In that style of code, drawing is performed imperatively in the main loop, | |||||
but with Pugl, the application must draw only while handling an expose event. | |||||
This is because Pugl supports event-driven applications that only draw the damaged region when necessary, | |||||
and handles exposure internally to provide optimized and consistent behavior across platforms. | |||||
Cairo Context | |||||
------------- | |||||
A Cairo context is created for each :struct:`PuglExposeEvent`, | |||||
and only exists during the handling of that event. | |||||
Null is returned by :func:`puglGetContext` at any other time. | |||||
OpenGL Context | |||||
-------------- | |||||
The OpenGL context is only active during the handling of these events: | |||||
- :struct:`PuglCreateEvent` | |||||
- :struct:`PuglDestroyEvent` | |||||
- :struct:`PuglConfigureEvent` | |||||
- :struct:`PuglExposeEvent` | |||||
As always, drawing is only possible during an expose. | |||||
Vulkan Context | |||||
-------------- | |||||
With Vulkan, the graphics context is managed by the application rather than Pugl. | |||||
However, drawing must still only be performed during an expose. | |||||
@@ -1,11 +0,0 @@ | |||||
#### | |||||
Pugl | |||||
#### | |||||
.. include:: summary.rst | |||||
.. toctree:: | |||||
deployment | |||||
overview | |||||
api/pugl |
@@ -1,48 +0,0 @@ | |||||
config = configuration_data() | |||||
config.set('PUGL_VERSION', meson.project_version()) | |||||
conf_py = configure_file(configuration: config, | |||||
input: '../conf.py.in', | |||||
output: 'conf.py') | |||||
configure_file(copy: true, input: '../deployment.rst', output: 'deployment.rst') | |||||
configure_file(copy: true, input: '../summary.rst', output: 'summary.rst') | |||||
c_rst_files = files( | |||||
'index.rst', | |||||
'overview.rst', | |||||
'world.rst', | |||||
'view.rst', | |||||
'events.rst', | |||||
'event-loop.rst', | |||||
'shutting-down.rst' | |||||
) | |||||
foreach f : c_rst_files | |||||
configure_file(copy: true, input: f, output: '@PLAINNAME@') | |||||
endforeach | |||||
subdir('xml') | |||||
subdir('api') | |||||
docs = custom_target( | |||||
'singlehtml C documentation for pugl', | |||||
command: [sphinx_build, '-M', 'singlehtml', | |||||
meson.current_build_dir(), meson.current_build_dir(), | |||||
'-E', '-q', '-t', 'singlehtml'], | |||||
input: [c_rst_files, c_pugl_rst, c_index_xml], | |||||
output: 'singlehtml', | |||||
build_by_default: true, | |||||
install: true, | |||||
install_dir: docdir / 'pugl-0') | |||||
docs = custom_target( | |||||
'html C documentation for pugl', | |||||
command: [sphinx_build, '-M', 'html', | |||||
meson.current_build_dir(), meson.current_build_dir(), | |||||
'-E', '-q', '-t', 'html'], | |||||
input: [c_rst_files, c_pugl_rst, c_index_xml], | |||||
output: 'html', | |||||
build_by_default: true, | |||||
install: true, | |||||
install_dir: docdir / 'pugl-0') |
@@ -1,26 +0,0 @@ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
######## | |||||
Overview | |||||
######## | |||||
The Pugl API revolves around two main objects: the `world` and the `view`. | |||||
An application creates a world to manage top-level state, | |||||
then creates one or more views to display. | |||||
The core API (excluding backend-specific components) is declared in ``pugl.h``: | |||||
.. code-block:: c | |||||
#include <pugl/pugl.h> | |||||
.. toctree:: | |||||
world | |||||
view | |||||
events | |||||
event-loop | |||||
shutting-down | |||||
.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/ |
@@ -1,20 +0,0 @@ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
############# | |||||
Shutting Down | |||||
############# | |||||
When a view is closed, | |||||
it will receive a :struct:`PuglCloseEvent`. | |||||
An application may also set a flag based on user input or other conditions, | |||||
which can be used to break out of the main loop and stop calling :func:`puglUpdate`. | |||||
When the main event loop has finished running, | |||||
any views and the world need to be destroyed, in that order. | |||||
For example: | |||||
.. code-block:: c | |||||
puglFreeView(view); | |||||
puglFreeWorld(world); |
@@ -1,321 +0,0 @@ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
############### | |||||
Creating a View | |||||
############### | |||||
A view is a drawable region that receives events. | |||||
You may think of it as a window, | |||||
though it may be embedded and not represent a top-level system window. [#f1]_ | |||||
Creating a visible view is a multi-step process. | |||||
When a new view is created with :func:`puglNewView`, | |||||
it does not yet represent a "real" system view: | |||||
.. code-block:: c | |||||
PuglView* view = puglNewView(world); | |||||
********************* | |||||
Configuring the Frame | |||||
********************* | |||||
Before display, | |||||
the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set. | |||||
These allow the window system (or plugin host) to arrange the view properly. | |||||
For example: | |||||
.. code-block:: c | |||||
const double defaultWidth = 1920.0; | |||||
const double defaultHeight = 1080.0; | |||||
puglSetWindowTitle(view, "My Window"); | |||||
puglSetDefaultSize(view, defaultWidth, defaultHeight); | |||||
puglSetMinSize(view, defaultWidth / 4.0, defaultHeight / 4.0); | |||||
puglSetAspectRatio(view, 1, 1, 16, 9); | |||||
There are also several :enum:`hints <PuglViewHint>` for basic attributes that can be set: | |||||
.. code-block:: c | |||||
puglSetViewHint(view, PUGL_RESIZABLE, PUGL_TRUE); | |||||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE); | |||||
********* | |||||
Embedding | |||||
********* | |||||
To embed the view in another window, | |||||
you will need to somehow get the :type:`native view handle <PuglNativeView>` for the parent, | |||||
then set it with :func:`puglSetParentWindow`. | |||||
If the parent is a Pugl view, | |||||
the native handle can be accessed with :func:`puglGetNativeWindow`. | |||||
For example: | |||||
.. code-block:: c | |||||
puglSetParentWindow(view, puglGetNativeWindow(parent)); | |||||
************************ | |||||
Setting an Event Handler | |||||
************************ | |||||
In order to actually do anything, a view must process events from the system. | |||||
Pugl dispatches all events to a single :type:`event handling function <PuglEventFunc>`, | |||||
which is set with :func:`puglSetEventFunc`: | |||||
.. code-block:: c | |||||
puglSetEventFunc(view, onEvent); | |||||
See :doc:`events` for details on writing the event handler itself. | |||||
***************** | |||||
Setting View Data | |||||
***************** | |||||
Since the event handler is called with only a view pointer and an event, | |||||
there needs to be some way to access application data associated with the view. | |||||
Similar to :ref:`setting application data <setting-application-data>`, | |||||
this is done by setting an opaque handle on the view with :func:`puglSetHandle`, | |||||
for example: | |||||
.. code-block:: c | |||||
puglSetHandle(view, myViewData); | |||||
The handle can be later retrieved, | |||||
likely in the event handler, | |||||
with :func:`puglGetHandle`: | |||||
.. code-block:: c | |||||
MyViewData* data = (MyViewData*)puglGetHandle(view); | |||||
All non-constant data should be accessed via this handle, | |||||
to avoid problems associated with static mutable data. | |||||
If data is also associated with the world, | |||||
it can be retrieved via the view using :func:`puglGetWorld`: | |||||
.. code-block:: c | |||||
PuglWorld* world = puglGetWorld(view); | |||||
MyApp* app = (MyApp*)puglGetWorldHandle(world); | |||||
***************** | |||||
Setting a Backend | |||||
***************** | |||||
Before being realized, the view must have a backend set with :func:`puglSetBackend`. | |||||
The backend manages the graphics API that will be used for drawing. | |||||
Pugl includes backends and supporting API for | |||||
:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`. | |||||
Using Cairo | |||||
=========== | |||||
Cairo-specific API is declared in the ``cairo.h`` header: | |||||
.. code-block:: c | |||||
#include <pugl/cairo.h> | |||||
The Cairo backend is provided by :func:`puglCairoBackend()`: | |||||
.. code-block:: c | |||||
puglSetBackend(view, puglCairoBackend()); | |||||
No additional configuration is required for Cairo. | |||||
To draw when handling an expose event, | |||||
the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`puglGetContext`: | |||||
.. code-block:: c | |||||
cairo_t* cr = (cairo_t*)puglGetContext(view); | |||||
Using OpenGL | |||||
============ | |||||
OpenGL-specific API is declared in the ``gl.h`` header: | |||||
.. code-block:: c | |||||
#include <pugl/gl.h> | |||||
The OpenGL backend is provided by :func:`puglGlBackend()`: | |||||
.. code-block:: c | |||||
puglSetBackend(view, puglGlBackend()); | |||||
Some hints must also be set so that the context can be set up correctly. | |||||
For example, to use OpenGL 3.3 Core Profile: | |||||
.. code-block:: c | |||||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | |||||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MINOR, 3); | |||||
If you need to perform some setup using the OpenGL API, | |||||
there are two ways to do so. | |||||
The OpenGL context is active when | |||||
:enumerator:`PUGL_CREATE <PuglEventType.PUGL_CREATE>` and | |||||
:enumerator:`PUGL_DESTROY <PuglEventType.PUGL_DESTROY>` | |||||
events are dispatched, | |||||
so things like creating and destroying shaders and textures can be done then. | |||||
Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler, | |||||
:func:`puglEnterContext` and :func:`puglLeaveContext` can be used to manually activate the OpenGL context during application setup. | |||||
Note, however, that unlike many other APIs, these functions must not be used for drawing. | |||||
It is only valid to use the OpenGL API for configuration in a manually entered context, | |||||
rendering will not work. | |||||
For example: | |||||
.. code-block:: c | |||||
puglEnterContext(view); | |||||
setupOpenGL(myApp); | |||||
puglLeaveContext(view); | |||||
while (!myApp->quit) { | |||||
puglUpdate(world, 0.0); | |||||
} | |||||
puglEnterContext(view); | |||||
teardownOpenGL(myApp); | |||||
puglLeaveContext(view); | |||||
Using Vulkan | |||||
============ | |||||
Vulkan-specific API is declared in the ``vulkan.h`` header. | |||||
This header includes Vulkan headers, | |||||
so if you are dynamically loading Vulkan at runtime, | |||||
you should define ``VK_NO_PROTOTYPES`` before including it. | |||||
.. code-block:: c | |||||
#define VK_NO_PROTOTYPES | |||||
#include <pugl/vulkan.h> | |||||
The Vulkan backend is provided by :func:`puglVulkanBackend()`: | |||||
.. code-block:: c | |||||
puglSetBackend(view, puglVulkanBackend()); | |||||
Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly. | |||||
Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API. | |||||
Loading Vulkan | |||||
-------------- | |||||
For maximum compatibility, | |||||
it is best to not link to Vulkan at compile-time, | |||||
but instead load the Vulkan API at run-time. | |||||
To do so, first create a :struct:`PuglVulkanLoader`: | |||||
.. code-block:: c | |||||
PuglVulkanLoader* loader = puglNewVulkanLoader(world); | |||||
The loader manages the dynamically loaded Vulkan library, | |||||
so it must be kept alive for as long as the application is using Vulkan. | |||||
You can get the function used to load Vulkan functions with :func:`puglGetInstanceProcAddrFunc`: | |||||
.. code-block:: c | |||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = | |||||
puglGetInstanceProcAddrFunc(loader); | |||||
This vkGetInstanceProcAddr_ function can be used to load the rest of the Vulkan API. | |||||
For example, you can use it to get the vkCreateInstance_ function, | |||||
then use that to create your Vulkan instance. | |||||
In practice, you will want to use some loader or wrapper API since there are many Vulkan functions. | |||||
For advanced situations, | |||||
there is also :func:`puglGetDeviceProcAddrFunc` which retrieves the vkGetDeviceProcAddr_ function instead. | |||||
The Vulkan loader is provided for convenience, | |||||
so that applications to not need to write platform-specific code to load Vulkan. | |||||
Its use it not mandatory and Pugl can be used with Vulkan loaded by some other method. | |||||
Linking with Vulkan | |||||
------------------- | |||||
If you do want to link to the Vulkan library at compile time, | |||||
note that the Pugl Vulkan backend does not depend on it, | |||||
so you will have to do so explicitly. | |||||
Creating a Surface | |||||
------------------ | |||||
The details of using Vulkan are far beyond the scope of this documentation, | |||||
but Pugl provides a portable function, :func:`puglCreateSurface`, | |||||
to get the Vulkan surface for a view. | |||||
Assuming you have somehow created your ``VkInstance``, | |||||
you can get the surface for a view using :func:`puglCreateSurface`: | |||||
.. code-block:: c | |||||
VkSurfaceKHR* surface = NULL; | |||||
puglCreateSurface(puglGetDeviceProcAddrFunc(loader), | |||||
view, | |||||
vulkanInstance, | |||||
NULL, | |||||
&surface); | |||||
**************** | |||||
Showing the View | |||||
**************** | |||||
Once the view is configured, it can be "realized" with :func:`puglRealize`. | |||||
This creates a "real" system view, for example: | |||||
.. code-block:: c | |||||
PuglStatus status = puglRealize(view); | |||||
if (status) { | |||||
fprintf(stderr, "Error realizing view (%s)\n", puglStrerror(status)); | |||||
} | |||||
Note that realizing a view can fail for many reasons, | |||||
so the return code should always be checked. | |||||
This is generally the case for any function that interacts with the window system. | |||||
Most functions also return a :enum:`PuglStatus`, | |||||
but these checks are omitted for brevity in the rest of this documentation. | |||||
A realized view is not initially visible, | |||||
but can be shown with :func:`puglShow`: | |||||
.. code-block:: c | |||||
puglShow(view); | |||||
To create an initially visible view, | |||||
it is also possible to simply call :func:`puglShow` right away. | |||||
The view will be automatically realized if necessary. | |||||
.. rubric:: Footnotes | |||||
.. [#f1] MacOS has a strong distinction between | |||||
`views <https://developer.apple.com/documentation/appkit/nsview>`_, | |||||
which may be nested, and | |||||
`windows <https://developer.apple.com/documentation/appkit/nswindow>`_, | |||||
which may not. | |||||
On Windows and X11, everything is a nestable window, | |||||
but top-level windows are configured differently. | |||||
.. _vkCreateInstance: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html | |||||
.. _vkGetDeviceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetDeviceProcAddr.html | |||||
.. _vkGetInstanceProcAddr: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html |
@@ -1,65 +0,0 @@ | |||||
################ | |||||
Creating a World | |||||
################ | |||||
.. default-domain:: c | |||||
.. highlight:: c | |||||
The world is the top-level object which represents an instance of Pugl. | |||||
It handles the connection to the window system, | |||||
and manages views and the event loop. | |||||
An application typically has a single world, | |||||
which is constructed once on startup and used to drive the main event loop. | |||||
************ | |||||
Construction | |||||
************ | |||||
A world must be created before any views, and it must outlive all of its views. | |||||
A world is created with :func:`puglNewWorld`, for example: | |||||
.. code-block:: c | |||||
PuglWorld* world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
For a plugin, specify :enumerator:`PUGL_MODULE <PuglWorldType.PUGL_MODULE>` instead. | |||||
In some cases, it is necessary to pass additional flags. | |||||
For example, Vulkan requires thread support: | |||||
.. code-block:: c | |||||
PuglWorld* world = puglNewWorld(PUGL_MODULE, PUGL_WORLD_THREADS) | |||||
It is a good idea to set a class name for your project with :func:`puglSetClassName`. | |||||
This allows the window system to distinguish different applications and, | |||||
for example, users to set up rules to manage their windows nicely: | |||||
.. code-block:: c | |||||
puglSetClassName(world, "MyAwesomeProject") | |||||
.. _setting-application-data: | |||||
************************ | |||||
Setting Application Data | |||||
************************ | |||||
Pugl will call an event handler in the application with only a view pointer and an event, | |||||
so there needs to be some way to access the data you use in your application. | |||||
This is done by setting an opaque handle on the world with :func:`puglSetWorldHandle`, | |||||
for example: | |||||
.. code-block:: c | |||||
puglSetWorldHandle(world, myApp); | |||||
The handle can be later retrieved with :func:`puglGetWorldHandle`: | |||||
.. code-block:: c | |||||
MyApp* app = (MyApp*)puglGetWorldHandle(world); | |||||
All non-constant data should be accessed via this handle, | |||||
to avoid problems associated with static mutable data. | |||||
@@ -1,19 +0,0 @@ | |||||
doxygen = find_program('doxygen') | |||||
c_doxygen_input = [] | |||||
foreach h : c_headers | |||||
c_doxygen_input += ['..' / h] | |||||
endforeach | |||||
config = configuration_data() | |||||
config.set('PUGL_SRCDIR', pugl_src_root) | |||||
config.set('DOX_OUTPUT', meson.current_build_dir() / '..') | |||||
c_doxyfile = configure_file(configuration: config, | |||||
input: '../Doxyfile.in', | |||||
output: 'Doxyfile') | |||||
c_index_xml = custom_target('c-index.xml', | |||||
command: [doxygen, '@INPUT0@'], | |||||
input: [c_doxyfile] + c_header_files, | |||||
output: 'index.xml') |
@@ -1,85 +0,0 @@ | |||||
# Project information | |||||
project = "Pugl" | |||||
copyright = "2020, David Robillard" | |||||
author = "David Robillard" | |||||
release = "@PUGL_VERSION@" | |||||
# General configuration | |||||
exclude_patterns = ["xml"] | |||||
language = "en" | |||||
nitpicky = True | |||||
pygments_style = "friendly" | |||||
# Ignore everything opaque or external for nitpicky mode | |||||
_opaque = [ | |||||
"PFN_vkGetDeviceProcAddr", | |||||
"PFN_vkGetInstanceProcAddr", | |||||
"PuglBackendImpl", | |||||
"PuglViewImpl", | |||||
"PuglVulkanLoaderImpl", | |||||
"PuglWorldImpl", | |||||
"VkAllocationCallbacks", | |||||
"VkInstance", | |||||
"VkResult", | |||||
"VkSurfaceKHR", | |||||
"size_t", | |||||
"uint32_t", | |||||
"uintptr_t", | |||||
] | |||||
_c_nitpick_ignore = map(lambda x: ("c:identifier", x), _opaque) | |||||
_cpp_nitpick_ignore = map(lambda x: ("cpp:identifier", x), _opaque) | |||||
nitpick_ignore = list(_c_nitpick_ignore) + list(_cpp_nitpick_ignore) | |||||
# C++ | |||||
cpp_index_common_prefix = ["pugl::"] | |||||
# HTML output | |||||
html_copy_source = False | |||||
html_short_title = "Pugl" | |||||
html_static_path = ["../_static"] | |||||
html_theme = "sphinx_lv2_theme" | |||||
if tags.has('singlehtml'): | |||||
html_sidebars = { | |||||
"**": [ | |||||
"globaltoc.html", | |||||
] | |||||
} | |||||
html_theme_options = { | |||||
"body_max_width": "51em", | |||||
"body_min_width": "51em", | |||||
"description": "A minimal portable API for embeddable GUIs.", | |||||
"show_footer_version": True, | |||||
"show_logo_version": False, | |||||
"logo": "pugl.svg", | |||||
"logo_name": True, | |||||
"logo_width": "8em", | |||||
"nosidebar": False, | |||||
"page_width": "80em", | |||||
"sidebar_width": "16em", | |||||
"globaltoc_maxdepth": 3, | |||||
"globaltoc_collapse": False, | |||||
} | |||||
else: | |||||
html_theme_options = { | |||||
"body_max_width": "60em", | |||||
"body_min_width": "40em", | |||||
"description": "A minimal portable API for embeddable GUIs.", | |||||
"show_footer_version": True, | |||||
"show_logo_version": False, | |||||
"logo": "pugl.svg", | |||||
"logo_name": True, | |||||
"logo_width": "8em", | |||||
"nosidebar": True, | |||||
"page_width": "60em", | |||||
"sidebar_width": "14em", | |||||
"globaltoc_maxdepth": 1, | |||||
"globaltoc_collapse": True, | |||||
} |
@@ -1,37 +0,0 @@ | |||||
PROJECT_NAME = Pugl | |||||
PROJECT_BRIEF = "A minimal portable API for embeddable GUIs" | |||||
QUIET = YES | |||||
WARN_AS_ERROR = YES | |||||
WARN_IF_UNDOCUMENTED = NO | |||||
WARN_NO_PARAMDOC = NO | |||||
JAVADOC_AUTOBRIEF = YES | |||||
CASE_SENSE_NAMES = YES | |||||
EXCLUDE_SYMBOLS = pugl::detail | |||||
EXTRACT_LOCAL_CLASSES = NO | |||||
EXTRACT_PRIVATE = NO | |||||
HIDE_IN_BODY_DOCS = YES | |||||
HIDE_UNDOC_CLASSES = YES | |||||
HIDE_UNDOC_MEMBERS = YES | |||||
REFERENCES_LINK_SOURCE = NO | |||||
GENERATE_HTML = NO | |||||
GENERATE_LATEX = NO | |||||
GENERATE_XML = YES | |||||
XML_PROGRAMLISTING = NO | |||||
SHOW_FILES = NO | |||||
MACRO_EXPANSION = YES | |||||
PREDEFINED = PUGL_API \ | |||||
PUGL_DISABLE_DEPRECATED \ | |||||
PUGL_CONST_API= \ | |||||
PUGL_CONST_FUNC= | |||||
RECURSIVE = YES | |||||
STRIP_FROM_PATH = @PUGL_SRCDIR@ | |||||
INPUT = @PUGL_SRCDIR@/include \ | |||||
@PUGL_SRCDIR@/bindings/cpp/include | |||||
OUTPUT_DIRECTORY = @DOX_OUTPUT@ |
@@ -1,5 +0,0 @@ | |||||
cpp_pugl_rst = custom_target( | |||||
'C++ API ReST Documentation', | |||||
command: [dox_to_sphinx, '-l', 'cpp', '-f', '@INPUT@', meson.current_build_dir()], | |||||
input: cpp_index_xml, | |||||
output: 'pugl.rst') |
@@ -1,37 +0,0 @@ | |||||
.. default-domain:: cpp | |||||
.. highlight:: cpp | |||||
.. namespace:: pugl | |||||
###################### | |||||
Driving the Event Loop | |||||
###################### | |||||
Pugl does not contain any threads or other event loop "magic". | |||||
For flexibility, the event loop is driven manually by repeatedly calling :func:`World::update`, | |||||
which processes events from the window system and dispatches them to views when necessary. | |||||
The exact use of :func:`World::update` depends on the application. | |||||
Plugins typically call it with a ``timeout`` of 0 in a callback driven by the host. | |||||
This avoids blocking the main loop, | |||||
since other plugins and the host itself need to run as well. | |||||
A program can use whatever timeout is appropriate: | |||||
event-driven applications may wait forever by using a ``timeout`` of -1, | |||||
while those that draw continuously may use a significant fraction of the frame period | |||||
(with enough time left over to render). | |||||
********* | |||||
Redrawing | |||||
********* | |||||
Occasional redrawing can be requested by calling :func:`View::postRedisplay` or :func:`View::postRedisplayRect`. | |||||
After these are called, | |||||
a :type:`ExposeEvent` will be dispatched on the next call to :func:`World::update`. | |||||
Note, however, that this will not wake up a blocked :func:`World::update` call on MacOS | |||||
(which does not handle drawing via events). | |||||
For continuous redrawing, | |||||
call :func:`View::postRedisplay` while handling a :type:`UpdateEvent`. | |||||
This event is sent just before views are redrawn, | |||||
so it can be used as a hook to expand the update region right before the view is exposed. | |||||
Anything else that needs to be done every frame can be handled similarly. |
@@ -1,43 +0,0 @@ | |||||
.. default-domain:: cpp | |||||
.. highlight:: cpp | |||||
.. namespace:: pugl | |||||
############### | |||||
Handling Events | |||||
############### | |||||
Events are sent to a view when it has received user input, | |||||
must be drawn, or in other situations that may need to be handled such as resizing. | |||||
Events are sent to the ``onEvent`` method that takes the matching event type. | |||||
The application must handle at least :type:`ConfigureEvent` | |||||
and :type:`ExposeEvent` to draw anything, | |||||
but there are many other :type:`event types <pugl::EventType>`. | |||||
For example, basic event handling for our above class might look something like: | |||||
.. code-block:: cpp | |||||
pugl::Status | |||||
MyView::onEvent(const pugl::ConfigureEvent& event) noexcept | |||||
{ | |||||
return resize(event.width, event.height); | |||||
} | |||||
pugl::Status | |||||
MyView::onEvent(const pugl::ExposeEvent& event) noexcept | |||||
{ | |||||
return drawMyAwesomeInterface(event.x, event.y, event.width, event.height); | |||||
} | |||||
******* | |||||
Drawing | |||||
******* | |||||
Note that Pugl uses a different drawing model than many libraries, | |||||
particularly those designed for game-style main loops like `SDL <https://libsdl.org/>`_ and `GLFW <https://www.glfw.org/>`_. | |||||
In that style of code, drawing is performed imperatively in the main loop, | |||||
but with Pugl, the application must draw only while handling an expose event. | |||||
This is because Pugl supports event-driven applications that only draw the damaged region when necessary, | |||||
and handles exposure internally to provide optimized and consistent behavior across platforms. |
@@ -1,12 +0,0 @@ | |||||
#### | |||||
Pugl | |||||
#### | |||||
.. include:: summary.rst | |||||
.. toctree:: | |||||
deployment | |||||
overview | |||||
api/pugl | |||||
api/puglpp |
@@ -1,47 +0,0 @@ | |||||
config = configuration_data() | |||||
config.set('PUGL_VERSION', meson.project_version()) | |||||
conf_py = configure_file(configuration: config, | |||||
input: '../conf.py.in', | |||||
output: 'conf.py') | |||||
configure_file(copy: true, input: '../deployment.rst', output: 'deployment.rst') | |||||
configure_file(copy: true, input: '../summary.rst', output: 'summary.rst') | |||||
cpp_rst_files = files( | |||||
'index.rst', | |||||
'overview.rst', | |||||
'world.rst', | |||||
'view.rst', | |||||
'events.rst', | |||||
'event-loop.rst', | |||||
) | |||||
foreach f : cpp_rst_files | |||||
configure_file(copy: true, input: f, output: '@PLAINNAME@') | |||||
endforeach | |||||
subdir('xml') | |||||
subdir('api') | |||||
docs = custom_target( | |||||
'singlehtml C++ documentation for pugl', | |||||
command: [sphinx_build, '-M', 'singlehtml', | |||||
meson.current_build_dir(), meson.current_build_dir(), | |||||
'-E', '-q', '-t', 'singlehtml'], | |||||
input: [cpp_rst_files, cpp_pugl_rst, cpp_index_xml], | |||||
output: 'singlehtml', | |||||
build_by_default: true, | |||||
install: true, | |||||
install_dir: docdir / 'puglpp-0') | |||||
docs = custom_target( | |||||
'html C++ documentation for pugl', | |||||
command: [sphinx_build, '-M', 'html', | |||||
meson.current_build_dir(), meson.current_build_dir(), | |||||
'-E', '-q', '-t', 'html'], | |||||
input: [cpp_rst_files, cpp_pugl_rst, cpp_index_xml], | |||||
output: 'html', | |||||
build_by_default: true, | |||||
install: true, | |||||
install_dir: docdir / 'puglpp-0') |
@@ -1,35 +0,0 @@ | |||||
.. default-domain:: cpp | |||||
.. highlight:: cpp | |||||
.. namespace:: pugl | |||||
######## | |||||
Overview | |||||
######## | |||||
Pugl is a C library, | |||||
but the bindings documented here provide a more idiomatic and type-safe API for C++. | |||||
If you would rather use C, | |||||
refer instead to the `C API documentation <../../c/singlehtml/index.html>`_. | |||||
The C++ bindings are very lightweight and do not require virtual functions, | |||||
RTTI, | |||||
exceptions, | |||||
or linking to the C++ standard library. | |||||
They are provided by the package ``puglpp-0`` which must be used in addition to the desired platform+backend package above. | |||||
The core API (excluding backend-specific components) is declared in ``pugl.hpp``: | |||||
.. code-block:: cpp | |||||
#include <pugl/pugl.hpp> | |||||
The API revolves around two main objects: the `world` and the `view`. | |||||
An application creates a world to manage top-level state, | |||||
then creates one or more views to display. | |||||
.. toctree:: | |||||
world | |||||
view | |||||
events | |||||
event-loop |
@@ -1,299 +0,0 @@ | |||||
.. default-domain:: cpp | |||||
.. highlight:: cpp | |||||
.. namespace:: pugl | |||||
############### | |||||
Creating a View | |||||
############### | |||||
A `view` is a drawable region that receives events. | |||||
You may think of it as a window, | |||||
though it may be embedded and not represent a top-level system window. [#f1]_ | |||||
Pugl communicates with views by dispatching events. | |||||
For flexibility, the event handler can be a different object than the view. | |||||
This allows using :class:`View` along with a separate event handler class. | |||||
Alternatively, a view class can inherit from :class:`View` and set itself as its event handler, | |||||
for a more object-oriented style. | |||||
This documentation will use the latter approach, | |||||
so we will define a class for our view that contains everything needed: | |||||
.. code-block:: cpp | |||||
class MyView : public pugl::View | |||||
{ | |||||
public: | |||||
explicit MyView(pugl::World& world) | |||||
: pugl::View{world} | |||||
{ | |||||
setEventHandler(*this); | |||||
} | |||||
pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; | |||||
// With other handlers here as needed... | |||||
// Fallback handler for all other events | |||||
template<PuglEventType t, class Base> | |||||
pugl::Status onEvent(const pugl::Event<t, Base>&) noexcept | |||||
{ | |||||
return pugl::Status::success; | |||||
} | |||||
private: | |||||
// Some data... | |||||
}; | |||||
Pugl will call an ``onEvent`` method of the event handler (the view in this case) for every event. | |||||
Note that Pugl uses a static dispatching mechanism rather than virtual functions to minimize overhead. | |||||
It is therefore necessary for the final class to define a handler for every event type. | |||||
A terse way to do this without writing every implementation is to define a fallback handler as a template, | |||||
as in the example above. | |||||
Alternatively, you can define an explicit handler for each event that simply returns :enumerator:`Status::success`. | |||||
This way, it will be a compile error if any event is not explicitly handled. | |||||
********************* | |||||
Configuring the Frame | |||||
********************* | |||||
Before display, | |||||
the necessary :doc:`frame <api/frame>` and :doc:`window <api/window>` attributes should be set. | |||||
These allow the window system (or plugin host) to arrange the view properly. | |||||
Derived classes can configure themselves during construction, | |||||
but we assume here that configuration is being done outside the view. | |||||
For example: | |||||
.. code-block:: cpp | |||||
const double defaultWidth = 1920.0; | |||||
const double defaultHeight = 1080.0; | |||||
view.setWindowTitle("My Window"); | |||||
view.setDefaultSize(defaultWidth, defaultHeight); | |||||
view.setMinSize(defaultWidth / 4.0, defaultHeight / 4.0); | |||||
view.setAspectRatio(1, 1, 16, 9); | |||||
There are also several :type:`hints <PuglViewHint>` for basic attributes that can be set: | |||||
.. code-block:: cpp | |||||
view.setHint(pugl::ViewHint::resizable, true); | |||||
view.setHint(pugl::ViewHint::ignoreKeyRepeat, true); | |||||
********* | |||||
Embedding | |||||
********* | |||||
To embed the view in another window, | |||||
you will need to somehow get the :type:`native view handle <pugl::NativeView>` for the parent, | |||||
then set it with :func:`View::setParentWindow`. | |||||
If the parent is a Pugl view, | |||||
the native handle can be accessed with :func:`View::nativeWindow`. | |||||
For example: | |||||
.. code-block:: cpp | |||||
view.setParentWindow(view, parent.getNativeWindow()); | |||||
***************** | |||||
Setting a Backend | |||||
***************** | |||||
Before being realized, the view must have a backend set with :func:`View::setBackend`. | |||||
The backend manages the graphics API that will be used for drawing. | |||||
Pugl includes backends and supporting API for | |||||
:doc:`Cairo <api/cairo>`, :doc:`OpenGL <api/gl>`, and :doc:`Vulkan <api/vulkan>`. | |||||
Using Cairo | |||||
=========== | |||||
Cairo-specific API is declared in the ``cairo.hpp`` header: | |||||
.. code-block:: cpp | |||||
#include <pugl/cairo.hpp> | |||||
The Cairo backend is provided by :func:`cairoBackend()`: | |||||
.. code-block:: cpp | |||||
view.setBackend(pugl::cairoBackend()); | |||||
No additional configuration is required for Cairo. | |||||
To draw when handling an expose event, | |||||
the `Cairo context <https://www.cairographics.org/manual/cairo-cairo-t.html>`_ can be accessed with :func:`View::context`: | |||||
.. code-block:: cpp | |||||
cairo_t* cr = static_cast<cairo_t*>(view.context()); | |||||
Using OpenGL | |||||
============ | |||||
OpenGL-specific API is declared in the ``gl.hpp`` header: | |||||
.. code-block:: cpp | |||||
#include <pugl/gl.hpp> | |||||
The OpenGL backend is provided by :func:`glBackend()`: | |||||
.. code-block:: cpp | |||||
view.setBackend(pugl::glBackend()); | |||||
Some hints must also be set so that the context can be set up correctly. | |||||
For example, to use OpenGL 3.3 Core Profile: | |||||
.. code-block:: cpp | |||||
view.setHint(pugl::ViewHint::useCompatProfile, false); | |||||
view.setHint(pugl::ViewHint::contextVersionMajor, 3); | |||||
view.setHint(pugl::ViewHint::contextVersionMinor, 3); | |||||
If you need to perform some setup using the OpenGL API, | |||||
there are two ways to do so. | |||||
The OpenGL context is active when | |||||
:type:`CreateEvent` and | |||||
:type:`DestroyEvent` | |||||
events are dispatched, | |||||
so things like creating and destroying shaders and textures can be done then. | |||||
Alternatively, if it is cumbersome to set up and tear down OpenGL in the event handler, | |||||
:func:`enterContext` and :func:`leaveContext` can be used to manually activate the OpenGL context during application setup. | |||||
Note, however, that unlike many other APIs, these functions must not be used for drawing. | |||||
It is only valid to use the OpenGL API for configuration in a manually entered context, | |||||
rendering will not work. | |||||
For example: | |||||
.. code-block:: cpp | |||||
pugl::enterContext(view); | |||||
myApp.setupOpenGL(); | |||||
pugl::leaveContext(view); | |||||
while (!myApp.quit()) { | |||||
world.update(0.0); | |||||
} | |||||
pugl::enterContext(view); | |||||
myApp.teardownOpenGL(); | |||||
pugl::leaveContext(view); | |||||
Using Vulkan | |||||
============ | |||||
Vulkan-specific API is declared in the ``vulkan.hpp`` header. | |||||
This header includes Vulkan headers, | |||||
so if you are dynamically loading Vulkan at runtime, | |||||
you should define ``VK_NO_PROTOTYPES`` before including it. | |||||
.. code-block:: cpp | |||||
#define VK_NO_PROTOTYPES | |||||
#include <pugl/vulkan.hpp> | |||||
The Vulkan backend is provided by :func:`vulkanBackend()`: | |||||
.. code-block:: cpp | |||||
view.setBackend(pugl::vulkanBackend()); | |||||
Unlike OpenGL, almost all Vulkan configuration is done using the Vulkan API directly. | |||||
Pugl only provides a portable mechanism to load the Vulkan library and get the functions used to load the rest of the Vulkan API. | |||||
Loading Vulkan | |||||
-------------- | |||||
For maximum compatibility, | |||||
it is best to not link to Vulkan at compile-time, | |||||
but instead load the Vulkan API at run-time. | |||||
To do so, first create a :class:`VulkanLoader`: | |||||
.. code-block:: cpp | |||||
pugl::VulkanLoader loader{world}; | |||||
The loader manages the dynamically loaded Vulkan library, | |||||
so it must be kept alive for as long as the application is using Vulkan. | |||||
You can get the function used to load Vulkan functions with :func:`VulkanLoader::getInstanceProcAddrFunc`: | |||||
.. code-block:: cpp | |||||
auto vkGetInstanceProcAddr = loader.getInstanceProcAddrFunc(); | |||||
It is best to use this function to load everything at run time, | |||||
rather than link to the Vulkan library at run time. | |||||
You can, for example, pass this to get the ``vkCreateInstance`` function using this, | |||||
then use that to create your Vulkan instance. | |||||
In practice, you will want to use some loader or wrapper API since there are many Vulkan functions. | |||||
It is not necessary to use :class:`VulkanLoader`, | |||||
you can, for example, use the ``DynamicLoader`` from ``vulkan.hpp`` in the Vulkan SDK instead. | |||||
The details of using Vulkan are far beyond the scope of this documentation, | |||||
but Pugl provides a portable function, :func:`createSurface`, | |||||
to get the Vulkan surface for a view. | |||||
Assuming you have somehow created your ``VkInstance``, | |||||
you can get the surface for a view using :func:`createSurface`: | |||||
.. code-block:: cpp | |||||
VkSurfaceKHR* surface = nullptr; | |||||
puglCreateSurface(loader.getDeviceProcAddrFunc(), | |||||
view, | |||||
vulkanInstance, | |||||
nullptr, | |||||
&surface); | |||||
Pugl does not provide API that uses ``vulkan.hpp`` to avoid the onerous dependency, | |||||
but if you are using it with exceptions and unique handles, | |||||
it is straightforward to wrap the surface handle yourself. | |||||
**************** | |||||
Showing the View | |||||
**************** | |||||
Once the view is configured, it can be "realized" with :func:`View::realize`. | |||||
This creates a "real" system view, for example: | |||||
.. code-block:: cpp | |||||
pugl::Status status = view.realize(); | |||||
if (status != pugl::Status::success) { | |||||
std::cerr << "Error realizing view: " << pugl::strerror(status) << "\n"; | |||||
} | |||||
Note that realizing a view can fail for many reasons, | |||||
so the return code should always be checked. | |||||
This is generally the case for any function that interacts with the window system. | |||||
Most functions also return a :enum:`Status`, | |||||
but these checks are omitted for brevity in the rest of this documentation. | |||||
A realized view is not initially visible, | |||||
but can be shown with :func:`View::show`: | |||||
.. code-block:: cpp | |||||
view.show(); | |||||
To create an initially visible view, | |||||
it is also possible to simply call :func:`View::show()` right away. | |||||
The view will be automatically realized if necessary. | |||||
.. rubric:: Footnotes | |||||
.. [#f1] MacOS has a strong distinction between | |||||
`views <https://developer.apple.com/documentation/appkit/nsview>`_, | |||||
which may be nested, and | |||||
`windows <https://developer.apple.com/documentation/appkit/nswindow>`_, | |||||
which may not. | |||||
On Windows and X11, everything is a nestable window, | |||||
but top-level windows are configured differently. |
@@ -1,41 +0,0 @@ | |||||
.. default-domain:: cpp | |||||
.. highlight:: cpp | |||||
.. namespace:: pugl | |||||
################ | |||||
Creating a World | |||||
################ | |||||
The world is the top-level object which represents an instance of Pugl. | |||||
It handles the connection to the window system, | |||||
and manages views and the event loop. | |||||
An application typically has a single world, | |||||
which is constructed once on startup and used to drive the main event loop. | |||||
************ | |||||
Construction | |||||
************ | |||||
A world must be created before any views, and it must outlive all of its views. | |||||
The world constructor requires an argument to specify the application type: | |||||
.. code-block:: cpp | |||||
pugl::World world{pugl::WorldType::program}; | |||||
For a plugin, specify :enumerator:`WorldType::module` instead. | |||||
In some cases, it is necessary to pass additional flags. | |||||
For example, Vulkan requires thread support: | |||||
.. code-block:: cpp | |||||
pugl::World world{pugl::WorldType::program, pugl::WorldFlag::threads}; | |||||
It is a good idea to set a class name for your project with :func:`World::setClassName`. | |||||
This allows the window system to distinguish different applications and, | |||||
for example, users to set up rules to manage their windows nicely: | |||||
.. code-block:: cpp | |||||
world.setClassName("MyAwesomeProject"); |
@@ -1,21 +0,0 @@ | |||||
doxygen = find_program('doxygen') | |||||
cpp_doxygen_input = [] | |||||
foreach h : c_headers + cpp_headers | |||||
cpp_doxygen_input += ['..' / h] | |||||
endforeach | |||||
config = configuration_data() | |||||
config.set('PUGL_SRCDIR', pugl_src_root) | |||||
config.set('DOX_OUTPUT', meson.current_build_dir() / '..') | |||||
cpp_doxyfile = configure_file(configuration: config, | |||||
input: '../Doxyfile.in', | |||||
output: 'Doxyfile') | |||||
cpp_index_xml = custom_target( | |||||
'cpp-index.xml', | |||||
command: [doxygen, '@INPUT0@'], | |||||
input: [cpp_doxyfile] + c_header_files + cpp_header_files, | |||||
output: 'index.xml') | |||||
@@ -1,23 +0,0 @@ | |||||
##### | |||||
Usage | |||||
##### | |||||
********************* | |||||
Building Against Pugl | |||||
********************* | |||||
When Pugl is installed, | |||||
pkg-config_ packages are provided that link with the core platform library and desired backend: | |||||
- ``pugl-cairo-0`` | |||||
- ``pugl-gl-0`` | |||||
- ``pugl-vulkan-0`` | |||||
Depending on one of these packages should be all that is necessary to use Pugl, | |||||
but details on the individual libraries that are installed are available in the README. | |||||
If you are instead including the source directly in your project, | |||||
the structure is quite simple and hopefully obvious. | |||||
It is only necessary to copy the platform and backend implementations that you need. | |||||
.. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/ |
@@ -1,13 +0,0 @@ | |||||
docdir = get_option('datadir') / 'doc' | |||||
doxygen = find_program('doxygen', required: get_option('docs')) | |||||
dox_to_sphinx = find_program('../scripts/dox_to_sphinx.py') | |||||
sphinx_build = find_program('sphinx-build', required: get_option('docs')) | |||||
build_docs = doxygen.found() and sphinx_build.found() | |||||
if build_docs | |||||
subdir('_static') | |||||
subdir('c') | |||||
subdir('cpp') | |||||
endif |
@@ -1,22 +0,0 @@ | |||||
Pugl is an API for writing portable and embeddable GUIs. | |||||
Pugl is not a toolkit or framework, | |||||
but a minimal portability layer that sets up a drawing context and delivers events. | |||||
Compared to other libraries, | |||||
Pugl is particularly suitable for use in plugins or other loadable modules. | |||||
There is no implicit context or static data in the library, | |||||
so it may be statically linked and used multiple times in the same process. | |||||
Pugl has a modular design that separates the core library from graphics backends. | |||||
The core library is graphics agnostic, | |||||
it implements platform support and depends only on standard system libraries. | |||||
MacOS, Windows, and X11 are currently supported as platforms. | |||||
Graphics backends are separate so that applications only depend on the API that they use. | |||||
Pugl includes graphics backends for Cairo_, OpenGL_, and Vulkan_. | |||||
It is also possible to use some other graphics API by implementing a custom backend, | |||||
or simply accessing the native platform handle for a window. | |||||
.. _Cairo: https://www.cairographics.org/ | |||||
.. _OpenGL: https://www.opengl.org/ | |||||
.. _Vulkan: https://www.khronos.org/vulkan/ |
@@ -1,43 +0,0 @@ | |||||
Checks: > | |||||
*, | |||||
-*-non-private-member-variables-in-classes, | |||||
-*avoid-c-arrays, | |||||
-*magic-numbers, | |||||
-*uppercase-literal-suffix, | |||||
-altera-struct-pack-align, | |||||
-android-cloexec-fopen, | |||||
-bugprone-macro-parentheses, | |||||
-bugprone-reserved-identifier, | |||||
-bugprone-suspicious-string-compare, | |||||
-cert-dcl37-c, | |||||
-cert-dcl51-cpp, | |||||
-cert-flp30-c, | |||||
-clang-analyzer-alpha.*, | |||||
-clang-analyzer-security.FloatLoopCounter, | |||||
-concurrency-mt-unsafe, | |||||
-cppcoreguidelines-avoid-non-const-global-variables, | |||||
-cppcoreguidelines-macro-usage, | |||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, | |||||
-cppcoreguidelines-pro-bounds-constant-array-index, | |||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic, | |||||
-cppcoreguidelines-pro-type-reinterpret-cast, | |||||
-cppcoreguidelines-pro-type-vararg, | |||||
-fuchsia-default-arguments, | |||||
-fuchsia-default-arguments-calls, | |||||
-fuchsia-overloaded-operator, | |||||
-google-runtime-references, | |||||
-hicpp-multiway-paths-covered, | |||||
-hicpp-named-parameter, | |||||
-hicpp-no-array-decay, | |||||
-hicpp-signed-bitwise, | |||||
-hicpp-vararg, | |||||
-llvm-header-guard, | |||||
-llvmlibc-*, | |||||
-misc-misplaced-const, | |||||
-modernize-use-trailing-return-type, | |||||
-performance-no-int-to-ptr, | |||||
-readability-function-cognitive-complexity, | |||||
-readability-implicit-bool-conversion, | |||||
-readability-named-parameter, | |||||
FormatStyle: file | |||||
HeaderFilterRegex: 'pugl/.*|test/.*|examples/.*' |
@@ -1,193 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef EXAMPLES_CUBE_VIEW_H | |||||
#define EXAMPLES_CUBE_VIEW_H | |||||
#define GL_SILENCE_DEPRECATION 1 | |||||
#include "demo_utils.h" | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include <stdbool.h> | |||||
// clang-format off | |||||
static const float cubeStripVertices[] = { | |||||
-1.0f, 1.0f, 1.0f, // Front top left | |||||
1.0f, 1.0f, 1.0f, // Front top right | |||||
-1.0f, -1.0f, 1.0f, // Front bottom left | |||||
1.0f, -1.0f, 1.0f, // Front bottom right | |||||
1.0f, -1.0f, -1.0f, // Back bottom right | |||||
1.0f, 1.0f, 1.0f, // Front top right | |||||
1.0f, 1.0f, -1.0f, // Back top right | |||||
-1.0f, 1.0f, 1.0f, // Front top left | |||||
-1.0f, 1.0f, -1.0f, // Back top left | |||||
-1.0f, -1.0f, 1.0f, // Front bottom left | |||||
-1.0f, -1.0f, -1.0f, // Back bottom left | |||||
1.0f, -1.0f, -1.0f, // Back bottom right | |||||
-1.0f, 1.0f, -1.0f, // Back top left | |||||
1.0f, 1.0f, -1.0f, // Back top right | |||||
}; | |||||
static const float cubeStripColorVertices[] = { | |||||
0.25f, 0.75f, 0.75f, // Front top left | |||||
0.75f, 0.75f, 0.75f, // Front top right | |||||
0.25f, 0.25f, 0.75f, // Front bottom left | |||||
0.75f, 0.25f, 0.75f, // Front bottom right | |||||
0.75f, 0.25f, 0.25f, // Back bottom right | |||||
0.75f, 0.75f, 0.75f, // Front top right | |||||
0.75f, 0.75f, 0.25f, // Back top right | |||||
0.25f, 0.75f, 0.75f, // Front top left | |||||
0.25f, 0.75f, 0.25f, // Back top left | |||||
0.25f, 0.25f, 0.75f, // Front bottom left | |||||
0.25f, 0.25f, 0.25f, // Back bottom left | |||||
0.75f, 0.25f, 0.25f, // Back bottom right | |||||
0.25f, 0.75f, 0.25f, // Back top left | |||||
0.75f, 0.75f, 0.25f, // Back top right | |||||
}; | |||||
static const float cubeFrontLineLoop[] = { | |||||
-1.0f, 1.0f, 1.0f, // Front top left | |||||
1.0f, 1.0f, 1.0f, // Front top right | |||||
1.0f, -1.0f, 1.0f, // Front bottom right | |||||
-1.0f, -1.0f, 1.0f, // Front bottom left | |||||
}; | |||||
static const float cubeFrontLineLoopColors[] = { | |||||
0.25f, 0.75f, 0.75f, // Front top left | |||||
0.75f, 0.75f, 0.75f, // Front top right | |||||
0.75f, 0.25f, 0.75f, // Front bottom right | |||||
0.25f, 0.25f, 0.75f, // Front bottom left | |||||
}; | |||||
static const float cubeBackLineLoop[] = { | |||||
-1.0f, 1.0f, -1.0f, // Back top left | |||||
1.0f, 1.0f, -1.0f, // Back top right | |||||
1.0f, -1.0f, -1.0f, // Back bottom right | |||||
-1.0f, -1.0f, -1.0f, // Back bottom left | |||||
}; | |||||
static const float cubeBackLineLoopColors[] = { | |||||
0.25f, 0.75f, 0.25f, // Back top left | |||||
0.75f, 0.75f, 0.25f, // Back top right | |||||
0.75f, 0.25f, 0.25f, // Back bottom right | |||||
0.25f, 0.25f, 0.25f, // Back bottom left | |||||
}; | |||||
static const float cubeSideLines[] = { | |||||
-1.0f, 1.0f, 1.0f, // Front top left | |||||
-1.0f, 1.0f, -1.0f, // Back top left | |||||
-1.0f, -1.0f, 1.0f, // Front bottom left | |||||
-1.0f, -1.0f, -1.0f, // Back bottom left | |||||
1.0f, 1.0f, 1.0f, // Front top right | |||||
1.0f, 1.0f, -1.0f, // Back top right | |||||
1.0f, -1.0f, 1.0f, // Front bottom right | |||||
1.0f, -1.0f, -1.0f, // Back bottom right | |||||
}; | |||||
static const float cubeSideLineColors[] = { | |||||
0.25f, 0.75f, 0.75f, // Front top left | |||||
0.25f, 0.75f, 0.25f, // Back top left | |||||
0.25f, 0.25f, 0.75f, // Front bottom left | |||||
0.25f, 0.25f, 0.25f, // Back bottom left | |||||
0.75f, 0.75f, 0.75f, // Front top right | |||||
0.75f, 0.75f, 0.25f, // Back top right | |||||
0.75f, 0.25f, 0.75f, // Front bottom right | |||||
0.75f, 0.25f, 0.25f, // Back bottom right | |||||
}; | |||||
// clang-format on | |||||
static inline void | |||||
reshapeCube(const float width, const float height) | |||||
{ | |||||
const float aspect = width / height; | |||||
glEnable(GL_CULL_FACE); | |||||
glCullFace(GL_BACK); | |||||
glFrontFace(GL_CW); | |||||
glEnable(GL_DEPTH_TEST); | |||||
glDepthFunc(GL_LESS); | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
glViewport(0, 0, (int)width, (int)height); | |||||
float projection[16]; | |||||
perspective(projection, 1.8f, aspect, 1.0f, 100.0f); | |||||
glLoadMatrixf(projection); | |||||
} | |||||
static inline void | |||||
displayCube(PuglView* const view, | |||||
const float distance, | |||||
const float xAngle, | |||||
const float yAngle, | |||||
const bool entered) | |||||
{ | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
glTranslatef(0.0f, 0.0f, distance * -1.0f); | |||||
glRotatef(xAngle, 0.0f, 1.0f, 0.0f); | |||||
glRotatef(yAngle, 1.0f, 0.0f, 0.0f); | |||||
if (entered) { | |||||
glClearColor(0.13f, 0.14f, 0.14f, 1.0f); | |||||
} else { | |||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |||||
} | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
if (puglHasFocus(view)) { | |||||
// Draw cube surfaces | |||||
glEnableClientState(GL_VERTEX_ARRAY); | |||||
glEnableClientState(GL_COLOR_ARRAY); | |||||
glVertexPointer(3, GL_FLOAT, 0, cubeStripVertices); | |||||
glColorPointer(3, GL_FLOAT, 0, cubeStripColorVertices); | |||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 14); | |||||
glDisableClientState(GL_COLOR_ARRAY); | |||||
glDisableClientState(GL_VERTEX_ARRAY); | |||||
glColor3f(0.0f, 0.0f, 0.0f); | |||||
} else { | |||||
// Draw cube wireframe | |||||
glEnableClientState(GL_VERTEX_ARRAY); | |||||
glEnableClientState(GL_COLOR_ARRAY); | |||||
glVertexPointer(3, GL_FLOAT, 0, cubeFrontLineLoop); | |||||
glColorPointer(3, GL_FLOAT, 0, cubeFrontLineLoopColors); | |||||
glDrawArrays(GL_LINE_LOOP, 0, 4); | |||||
glVertexPointer(3, GL_FLOAT, 0, cubeBackLineLoop); | |||||
glColorPointer(3, GL_FLOAT, 0, cubeBackLineLoopColors); | |||||
glDrawArrays(GL_LINE_LOOP, 0, 4); | |||||
glVertexPointer(3, GL_FLOAT, 0, cubeSideLines); | |||||
glColorPointer(3, GL_FLOAT, 0, cubeSideLineColors); | |||||
glDrawArrays(GL_LINES, 0, 8); | |||||
glDisableClientState(GL_VERTEX_ARRAY); | |||||
} | |||||
} | |||||
#endif // EXAMPLES_CUBE_VIEW_H |
@@ -1,124 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2019 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef EXAMPLES_DEMO_UTILS_H | |||||
#define EXAMPLES_DEMO_UTILS_H | |||||
#include "pugl/pugl.h" | |||||
#include <math.h> | |||||
#include <stdio.h> | |||||
typedef struct { | |||||
double lastReportTime; | |||||
} PuglFpsPrinter; | |||||
typedef float vec4[4]; | |||||
typedef vec4 mat4[4]; | |||||
static inline void | |||||
mat4Identity(mat4 m) | |||||
{ | |||||
for (int c = 0; c < 4; ++c) { | |||||
for (int r = 0; r < 4; ++r) { | |||||
m[c][r] = c == r ? 1.0f : 0.0f; | |||||
} | |||||
} | |||||
} | |||||
static inline void | |||||
mat4Translate(mat4 m, const float x, const float y, const float z) | |||||
{ | |||||
m[3][0] = x; | |||||
m[3][1] = y; | |||||
m[3][2] = z; | |||||
} | |||||
static inline void | |||||
mat4Mul(mat4 m, mat4 a, mat4 b) | |||||
{ | |||||
for (int c = 0; c < 4; ++c) { | |||||
for (int r = 0; r < 4; ++r) { | |||||
m[c][r] = 0.0f; | |||||
for (int k = 0; k < 4; ++k) { | |||||
m[c][r] += a[k][r] * b[c][k]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
static inline void | |||||
mat4Ortho(mat4 m, | |||||
const float l, | |||||
const float r, | |||||
const float b, | |||||
const float t, | |||||
const float n, | |||||
const float f) | |||||
{ | |||||
m[0][0] = 2.0f / (r - l); | |||||
m[0][1] = m[0][2] = m[0][3] = 0.0f; | |||||
m[1][1] = 2.0f / (t - b); | |||||
m[1][0] = m[1][2] = m[1][3] = 0.0f; | |||||
m[2][2] = -2.0f / (f - n); | |||||
m[2][0] = m[2][1] = m[2][3] = 0.0f; | |||||
m[3][0] = -(r + l) / (r - l); | |||||
m[3][1] = -(t + b) / (t - b); | |||||
m[3][2] = -(f + n) / (f - n); | |||||
m[3][3] = 1.0f; | |||||
} | |||||
/// Calculate a projection matrix for a given perspective | |||||
static inline void | |||||
perspective(float* m, float fov, float aspect, float zNear, float zFar) | |||||
{ | |||||
const float h = tanf(fov); | |||||
const float w = h / aspect; | |||||
const float depth = zNear - zFar; | |||||
const float q = (zFar + zNear) / depth; | |||||
const float qn = 2 * zFar * zNear / depth; | |||||
// clang-format off | |||||
m[0] = w; m[1] = 0; m[2] = 0; m[3] = 0; | |||||
m[4] = 0; m[5] = h; m[6] = 0; m[7] = 0; | |||||
m[8] = 0; m[9] = 0; m[10] = q; m[11] = -1; | |||||
m[12] = 0; m[13] = 0; m[14] = qn; m[15] = 0; | |||||
// clang-format on | |||||
} | |||||
static inline void | |||||
puglPrintFps(const PuglWorld* world, | |||||
PuglFpsPrinter* printer, | |||||
unsigned* const framesDrawn) | |||||
{ | |||||
const double thisTime = puglGetTime(world); | |||||
if (thisTime > printer->lastReportTime + 5) { | |||||
const double fps = *framesDrawn / (thisTime - printer->lastReportTime); | |||||
fprintf(stderr, | |||||
"FPS: %.2f (%u frames in %.0f seconds)\n", | |||||
fps, | |||||
*framesDrawn, | |||||
thisTime - printer->lastReportTime); | |||||
printer->lastReportTime = thisTime; | |||||
*framesDrawn = 0; | |||||
} | |||||
} | |||||
#endif // EXAMPLES_DEMO_UTILS_H |
@@ -1,68 +0,0 @@ | |||||
/* | |||||
Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#if !defined(__APPLE__) && !defined(_GNU_SOURCE) | |||||
# define _GNU_SOURCE | |||||
#endif | |||||
#include "file_utils.h" | |||||
#ifdef _WIN32 | |||||
# include <io.h> | |||||
# include <windows.h> | |||||
# define F_OK 0 | |||||
#else | |||||
# include <libgen.h> | |||||
# include <unistd.h> | |||||
#endif | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
char* | |||||
resourcePath(const char* const programPath, const char* const name) | |||||
{ | |||||
char* const binary = strdup(programPath); | |||||
#ifdef _WIN32 | |||||
char programDir[_MAX_DIR]; | |||||
_splitpath(binary, programDir, NULL, NULL, NULL); | |||||
_splitpath(binary, NULL, programDir + strlen(programDir), NULL, NULL); | |||||
programDir[strlen(programDir) - 1] = '\0'; | |||||
#else | |||||
char* const programDir = dirname(binary); | |||||
#endif | |||||
const size_t programDirLen = strlen(programDir); | |||||
const size_t nameLen = strlen(name); | |||||
const size_t totalLen = programDirLen + nameLen + 4; | |||||
char* const programRelative = (char*)calloc(totalLen, 1); | |||||
snprintf(programRelative, totalLen, "%s/%s", programDir, name); | |||||
if (!access(programRelative, F_OK)) { | |||||
free(binary); | |||||
return programRelative; | |||||
} | |||||
free(programRelative); | |||||
free(binary); | |||||
const size_t sysPathLen = strlen(PUGL_DATA_DIR) + nameLen + 4; | |||||
char* const sysPath = (char*)calloc(sysPathLen, 1); | |||||
snprintf(sysPath, sysPathLen, "%s/%s", PUGL_DATA_DIR, name); | |||||
return sysPath; | |||||
} |
@@ -1,41 +0,0 @@ | |||||
/* | |||||
Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef EXAMPLES_FILE_UTILS_H | |||||
#define EXAMPLES_FILE_UTILS_H | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
/** | |||||
Return the path to a resource file. | |||||
This takes a name like "shaders/something.glsl" and returns the actual | |||||
path that can be used to load that resource, which may be relative to the | |||||
current executable (for running in bundles or the build directory), or a | |||||
shared system directory for installs. | |||||
The returned path must be freed with free(). | |||||
*/ | |||||
char* | |||||
resourcePath(const char* programPath, const char* name); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#endif // EXAMPLES_FILE_UTILS_H |
@@ -1,290 +0,0 @@ | |||||
#ifndef __khrplatform_h_ | |||||
#define __khrplatform_h_ | |||||
/* | |||||
** Copyright (c) 2008-2018 The Khronos Group Inc. | |||||
** | |||||
** Permission is hereby granted, free of charge, to any person obtaining a | |||||
** copy of this software and/or associated documentation files (the | |||||
** "Materials"), to deal in the Materials without restriction, including | |||||
** without limitation the rights to use, copy, modify, merge, publish, | |||||
** distribute, sublicense, and/or sell copies of the Materials, and to | |||||
** permit persons to whom the Materials are furnished to do so, subject to | |||||
** the following conditions: | |||||
** | |||||
** The above copyright notice and this permission notice shall be included | |||||
** in all copies or substantial portions of the Materials. | |||||
** | |||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. | |||||
*/ | |||||
/* Khronos platform-specific types and definitions. | |||||
* | |||||
* The master copy of khrplatform.h is maintained in the Khronos EGL | |||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry | |||||
* The last semantic modification to khrplatform.h was at commit ID: | |||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692 | |||||
* | |||||
* Adopters may modify this file to suit their platform. Adopters are | |||||
* encouraged to submit platform specific modifications to the Khronos | |||||
* group so that they can be included in future versions of this file. | |||||
* Please submit changes by filing pull requests or issues on | |||||
* the EGL Registry repository linked above. | |||||
* | |||||
* | |||||
* See the Implementer's Guidelines for information about where this file | |||||
* should be located on your system and for more details of its use: | |||||
* http://www.khronos.org/registry/implementers_guide.pdf | |||||
* | |||||
* This file should be included as | |||||
* #include <KHR/khrplatform.h> | |||||
* by Khronos client API header files that use its types and defines. | |||||
* | |||||
* The types in khrplatform.h should only be used to define API-specific types. | |||||
* | |||||
* Types defined in khrplatform.h: | |||||
* khronos_int8_t signed 8 bit | |||||
* khronos_uint8_t unsigned 8 bit | |||||
* khronos_int16_t signed 16 bit | |||||
* khronos_uint16_t unsigned 16 bit | |||||
* khronos_int32_t signed 32 bit | |||||
* khronos_uint32_t unsigned 32 bit | |||||
* khronos_int64_t signed 64 bit | |||||
* khronos_uint64_t unsigned 64 bit | |||||
* khronos_intptr_t signed same number of bits as a pointer | |||||
* khronos_uintptr_t unsigned same number of bits as a pointer | |||||
* khronos_ssize_t signed size | |||||
* khronos_usize_t unsigned size | |||||
* khronos_float_t signed 32 bit floating point | |||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds | |||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in | |||||
* nanoseconds | |||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds | |||||
* khronos_boolean_enum_t enumerated boolean type. This should | |||||
* only be used as a base type when a client API's boolean type is | |||||
* an enum. Client APIs which use an integer or other type for | |||||
* booleans cannot use this as the base type for their boolean. | |||||
* | |||||
* Tokens defined in khrplatform.h: | |||||
* | |||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. | |||||
* | |||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. | |||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. | |||||
* | |||||
* Calling convention macros defined in this file: | |||||
* KHRONOS_APICALL | |||||
* KHRONOS_APIENTRY | |||||
* KHRONOS_APIATTRIBUTES | |||||
* | |||||
* These may be used in function prototypes as: | |||||
* | |||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname( | |||||
* int arg1, | |||||
* int arg2) KHRONOS_APIATTRIBUTES; | |||||
*/ | |||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) | |||||
# define KHRONOS_STATIC 1 | |||||
#endif | |||||
/*------------------------------------------------------------------------- | |||||
* Definition of KHRONOS_APICALL | |||||
*------------------------------------------------------------------------- | |||||
* This precedes the return type of the function in the function prototype. | |||||
*/ | |||||
#if defined(KHRONOS_STATIC) | |||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the | |||||
* header compatible with static linking. */ | |||||
# define KHRONOS_APICALL | |||||
#elif defined(_WIN32) | |||||
# define KHRONOS_APICALL __declspec(dllimport) | |||||
#elif defined (__SYMBIAN32__) | |||||
# define KHRONOS_APICALL IMPORT_C | |||||
#elif defined(__ANDROID__) | |||||
# define KHRONOS_APICALL __attribute__((visibility("default"))) | |||||
#else | |||||
# define KHRONOS_APICALL | |||||
#endif | |||||
/*------------------------------------------------------------------------- | |||||
* Definition of KHRONOS_APIENTRY | |||||
*------------------------------------------------------------------------- | |||||
* This follows the return type of the function and precedes the function | |||||
* name in the function prototype. | |||||
*/ | |||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) | |||||
/* Win32 but not WinCE */ | |||||
# define KHRONOS_APIENTRY __stdcall | |||||
#else | |||||
# define KHRONOS_APIENTRY | |||||
#endif | |||||
/*------------------------------------------------------------------------- | |||||
* Definition of KHRONOS_APIATTRIBUTES | |||||
*------------------------------------------------------------------------- | |||||
* This follows the closing parenthesis of the function prototype arguments. | |||||
*/ | |||||
#if defined (__ARMCC_2__) | |||||
#define KHRONOS_APIATTRIBUTES __softfp | |||||
#else | |||||
#define KHRONOS_APIATTRIBUTES | |||||
#endif | |||||
/*------------------------------------------------------------------------- | |||||
* basic type definitions | |||||
*-----------------------------------------------------------------------*/ | |||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) | |||||
/* | |||||
* Using <stdint.h> | |||||
*/ | |||||
#include <stdint.h> | |||||
typedef int32_t khronos_int32_t; | |||||
typedef uint32_t khronos_uint32_t; | |||||
typedef int64_t khronos_int64_t; | |||||
typedef uint64_t khronos_uint64_t; | |||||
#define KHRONOS_SUPPORT_INT64 1 | |||||
#define KHRONOS_SUPPORT_FLOAT 1 | |||||
#elif defined(__VMS ) || defined(__sgi) | |||||
/* | |||||
* Using <inttypes.h> | |||||
*/ | |||||
#include <inttypes.h> | |||||
typedef int32_t khronos_int32_t; | |||||
typedef uint32_t khronos_uint32_t; | |||||
typedef int64_t khronos_int64_t; | |||||
typedef uint64_t khronos_uint64_t; | |||||
#define KHRONOS_SUPPORT_INT64 1 | |||||
#define KHRONOS_SUPPORT_FLOAT 1 | |||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) | |||||
/* | |||||
* Win32 | |||||
*/ | |||||
typedef __int32 khronos_int32_t; | |||||
typedef unsigned __int32 khronos_uint32_t; | |||||
typedef __int64 khronos_int64_t; | |||||
typedef unsigned __int64 khronos_uint64_t; | |||||
#define KHRONOS_SUPPORT_INT64 1 | |||||
#define KHRONOS_SUPPORT_FLOAT 1 | |||||
#elif defined(__sun__) || defined(__digital__) | |||||
/* | |||||
* Sun or Digital | |||||
*/ | |||||
typedef int khronos_int32_t; | |||||
typedef unsigned int khronos_uint32_t; | |||||
#if defined(__arch64__) || defined(_LP64) | |||||
typedef long int khronos_int64_t; | |||||
typedef unsigned long int khronos_uint64_t; | |||||
#else | |||||
typedef long long int khronos_int64_t; | |||||
typedef unsigned long long int khronos_uint64_t; | |||||
#endif /* __arch64__ */ | |||||
#define KHRONOS_SUPPORT_INT64 1 | |||||
#define KHRONOS_SUPPORT_FLOAT 1 | |||||
#elif 0 | |||||
/* | |||||
* Hypothetical platform with no float or int64 support | |||||
*/ | |||||
typedef int khronos_int32_t; | |||||
typedef unsigned int khronos_uint32_t; | |||||
#define KHRONOS_SUPPORT_INT64 0 | |||||
#define KHRONOS_SUPPORT_FLOAT 0 | |||||
#else | |||||
/* | |||||
* Generic fallback | |||||
*/ | |||||
#include <stdint.h> | |||||
typedef int32_t khronos_int32_t; | |||||
typedef uint32_t khronos_uint32_t; | |||||
typedef int64_t khronos_int64_t; | |||||
typedef uint64_t khronos_uint64_t; | |||||
#define KHRONOS_SUPPORT_INT64 1 | |||||
#define KHRONOS_SUPPORT_FLOAT 1 | |||||
#endif | |||||
/* | |||||
* Types that are (so far) the same on all platforms | |||||
*/ | |||||
typedef signed char khronos_int8_t; | |||||
typedef unsigned char khronos_uint8_t; | |||||
typedef signed short int khronos_int16_t; | |||||
typedef unsigned short int khronos_uint16_t; | |||||
/* | |||||
* Types that differ between LLP64 and LP64 architectures - in LLP64, | |||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears | |||||
* to be the only LLP64 architecture in current use. | |||||
*/ | |||||
#ifdef _WIN64 | |||||
typedef signed long long int khronos_intptr_t; | |||||
typedef unsigned long long int khronos_uintptr_t; | |||||
typedef signed long long int khronos_ssize_t; | |||||
typedef unsigned long long int khronos_usize_t; | |||||
#else | |||||
typedef signed long int khronos_intptr_t; | |||||
typedef unsigned long int khronos_uintptr_t; | |||||
typedef signed long int khronos_ssize_t; | |||||
typedef unsigned long int khronos_usize_t; | |||||
#endif | |||||
#if KHRONOS_SUPPORT_FLOAT | |||||
/* | |||||
* Float type | |||||
*/ | |||||
typedef float khronos_float_t; | |||||
#endif | |||||
#if KHRONOS_SUPPORT_INT64 | |||||
/* Time types | |||||
* | |||||
* These types can be used to represent a time interval in nanoseconds or | |||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number | |||||
* of nanoseconds since some arbitrary system event (e.g. since the last | |||||
* time the system booted). The Unadjusted System Time is an unsigned | |||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals | |||||
* may be either signed or unsigned. | |||||
*/ | |||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t; | |||||
typedef khronos_int64_t khronos_stime_nanoseconds_t; | |||||
#endif | |||||
/* | |||||
* Dummy value used to pad enum types to 32 bits. | |||||
*/ | |||||
#ifndef KHRONOS_MAX_ENUM | |||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF | |||||
#endif | |||||
/* | |||||
* Enumerated boolean type | |||||
* | |||||
* Values other than zero should be considered to be true. Therefore | |||||
* comparisons should not be made against KHRONOS_TRUE. | |||||
*/ | |||||
typedef enum { | |||||
KHRONOS_FALSE = 0, | |||||
KHRONOS_TRUE = 1, | |||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM | |||||
} khronos_boolean_enum_t; | |||||
#endif /* __khrplatform_h_ */ |
@@ -1,124 +0,0 @@ | |||||
data_dir = get_option('prefix') / get_option('datadir') / 'pugl-0' | |||||
example_defines = ['-DPUGL_DATA_DIR="@0@"'.format(data_dir)] | |||||
gl_examples = [ | |||||
'pugl_cpp_demo.cpp', | |||||
'pugl_embed_demo.c', | |||||
'pugl_print_events.c', | |||||
'pugl_shader_demo.c', | |||||
'pugl_window_demo.c', | |||||
] | |||||
cairo_examples = [ | |||||
'pugl_cairo_demo.c' | |||||
] | |||||
vulkan_examples = [ | |||||
'pugl_vulkan_cpp_demo.cpp', | |||||
'pugl_vulkan_demo.c', | |||||
] | |||||
includes = include_directories( | |||||
'..', | |||||
'../bindings/cpp/include', | |||||
'../include', | |||||
) | |||||
# Suppress some additional C warnings in examples | |||||
example_c_args = [] | |||||
if get_option('strict') | |||||
if cc.get_id() == 'clang' | |||||
example_c_args += [ | |||||
'-Wno-float-equal', | |||||
'-Wno-padded', | |||||
] | |||||
elif cc.get_id() == 'gcc' | |||||
example_c_args += [ | |||||
'-Wno-float-equal', | |||||
'-Wno-padded', | |||||
] | |||||
endif | |||||
example_c_args = cc.get_supported_arguments(example_c_args) | |||||
endif | |||||
# Suppress some additional C++ warnings in examples | |||||
example_cpp_args = [] | |||||
if is_variable('cpp') | |||||
if cpp.get_id() == 'clang' | |||||
example_cpp_args += [ | |||||
'-Wno-documentation', # Cairo | |||||
'-Wno-documentation-unknown-command', # Cairo | |||||
'-Wno-old-style-cast', | |||||
'-Wno-padded', | |||||
'-Wno-reserved-id-macro', | |||||
'-Wno-switch-enum', | |||||
] | |||||
elif cpp.get_id() == 'gcc' | |||||
example_cpp_args += [ | |||||
'-Wno-effc++', | |||||
'-Wno-old-style-cast', | |||||
'-Wno-padded', | |||||
'-Wno-switch-default', | |||||
'-Wno-switch-enum', | |||||
'-Wno-unused-const-variable', | |||||
'-Wno-useless-cast', | |||||
] | |||||
endif | |||||
example_cpp_args = cpp.get_supported_arguments(example_cpp_args) | |||||
endif | |||||
subdir('shaders') | |||||
# Build GL examples | |||||
if opengl_dep.found() | |||||
foreach example : gl_examples | |||||
source = [example] | |||||
target = example.split('.')[0] | |||||
dependencies = [gl_backend_dep] | |||||
if target == 'pugl_shader_demo' | |||||
source += ['file_utils.c', 'glad/glad.c'] | |||||
dependencies += [dl_dep] | |||||
elif target == 'pugl_print_events' | |||||
dependencies += [stub_backend_dep] | |||||
endif | |||||
executable(target, source, | |||||
include_directories: includes, | |||||
c_args: example_defines + example_c_args, | |||||
cpp_args: example_defines + example_cpp_args, | |||||
dependencies: dependencies) | |||||
endforeach | |||||
endif | |||||
# Build Cairo examples | |||||
if cairo_dep.found() | |||||
foreach example : cairo_examples | |||||
target = example.split('.')[0] | |||||
executable(target, example, | |||||
include_directories: includes, | |||||
c_args: example_defines + example_c_args, | |||||
dependencies: [pugl_dep, cairo_backend_dep]) | |||||
endforeach | |||||
endif | |||||
# Build Vulkan examples | |||||
if vulkan_dep.found() | |||||
foreach example : vulkan_examples | |||||
source = [example] | |||||
target = example.split('.')[0] | |||||
dependencies = [dl_dep, vulkan_backend_dep] | |||||
if target == 'pugl_vulkan_cpp_demo' | |||||
source += ['file_utils.c'] | |||||
endif | |||||
executable(target, source, | |||||
include_directories: includes, | |||||
c_args: example_defines + example_c_args, | |||||
cpp_args: example_defines + example_cpp_args, | |||||
dependencies: dependencies) | |||||
endforeach | |||||
endif |
@@ -1,261 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "demo_utils.h" | |||||
#include "test/test_utils.h" | |||||
#include "pugl/cairo.h" | |||||
#include "pugl/pugl.h" | |||||
#include <cairo.h> | |||||
#include <math.h> | |||||
#include <stdbool.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
typedef struct { | |||||
PuglWorld* world; | |||||
PuglTestOptions opts; | |||||
unsigned framesDrawn; | |||||
int quit; | |||||
bool entered; | |||||
bool mouseDown; | |||||
} PuglTestApp; | |||||
typedef struct { | |||||
int x; | |||||
int y; | |||||
int w; | |||||
int h; | |||||
const char* label; | |||||
} Button; | |||||
static const Button buttons[] = {{128, 128, 64, 64, "1"}, | |||||
{384, 128, 64, 64, "2"}, | |||||
{128, 384, 64, 64, "3"}, | |||||
{384, 384, 64, 64, "4"}, | |||||
{0, 0, 0, 0, NULL}}; | |||||
static void | |||||
roundedBox(cairo_t* cr, double x, double y, double w, double h) | |||||
{ | |||||
static const double radius = 10; | |||||
static const double degrees = 3.14159265 / 180.0; | |||||
cairo_new_sub_path(cr); | |||||
cairo_arc(cr, x + w - radius, y + radius, radius, -90 * degrees, 0 * degrees); | |||||
cairo_arc( | |||||
cr, x + w - radius, y + h - radius, radius, 0 * degrees, 90 * degrees); | |||||
cairo_arc( | |||||
cr, x + radius, y + h - radius, radius, 90 * degrees, 180 * degrees); | |||||
cairo_arc(cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); | |||||
cairo_close_path(cr); | |||||
} | |||||
static void | |||||
buttonDraw(PuglTestApp* app, cairo_t* cr, const Button* but, const double time) | |||||
{ | |||||
cairo_save(cr); | |||||
cairo_translate(cr, but->x, but->y); | |||||
cairo_rotate(cr, sin(time) * 3.141592); | |||||
// Draw base | |||||
if (app->mouseDown) { | |||||
cairo_set_source_rgba(cr, 0.4, 0.9, 0.1, 1); | |||||
} else { | |||||
cairo_set_source_rgba(cr, 0.3, 0.5, 0.1, 1); | |||||
} | |||||
roundedBox(cr, 0, 0, but->w, but->h); | |||||
cairo_fill_preserve(cr); | |||||
// Draw border | |||||
cairo_set_source_rgba(cr, 0.4, 0.9, 0.1, 1); | |||||
cairo_set_line_width(cr, 4.0); | |||||
cairo_stroke(cr); | |||||
// Draw label | |||||
cairo_text_extents_t extents; | |||||
cairo_set_font_size(cr, 32.0); | |||||
cairo_text_extents(cr, but->label, &extents); | |||||
cairo_move_to(cr, | |||||
(but->w / 2.0) - extents.width / 2, | |||||
(but->h / 2.0) + extents.height / 2); | |||||
cairo_set_source_rgba(cr, 0, 0, 0, 1); | |||||
cairo_show_text(cr, but->label); | |||||
cairo_restore(cr); | |||||
} | |||||
static void | |||||
postButtonRedisplay(PuglView* view) | |||||
{ | |||||
const PuglRect frame = puglGetFrame(view); | |||||
const double width = frame.width; | |||||
const double height = frame.height; | |||||
const double scaleX = (width - (512 / width)) / 512.0; | |||||
const double scaleY = (height - (512 / height)) / 512.0; | |||||
for (const Button* b = buttons; b->label; ++b) { | |||||
const double span = sqrt(b->w * b->w + b->h * b->h); | |||||
const PuglRect rect = {(b->x - span) * scaleX, | |||||
(b->y - span) * scaleY, | |||||
span * 2.0 * scaleX, | |||||
span * 2.0 * scaleY}; | |||||
puglPostRedisplayRect(view, rect); | |||||
} | |||||
} | |||||
static void | |||||
onDisplay(PuglTestApp* app, PuglView* view, const PuglExposeEvent* event) | |||||
{ | |||||
cairo_t* cr = (cairo_t*)puglGetContext(view); | |||||
cairo_rectangle(cr, event->x, event->y, event->width, event->height); | |||||
cairo_clip_preserve(cr); | |||||
// Draw background | |||||
const PuglRect frame = puglGetFrame(view); | |||||
const double width = frame.width; | |||||
const double height = frame.height; | |||||
if (app->entered) { | |||||
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); | |||||
} else { | |||||
cairo_set_source_rgb(cr, 0, 0, 0); | |||||
} | |||||
cairo_fill(cr); | |||||
// Scale to view size | |||||
const double scaleX = (width - (512 / width)) / 512.0; | |||||
const double scaleY = (height - (512 / height)) / 512.0; | |||||
cairo_scale(cr, scaleX, scaleY); | |||||
// Draw button | |||||
for (const Button* b = buttons; b->label; ++b) { | |||||
buttonDraw( | |||||
app, cr, b, app->opts.continuous ? puglGetTime(app->world) : 0.0); | |||||
} | |||||
++app->framesDrawn; | |||||
} | |||||
static void | |||||
onClose(PuglView* view) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
app->quit = 1; | |||||
} | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
printEvent(event, "Event: ", app->opts.verbose); | |||||
switch (event->type) { | |||||
case PUGL_KEY_PRESS: | |||||
if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) { | |||||
app->quit = 1; | |||||
} | |||||
break; | |||||
case PUGL_BUTTON_PRESS: | |||||
app->mouseDown = true; | |||||
postButtonRedisplay(view); | |||||
break; | |||||
case PUGL_BUTTON_RELEASE: | |||||
app->mouseDown = false; | |||||
postButtonRedisplay(view); | |||||
break; | |||||
case PUGL_POINTER_IN: | |||||
app->entered = true; | |||||
puglPostRedisplay(view); | |||||
break; | |||||
case PUGL_POINTER_OUT: | |||||
app->entered = false; | |||||
puglPostRedisplay(view); | |||||
break; | |||||
case PUGL_UPDATE: | |||||
if (app->opts.continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
onDisplay(app, view, &event->expose); | |||||
break; | |||||
case PUGL_CLOSE: | |||||
onClose(view); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
PuglTestApp app; | |||||
memset(&app, 0, sizeof(app)); | |||||
app.opts = puglParseTestOptions(&argc, &argv); | |||||
if (app.opts.help) { | |||||
puglPrintTestUsage("pugl_test", ""); | |||||
return 1; | |||||
} | |||||
app.world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
puglSetClassName(app.world, "PuglCairoTest"); | |||||
PuglView* view = puglNewView(app.world); | |||||
puglSetWindowTitle(view, "Pugl Cairo Demo"); | |||||
puglSetDefaultSize(view, 512, 512); | |||||
puglSetMinSize(view, 256, 256); | |||||
puglSetMaxSize(view, 2048, 2048); | |||||
puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable); | |||||
puglSetHandle(view, &app); | |||||
puglSetBackend(view, puglCairoBackend()); | |||||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, app.opts.ignoreKeyRepeat); | |||||
puglSetEventFunc(view, onEvent); | |||||
PuglStatus st = puglRealize(view); | |||||
if (st) { | |||||
return logError("Failed to create window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglShow(view); | |||||
PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)}; | |||||
const double timeout = app.opts.continuous ? (1 / 60.0) : -1.0; | |||||
while (!app.quit) { | |||||
puglUpdate(app.world, timeout); | |||||
if (app.opts.continuous) { | |||||
puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn); | |||||
} | |||||
} | |||||
puglFreeView(view); | |||||
puglFreeWorld(app.world); | |||||
return 0; | |||||
} |
@@ -1,153 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "cube_view.h" | |||||
#include "demo_utils.h" | |||||
#include "test/test_utils.h" | |||||
#include "pugl/gl.hpp" | |||||
#include "pugl/pugl.h" | |||||
#include "pugl/pugl.hpp" | |||||
#include <cmath> | |||||
class CubeView : public pugl::View | |||||
{ | |||||
public: | |||||
explicit CubeView(pugl::World& world) | |||||
: pugl::View{world} | |||||
{ | |||||
setEventHandler(*this); | |||||
} | |||||
template<PuglEventType t, class Base> | |||||
pugl::Status onEvent(const pugl::Event<t, Base>&) noexcept | |||||
{ | |||||
return pugl::Status::success; | |||||
} | |||||
static pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::UpdateEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::KeyPressEvent& event) noexcept; | |||||
pugl::Status onEvent(const pugl::CloseEvent& event) noexcept; | |||||
bool quit() const { return _quit; } | |||||
private: | |||||
double _xAngle{0.0}; | |||||
double _yAngle{0.0}; | |||||
double _lastDrawTime{0.0}; | |||||
bool _quit{false}; | |||||
}; | |||||
pugl::Status | |||||
CubeView::onEvent(const pugl::ConfigureEvent& event) noexcept | |||||
{ | |||||
reshapeCube(static_cast<float>(event.width), | |||||
static_cast<float>(event.height)); | |||||
return pugl::Status::success; | |||||
} | |||||
pugl::Status | |||||
CubeView::onEvent(const pugl::UpdateEvent&) noexcept | |||||
{ | |||||
// Normally, we would post a redisplay: | |||||
// return postRedisplay(); | |||||
// But for testing, use sendEvent() instead: | |||||
return sendEvent( | |||||
pugl::ExposeEvent{0u, 0.0, 0.0, frame().width, frame().height}); | |||||
} | |||||
pugl::Status | |||||
CubeView::onEvent(const pugl::ExposeEvent&) noexcept | |||||
{ | |||||
const double thisTime = world().time(); | |||||
const double dTime = thisTime - _lastDrawTime; | |||||
const double dAngle = dTime * 100.0; | |||||
_xAngle = fmod(_xAngle + dAngle, 360.0); | |||||
_yAngle = fmod(_yAngle + dAngle, 360.0); | |||||
displayCube(cobj(), | |||||
8.0f, | |||||
static_cast<float>(_xAngle), | |||||
static_cast<float>(_yAngle), | |||||
false); | |||||
_lastDrawTime = thisTime; | |||||
return pugl::Status::success; | |||||
} | |||||
pugl::Status | |||||
CubeView::onEvent(const pugl::KeyPressEvent& event) noexcept | |||||
{ | |||||
if (event.key == PUGL_KEY_ESCAPE || event.key == 'q') { | |||||
_quit = true; | |||||
} | |||||
return pugl::Status::success; | |||||
} | |||||
pugl::Status | |||||
CubeView::onEvent(const pugl::CloseEvent&) noexcept | |||||
{ | |||||
_quit = true; | |||||
return pugl::Status::success; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); | |||||
if (opts.help) { | |||||
puglPrintTestUsage("pugl_cpp_demo", ""); | |||||
return 1; | |||||
} | |||||
pugl::World world{pugl::WorldType::program}; | |||||
CubeView view{world}; | |||||
PuglFpsPrinter fpsPrinter{}; | |||||
world.setClassName("PuglCppTest"); | |||||
view.setWindowTitle("Pugl C++ Test"); | |||||
view.setDefaultSize(512, 512); | |||||
view.setMinSize(64, 64); | |||||
view.setMaxSize(256, 256); | |||||
view.setAspectRatio(1, 1, 16, 9); | |||||
view.setBackend(pugl::glBackend()); | |||||
view.setHint(pugl::ViewHint::resizable, opts.resizable); | |||||
view.setHint(pugl::ViewHint::samples, opts.samples); | |||||
view.setHint(pugl::ViewHint::doubleBuffer, opts.doubleBuffer); | |||||
view.setHint(pugl::ViewHint::swapInterval, opts.sync); | |||||
view.setHint(pugl::ViewHint::ignoreKeyRepeat, opts.ignoreKeyRepeat); | |||||
view.realize(); | |||||
view.show(); | |||||
unsigned framesDrawn = 0; | |||||
while (!view.quit()) { | |||||
world.update(0.0); | |||||
++framesDrawn; | |||||
puglPrintFps(world.cobj(), &fpsPrinter, &framesDrawn); | |||||
} | |||||
return 0; | |||||
} |
@@ -1,169 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "test/test_utils.h" | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include <stdbool.h> | |||||
static const int N_CURSORS = 7; | |||||
static const int N_ROWS = 2; | |||||
static const int N_COLS = 4; | |||||
typedef struct { | |||||
PuglWorld* world; | |||||
PuglTestOptions opts; | |||||
bool quit; | |||||
} PuglTestApp; | |||||
static void | |||||
onConfigure(const double width, const double height) | |||||
{ | |||||
glEnable(GL_DEPTH_TEST); | |||||
glDepthFunc(GL_LESS); | |||||
glClearColor(0.2f, 0.2f, 0.2f, 1.0f); | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
glViewport(0, 0, (int)width, (int)height); | |||||
} | |||||
static void | |||||
onExpose(void) | |||||
{ | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
glColor3f(0.6f, 0.6f, 0.6f); | |||||
for (int row = 1; row < N_ROWS; ++row) { | |||||
const float y = (float)row * (2.0f / (float)N_ROWS) - 1.0f; | |||||
glBegin(GL_LINES); | |||||
glVertex2f(-1.0f, y); | |||||
glVertex2f(1.0f, y); | |||||
glEnd(); | |||||
} | |||||
for (int col = 1; col < N_COLS; ++col) { | |||||
const float x = (float)col * (2.0f / (float)N_COLS) - 1.0f; | |||||
glBegin(GL_LINES); | |||||
glVertex2f(x, -1.0f); | |||||
glVertex2f(x, 1.0f); | |||||
glEnd(); | |||||
} | |||||
} | |||||
static void | |||||
onMotion(PuglView* view, double x, double y) | |||||
{ | |||||
const PuglRect frame = puglGetFrame(view); | |||||
int row = (int)(y * N_ROWS / frame.height); | |||||
int col = (int)(x * N_COLS / frame.width); | |||||
row = (row < 0) ? 0 : (row >= N_ROWS) ? (N_ROWS - 1) : row; | |||||
col = (col < 0) ? 0 : (col >= N_COLS) ? (N_COLS - 1) : col; | |||||
const PuglCursor cursor = (PuglCursor)((row * N_COLS + col) % N_CURSORS); | |||||
puglSetCursor(view, cursor); | |||||
} | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
printEvent(event, "Event: ", app->opts.verbose); | |||||
switch (event->type) { | |||||
case PUGL_CONFIGURE: | |||||
onConfigure(event->configure.width, event->configure.height); | |||||
break; | |||||
case PUGL_KEY_PRESS: | |||||
if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) { | |||||
app->quit = 1; | |||||
} | |||||
break; | |||||
case PUGL_MOTION: | |||||
onMotion(view, event->motion.x, event->motion.y); | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
onExpose(); | |||||
break; | |||||
case PUGL_POINTER_OUT: | |||||
puglSetCursor(view, PUGL_CURSOR_ARROW); | |||||
break; | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
PuglTestApp app = {0}; | |||||
app.opts = puglParseTestOptions(&argc, &argv); | |||||
if (app.opts.help) { | |||||
puglPrintTestUsage(argv[0], ""); | |||||
return 1; | |||||
} | |||||
app.world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
puglSetWorldHandle(app.world, &app); | |||||
puglSetClassName(app.world, "Pugl Test"); | |||||
PuglView* view = puglNewView(app.world); | |||||
puglSetWindowTitle(view, "Pugl Window Demo"); | |||||
puglSetDefaultSize(view, 512, 256); | |||||
puglSetMinSize(view, 128, 64); | |||||
puglSetMaxSize(view, 512, 256); | |||||
puglSetBackend(view, puglGlBackend()); | |||||
puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, app.opts.errorChecking); | |||||
puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable); | |||||
puglSetViewHint(view, PUGL_SAMPLES, app.opts.samples); | |||||
puglSetViewHint(view, PUGL_DOUBLE_BUFFER, app.opts.doubleBuffer); | |||||
puglSetViewHint(view, PUGL_SWAP_INTERVAL, app.opts.sync); | |||||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, app.opts.ignoreKeyRepeat); | |||||
puglSetHandle(view, &app); | |||||
puglSetEventFunc(view, onEvent); | |||||
const PuglStatus st = puglRealize(view); | |||||
if (st) { | |||||
return logError("Failed to create window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglShow(view); | |||||
while (!app.quit) { | |||||
puglUpdate(app.world, -1.0); | |||||
} | |||||
puglFreeView(view); | |||||
puglFreeWorld(app.world); | |||||
return 0; | |||||
} |
@@ -1,363 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "cube_view.h" | |||||
#include "demo_utils.h" | |||||
#include "test/test_utils.h" | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include <math.h> | |||||
#include <stdbool.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
static const int borderWidth = 64; | |||||
static const uintptr_t reverseTimerId = 1u; | |||||
typedef struct { | |||||
PuglWorld* world; | |||||
PuglView* parent; | |||||
PuglView* child; | |||||
double xAngle; | |||||
double yAngle; | |||||
double lastMouseX; | |||||
double lastMouseY; | |||||
double lastDrawTime; | |||||
float dist; | |||||
int quit; | |||||
bool continuous; | |||||
bool mouseEntered; | |||||
bool verbose; | |||||
bool reversing; | |||||
} PuglTestApp; | |||||
// clang-format off | |||||
static const float backgroundVertices[] = { | |||||
-1.0f, 1.0f, -1.0f, // Top left | |||||
1.0f, 1.0f, -1.0f, // Top right | |||||
-1.0f, -1.0f, -1.0f, // Bottom left | |||||
1.0f, -1.0f, -1.0f, // Bottom right | |||||
}; | |||||
static const float backgroundColorVertices[] = { | |||||
0.25f, 0.25f, 0.25f, // Top left | |||||
0.25f, 0.50f, 0.25f, // Top right | |||||
0.25f, 0.50f, 0.25f, // Bottom left | |||||
0.25f, 0.75f, 0.5f, // Bottom right | |||||
}; | |||||
// clang-format on | |||||
static PuglRect | |||||
getChildFrame(const PuglRect parentFrame) | |||||
{ | |||||
const PuglRect childFrame = {borderWidth, | |||||
borderWidth, | |||||
parentFrame.width - 2 * borderWidth, | |||||
parentFrame.height - 2 * borderWidth}; | |||||
return childFrame; | |||||
} | |||||
static void | |||||
onDisplay(PuglView* view) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
const double thisTime = puglGetTime(app->world); | |||||
if (app->continuous) { | |||||
const double dTime = | |||||
(thisTime - app->lastDrawTime) * (app->reversing ? -1.0 : 1.0); | |||||
app->xAngle = fmod(app->xAngle + dTime * 100.0, 360.0); | |||||
app->yAngle = fmod(app->yAngle + dTime * 100.0, 360.0); | |||||
} | |||||
displayCube( | |||||
view, app->dist, (float)app->xAngle, (float)app->yAngle, app->mouseEntered); | |||||
app->lastDrawTime = thisTime; | |||||
} | |||||
static void | |||||
swapFocus(PuglTestApp* app) | |||||
{ | |||||
if (puglHasFocus(app->parent)) { | |||||
puglGrabFocus(app->child); | |||||
} else { | |||||
puglGrabFocus(app->parent); | |||||
} | |||||
if (!app->continuous) { | |||||
puglPostRedisplay(app->parent); | |||||
puglPostRedisplay(app->child); | |||||
} | |||||
} | |||||
static void | |||||
onKeyPress(PuglView* view, const PuglKeyEvent* event, const char* prefix) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
PuglRect frame = puglGetFrame(view); | |||||
if (event->key == '\t') { | |||||
swapFocus(app); | |||||
} else if (event->key == 'q' || event->key == PUGL_KEY_ESCAPE) { | |||||
app->quit = 1; | |||||
} else if (event->state & PUGL_MOD_CTRL && event->key == 'c') { | |||||
puglSetClipboard(view, NULL, "Pugl test", strlen("Pugl test") + 1); | |||||
fprintf(stderr, "%sCopy \"Pugl test\"\n", prefix); | |||||
} else if (event->state & PUGL_MOD_CTRL && event->key == 'v') { | |||||
const char* type = NULL; | |||||
size_t len = 0; | |||||
const char* text = (const char*)puglGetClipboard(view, &type, &len); | |||||
fprintf(stderr, "%sPaste \"%s\"\n", prefix, text); | |||||
} else if (event->state & PUGL_MOD_SHIFT) { | |||||
if (event->key == PUGL_KEY_UP) { | |||||
frame.height += 10; | |||||
} else if (event->key == PUGL_KEY_DOWN) { | |||||
frame.height -= 10; | |||||
} else if (event->key == PUGL_KEY_LEFT) { | |||||
frame.width -= 10; | |||||
} else if (event->key == PUGL_KEY_RIGHT) { | |||||
frame.width += 10; | |||||
} else { | |||||
return; | |||||
} | |||||
puglSetFrame(view, frame); | |||||
} else { | |||||
if (event->key == PUGL_KEY_UP) { | |||||
frame.y -= 10; | |||||
} else if (event->key == PUGL_KEY_DOWN) { | |||||
frame.y += 10; | |||||
} else if (event->key == PUGL_KEY_LEFT) { | |||||
frame.x -= 10; | |||||
} else if (event->key == PUGL_KEY_RIGHT) { | |||||
frame.x += 10; | |||||
} else { | |||||
return; | |||||
} | |||||
puglSetFrame(view, frame); | |||||
} | |||||
} | |||||
static PuglStatus | |||||
onParentEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
const PuglRect parentFrame = puglGetFrame(view); | |||||
printEvent(event, "Parent: ", app->verbose); | |||||
switch (event->type) { | |||||
case PUGL_CONFIGURE: | |||||
reshapeCube((float)event->configure.width, (float)event->configure.height); | |||||
puglSetFrame(app->child, getChildFrame(parentFrame)); | |||||
break; | |||||
case PUGL_UPDATE: | |||||
if (app->continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
if (puglHasFocus(app->parent)) { | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
glEnableClientState(GL_VERTEX_ARRAY); | |||||
glEnableClientState(GL_COLOR_ARRAY); | |||||
glVertexPointer(3, GL_FLOAT, 0, backgroundVertices); | |||||
glColorPointer(3, GL_FLOAT, 0, backgroundColorVertices); | |||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |||||
glDisableClientState(GL_COLOR_ARRAY); | |||||
glDisableClientState(GL_VERTEX_ARRAY); | |||||
} else { | |||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
} | |||||
break; | |||||
case PUGL_KEY_PRESS: | |||||
onKeyPress(view, &event->key, "Parent: "); | |||||
break; | |||||
case PUGL_MOTION: | |||||
break; | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
printEvent(event, "Child: ", app->verbose); | |||||
switch (event->type) { | |||||
case PUGL_CONFIGURE: | |||||
reshapeCube((float)event->configure.width, (float)event->configure.height); | |||||
break; | |||||
case PUGL_UPDATE: | |||||
if (app->continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
onDisplay(view); | |||||
break; | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
case PUGL_KEY_PRESS: | |||||
onKeyPress(view, &event->key, "Child: "); | |||||
break; | |||||
case PUGL_MOTION: | |||||
app->xAngle -= event->motion.x - app->lastMouseX; | |||||
app->yAngle += event->motion.y - app->lastMouseY; | |||||
app->lastMouseX = event->motion.x; | |||||
app->lastMouseY = event->motion.y; | |||||
if (!app->continuous) { | |||||
puglPostRedisplay(view); | |||||
puglPostRedisplay(app->parent); | |||||
} | |||||
break; | |||||
case PUGL_SCROLL: | |||||
app->dist = fmaxf(10.0f, app->dist + (float)event->scroll.dy); | |||||
if (!app->continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
case PUGL_POINTER_IN: | |||||
app->mouseEntered = true; | |||||
break; | |||||
case PUGL_POINTER_OUT: | |||||
app->mouseEntered = false; | |||||
break; | |||||
case PUGL_TIMER: | |||||
app->reversing = !app->reversing; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
PuglTestApp app = {0}; | |||||
app.dist = 10; | |||||
const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); | |||||
if (opts.help) { | |||||
puglPrintTestUsage("pugl_test", ""); | |||||
return 1; | |||||
} | |||||
app.continuous = opts.continuous; | |||||
app.verbose = opts.verbose; | |||||
app.world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
app.parent = puglNewView(app.world); | |||||
app.child = puglNewView(app.world); | |||||
puglSetClassName(app.world, "Pugl Test"); | |||||
const PuglRect parentFrame = {0, 0, 512, 512}; | |||||
puglSetDefaultSize(app.parent, 512, 512); | |||||
puglSetMinSize(app.parent, borderWidth * 3, borderWidth * 3); | |||||
puglSetMaxSize(app.parent, 1024, 1024); | |||||
puglSetAspectRatio(app.parent, 1, 1, 16, 9); | |||||
puglSetBackend(app.parent, puglGlBackend()); | |||||
puglSetViewHint(app.parent, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking); | |||||
puglSetViewHint(app.parent, PUGL_RESIZABLE, opts.resizable); | |||||
puglSetViewHint(app.parent, PUGL_SAMPLES, opts.samples); | |||||
puglSetViewHint(app.parent, PUGL_DOUBLE_BUFFER, opts.doubleBuffer); | |||||
puglSetViewHint(app.parent, PUGL_SWAP_INTERVAL, opts.sync); | |||||
puglSetViewHint(app.parent, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat); | |||||
puglSetHandle(app.parent, &app); | |||||
puglSetEventFunc(app.parent, onParentEvent); | |||||
PuglStatus st = PUGL_SUCCESS; | |||||
const uint8_t title[] = { | |||||
'P', 'u', 'g', 'l', ' ', 'P', 'r', 0xC3, 0xBC, 'f', 'u', 'n', 'g', 0}; | |||||
puglSetWindowTitle(app.parent, (const char*)title); | |||||
if ((st = puglRealize(app.parent))) { | |||||
return logError("Failed to create parent window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglSetFrame(app.child, getChildFrame(parentFrame)); | |||||
puglSetParentWindow(app.child, puglGetNativeWindow(app.parent)); | |||||
puglSetViewHint(app.child, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking); | |||||
puglSetViewHint(app.child, PUGL_SAMPLES, opts.samples); | |||||
puglSetViewHint(app.child, PUGL_DOUBLE_BUFFER, opts.doubleBuffer); | |||||
puglSetViewHint(app.child, PUGL_SWAP_INTERVAL, opts.sync); | |||||
puglSetBackend(app.child, puglGlBackend()); | |||||
puglSetViewHint(app.child, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat); | |||||
puglSetHandle(app.child, &app); | |||||
puglSetEventFunc(app.child, onEvent); | |||||
if ((st = puglRealize(app.child))) { | |||||
return logError("Failed to create child window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglShow(app.parent); | |||||
puglShow(app.child); | |||||
puglStartTimer(app.child, reverseTimerId, 3.6); | |||||
PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)}; | |||||
unsigned framesDrawn = 0; | |||||
bool requestedAttention = false; | |||||
while (!app.quit) { | |||||
const double thisTime = puglGetTime(app.world); | |||||
puglUpdate(app.world, app.continuous ? 0.0 : -1.0); | |||||
++framesDrawn; | |||||
if (!requestedAttention && thisTime > 5.0) { | |||||
puglRequestAttention(app.parent); | |||||
requestedAttention = true; | |||||
} | |||||
if (app.continuous) { | |||||
puglPrintFps(app.world, &fpsPrinter, &framesDrawn); | |||||
} | |||||
} | |||||
puglFreeView(app.child); | |||||
puglFreeView(app.parent); | |||||
puglFreeWorld(app.world); | |||||
return 0; | |||||
} |
@@ -1,79 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "test/test_utils.h" | |||||
#include "pugl/pugl.h" | |||||
#include "pugl/stub.h" | |||||
#include <stdbool.h> | |||||
#include <stdio.h> | |||||
typedef struct { | |||||
PuglWorld* world; | |||||
PuglView* view; | |||||
int quit; | |||||
} PuglPrintEventsApp; | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglPrintEventsApp* app = (PuglPrintEventsApp*)puglGetHandle(view); | |||||
printEvent(event, "Event: ", true); | |||||
switch (event->type) { | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
int | |||||
main(void) | |||||
{ | |||||
PuglPrintEventsApp app = {NULL, NULL, 0}; | |||||
app.world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
app.view = puglNewView(app.world); | |||||
puglSetClassName(app.world, "Pugl Print Events"); | |||||
puglSetWindowTitle(app.view, "Pugl Event Printer"); | |||||
puglSetDefaultSize(app.view, 512, 512); | |||||
puglSetBackend(app.view, puglStubBackend()); | |||||
puglSetHandle(app.view, &app); | |||||
puglSetEventFunc(app.view, onEvent); | |||||
PuglStatus st = puglRealize(app.view); | |||||
if (st) { | |||||
return logError("Failed to create window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglShow(app.view); | |||||
while (!app.quit) { | |||||
puglUpdate(app.world, -1.0); | |||||
} | |||||
puglFreeView(app.view); | |||||
puglFreeWorld(app.world); | |||||
return 0; | |||||
} |
@@ -1,480 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/* | |||||
An example of drawing with OpenGL 3/4. | |||||
This is an example of using OpenGL for pixel-perfect 2D drawing. It uses | |||||
pixel coordinates for positions and sizes so that things work roughly like a | |||||
typical 2D graphics API. | |||||
The program draws a bunch of rectangles with borders, using instancing. | |||||
Each rectangle has origin, size, and fill color attributes, which are shared | |||||
for all four vertices. On each frame, a single buffer with all the | |||||
rectangle data is sent to the GPU, and everything is drawn with a single | |||||
draw call. | |||||
This is not particularly realistic or optimal, but serves as a decent rough | |||||
benchmark for how much simple geometry you can draw. The number of | |||||
rectangles can be given on the command line. For reference, it begins to | |||||
struggle to maintain 60 FPS on my machine (1950x + Vega64) with more than | |||||
about 100000 rectangles. | |||||
*/ | |||||
#include "demo_utils.h" | |||||
#include "file_utils.h" | |||||
#include "rects.h" | |||||
#include "shader_utils.h" | |||||
#include "test/test_utils.h" | |||||
#include "glad/glad.h" | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include <math.h> | |||||
#include <stddef.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
static const int defaultWidth = 512; | |||||
static const int defaultHeight = 512; | |||||
static const uintptr_t resizeTimerId = 1u; | |||||
typedef struct { | |||||
mat4 projection; | |||||
} RectUniforms; | |||||
typedef struct { | |||||
const char* programPath; | |||||
PuglWorld* world; | |||||
PuglView* view; | |||||
PuglTestOptions opts; | |||||
size_t numRects; | |||||
Rect* rects; | |||||
Program drawRect; | |||||
GLuint vao; | |||||
GLuint vbo; | |||||
GLuint instanceVbo; | |||||
GLuint ibo; | |||||
double lastDrawDuration; | |||||
double lastFrameEndTime; | |||||
unsigned framesDrawn; | |||||
int glMajorVersion; | |||||
int glMinorVersion; | |||||
int quit; | |||||
} PuglTestApp; | |||||
static PuglStatus | |||||
setupGl(PuglTestApp* app); | |||||
static void | |||||
teardownGl(PuglTestApp* app); | |||||
static void | |||||
onConfigure(PuglView* view, double width, double height) | |||||
{ | |||||
(void)view; | |||||
glEnable(GL_BLEND); | |||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); | |||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); | |||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |||||
glViewport(0, 0, (int)width, (int)height); | |||||
} | |||||
static void | |||||
onExpose(PuglView* view) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
const PuglRect frame = puglGetFrame(view); | |||||
const float width = (float)frame.width; | |||||
const float height = (float)frame.height; | |||||
const double time = puglGetTime(puglGetWorld(view)); | |||||
// Construct projection matrix for 2D window surface (in pixels) | |||||
mat4 proj; | |||||
mat4Ortho( | |||||
proj, 0.0f, (float)frame.width, 0.0f, (float)frame.height, -1.0f, 1.0f); | |||||
// Clear and bind everything that is the same for every rect | |||||
glClear(GL_COLOR_BUFFER_BIT); | |||||
glUseProgram(app->drawRect.program); | |||||
glBindVertexArray(app->vao); | |||||
for (size_t i = 0; i < app->numRects; ++i) { | |||||
moveRect(&app->rects[i], i, app->numRects, width, height, time); | |||||
} | |||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(proj), &proj, GL_STREAM_DRAW); | |||||
glBufferSubData( | |||||
GL_ARRAY_BUFFER, 0, (GLsizeiptr)(app->numRects * sizeof(Rect)), app->rects); | |||||
glDrawElementsInstanced( | |||||
GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, NULL, (GLsizei)(app->numRects * 4)); | |||||
++app->framesDrawn; | |||||
app->lastFrameEndTime = puglGetTime(puglGetWorld(view)); | |||||
app->lastDrawDuration = app->lastFrameEndTime - time; | |||||
} | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view); | |||||
printEvent(event, "Event: ", app->opts.verbose); | |||||
switch (event->type) { | |||||
case PUGL_CREATE: | |||||
setupGl(app); | |||||
break; | |||||
case PUGL_DESTROY: | |||||
teardownGl(app); | |||||
break; | |||||
case PUGL_CONFIGURE: | |||||
onConfigure(view, event->configure.width, event->configure.height); | |||||
break; | |||||
case PUGL_UPDATE: | |||||
puglPostRedisplay(view); | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
onExpose(view); | |||||
break; | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
case PUGL_LOOP_ENTER: | |||||
puglStartTimer(view, | |||||
resizeTimerId, | |||||
1.0 / (double)puglGetViewHint(view, PUGL_REFRESH_RATE)); | |||||
break; | |||||
case PUGL_LOOP_LEAVE: | |||||
puglStopTimer(view, resizeTimerId); | |||||
break; | |||||
case PUGL_KEY_PRESS: | |||||
if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) { | |||||
app->quit = 1; | |||||
} | |||||
break; | |||||
case PUGL_TIMER: | |||||
if (event->timer.id == resizeTimerId) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
static Rect* | |||||
makeRects(const size_t numRects) | |||||
{ | |||||
Rect* rects = (Rect*)calloc(numRects, sizeof(Rect)); | |||||
for (size_t i = 0; i < numRects; ++i) { | |||||
rects[i] = makeRect(i, (float)defaultWidth); | |||||
} | |||||
return rects; | |||||
} | |||||
static char* | |||||
loadShader(const char* const programPath, const char* const name) | |||||
{ | |||||
char* const path = resourcePath(programPath, name); | |||||
fprintf(stderr, "Loading shader %s\n", path); | |||||
FILE* const file = fopen(path, "r"); | |||||
if (!file) { | |||||
logError("Failed to open '%s'\n", path); | |||||
return NULL; | |||||
} | |||||
free(path); | |||||
fseek(file, 0, SEEK_END); | |||||
const size_t fileSize = (size_t)ftell(file); | |||||
fseek(file, 0, SEEK_SET); | |||||
char* source = (char*)calloc(1, fileSize + 1u); | |||||
fread(source, 1, fileSize, file); | |||||
fclose(file); | |||||
return source; | |||||
} | |||||
static int | |||||
parseOptions(PuglTestApp* app, int argc, char** argv) | |||||
{ | |||||
char* endptr = NULL; | |||||
// Parse command line options | |||||
app->numRects = 1024; | |||||
app->opts = puglParseTestOptions(&argc, &argv); | |||||
if (app->opts.help) { | |||||
return 1; | |||||
} | |||||
// Parse number of rectangles argument, if given | |||||
if (argc >= 1) { | |||||
app->numRects = (size_t)strtol(argv[0], &endptr, 10); | |||||
if (endptr != argv[0] + strlen(argv[0])) { | |||||
logError("Invalid number of rectangles: %s\n", argv[0]); | |||||
return 1; | |||||
} | |||||
} | |||||
// Parse OpenGL major version argument, if given | |||||
if (argc >= 2) { | |||||
app->glMajorVersion = (int)strtol(argv[1], &endptr, 10); | |||||
if (endptr != argv[1] + strlen(argv[1])) { | |||||
logError("Invalid GL major version: %s\n", argv[1]); | |||||
return 1; | |||||
} | |||||
if (app->glMajorVersion == 4) { | |||||
app->glMinorVersion = 2; | |||||
} else if (app->glMajorVersion != 3) { | |||||
logError("Unsupported GL major version %d\n", app->glMajorVersion); | |||||
return 1; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
static void | |||||
setupPugl(PuglTestApp* app) | |||||
{ | |||||
// Create world, view, and rect data | |||||
app->world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
app->view = puglNewView(app->world); | |||||
app->rects = makeRects(app->numRects); | |||||
// Set up world and view | |||||
puglSetClassName(app->world, "PuglGL3Demo"); | |||||
puglSetWindowTitle(app->view, "Pugl OpenGL 3"); | |||||
puglSetDefaultSize(app->view, defaultWidth, defaultHeight); | |||||
puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4); | |||||
puglSetMaxSize(app->view, defaultWidth * 4, defaultHeight * 4); | |||||
puglSetAspectRatio(app->view, 1, 1, 16, 9); | |||||
puglSetBackend(app->view, puglGlBackend()); | |||||
puglSetViewHint(app->view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | |||||
puglSetViewHint(app->view, PUGL_USE_DEBUG_CONTEXT, app->opts.errorChecking); | |||||
puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MAJOR, app->glMajorVersion); | |||||
puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, app->glMinorVersion); | |||||
puglSetViewHint(app->view, PUGL_RESIZABLE, app->opts.resizable); | |||||
puglSetViewHint(app->view, PUGL_SAMPLES, app->opts.samples); | |||||
puglSetViewHint(app->view, PUGL_DOUBLE_BUFFER, app->opts.doubleBuffer); | |||||
puglSetViewHint(app->view, PUGL_SWAP_INTERVAL, app->opts.sync); | |||||
puglSetViewHint(app->view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE); | |||||
puglSetHandle(app->view, app); | |||||
puglSetEventFunc(app->view, onEvent); | |||||
} | |||||
static PuglStatus | |||||
setupGl(PuglTestApp* app) | |||||
{ | |||||
// Load GL functions via GLAD | |||||
if (!gladLoadGLLoader((GLADloadproc)&puglGetProcAddress)) { | |||||
logError("Failed to load GL\n"); | |||||
return PUGL_FAILURE; | |||||
} | |||||
const char* const headerFile = | |||||
(app->glMajorVersion == 3 ? "shaders/header_330.glsl" | |||||
: "shaders/header_420.glsl"); | |||||
// Load shader sources | |||||
char* const headerSource = loadShader(app->programPath, headerFile); | |||||
char* const vertexSource = loadShader(app->programPath, "shaders/rect.vert"); | |||||
char* const fragmentSource = | |||||
loadShader(app->programPath, "shaders/rect.frag"); | |||||
if (!vertexSource || !fragmentSource) { | |||||
logError("Failed to load shader sources\n"); | |||||
return PUGL_FAILURE; | |||||
} | |||||
// Compile rectangle shaders and program | |||||
app->drawRect = compileProgram(headerSource, vertexSource, fragmentSource); | |||||
free(fragmentSource); | |||||
free(vertexSource); | |||||
free(headerSource); | |||||
if (!app->drawRect.program) { | |||||
return PUGL_FAILURE; | |||||
} | |||||
// Get location of rectangle shader uniform block | |||||
const GLuint globalsIndex = | |||||
glGetUniformBlockIndex(app->drawRect.program, "UniformBufferObject"); | |||||
// Generate/bind a uniform buffer for setting rectangle properties | |||||
GLuint uboHandle = 0; | |||||
glGenBuffers(1, &uboHandle); | |||||
glBindBuffer(GL_UNIFORM_BUFFER, uboHandle); | |||||
glBindBufferBase(GL_UNIFORM_BUFFER, globalsIndex, uboHandle); | |||||
// Generate/bind a VAO to track state | |||||
glGenVertexArrays(1, &app->vao); | |||||
glBindVertexArray(app->vao); | |||||
// Generate/bind a VBO to store vertex position data | |||||
glGenBuffers(1, &app->vbo); | |||||
glBindBuffer(GL_ARRAY_BUFFER, app->vbo); | |||||
glBufferData( | |||||
GL_ARRAY_BUFFER, sizeof(rectVertices), rectVertices, GL_STATIC_DRAW); | |||||
// Attribute 0 is position, 2 floats from the VBO | |||||
glEnableVertexAttribArray(0); | |||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL); | |||||
// Generate/bind a VBO to store instance attribute data | |||||
glGenBuffers(1, &app->instanceVbo); | |||||
glBindBuffer(GL_ARRAY_BUFFER, app->instanceVbo); | |||||
glBufferData(GL_ARRAY_BUFFER, | |||||
(GLsizeiptr)(app->numRects * sizeof(Rect)), | |||||
app->rects, | |||||
GL_STREAM_DRAW); | |||||
// Attribute 1 is Rect::position | |||||
glEnableVertexAttribArray(1); | |||||
glVertexAttribDivisor(1, 4); | |||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), NULL); | |||||
// Attribute 2 is Rect::size | |||||
glEnableVertexAttribArray(2); | |||||
glVertexAttribDivisor(2, 4); | |||||
glVertexAttribPointer( | |||||
2, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (const void*)offsetof(Rect, size)); | |||||
// Attribute 3 is Rect::fillColor | |||||
glEnableVertexAttribArray(3); | |||||
glVertexAttribDivisor(3, 4); | |||||
glVertexAttribPointer(3, | |||||
4, | |||||
GL_FLOAT, | |||||
GL_FALSE, | |||||
sizeof(Rect), | |||||
(const void*)offsetof(Rect, fillColor)); | |||||
// Set up the IBO to index into the VBO | |||||
glGenBuffers(1, &app->ibo); | |||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app->ibo); | |||||
glBufferData( | |||||
GL_ELEMENT_ARRAY_BUFFER, sizeof(rectIndices), rectIndices, GL_STATIC_DRAW); | |||||
return PUGL_SUCCESS; | |||||
} | |||||
static void | |||||
teardownGl(PuglTestApp* app) | |||||
{ | |||||
glDeleteBuffers(1, &app->ibo); | |||||
glDeleteBuffers(1, &app->vbo); | |||||
glDeleteBuffers(1, &app->instanceVbo); | |||||
glDeleteVertexArrays(1, &app->vao); | |||||
deleteProgram(app->drawRect); | |||||
} | |||||
static double | |||||
updateTimeout(const PuglTestApp* const app) | |||||
{ | |||||
if (!puglGetVisible(app->view)) { | |||||
return -1.0; // View is invisible (minimized), wait until something happens | |||||
} | |||||
if (!app->opts.sync) { | |||||
return 0.0; // VSync explicitly disabled, run as fast as possible | |||||
} | |||||
/* To minimize input latency and get smooth performance during window | |||||
resizing, we want to poll for events as long as possible before starting | |||||
to draw the next frame. This ensures that as many events are consumed as | |||||
possible before starting to draw, or, equivalently, that the next rendered | |||||
frame represents the latest events possible. This is particularly | |||||
important for mouse input and "live" window resizing, where many events | |||||
tend to pile up within a frame. | |||||
To do this, we keep track of the time when the last frame was finished | |||||
drawing, and how long it took to expose (and assume this is relatively | |||||
stable). Then, we can calculate how much time there is from now until the | |||||
time when we should start drawing to not miss the deadline, and use that | |||||
as the timeout for puglUpdate(). | |||||
*/ | |||||
const int refreshRate = puglGetViewHint(app->view, PUGL_REFRESH_RATE); | |||||
const double now = puglGetTime(app->world); | |||||
const double nextFrameEndTime = app->lastFrameEndTime + (1.0 / refreshRate); | |||||
const double nextExposeTime = nextFrameEndTime - app->lastDrawDuration; | |||||
const double timeUntilNext = nextExposeTime - now; | |||||
return timeUntilNext; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
PuglTestApp app = {0}; | |||||
app.programPath = argv[0]; | |||||
app.glMajorVersion = 3; | |||||
app.glMinorVersion = 3; | |||||
// Parse command line options | |||||
if (parseOptions(&app, argc, argv)) { | |||||
puglPrintTestUsage("pugl_shader_demo", "[NUM_RECTS] [GL_MAJOR]"); | |||||
return 1; | |||||
} | |||||
// Create and configure world and view | |||||
setupPugl(&app); | |||||
// Create window (which will send a PUGL_CREATE event) | |||||
const PuglStatus st = puglRealize(app.view); | |||||
if (st) { | |||||
return logError("Failed to create window (%s)\n", puglStrerror(st)); | |||||
} | |||||
// Show window | |||||
printViewHints(app.view); | |||||
puglShow(app.view); | |||||
// Grind away, drawing continuously | |||||
const double startTime = puglGetTime(app.world); | |||||
PuglFpsPrinter fpsPrinter = {startTime}; | |||||
while (!app.quit) { | |||||
puglUpdate(app.world, fmax(0.0, updateTimeout(&app))); | |||||
puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn); | |||||
} | |||||
// Destroy window (which will send a PUGL_DESTROY event) | |||||
puglFreeView(app.view); | |||||
// Free everything else | |||||
puglFreeWorld(app.world); | |||||
free(app.rects); | |||||
return 0; | |||||
} |
@@ -1,254 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/* | |||||
A demonstration of using multiple top-level windows. | |||||
*/ | |||||
#include "cube_view.h" | |||||
#include "demo_utils.h" | |||||
#include "test/test_utils.h" | |||||
#include "pugl/gl.h" | |||||
#include "pugl/pugl.h" | |||||
#include <math.h> | |||||
#include <stdbool.h> | |||||
#include <string.h> | |||||
typedef struct { | |||||
PuglView* view; | |||||
double xAngle; | |||||
double yAngle; | |||||
double lastMouseX; | |||||
double lastMouseY; | |||||
double lastDrawTime; | |||||
float dist; | |||||
bool entered; | |||||
} CubeView; | |||||
typedef struct { | |||||
PuglWorld* world; | |||||
CubeView cubes[2]; | |||||
int quit; | |||||
bool continuous; | |||||
bool verbose; | |||||
} PuglTestApp; | |||||
static const double pad = 64.0; | |||||
static void | |||||
onDisplay(PuglView* view) | |||||
{ | |||||
PuglWorld* world = puglGetWorld(view); | |||||
PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world); | |||||
CubeView* cube = (CubeView*)puglGetHandle(view); | |||||
const double thisTime = puglGetTime(app->world); | |||||
if (app->continuous) { | |||||
const double dTime = thisTime - cube->lastDrawTime; | |||||
cube->xAngle = fmod(cube->xAngle + dTime * 100.0, 360.0); | |||||
cube->yAngle = fmod(cube->yAngle + dTime * 100.0, 360.0); | |||||
} | |||||
displayCube( | |||||
view, cube->dist, (float)cube->xAngle, (float)cube->yAngle, cube->entered); | |||||
cube->lastDrawTime = thisTime; | |||||
} | |||||
static void | |||||
onKeyPress(PuglView* view, const PuglKeyEvent* event) | |||||
{ | |||||
PuglWorld* world = puglGetWorld(view); | |||||
PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world); | |||||
PuglRect frame = puglGetFrame(view); | |||||
if (event->key == 'q' || event->key == PUGL_KEY_ESCAPE) { | |||||
app->quit = 1; | |||||
} else if (event->state & PUGL_MOD_SHIFT) { | |||||
if (event->key == PUGL_KEY_UP) { | |||||
frame.height += 10; | |||||
} else if (event->key == PUGL_KEY_DOWN) { | |||||
frame.height -= 10; | |||||
} else if (event->key == PUGL_KEY_LEFT) { | |||||
frame.width -= 10; | |||||
} else if (event->key == PUGL_KEY_RIGHT) { | |||||
frame.width += 10; | |||||
} else { | |||||
return; | |||||
} | |||||
puglSetFrame(view, frame); | |||||
} else { | |||||
if (event->key == PUGL_KEY_UP) { | |||||
frame.y -= 10; | |||||
} else if (event->key == PUGL_KEY_DOWN) { | |||||
frame.y += 10; | |||||
} else if (event->key == PUGL_KEY_LEFT) { | |||||
frame.x -= 10; | |||||
} else if (event->key == PUGL_KEY_RIGHT) { | |||||
frame.x += 10; | |||||
} else { | |||||
return; | |||||
} | |||||
puglSetFrame(view, frame); | |||||
} | |||||
} | |||||
static void | |||||
redisplayView(PuglTestApp* app, PuglView* view) | |||||
{ | |||||
if (!app->continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
} | |||||
static PuglStatus | |||||
onEvent(PuglView* view, const PuglEvent* event) | |||||
{ | |||||
PuglWorld* world = puglGetWorld(view); | |||||
PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world); | |||||
CubeView* cube = (CubeView*)puglGetHandle(view); | |||||
const char* const prefix = cube == &app->cubes[0] ? "View 1: " : "View 2: "; | |||||
printEvent(event, prefix, app->verbose); | |||||
switch (event->type) { | |||||
case PUGL_CONFIGURE: | |||||
reshapeCube((float)event->configure.width, (float)event->configure.height); | |||||
break; | |||||
case PUGL_UPDATE: | |||||
if (app->continuous) { | |||||
puglPostRedisplay(view); | |||||
} | |||||
break; | |||||
case PUGL_EXPOSE: | |||||
onDisplay(view); | |||||
break; | |||||
case PUGL_CLOSE: | |||||
app->quit = 1; | |||||
break; | |||||
case PUGL_KEY_PRESS: | |||||
onKeyPress(view, &event->key); | |||||
break; | |||||
case PUGL_MOTION: | |||||
cube->xAngle -= event->motion.x - cube->lastMouseX; | |||||
cube->yAngle += event->motion.y - cube->lastMouseY; | |||||
cube->lastMouseX = event->motion.x; | |||||
cube->lastMouseY = event->motion.y; | |||||
redisplayView(app, view); | |||||
break; | |||||
case PUGL_SCROLL: | |||||
cube->dist = fmaxf(10.0f, cube->dist + (float)event->scroll.dy); | |||||
redisplayView(app, view); | |||||
break; | |||||
case PUGL_POINTER_IN: | |||||
cube->entered = true; | |||||
redisplayView(app, view); | |||||
break; | |||||
case PUGL_POINTER_OUT: | |||||
cube->entered = false; | |||||
redisplayView(app, view); | |||||
break; | |||||
case PUGL_FOCUS_IN: | |||||
case PUGL_FOCUS_OUT: | |||||
redisplayView(app, view); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
int | |||||
main(int argc, char** argv) | |||||
{ | |||||
PuglTestApp app = {0}; | |||||
const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); | |||||
if (opts.help) { | |||||
puglPrintTestUsage(argv[0], ""); | |||||
return 1; | |||||
} | |||||
app.continuous = opts.continuous; | |||||
app.verbose = opts.verbose; | |||||
app.world = puglNewWorld(PUGL_PROGRAM, 0); | |||||
app.cubes[0].view = puglNewView(app.world); | |||||
app.cubes[1].view = puglNewView(app.world); | |||||
puglSetWorldHandle(app.world, &app); | |||||
puglSetClassName(app.world, "Pugl Test"); | |||||
PuglStatus st = PUGL_SUCCESS; | |||||
for (unsigned i = 0; i < 2; ++i) { | |||||
CubeView* cube = &app.cubes[i]; | |||||
PuglView* view = cube->view; | |||||
const PuglRect frame = { | |||||
pad + (128.0 + pad) * i, pad + (128.0 + pad) * i, 512.0, 512.0}; | |||||
cube->dist = 10; | |||||
puglSetWindowTitle(view, "Pugl Window Demo"); | |||||
puglSetFrame(view, frame); | |||||
puglSetDefaultSize(view, 512, 512); | |||||
puglSetMinSize(view, 128, 128); | |||||
puglSetMaxSize(view, 2048, 2048); | |||||
puglSetBackend(view, puglGlBackend()); | |||||
puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking); | |||||
puglSetViewHint(view, PUGL_RESIZABLE, opts.resizable); | |||||
puglSetViewHint(view, PUGL_SAMPLES, opts.samples); | |||||
puglSetViewHint(view, PUGL_DOUBLE_BUFFER, opts.doubleBuffer); | |||||
puglSetViewHint(view, PUGL_SWAP_INTERVAL, opts.sync); | |||||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat); | |||||
puglSetHandle(view, cube); | |||||
puglSetEventFunc(view, onEvent); | |||||
if (i == 1) { | |||||
puglSetTransientFor(app.cubes[1].view, | |||||
puglGetNativeWindow(app.cubes[0].view)); | |||||
} | |||||
if ((st = puglRealize(view))) { | |||||
return logError("Failed to create window (%s)\n", puglStrerror(st)); | |||||
} | |||||
puglShow(view); | |||||
} | |||||
PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)}; | |||||
unsigned framesDrawn = 0; | |||||
while (!app.quit) { | |||||
puglUpdate(app.world, app.continuous ? 0.0 : -1.0); | |||||
++framesDrawn; | |||||
if (app.continuous) { | |||||
puglPrintFps(app.world, &fpsPrinter, &framesDrawn); | |||||
} | |||||
} | |||||
for (size_t i = 0; i < 2; ++i) { | |||||
puglFreeView(app.cubes[i].view); | |||||
} | |||||
puglFreeWorld(app.world); | |||||
return 0; | |||||
} |
@@ -1,79 +0,0 @@ | |||||
/* | |||||
Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef EXAMPLES_RECTS_H | |||||
#define EXAMPLES_RECTS_H | |||||
#include <math.h> | |||||
#include <stddef.h> | |||||
typedef float vec2[2]; | |||||
typedef struct { | |||||
float pos[2]; | |||||
float size[2]; | |||||
float fillColor[4]; | |||||
} Rect; | |||||
static const vec2 rectVertices[] = { | |||||
{0.0f, 0.0f}, // TL | |||||
{1.0f, 0.0f}, // TR | |||||
{0.0f, 1.0f}, // BL | |||||
{1.0f, 1.0f} // BR | |||||
}; | |||||
static const unsigned rectIndices[4] = {0, 1, 2, 3}; | |||||
/// Make a new rectangle with the given index (each is slightly different) | |||||
static inline Rect | |||||
makeRect(const size_t index, const float frameWidth) | |||||
{ | |||||
static const float alpha = 0.3f; | |||||
const float minSize = frameWidth / 64.0f; | |||||
const float maxSize = frameWidth / 6.0f; | |||||
const float s = (sinf((float)index) / 2.0f + 0.5f); | |||||
const float c = (cosf((float)index) / 2.0f + 0.5f); | |||||
const Rect rect = { | |||||
{0.0f, 0.0f}, // Position is set later during expose | |||||
{minSize + s * maxSize, minSize + c * maxSize}, | |||||
{0.0f, s / 2.0f + 0.25f, c / 2.0f + 0.25f, alpha}, | |||||
}; | |||||
return rect; | |||||
} | |||||
/// Move `rect` with the given index around in an arbitrary way that looks cool | |||||
static inline void | |||||
moveRect(Rect* const rect, | |||||
const size_t index, | |||||
const size_t numRects, | |||||
const float frameWidth, | |||||
const float frameHeight, | |||||
const double time) | |||||
{ | |||||
const float normal = (float)index / (float)numRects; | |||||
const float offset[2] = {normal * 128.0f, normal * 128.0f}; | |||||
rect->pos[0] = (frameWidth - rect->size[0] + offset[0]) * | |||||
(sinf((float)time * rect->size[0] / 64.0f + normal) + 1.0f) / | |||||
2.0f; | |||||
rect->pos[1] = (frameHeight - rect->size[1] + offset[1]) * | |||||
(cosf((float)time * rect->size[1] / 64.0f + normal) + 1.0f) / | |||||
2.0f; | |||||
} | |||||
#endif // EXAMPLES_RECTS_H |
@@ -1,106 +0,0 @@ | |||||
/* | |||||
Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef EXAMPLES_SHADER_UTILS_H | |||||
#define EXAMPLES_SHADER_UTILS_H | |||||
#include "glad/glad.h" | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
typedef struct { | |||||
GLuint vertexShader; | |||||
GLuint fragmentShader; | |||||
GLuint program; | |||||
} Program; | |||||
static GLuint | |||||
compileShader(const char* header, const char* source, const GLenum type) | |||||
{ | |||||
const GLchar* sources[] = {header, source}; | |||||
const GLint lengths[] = {(GLint)strlen(header), (GLint)strlen(source)}; | |||||
GLuint shader = glCreateShader(type); | |||||
glShaderSource(shader, 2, sources, lengths); | |||||
glCompileShader(shader); | |||||
int status = 0; | |||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | |||||
if (status == GL_FALSE) { | |||||
GLint length = 0; | |||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); | |||||
char* log = (char*)calloc(1, (size_t)length); | |||||
glGetShaderInfoLog(shader, length, &length, log); | |||||
fprintf(stderr, "error: Failed to compile shader (%s)\n", log); | |||||
free(log); | |||||
return 0; | |||||
} | |||||
return shader; | |||||
} | |||||
static void | |||||
deleteProgram(Program program) | |||||
{ | |||||
glDeleteShader(program.vertexShader); | |||||
glDeleteShader(program.fragmentShader); | |||||
glDeleteProgram(program.program); | |||||
} | |||||
static Program | |||||
compileProgram(const char* headerSource, | |||||
const char* vertexSource, | |||||
const char* fragmentSource) | |||||
{ | |||||
static const Program nullProgram = {0, 0, 0}; | |||||
Program program = { | |||||
compileShader(headerSource, vertexSource, GL_VERTEX_SHADER), | |||||
compileShader(headerSource, fragmentSource, GL_FRAGMENT_SHADER), | |||||
glCreateProgram(), | |||||
}; | |||||
if (!program.vertexShader || !program.fragmentShader || !program.program) { | |||||
deleteProgram(program); | |||||
return nullProgram; | |||||
} | |||||
glAttachShader(program.program, program.vertexShader); | |||||
glAttachShader(program.program, program.fragmentShader); | |||||
glLinkProgram(program.program); | |||||
GLint status = 0; | |||||
glGetProgramiv(program.program, GL_LINK_STATUS, &status); | |||||
if (status == GL_FALSE) { | |||||
GLint length = 0; | |||||
glGetProgramiv(program.program, GL_INFO_LOG_LENGTH, &length); | |||||
char* log = (char*)calloc(1, (size_t)length); | |||||
glGetProgramInfoLog(program.program, length, &length, &log[0]); | |||||
fprintf(stderr, "error: Failed to link program (%s)\n", log); | |||||
free(log); | |||||
deleteProgram(program); | |||||
return nullProgram; | |||||
} | |||||
return program; | |||||
} | |||||
#endif // EXAMPLES_SHADER_UTILS_H |
@@ -1,4 +0,0 @@ | |||||
#version 330 core | |||||
#define INTER(qualifiers) | |||||
#define UBO(qualifiers) layout(std140) |
@@ -1,4 +0,0 @@ | |||||
#version 420 core | |||||
#define INTER(qualifiers) layout(qualifiers) | |||||
#define UBO(qualifiers) layout(std140, qualifiers) |
@@ -1,35 +0,0 @@ | |||||
shader_files = [ | |||||
'header_330.glsl', | |||||
'header_420.glsl', | |||||
'rect.frag', | |||||
'rect.vert', | |||||
] | |||||
# Copy shader sources for GL examples | |||||
foreach shader_file : shader_files | |||||
configure_file(copy: true, input: shader_file, output: shader_file) | |||||
endforeach | |||||
# Build SPV shader binaries for Vulkan examples | |||||
if vulkan_dep.found() | |||||
cat = find_program('../../scripts/cat.py') | |||||
glslang = find_program('glslangValidator') | |||||
shaders = ['rect.vert', 'rect.frag'] | |||||
foreach shader : shaders | |||||
source = shader.split('.')[0] + '.vulkan.' + shader.split('.')[1] | |||||
shader_input = custom_target(source, | |||||
output: source, | |||||
input: ['header_420.glsl', shader], | |||||
command: [cat, '@INPUT@'], | |||||
build_by_default: true, | |||||
capture: true) | |||||
mytarget = custom_target(shader, | |||||
output: shader + '.spv', | |||||
input: shader_input, | |||||
command: [glslang, '-V', '-o', '@OUTPUT@', '@INPUT@'], | |||||
build_by_default: true, | |||||
install: false) | |||||
endforeach | |||||
endif |
@@ -1,33 +0,0 @@ | |||||
/* The fragment shader uses the UV coordinates to calculate whether it is in | |||||
the T, R, B, or L border. These are then mixed with the border color, and | |||||
their inverse is mixed with the fill color, to calculate the fragment color. | |||||
For example, if we are in the top border, then T=1, so the border mix factor | |||||
TRBL=1, and the fill mix factor (1-TRBL) is 0. | |||||
The use of pixel units here is handy because the border width can be | |||||
specified precisely in pixels to draw sharp lines. The border width is just | |||||
hardcoded, but could be made a uniform or vertex attribute easily enough. */ | |||||
INTER(location = 0) noperspective in vec2 f_uv; | |||||
INTER(location = 1) noperspective in vec2 f_size; | |||||
INTER(location = 2) noperspective in vec4 f_fillColor; | |||||
layout(location = 0) out vec4 FragColor; | |||||
void | |||||
main() | |||||
{ | |||||
const float borderWidth = 2.0; | |||||
vec4 borderColor = f_fillColor + vec4(0.0, 0.4, 0.4, 0.0); | |||||
float t = step(borderWidth, f_uv[1]); | |||||
float r = step(borderWidth, f_size.x - f_uv[0]); | |||||
float b = step(borderWidth, f_size.y - f_uv[1]); | |||||
float l = step(borderWidth, f_uv[0]); | |||||
float fillMix = t * r * b * l; | |||||
float borderMix = 1.0 - fillMix; | |||||
vec4 fill = fillMix * f_fillColor; | |||||
vec4 border = borderMix * borderColor; | |||||
FragColor = fill + border; | |||||
} |
@@ -1,36 +0,0 @@ | |||||
/* The vertex shader is trivial, but forwards scaled UV coordinates (in pixels) | |||||
to the fragment shader for drawing the border. */ | |||||
UBO(binding = 0) uniform UniformBufferObject | |||||
{ | |||||
mat4 projection; | |||||
} | |||||
ubo; | |||||
layout(location = 0) in vec2 v_position; | |||||
layout(location = 1) in vec2 v_origin; | |||||
layout(location = 2) in vec2 v_size; | |||||
layout(location = 3) in vec4 v_fillColor; | |||||
INTER(location = 0) noperspective out vec2 f_uv; | |||||
INTER(location = 1) noperspective out vec2 f_size; | |||||
INTER(location = 2) noperspective out vec4 f_fillColor; | |||||
void | |||||
main() | |||||
{ | |||||
// clang-format off | |||||
mat4 m = mat4(v_size[0], 0.0, 0.0, 0.0, | |||||
0.0, v_size[1], 0.0, 0.0, | |||||
0.0, 0.0, 1.0, 0.0, | |||||
v_origin[0], v_origin[1], 0.0, 1.0); | |||||
// clang-format on | |||||
mat4 MVP = ubo.projection * m; | |||||
f_uv = v_position * v_size; | |||||
f_size = v_size; | |||||
f_fillColor = v_fillColor; | |||||
gl_Position = MVP * vec4(v_position, 0.0, 1.0); | |||||
} |
@@ -2,7 +2,7 @@ Checks: > | |||||
*, | *, | ||||
-*-magic-numbers, | -*-magic-numbers, | ||||
-*-uppercase-literal-suffix, | -*-uppercase-literal-suffix, | ||||
-altera-struct-pack-align, | |||||
-altera*, | |||||
-clang-diagnostic-unused-function, | -clang-diagnostic-unused-function, | ||||
-clang-diagnostic-unused-macros, | -clang-diagnostic-unused-macros, | ||||
-llvmlibc-*, | -llvmlibc-*, | ||||
@@ -1,3 +1,6 @@ | |||||
# Copyright 2021 David Robillard <d@drobilla.net> | |||||
# SPDX-License-Identifier: CC0-1.0 OR ISC | |||||
c_headers = [ | c_headers = [ | ||||
'pugl/pugl.h', | 'pugl/pugl.h', | ||||
@@ -1,18 +1,5 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
// Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
// SPDX-License-Identifier: ISC | |||||
#ifndef PUGL_CAIRO_H | #ifndef PUGL_CAIRO_H | ||||
#define PUGL_CAIRO_H | #define PUGL_CAIRO_H | ||||
@@ -1,18 +1,5 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
// Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
// SPDX-License-Identifier: ISC | |||||
#ifndef PUGL_GL_H | #ifndef PUGL_GL_H | ||||
#define PUGL_GL_H | #define PUGL_GL_H | ||||
@@ -1,26 +1,16 @@ | |||||
/* | |||||
Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
// Copyright 2012-2022 David Robillard <d@drobilla.net> | |||||
// SPDX-License-Identifier: ISC | |||||
#ifndef PUGL_PUGL_H | #ifndef PUGL_PUGL_H | ||||
#define PUGL_PUGL_H | #define PUGL_PUGL_H | ||||
#include <stdbool.h> | |||||
#include <stddef.h> | #include <stddef.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#ifndef __cplusplus | |||||
# include <stdbool.h> | |||||
#endif | |||||
#ifndef PUGL_API | #ifndef PUGL_API | ||||
# if defined(_WIN32) && !defined(PUGL_STATIC) && defined(PUGL_INTERNAL) | # if defined(_WIN32) && !defined(PUGL_STATIC) && defined(PUGL_INTERNAL) | ||||
# define PUGL_API __declspec(dllexport) | # define PUGL_API __declspec(dllexport) | ||||
@@ -53,13 +43,8 @@ | |||||
PUGL_API \ | PUGL_API \ | ||||
PUGL_CONST_FUNC | PUGL_CONST_FUNC | ||||
#ifdef __cplusplus | |||||
# define PUGL_BEGIN_DECLS extern "C" { | |||||
# define PUGL_END_DECLS } | |||||
#else | |||||
# define PUGL_BEGIN_DECLS | |||||
# define PUGL_END_DECLS | |||||
#endif | |||||
#define PUGL_BEGIN_DECLS | |||||
#define PUGL_END_DECLS | |||||
PUGL_BEGIN_DECLS | PUGL_BEGIN_DECLS | ||||
@@ -70,16 +55,40 @@ PUGL_BEGIN_DECLS | |||||
*/ | */ | ||||
/** | /** | ||||
A rectangle. | |||||
A pixel coordinate within/of a view. | |||||
This is relative to the top left corner of the view's parent, or to the top | |||||
left corner of the view itself, depending on the context. | |||||
This is used to describe things like view position and size. Pugl generally | |||||
uses coordinates where the top left corner is 0,0. | |||||
There are platform-imposed limits on window positions. For portability, | |||||
applications should keep coordinates between -16000 and 16000. Note that | |||||
negative frame coordinates are possible, for example with multiple screens. | |||||
*/ | |||||
typedef int16_t PuglCoord; | |||||
/** | |||||
A pixel span (width or height) within/of a view. | |||||
Due to platform limits, the span of a view in either dimension should be | |||||
between 1 and 10000. | |||||
*/ | |||||
typedef uint16_t PuglSpan; | |||||
/** | |||||
A rectangle in a view or on the screen. | |||||
This type is used to describe two things: the position and size of a view | |||||
(for configuring), or a rectangle within a view (for exposing). | |||||
The coordinate (0, 0) represents the top-left pixel of the parent window (or | |||||
display if there isn't one), or the top-left pixel of the view, | |||||
respectively. | |||||
*/ | */ | ||||
typedef struct { | typedef struct { | ||||
double x; | |||||
double y; | |||||
double width; | |||||
double height; | |||||
PuglCoord x; | |||||
PuglCoord y; | |||||
PuglSpan width; | |||||
PuglSpan height; | |||||
} PuglRect; | } PuglRect; | ||||
/** | /** | ||||
@@ -192,6 +201,8 @@ typedef enum { | |||||
PUGL_TIMER, ///< Timer triggered, a #PuglTimerEvent | PUGL_TIMER, ///< Timer triggered, a #PuglTimerEvent | ||||
PUGL_LOOP_ENTER, ///< Recursive loop entered, a #PuglLoopEnterEvent | PUGL_LOOP_ENTER, ///< Recursive loop entered, a #PuglLoopEnterEvent | ||||
PUGL_LOOP_LEAVE, ///< Recursive loop left, a #PuglLoopLeaveEvent | PUGL_LOOP_LEAVE, ///< Recursive loop left, a #PuglLoopLeaveEvent | ||||
PUGL_DATA_OFFER, ///< Data offered from clipboard, a #PuglDataOfferEvent | |||||
PUGL_DATA, ///< Data available from clipboard, a #PuglDataEvent | |||||
} PuglEventType; | } PuglEventType; | ||||
/// Common flags for all event types | /// Common flags for all event types | ||||
@@ -269,10 +280,10 @@ typedef PuglAnyEvent PuglDestroyEvent; | |||||
typedef struct { | typedef struct { | ||||
PuglEventType type; ///< #PUGL_CONFIGURE | PuglEventType type; ///< #PUGL_CONFIGURE | ||||
PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | ||||
double x; ///< New parent-relative X coordinate | |||||
double y; ///< New parent-relative Y coordinate | |||||
double width; ///< New width | |||||
double height; ///< New height | |||||
PuglCoord x; ///< Parent-relative X coordinate of view | |||||
PuglCoord y; ///< Parent-relative Y coordinate of view | |||||
PuglSpan width; ///< Width of view | |||||
PuglSpan height; ///< Height of view | |||||
} PuglConfigureEvent; | } PuglConfigureEvent; | ||||
/** | /** | ||||
@@ -315,10 +326,10 @@ typedef PuglAnyEvent PuglUpdateEvent; | |||||
typedef struct { | typedef struct { | ||||
PuglEventType type; ///< #PUGL_EXPOSE | PuglEventType type; ///< #PUGL_EXPOSE | ||||
PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | ||||
double x; ///< View-relative X coordinate | |||||
double y; ///< View-relative Y coordinate | |||||
double width; ///< Width of exposed region | |||||
double height; ///< Height of exposed region | |||||
PuglCoord x; ///< View-relative top-left X coordinate of region | |||||
PuglCoord y; ///< View-relative top-left Y coordinate of region | |||||
PuglSpan width; ///< Width of exposed region | |||||
PuglSpan height; ///< Height of exposed region | |||||
} PuglExposeEvent; | } PuglExposeEvent; | ||||
/** | /** | ||||
@@ -419,6 +430,21 @@ typedef struct { | |||||
/** | /** | ||||
Button press or release event. | Button press or release event. | ||||
Button numbers start from 0, and are ordered: primary, secondary, middle. | |||||
So, on a typical right-handed mouse, the button numbers are: | |||||
Left: 0 | |||||
Right: 1 | |||||
Middle (often a wheel): 2 | |||||
Higher button numbers are reported in the same order they are represented on | |||||
the system. There is no universal standard here, but buttons 3 and 4 are | |||||
typically a pair of buttons or a rocker, which are usually bound to "back" | |||||
and "forward" operations. | |||||
Note that these numbers may differ from those used on the underlying | |||||
platform, since they are manipulated to provide a consistent portable API. | |||||
*/ | */ | ||||
typedef struct { | typedef struct { | ||||
PuglEventType type; ///< #PUGL_BUTTON_PRESS or #PUGL_BUTTON_RELEASE | PuglEventType type; ///< #PUGL_BUTTON_PRESS or #PUGL_BUTTON_RELEASE | ||||
@@ -429,7 +455,7 @@ typedef struct { | |||||
double xRoot; ///< Root-relative X coordinate | double xRoot; ///< Root-relative X coordinate | ||||
double yRoot; ///< Root-relative Y coordinate | double yRoot; ///< Root-relative Y coordinate | ||||
PuglMods state; ///< Bitwise OR of #PuglMod flags | PuglMods state; ///< Bitwise OR of #PuglMod flags | ||||
uint32_t button; ///< Button number starting from 1 | |||||
uint32_t button; ///< Button number starting from 0 | |||||
} PuglButtonEvent; | } PuglButtonEvent; | ||||
/** | /** | ||||
@@ -499,6 +525,34 @@ typedef struct { | |||||
uintptr_t id; ///< Timer ID | uintptr_t id; ///< Timer ID | ||||
} PuglTimerEvent; | } PuglTimerEvent; | ||||
/** | |||||
Clipboard data offer event. | |||||
This event is sent when a clipboard has data present, possibly with several | |||||
datatypes. While handling this event, the types can be investigated with | |||||
puglGetClipboardType() to decide whether to accept the offer with | |||||
puglAcceptOffer(). | |||||
*/ | |||||
typedef struct { | |||||
PuglEventType type; ///< #PUGL_DATA_OFFER | |||||
PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | |||||
double time; ///< Time in seconds | |||||
} PuglDataOfferEvent; | |||||
/** | |||||
Clipboard data event. | |||||
This event is sent after accepting a data offer when the data has been | |||||
retrieved and converted. While handling this event, the data can be | |||||
accessed with puglGetClipboard(). | |||||
*/ | |||||
typedef struct { | |||||
PuglEventType type; ///< #PUGL_DATA | |||||
PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values | |||||
double time; ///< Time in seconds | |||||
uint32_t typeIndex; ///< Index of datatype | |||||
} PuglDataEvent; | |||||
/** | /** | ||||
Recursive loop enter event. | Recursive loop enter event. | ||||
@@ -556,6 +610,8 @@ typedef union { | |||||
PuglFocusEvent focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT | PuglFocusEvent focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT | ||||
PuglClientEvent client; ///< #PUGL_CLIENT | PuglClientEvent client; ///< #PUGL_CLIENT | ||||
PuglTimerEvent timer; ///< #PUGL_TIMER | PuglTimerEvent timer; ///< #PUGL_TIMER | ||||
PuglDataOfferEvent offer; ///< #PUGL_DATA_OFFER | |||||
PuglDataEvent data; ///< #PUGL_DATA | |||||
} PuglEvent; | } PuglEvent; | ||||
/** | /** | ||||
@@ -580,7 +636,8 @@ typedef enum { | |||||
PUGL_REALIZE_FAILED, ///< System view realization failed | PUGL_REALIZE_FAILED, ///< System view realization failed | ||||
PUGL_SET_FORMAT_FAILED, ///< Failed to set pixel format | PUGL_SET_FORMAT_FAILED, ///< Failed to set pixel format | ||||
PUGL_CREATE_CONTEXT_FAILED, ///< Failed to create drawing context | PUGL_CREATE_CONTEXT_FAILED, ///< Failed to create drawing context | ||||
PUGL_UNSUPPORTED_TYPE, ///< Unsupported data type | |||||
PUGL_UNSUPPORTED, ///< Unsupported operation | |||||
PUGL_NO_MEMORY, ///< Failed to allocate memory | |||||
} PuglStatus; | } PuglStatus; | ||||
/// Return a string describing a status code | /// Return a string describing a status code | ||||
@@ -626,7 +683,7 @@ typedef enum { | |||||
/** | /** | ||||
Set up support for threads if necessary. | Set up support for threads if necessary. | ||||
- X11: Calls XInitThreads() which is required for some drivers. | |||||
X11: Calls XInitThreads() which is required for some drivers. | |||||
*/ | */ | ||||
PUGL_WORLD_THREADS = 1u << 0u | PUGL_WORLD_THREADS = 1u << 0u | ||||
} PuglWorldFlag; | } PuglWorldFlag; | ||||
@@ -693,6 +750,11 @@ PUGL_API | |||||
PuglStatus | PuglStatus | ||||
puglSetClassName(PuglWorld* world, const char* name); | puglSetClassName(PuglWorld* world, const char* name); | ||||
/// Get the class name of the application, or null | |||||
PUGL_API | |||||
const char* | |||||
puglGetClassName(const PuglWorld* world); | |||||
/** | /** | ||||
Return the time in seconds. | Return the time in seconds. | ||||
@@ -792,10 +854,11 @@ typedef enum { | |||||
PUGL_RESIZABLE, ///< True if view should be resizable | PUGL_RESIZABLE, ///< True if view should be resizable | ||||
PUGL_IGNORE_KEY_REPEAT, ///< True if key repeat events are ignored | PUGL_IGNORE_KEY_REPEAT, ///< True if key repeat events are ignored | ||||
PUGL_REFRESH_RATE, ///< Refresh rate in Hz | PUGL_REFRESH_RATE, ///< Refresh rate in Hz | ||||
PUGL_NUM_VIEW_HINTS | |||||
} PuglViewHint; | } PuglViewHint; | ||||
/// The number of #PuglViewHint values | |||||
#define PUGL_NUM_VIEW_HINTS ((unsigned)PUGL_REFRESH_RATE + 1u) | |||||
/// A special view hint value | /// A special view hint value | ||||
typedef enum { | typedef enum { | ||||
PUGL_DONT_CARE = -1, ///< Use best available value | PUGL_DONT_CARE = -1, ///< Use best available value | ||||
@@ -803,6 +866,46 @@ typedef enum { | |||||
PUGL_TRUE = 1 ///< Explicitly true | PUGL_TRUE = 1 ///< Explicitly true | ||||
} PuglViewHintValue; | } PuglViewHintValue; | ||||
/** | |||||
A hint for configuring/constraining the size of a view. | |||||
The system will attempt to make the view's window adhere to these, but they | |||||
are suggestions, not hard constraints. Applications should handle any view | |||||
size gracefully. | |||||
*/ | |||||
typedef enum { | |||||
PUGL_DEFAULT_SIZE, ///< Default size | |||||
PUGL_MIN_SIZE, ///< Minimum size | |||||
PUGL_MAX_SIZE, ///< Maximum size | |||||
/** | |||||
Fixed aspect ratio. | |||||
If set, the view's size should be constrained to this aspect ratio. | |||||
Mutually exclusive with #PUGL_MIN_ASPECT and #PUGL_MAX_ASPECT. | |||||
*/ | |||||
PUGL_FIXED_ASPECT, | |||||
/** | |||||
Minimum aspect ratio. | |||||
If set, the view's size should be constrained to an aspect ratio no lower | |||||
than this. Mutually exclusive with #PUGL_FIXED_ASPECT. | |||||
*/ | |||||
PUGL_MIN_ASPECT, | |||||
/** | |||||
Maximum aspect ratio. | |||||
If set, the view's size should be constrained to an aspect ratio no higher | |||||
than this. Mutually exclusive with #PUGL_FIXED_ASPECT. | |||||
*/ | |||||
PUGL_MAX_ASPECT | |||||
} PuglSizeHint; | |||||
/// The number of #PuglSizeHint values | |||||
#define PUGL_NUM_SIZE_HINTS ((unsigned)PUGL_MAX_ASPECT + 1u) | |||||
/// A function called when an event occurs | /// A function called when an event occurs | ||||
typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); | typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); | ||||
@@ -872,6 +975,10 @@ PUGL_API | |||||
PuglStatus | PuglStatus | ||||
puglSetBackend(PuglView* view, const PuglBackend* backend); | puglSetBackend(PuglView* view, const PuglBackend* backend); | ||||
/// Return the graphics backend used by a view | |||||
const PuglBackend* | |||||
puglGetBackend(const PuglView* view); | |||||
/// Set the function to call when an event occurs | /// Set the function to call when an event occurs | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
@@ -897,6 +1004,22 @@ PUGL_API | |||||
int | int | ||||
puglGetViewHint(const PuglView* view, PuglViewHint hint); | puglGetViewHint(const PuglView* view, PuglViewHint hint); | ||||
/** | |||||
Return the scale factor of the view. | |||||
This factor describe how large UI elements (especially text) should be | |||||
compared to "normal". For example, 2.0 means the UI should be drawn twice | |||||
as large. | |||||
"Normal" is loosely defined, but means a good size on a "standard DPI" | |||||
display (around 96 DPI). In other words, the scale 1.0 should have text | |||||
that is reasonably sized on a 96 DPI display, and the scale 2.0 should have | |||||
text twice that large. | |||||
*/ | |||||
PUGL_API | |||||
double | |||||
puglGetScaleFactor(const PuglView* view); | |||||
/** | /** | ||||
@} | @} | ||||
@defgroup frame Frame | @defgroup frame Frame | ||||
@@ -926,64 +1049,43 @@ PuglStatus | |||||
puglSetFrame(PuglView* view, PuglRect frame); | puglSetFrame(PuglView* view, PuglRect frame); | ||||
/** | /** | ||||
Set the default size of the view. | |||||
Set the current position of the view. | |||||
This should be called before puglResize() to set the default size of the | |||||
view, which will be the initial size of the window if this is a top level | |||||
view. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
*/ | |||||
PUGL_API | |||||
PuglStatus | |||||
puglSetDefaultSize(PuglView* view, int width, int height); | |||||
/** | |||||
Set the minimum size of the view. | |||||
If an initial minimum size is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is | |||||
unchanged. | |||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
puglSetMinSize(PuglView* view, int width, int height); | |||||
puglSetPosition(PuglView* view, int x, int y); | |||||
/** | /** | ||||
Set the maximum size of the view. | |||||
If an initial maximum size is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
Set the current size of the view. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is | |||||
unchanged. | |||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
puglSetMaxSize(PuglView* view, int width, int height); | |||||
puglSetSize(PuglView* view, unsigned width, unsigned height); | |||||
/** | /** | ||||
Set the view aspect ratio range. | |||||
Set a size hint for the view. | |||||
The x and y values here represent a ratio of width to height. To set a | |||||
fixed aspect ratio, set the minimum and maximum values to the same ratio. | |||||
This can be used to set the default, minimum, and maximum size of a view, | |||||
as well as the supported range of aspect ratios. | |||||
Note that setting different minimum and maximum constraints does not | |||||
currenty work on MacOS (the minimum is used), so only setting a fixed aspect | |||||
ratio works properly across all platforms. | |||||
If an initial aspect ratio is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
This should be called before puglRealize() so the initial window for the | |||||
view can be configured correctly. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | ||||
not yet realized. | not yet realized. | ||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY); | |||||
puglSetSizeHint(PuglView* view, | |||||
PuglSizeHint hint, | |||||
PuglSpan width, | |||||
PuglSpan height); | |||||
/** | /** | ||||
@} | @} | ||||
@@ -1003,6 +1105,11 @@ PUGL_API | |||||
PuglStatus | PuglStatus | ||||
puglSetWindowTitle(PuglView* view, const char* title); | puglSetWindowTitle(PuglView* view, const char* title); | ||||
/// Return the title of the window, or null | |||||
PUGL_API | |||||
const char* | |||||
puglGetWindowTitle(const PuglView* view); | |||||
/** | /** | ||||
Set the parent window for embedding a view in an existing window. | Set the parent window for embedding a view in an existing window. | ||||
@@ -1012,6 +1119,11 @@ PUGL_API | |||||
PuglStatus | PuglStatus | ||||
puglSetParentWindow(PuglView* view, PuglNativeView parent); | puglSetParentWindow(PuglView* view, PuglNativeView parent); | ||||
/// Return the parent window this view is embedded in, or null | |||||
PUGL_API | |||||
PuglNativeView | |||||
puglGetParentWindow(const PuglView* view); | |||||
/** | /** | ||||
Set the transient parent of the window. | Set the transient parent of the window. | ||||
@@ -1024,13 +1136,23 @@ puglSetParentWindow(PuglView* view, PuglNativeView parent); | |||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
puglSetTransientFor(PuglView* view, PuglNativeView parent); | |||||
puglSetTransientParent(PuglView* view, PuglNativeView parent); | |||||
/** | |||||
Return the transient parent of the window. | |||||
@return The native handle to the window this view is a transient child of, | |||||
or null. | |||||
*/ | |||||
PUGL_API | |||||
PuglNativeView | |||||
puglGetTransientParent(const PuglView* view); | |||||
/** | /** | ||||
Realize a view by creating a corresponding system view or window. | Realize a view by creating a corresponding system view or window. | ||||
After this call, the (initially invisible) underlying system view exists and | After this call, the (initially invisible) underlying system view exists and | ||||
can be accessed with puglGetNativeWindow(). There is currently no | |||||
can be accessed with puglGetNativeView(). There is currently no | |||||
corresponding unrealize function, the system view will be destroyed along | corresponding unrealize function, the system view will be destroyed along | ||||
with the view when puglFreeView() is called. | with the view when puglFreeView() is called. | ||||
@@ -1067,7 +1189,7 @@ puglGetVisible(const PuglView* view); | |||||
/// Return the native window handle | /// Return the native window handle | ||||
PUGL_API | PUGL_API | ||||
PuglNativeView | PuglNativeView | ||||
puglGetNativeWindow(PuglView* view); | |||||
puglGetNativeView(PuglView* view); | |||||
/** | /** | ||||
@} | @} | ||||
@@ -1139,7 +1261,17 @@ typedef enum { | |||||
PUGL_CURSOR_ANTI_DIAGONAL, ///< Bottom-left to top-right arrow for diagonal resize | PUGL_CURSOR_ANTI_DIAGONAL, ///< Bottom-left to top-right arrow for diagonal resize | ||||
} PuglCursor; | } PuglCursor; | ||||
/// Grab the keyboard input focus | |||||
/// The number of #PuglCursor values | |||||
#define PUGL_NUM_CURSORS ((unsigned)PUGL_CURSOR_ANTI_DIAGONAL + 1u) | |||||
/** | |||||
Grab the keyboard input focus. | |||||
Note that this will fail if the view is not mapped and so should not, for | |||||
example, be called immediately after puglShow(). | |||||
@return #PUGL_SUCCESS if the focus was successfully grabbed, or an error. | |||||
*/ | |||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
puglGrabFocus(PuglView* view); | puglGrabFocus(PuglView* view); | ||||
@@ -1149,6 +1281,59 @@ PUGL_API | |||||
bool | bool | ||||
puglHasFocus(const PuglView* view); | puglHasFocus(const PuglView* view); | ||||
/** | |||||
Request data from the general copy/paste clipboard. | |||||
A #PUGL_DATA_OFFER event will be sent if data is available. | |||||
*/ | |||||
PUGL_API | |||||
PuglStatus | |||||
puglPaste(PuglView* view); | |||||
/** | |||||
Return the number of types available for the data in a clipboard. | |||||
Returns zero if the clipboard is empty. | |||||
*/ | |||||
PUGL_API | |||||
uint32_t | |||||
puglGetNumClipboardTypes(const PuglView* view); | |||||
/** | |||||
Return the identifier of a type available in a clipboard. | |||||
This is usually a MIME type, but may also be another platform-specific type | |||||
identifier. Applications must ignore any type they do not recognize. | |||||
Returns null if `typeIndex` is out of bounds according to | |||||
puglGetNumClipboardTypes(). | |||||
*/ | |||||
PUGL_API | |||||
const char* | |||||
puglGetClipboardType(const PuglView* view, uint32_t typeIndex); | |||||
/** | |||||
Accept data offered from a clipboard. | |||||
To accept data, this must be called while handling a #PUGL_DATA_OFFER event. | |||||
Doing so will request the data from the source as the specified type. When | |||||
the data is available, a #PUGL_DATA event will be sent to the view which can | |||||
then retrieve the data with puglGetClipboard(). | |||||
@param view The view. | |||||
@param offer The data offer event. | |||||
@param typeIndex The index of the type that the view will accept. This is | |||||
the `typeIndex` argument to the call of puglGetClipboardType() that returned | |||||
the accepted type. | |||||
*/ | |||||
PUGL_API | |||||
PuglStatus | |||||
puglAcceptOffer(PuglView* view, | |||||
const PuglDataOfferEvent* offer, | |||||
uint32_t typeIndex); | |||||
/** | /** | ||||
Set the clipboard contents. | Set the clipboard contents. | ||||
@@ -1174,13 +1359,13 @@ puglSetClipboard(PuglView* view, | |||||
puglSetClipboard() or copied from another application. | puglSetClipboard() or copied from another application. | ||||
@param view The view. | @param view The view. | ||||
@param[out] type Set to the MIME type of the data. | |||||
@param typeIndex Index of the data type to get the item as. | |||||
@param[out] len Set to the length of the data in bytes. | @param[out] len Set to the length of the data in bytes. | ||||
@return The clipboard contents, or null. | @return The clipboard contents, or null. | ||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
const void* | const void* | ||||
puglGetClipboard(PuglView* view, const char** type, size_t* len); | |||||
puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); | |||||
/** | /** | ||||
Set the mouse cursor. | Set the mouse cursor. | ||||
@@ -1263,8 +1448,8 @@ puglStopTimer(PuglView* view, uintptr_t id); | |||||
puglPostRedisplayRect(), but will always send a message to the X server, | puglPostRedisplayRect(), but will always send a message to the X server, | ||||
even when called in an event handler. | even when called in an event handler. | ||||
@return #PUGL_UNSUPPORTED_TYPE if sending events of this type is not | |||||
supported, #PUGL_UNKNOWN_ERROR if sending the event failed. | |||||
@return #PUGL_UNSUPPORTED if sending events of this type is not supported, | |||||
#PUGL_UNKNOWN_ERROR if sending the event failed. | |||||
*/ | */ | ||||
PUGL_API | PUGL_API | ||||
PuglStatus | PuglStatus | ||||
@@ -1348,6 +1533,7 @@ typedef PuglLoopLeaveEvent PuglEventLoopLeave; | |||||
Windows: This is a `HWND`. | Windows: This is a `HWND`. | ||||
*/ | */ | ||||
PUGL_DEPRECATED_BY("PuglNativeView") | |||||
typedef uintptr_t PuglNativeWindow; | typedef uintptr_t PuglNativeWindow; | ||||
/** | /** | ||||
@@ -1408,8 +1594,8 @@ puglInitWindowSize(PuglView* view, int width, int height) | |||||
{ | { | ||||
PuglRect frame = puglGetFrame(view); | PuglRect frame = puglGetFrame(view); | ||||
frame.width = width; | |||||
frame.height = height; | |||||
frame.width = (PuglSpan)width; | |||||
frame.height = (PuglSpan)height; | |||||
puglSetFrame(view, frame); | puglSetFrame(view, frame); | ||||
} | } | ||||
@@ -1421,7 +1607,7 @@ static inline PUGL_DEPRECATED_BY("puglSetMinSize") | |||||
void | void | ||||
puglInitWindowMinSize(PuglView* view, int width, int height) | puglInitWindowMinSize(PuglView* view, int width, int height) | ||||
{ | { | ||||
puglSetMinSize(view, width, height); | |||||
puglSetSizeHint(view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); | |||||
} | } | ||||
/** | /** | ||||
@@ -1431,8 +1617,8 @@ puglInitWindowMinSize(PuglView* view, int width, int height) | |||||
fixed aspect ratio, set the minimum and maximum values to the same ratio. | fixed aspect ratio, set the minimum and maximum values to the same ratio. | ||||
Note that setting different minimum and maximum constraints does not | Note that setting different minimum and maximum constraints does not | ||||
currenty work on MacOS (the minimum is used), so only setting a fixed aspect | |||||
ratio works properly across all platforms. | |||||
currently work on MacOS (the minimum is used), so only setting a fixed | |||||
aspect ratio works properly across all platforms. | |||||
*/ | */ | ||||
static inline PUGL_DEPRECATED_BY("puglSetAspectRatio") | static inline PUGL_DEPRECATED_BY("puglSetAspectRatio") | ||||
void | void | ||||
@@ -1442,7 +1628,8 @@ puglInitWindowAspectRatio(PuglView* view, | |||||
int maxX, | int maxX, | ||||
int maxY) | int maxY) | ||||
{ | { | ||||
puglSetAspectRatio(view, minX, minY, maxX, maxY); | |||||
puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); | |||||
puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); | |||||
} | } | ||||
/** | /** | ||||
@@ -1451,11 +1638,23 @@ puglInitWindowAspectRatio(PuglView* view, | |||||
On X11, parent must be a Window. | On X11, parent must be a Window. | ||||
On OSX, parent must be an NSView*. | On OSX, parent must be an NSView*. | ||||
*/ | */ | ||||
static inline PUGL_DEPRECATED_BY("puglSetTransientFor") | |||||
static inline PUGL_DEPRECATED_BY("puglSetTransientParent") | |||||
void | void | ||||
puglInitTransientFor(PuglView* view, uintptr_t parent) | puglInitTransientFor(PuglView* view, uintptr_t parent) | ||||
{ | { | ||||
puglSetTransientFor(view, (PuglNativeWindow)parent); | |||||
puglSetTransientParent(view, (PuglNativeWindow)parent); | |||||
} | |||||
/** | |||||
Set transient parent before creating a window. | |||||
@deprecated Use puglSetTransientParent(). | |||||
*/ | |||||
static inline PUGL_DEPRECATED_BY("puglSetTransientParent") | |||||
PuglStatus | |||||
puglSetTransientFor(PuglView* view, uintptr_t parent) | |||||
{ | |||||
return puglSetTransientParent(view, (PuglNativeWindow)parent); | |||||
} | } | ||||
/** | /** | ||||
@@ -1594,10 +1793,12 @@ puglProcessEvents(PuglView* view); | |||||
@deprecated Use puglUpdate(). | @deprecated Use puglUpdate(). | ||||
*/ | */ | ||||
PUGL_API | |||||
PUGL_DEPRECATED_BY("puglUpdate") | |||||
static inline PUGL_DEPRECATED_BY("puglUpdate") | |||||
PuglStatus | PuglStatus | ||||
puglPollEvents(PuglWorld* world, double timeout); | |||||
puglPollEvents(PuglWorld* world, double timeout) | |||||
{ | |||||
return puglUpdate(world, timeout); | |||||
} | |||||
/** | /** | ||||
Dispatch any pending events to views. | Dispatch any pending events to views. | ||||
@@ -1609,20 +1810,115 @@ puglPollEvents(PuglWorld* world, double timeout); | |||||
@deprecated Use puglUpdate(). | @deprecated Use puglUpdate(). | ||||
*/ | */ | ||||
PUGL_API | |||||
PUGL_DEPRECATED_BY("puglUpdate") | |||||
static inline PUGL_DEPRECATED_BY("puglUpdate") | |||||
PuglStatus | PuglStatus | ||||
puglDispatchEvents(PuglWorld* world); | |||||
puglDispatchEvents(PuglWorld* world) | |||||
{ | |||||
return puglUpdate(world, 0.0); | |||||
} | |||||
PUGL_API | |||||
PUGL_DEPRECATED_BY("puglShow") | |||||
static inline PUGL_DEPRECATED_BY("puglShow") | |||||
PuglStatus | PuglStatus | ||||
puglShowWindow(PuglView* view); | |||||
puglShowWindow(PuglView* view) | |||||
{ | |||||
return puglShow(view); | |||||
} | |||||
PUGL_API | |||||
PUGL_DEPRECATED_BY("puglHide") | |||||
static inline PUGL_DEPRECATED_BY("puglHide") | |||||
PuglStatus | |||||
puglHideWindow(PuglView* view) | |||||
{ | |||||
return puglHide(view); | |||||
} | |||||
/** | |||||
Set the default size of the view. | |||||
This should be called before puglRealize() to set the default size of the | |||||
view, which will be the initial size of the window if this is a top level | |||||
view. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
*/ | |||||
static inline PUGL_DEPRECATED_BY("puglSetSizeHint") | |||||
PuglStatus | |||||
puglSetDefaultSize(PuglView* view, int width, int height) | |||||
{ | |||||
return puglSetSizeHint( | |||||
view, PUGL_DEFAULT_SIZE, (PuglSpan)width, (PuglSpan)height); | |||||
} | |||||
/** | |||||
Set the minimum size of the view. | |||||
If an initial minimum size is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
*/ | |||||
static inline PUGL_DEPRECATED_BY("puglSetSizeHint") | |||||
PuglStatus | |||||
puglSetMinSize(PuglView* view, int width, int height) | |||||
{ | |||||
return puglSetSizeHint( | |||||
view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); | |||||
} | |||||
/** | |||||
Set the maximum size of the view. | |||||
If an initial maximum size is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
*/ | |||||
static inline PUGL_DEPRECATED_BY("puglSetSizeHint") | |||||
PuglStatus | PuglStatus | ||||
puglHideWindow(PuglView* view); | |||||
puglSetMaxSize(PuglView* view, int width, int height) | |||||
{ | |||||
return puglSetSizeHint( | |||||
view, PUGL_MAX_SIZE, (PuglSpan)width, (PuglSpan)height); | |||||
} | |||||
/** | |||||
Set the view aspect ratio range. | |||||
The x and y values here represent a ratio of width to height. To set a | |||||
fixed aspect ratio, set the minimum and maximum values to the same ratio. | |||||
Note that setting different minimum and maximum constraints does not | |||||
currently work on MacOS (the minimum is used), so only setting a fixed | |||||
aspect ratio works properly across all platforms. | |||||
If an initial aspect ratio is known, this should be called before | |||||
puglRealize() to avoid stutter, though it can be called afterwards as well. | |||||
@return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is | |||||
not yet realized. | |||||
*/ | |||||
static inline PUGL_DEPRECATED_BY("puglSetSizeHint") | |||||
PuglStatus | |||||
puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY) | |||||
{ | |||||
const PuglStatus st0 = | |||||
puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); | |||||
const PuglStatus st1 = | |||||
puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); | |||||
return st0 ? st0 : st1; | |||||
} | |||||
/// Return the native window handle | |||||
static inline PUGL_DEPRECATED_BY("puglGetNativeView") | |||||
PuglNativeView | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return puglGetNativeView(view); | |||||
} | |||||
#endif // PUGL_DISABLE_DEPRECATED | #endif // PUGL_DISABLE_DEPRECATED | ||||
@@ -1,18 +1,5 @@ | |||||
/* | |||||
Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
// Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
// SPDX-License-Identifier: ISC | |||||
#ifndef PUGL_STUB_H | #ifndef PUGL_STUB_H | ||||
#define PUGL_STUB_H | #define PUGL_STUB_H | ||||