@@ -25,30 +25,36 @@ ifneq ($(HAIKU),true) | |||
ifneq ($(HURD),true) | |||
ifneq ($(LINUX),true) | |||
ifneq ($(MACOS),true) | |||
ifneq ($(WASM),true) | |||
ifneq ($(WINDOWS),true) | |||
ifneq (,$(findstring bsd,$(TARGET_MACHINE))) | |||
BSD=true | |||
BSD = true | |||
else ifneq (,$(findstring haiku,$(TARGET_MACHINE))) | |||
HAIKU=true | |||
HAIKU = true | |||
else ifneq (,$(findstring linux,$(TARGET_MACHINE))) | |||
LINUX=true | |||
LINUX = true | |||
else ifneq (,$(findstring gnu,$(TARGET_MACHINE))) | |||
HURD=true | |||
HURD = true | |||
else ifneq (,$(findstring apple,$(TARGET_MACHINE))) | |||
MACOS=true | |||
MACOS = true | |||
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))) | |||
WINDOWS=true | |||
WINDOWS = true | |||
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 | |||
@@ -56,81 +62,105 @@ endif | |||
TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) | |||
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 | |||
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 | |||
ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) | |||
CPU_ARM=true | |||
CPU_ARM_OR_AARCH64=true | |||
CPU_ARM = true | |||
CPU_ARM_OR_AARCH64 = true | |||
endif | |||
ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) | |||
CPU_ARM64=true | |||
CPU_ARM_OR_AARCH64=true | |||
CPU_ARM64 = true | |||
CPU_ARM_OR_AARCH64 = true | |||
endif | |||
ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) | |||
CPU_AARCH64=true | |||
CPU_ARM_OR_AARCH64=true | |||
CPU_AARCH64 = true | |||
CPU_ARM_OR_AARCH64 = true | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# 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 | |||
PKG_CONFIG ?= pkg-config --static | |||
else | |||
PKG_CONFIG ?= pkg-config | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set cross compiling flag | |||
ifeq ($(WASM),true) | |||
CROSS_COMPILING = true | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set LINUX_OR_MACOS | |||
ifeq ($(LINUX),true) | |||
LINUX_OR_MACOS=true | |||
LINUX_OR_MACOS = true | |||
endif | |||
ifeq ($(MACOS),true) | |||
LINUX_OR_MACOS=true | |||
LINUX_OR_MACOS = true | |||
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) | |||
HAIKU_OR_MACOS_OR_WINDOWS=true | |||
HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true | |||
HAIKU_OR_MACOS_OR_WINDOWS = true | |||
endif | |||
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 | |||
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 | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set UNIX | |||
ifeq ($(BSD),true) | |||
UNIX=true | |||
UNIX = true | |||
endif | |||
ifeq ($(HURD),true) | |||
UNIX=true | |||
UNIX = true | |||
endif | |||
ifeq ($(LINUX),true) | |||
UNIX=true | |||
UNIX = true | |||
endif | |||
ifeq ($(MACOS),true) | |||
UNIX=true | |||
UNIX = true | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -140,7 +170,12 @@ BASE_FLAGS = -Wall -Wextra -pipe -MD -MP | |||
BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections | |||
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 | |||
ifeq ($(CPU_ARM),true) | |||
@@ -150,19 +185,26 @@ endif | |||
endif | |||
ifeq ($(MACOS),true) | |||
# 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) | |||
LINK_OPTS += -Wl,-x | |||
endif | |||
else | |||
# 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) | |||
LINK_OPTS += -Wl,--strip-all | |||
endif | |||
endif | |||
endif | |||
ifeq ($(SKIP_STRIPPING),true) | |||
BASE_FLAGS += -g | |||
endif | |||
@@ -172,9 +214,15 @@ ifeq ($(NOOPT),true) | |||
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | |||
endif | |||
ifneq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||
ifneq ($(BSD),true) | |||
BASE_FLAGS += -fno-gnu-unique | |||
endif | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
# 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 | |||
BASE_FLAGS += -mstackrealign | |||
else | |||
@@ -185,29 +233,45 @@ endif | |||
ifeq ($(DEBUG),true) | |||
BASE_FLAGS += -DDEBUG -O0 -g | |||
LINK_OPTS = | |||
ifeq ($(WASM),true) | |||
LINK_OPTS += -sASSERTIONS=1 | |||
endif | |||
else | |||
BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | |||
CXXFLAGS += -fvisibility-inlines-hidden | |||
endif | |||
ifeq ($(STATIC_BUILD),true) | |||
BASE_FLAGS += -DSTATIC_BUILD | |||
# LINK_OPTS += -static | |||
endif | |||
ifeq ($(WITH_LTO),true) | |||
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 | |||
BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) | |||
BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++11 $(CXXFLAGS) | |||
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 | |||
LINK_FLAGS += -Wl,--no-undefined | |||
LINK_FLAGS += -Wl,--no-undefined | |||
endif | |||
ifeq ($(MACOS_OLD),true) | |||
BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) -DHAVE_CPP11_SUPPORT=0 | |||
endif | |||
ifeq ($(WASM_EXCEPTIONS),true) | |||
BUILD_CXX_FLAGS += -fexceptions | |||
LINK_FLAGS += -fexceptions | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
# Always build statically on windows | |||
LINK_FLAGS += -static -static-libgcc -static-libstdc++ | |||
@@ -241,46 +305,47 @@ endif | |||
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 | |||
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_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | |||
HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) | |||
HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) | |||
HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) | |||
endif | |||
endif | |||
# Vulkan is not supported yet | |||
# HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Check for optional libraries | |||
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) | |||
HAVE_RTAUDIO = true | |||
else ifeq ($(WINDOWS),true) | |||
HAVE_RTAUDIO = true | |||
else ifneq ($(HAIKU),true) | |||
else | |||
HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && 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) | |||
HAVE_RTAUDIO = true | |||
else ifeq ($(HAVE_PULSEAUDIO),true) | |||
HAVE_RTAUDIO = true | |||
endif | |||
endif | |||
endif | |||
endif | |||
# backwards compat | |||
# backwards compat, always available/enabled | |||
HAVE_JACK = true | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -288,18 +353,13 @@ HAVE_JACK = true | |||
ifeq ($(HAIKU),true) | |||
DGL_SYSTEM_LIBS += -lbe | |||
endif | |||
ifeq ($(MACOS),true) | |||
else ifeq ($(MACOS),true) | |||
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
else ifeq ($(WASM),true) | |||
else ifeq ($(WINDOWS),true) | |||
DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | |||
# -lole32 | |||
endif | |||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||
else | |||
ifeq ($(HAVE_DBUS),true) | |||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | |||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | |||
@@ -346,18 +406,18 @@ DGL_FLAGS += -DHAVE_OPENGL | |||
ifeq ($(HAIKU),true) | |||
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags 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_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 | |||
ifeq ($(WINDOWS),true) | |||
OPENGL_LIBS = -lopengl32 | |||
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_LIBS = $(shell $(PKG_CONFIG) --libs gl x11) | |||
endif | |||
@@ -369,7 +429,7 @@ endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set Stub specific stuff | |||
ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||
HAVE_STUB = true | |||
else | |||
HAVE_STUB = $(HAVE_X11) | |||
@@ -409,21 +469,40 @@ PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple) | |||
PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) | |||
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 | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Backwards-compatible HAVE_DGL | |||
ifeq ($(MACOS_OR_WINDOWS),true) | |||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||
HAVE_DGL = true | |||
else ifeq ($(HAVE_OPENGL),true) | |||
ifeq ($(HAIKU),true) | |||
HAVE_DGL = true | |||
else | |||
HAVE_DGL = $(HAVE_X11) | |||
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 | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -441,6 +520,18 @@ ifeq ($(FILE_BROWSER_DISABLED),true) | |||
BUILD_CXX_FLAGS += -DDGL_FILE_BROWSER_DISABLED | |||
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) | |||
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 | |||
endif | |||
@@ -457,25 +548,26 @@ ifeq ($(USE_RGBA),true) | |||
BUILD_CXX_FLAGS += -DDGL_USE_RGBA | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set app extension | |||
ifeq ($(WINDOWS),true) | |||
ifeq ($(WASM),true) | |||
APP_EXT = .html | |||
else ifeq ($(WINDOWS),true) | |||
APP_EXT = .exe | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set shared lib extension | |||
LIB_EXT = .so | |||
ifeq ($(MACOS),true) | |||
LIB_EXT = .dylib | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
else ifeq ($(WASM),true) | |||
LIB_EXT = .wasm | |||
else ifeq ($(WINDOWS),true) | |||
LIB_EXT = .dll | |||
else | |||
LIB_EXT = .so | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -483,6 +575,8 @@ endif | |||
ifeq ($(MACOS),true) | |||
SHARED = -dynamiclib | |||
else ifeq ($(WASM),true) | |||
SHARED = -sSIDE_MODULE=2 | |||
else | |||
SHARED = -shared | |||
endif | |||
@@ -490,8 +584,12 @@ endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Handle the verbosity switch | |||
ifeq ($(VERBOSE),true) | |||
SILENT = | |||
ifeq ($(VERBOSE),1) | |||
else ifeq ($(VERBOSE),y) | |||
else ifeq ($(VERBOSE),yes) | |||
else ifeq ($(VERBOSE),true) | |||
else | |||
SILENT = @ | |||
endif | |||
@@ -520,9 +618,12 @@ features: | |||
$(call print_available,HURD) | |||
$(call print_available,LINUX) | |||
$(call print_available,MACOS) | |||
$(call print_available,WASM) | |||
$(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,LINUX_OR_MACOS) | |||
$(call print_available,MACOS_OR_WASM_OR_WINDOWS) | |||
$(call print_available,MACOS_OR_WINDOWS) | |||
$(call print_available,UNIX) | |||
@echo === Detected features | |||
@@ -534,6 +635,7 @@ features: | |||
$(call print_available,HAVE_OPENGL) | |||
$(call print_available,HAVE_PULSEAUDIO) | |||
$(call print_available,HAVE_RTAUDIO) | |||
$(call print_available,HAVE_SDL2) | |||
$(call print_available,HAVE_STUB) | |||
$(call print_available,HAVE_VULKAN) | |||
$(call print_available,HAVE_X11) | |||
@@ -38,6 +38,10 @@ ifeq ($(HAVE_ALSA),true) | |||
BASE_FLAGS += -DHAVE_ALSA | |||
endif | |||
ifeq ($(HAVE_JACK),true) | |||
BASE_FLAGS += -DHAVE_JACK | |||
endif | |||
ifeq ($(HAVE_LIBLO),true) | |||
BASE_FLAGS += -DHAVE_LIBLO | |||
endif | |||
@@ -46,6 +50,36 @@ ifeq ($(HAVE_PULSEAUDIO),true) | |||
BASE_FLAGS += -DHAVE_PULSEAUDIO | |||
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) | |||
JACK_LIBS += -framework CoreAudio -framework CoreFoundation | |||
else ifeq ($(WINDOWS),true) | |||
@@ -54,26 +88,25 @@ JACK_LIBS += -lole32 -lwinmm | |||
JACK_LIBS += -ldsound | |||
# WASAPI | |||
# 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_LIBS += $(PULSEAUDIO_LIBS) | |||
else ifeq ($(HAVE_ALSA),true) | |||
JACK_FLAGS += $(ALSA_FLAGS) | |||
JACK_LIBS += $(ALSA_LIBS) | |||
endif | |||
ifeq ($(HAVE_RTAUDIO),true) | |||
ifneq ($(HAIKU),true) | |||
JACK_LIBS += -lpthread | |||
endif # !HAIKU | |||
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 | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -103,13 +136,17 @@ endif | |||
ifeq ($(LINUX),true) | |||
VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so | |||
endif | |||
ifeq ($(MACOS),true) | |||
else ifeq ($(MACOS),true) | |||
VST3_CONTENTS = $(NAME).vst3/Contents | |||
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 | |||
ifeq ($(WINDOWS),true) | |||
VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-win/$(NAME).vst3 | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -127,6 +164,7 @@ ifneq ($(VST3_FILENAME),) | |||
vst3 = $(TARGET_DIR)/$(VST3_FILENAME) | |||
endif | |||
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) | |||
static = $(TARGET_DIR)/$(NAME).a | |||
ifeq ($(MACOS),true) | |||
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_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.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) | |||
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def | |||
SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def | |||
@@ -314,7 +361,11 @@ $(BUILD_DIR)/%.mm.o: %.mm | |||
clean: | |||
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 | |||
@@ -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) | |||
-@mkdir -p $(BUILD_DIR) | |||
@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) | |||
-@mkdir -p $(BUILD_DIR) | |||
@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) | |||
-@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) | |||
-@mkdir -p $(BUILD_DIR) | |||
@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 | |||
@@ -423,12 +472,12 @@ endif | |||
$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | |||
-@mkdir -p $(shell dirname $@) | |||
@echo "Creating LV2 plugin library for $(NAME)" | |||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(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) | |||
-@mkdir -p $(shell dirname $@) | |||
@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 | |||
@@ -472,6 +521,21 @@ endif | |||
@echo "Creating shared library for $(NAME)" | |||
$(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 | |||
@@ -501,6 +565,7 @@ endif | |||
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.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_DSSI.cpp.d | |||
@@ -508,5 +573,6 @@ endif | |||
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_VST3.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)) | |||
target_link_libraries("${NAME}-ui" PRIVATE "dl") | |||
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") | |||
else() | |||
add_library("${NAME}-ui" INTERFACE) | |||
@@ -468,9 +468,9 @@ function(dpf__add_dgl_cairo) | |||
if(NOT APPLE) | |||
target_sources(dgl-cairo PRIVATE | |||
"${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() | |||
target_include_directories(dgl-cairo PUBLIC | |||
"${DPF_ROOT_DIR}/dgl") | |||
@@ -530,9 +530,9 @@ function(dpf__add_dgl_opengl) | |||
if(NOT APPLE) | |||
target_sources(dgl-opengl PRIVATE | |||
"${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() | |||
target_include_directories(dgl-opengl PUBLIC | |||
"${DPF_ROOT_DIR}/dgl") | |||
@@ -556,19 +556,12 @@ endfunction() | |||
# 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) | |||
if(APPLE) | |||
target_sources("${NAME}" PRIVATE | |||
"${DPF_ROOT_DIR}/distrho/DistrhoUI_macOS.mm") | |||
string(SHA256 _hash "${NAME}:${PROJECT_VERSION}") | |||
target_compile_definitions("${NAME}" PUBLIC "PUGL_NAMESPACE=${_hash}") | |||
endif() | |||
endfunction() | |||
@@ -596,22 +589,22 @@ function(dpf__add_dgl_system_libs) | |||
target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") | |||
target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") | |||
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) | |||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") | |||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") | |||
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) | |||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") | |||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") | |||
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() | |||
if(MSVC) | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,6 +19,12 @@ | |||
#include "Base.hpp" | |||
#ifdef DISTRHO_NAMESPACE | |||
START_NAMESPACE_DISTRHO | |||
class PluginApplication; | |||
END_NAMESPACE_DISTRHO | |||
#endif | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -33,7 +39,7 @@ START_NAMESPACE_DGL | |||
Unless stated otherwise, functions within this class are not thread-safe. | |||
*/ | |||
class Application | |||
class DISTRHO_API Application | |||
{ | |||
public: | |||
/** | |||
@@ -103,7 +109,7 @@ public: | |||
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. | |||
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. | |||
*/ | |||
const char* getClassName() const noexcept; | |||
/** | |||
Set the class name of the application. | |||
@see getClassName | |||
*/ | |||
void setClassName(const char* name); | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
friend class PluginApplication; | |||
friend class Window; | |||
#ifdef DISTRHO_NAMESPACE | |||
friend class DISTRHO_NAMESPACE::PluginApplication; | |||
#endif | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | |||
}; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* 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 | |||
}; | |||
/** | |||
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. | |||
This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. | |||
*/ | |||
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. | |||
*/ | |||
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) | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,7 +20,7 @@ | |||
#include "ImageBase.hpp" | |||
#include "ImageBaseWidgets.hpp" | |||
#include <cairo/cairo.h> | |||
#include <cairo.h> | |||
START_NAMESPACE_DGL | |||
@@ -151,7 +151,7 @@ public: | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~CairoBaseWidget() {} | |||
~CairoBaseWidget() override {} | |||
protected: | |||
/** | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* 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. | |||
*/ | |||
#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 | |||
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 | |||
# 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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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 | |||
-@mkdir -p ../build/dgl | |||
@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. | |||
*/ | |||
virtual ~NanoBaseWidget() {} | |||
~NanoBaseWidget() override {} | |||
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) | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,96 +20,7 @@ | |||
#include "ImageBase.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 | |||
@@ -120,6 +31,8 @@ START_NAMESPACE_DGL | |||
*/ | |||
struct OpenGLGraphicsContext : GraphicsContext | |||
{ | |||
#ifdef DGL_USE_OPENGL3 | |||
#endif | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -238,11 +151,11 @@ public: | |||
// FIXME this should not be needed | |||
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) | |||
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); }; | |||
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); } | |||
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) | |||
{ drawAt(context, Point<int>(x, y)); }; | |||
{ drawAt(context, Point<int>(x, y)); } | |||
/** | |||
Constructor using raw image data, specifying an OpenGL image format. | |||
@@ -305,4 +218,4 @@ typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||
END_NAMESPACE_DGL | |||
#endif | |||
#endif // DGL_OPENGL_HPP_INCLUDED |
@@ -71,6 +71,7 @@ public: | |||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) | |||
{ return Window::addIdleCallback(callback, timerFrequencyInMs); } | |||
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | |||
Application& getApp() const noexcept { return Window::getApp(); } | |||
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | |||
double getScaleFactor() const noexcept { return Window::getScaleFactor(); } | |||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | |||
@@ -47,7 +47,7 @@ public: | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~SubWidget(); | |||
~SubWidget() override; | |||
/** | |||
Check if this widget contains the point defined by @a x and @a y. | |||
@@ -54,7 +54,7 @@ public: | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~TopLevelWidget(); | |||
~TopLevelWidget() override; | |||
/** | |||
Get the application associated with this top-level widget's window. | |||
@@ -101,8 +101,8 @@ public: | |||
void repaint(const Rectangle<uint>& rect) noexcept; | |||
// 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); | |||
const void* getClipboard(const char*& mimeType, size_t& dataSize); | |||
bool setCursor(MouseCursor cursor); | |||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | |||
bool removeIdleCallback(IdleCallback* callback); | |||
@@ -56,17 +56,16 @@ public: | |||
/** | |||
Base event data. | |||
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 { | |||
/** Currently active keyboard modifiers. @see Modifier */ | |||
uint mod; | |||
/** Event flags. @see EventFlag */ | |||
uint flags; | |||
/** Event timestamp (if any). */ | |||
uint time; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | |||
/** Destuctor */ | |||
virtual ~BaseEvent() noexcept {} | |||
@@ -86,17 +85,17 @@ public: | |||
Alternatively, the raw @a keycode can be used to work directly with physical keys, | |||
but note that this value is not portable and differs between platforms and hardware. | |||
@a press True if the key was pressed, false if released. | |||
@a key Unicode point of the key pressed. | |||
@a keycode Raw keycode. | |||
@see onKeyboard | |||
*/ | |||
struct KeyboardEvent : BaseEvent { | |||
/** True if the key was pressed, false if released. */ | |||
bool press; | |||
/** Unicode point of the key pressed. */ | |||
uint key; | |||
/** Raw keycode. */ | |||
uint keycode; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
KeyboardEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
@@ -112,9 +111,9 @@ public: | |||
*/ | |||
struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { | |||
bool press; | |||
Key key; | |||
Key key; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
SpecialEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
@@ -131,17 +130,17 @@ public: | |||
so there is not necessarily a direct correspondence between text events and physical key presses. | |||
For example, with some input methods a sequence of several key presses will generate a single character. | |||
@a keycode Raw key code. | |||
@a character Unicode character code. | |||
@a string UTF-8 string. | |||
@see onCharacterInput | |||
*/ | |||
struct CharacterInputEvent : BaseEvent { | |||
/** Raw key code. */ | |||
uint keycode; | |||
/** Unicode character code. */ | |||
uint character; | |||
/** UTF-8 string. */ | |||
char string[8]; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
CharacterInputEvent() noexcept | |||
: BaseEvent(), | |||
keycode(0), | |||
@@ -155,20 +154,19 @@ public: | |||
/** | |||
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 | |||
*/ | |||
struct MouseEvent : BaseEvent { | |||
/** The button number starting from 1. @see MouseButton */ | |||
uint button; | |||
/** True if the button was pressed, false if released. */ | |||
bool press; | |||
/** The widget-relative coordinates of the pointer. */ | |||
Point<double> pos; | |||
/** The absolute coordinates of the pointer. */ | |||
Point<double> absolutePos; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
MouseEvent() noexcept | |||
: BaseEvent(), | |||
button(0), | |||
@@ -179,16 +177,15 @@ public: | |||
/** | |||
Mouse motion event. | |||
@a pos The widget-relative coordinates of the pointer. | |||
@a absolutePos The absolute coordinates of the pointer. | |||
@see onMotion | |||
*/ | |||
struct MotionEvent : BaseEvent { | |||
/** The widget-relative coordinates of the pointer. */ | |||
Point<double> pos; | |||
/** The absolute coordinates of the pointer. */ | |||
Point<double> absolutePos; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
MotionEvent() noexcept | |||
: BaseEvent(), | |||
pos(0.0, 0.0), | |||
@@ -204,19 +201,19 @@ public: | |||
Some systems and devices support finer resolution and/or higher values for fast scrolls, | |||
so programs should handle any value gracefully. | |||
@a pos The widget-relative coordinates of the pointer. | |||
@a absolutePos The absolute coordinates of the pointer. | |||
@a delta The scroll distance. | |||
@a direction The direction of the scroll or "smooth". | |||
@see onScroll | |||
*/ | |||
struct ScrollEvent : BaseEvent { | |||
/** The widget-relative coordinates of the pointer. */ | |||
Point<double> pos; | |||
/** The absolute coordinates of the pointer. */ | |||
Point<double> absolutePos; | |||
/** The scroll distance. */ | |||
Point<double> delta; | |||
/** The direction of the scroll or "smooth". */ | |||
ScrollDirection direction; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
ScrollEvent() noexcept | |||
: BaseEvent(), | |||
pos(0.0, 0.0), | |||
@@ -227,15 +224,15 @@ public: | |||
/** | |||
Resize event. | |||
@a size The new widget size. | |||
@a oldSize The previous size, may be null. | |||
@see onResize | |||
*/ | |||
struct ResizeEvent { | |||
/** The new widget size. */ | |||
Size<uint> size; | |||
/** The previous size, can be null. */ | |||
Size<uint> oldSize; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
ResizeEvent() noexcept | |||
: size(0, 0), | |||
oldSize(0, 0) {} | |||
@@ -243,15 +240,15 @@ public: | |||
/** | |||
Widget position changed event. | |||
@a pos The new absolute position of the widget. | |||
@a oldPos The previous absolute position of the widget. | |||
@see onPositionChanged | |||
*/ | |||
struct PositionChangedEvent { | |||
/** The new absolute position of the widget. */ | |||
Point<int> pos; | |||
/** The previous absolute position of the widget. */ | |||
Point<int> oldPos; | |||
/** Constructor */ | |||
/** Constructor for default/null values */ | |||
PositionChangedEvent() noexcept | |||
: pos(0, 0), | |||
oldPos(0, 0) {} | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,13 +20,20 @@ | |||
#include "Geometry.hpp" | |||
#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 | |||
START_NAMESPACE_DGL | |||
class Application; | |||
class PluginWindow; | |||
class TopLevelWidget; | |||
// ----------------------------------------------------------------------- | |||
@@ -52,16 +59,11 @@ class TopLevelWidget; | |||
... | |||
*/ | |||
class Window | |||
class DISTRHO_API Window | |||
{ | |||
struct PrivateData; | |||
public: | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; | |||
typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; | |||
#endif | |||
/** | |||
Window graphics context as a scoped struct. | |||
This class gives graphics context drawing time to a window's widgets. | |||
@@ -206,6 +208,41 @@ public: | |||
*/ | |||
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. | |||
*/ | |||
@@ -263,6 +300,19 @@ public: | |||
*/ | |||
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. | |||
@@ -274,16 +324,6 @@ public: | |||
*/ | |||
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. | |||
@@ -360,7 +400,7 @@ public: | |||
This function does not block the event loop. | |||
*/ | |||
bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); | |||
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); | |||
#endif | |||
/** | |||
@@ -402,6 +442,14 @@ public: | |||
bool automaticallyScale = false, | |||
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(). */ | |||
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") | |||
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } | |||
@@ -415,6 +463,23 @@ public: | |||
inline void exec(bool blockWait = false) { runAsModal(blockWait); } | |||
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. | |||
Returning true closes the window, which is the default behaviour. | |||
@@ -464,8 +529,10 @@ protected: | |||
private: | |||
PrivateData* const pData; | |||
friend class Application; | |||
friend class PluginWindow; | |||
friend class TopLevelWidget; | |||
#ifdef DISTRHO_NAMESPACE | |||
friend class DISTRHO_NAMESPACE::PluginWindow; | |||
#endif | |||
/** @internal */ | |||
explicit Window(Application& app, | |||
@@ -477,7 +544,7 @@ private: | |||
bool isVST3, | |||
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) | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -16,10 +16,21 @@ | |||
#include "ApplicationPrivateData.hpp" | |||
#ifdef __EMSCRIPTEN__ | |||
# include <emscripten/emscripten.h> | |||
#endif | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#ifdef __EMSCRIPTEN__ | |||
static void app_idle(void* const app) | |||
{ | |||
static_cast<Application*>(app)->idle(); | |||
} | |||
#endif | |||
Application::Application(const bool isStandalone) | |||
: pData(new PrivateData(isStandalone)) {} | |||
@@ -37,8 +48,12 @@ void Application::exec(const uint idleTimeInMs) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | |||
#ifdef __EMSCRIPTEN__ | |||
emscripten_set_main_loop_arg(app_idle, this, 0, true); | |||
#else | |||
while (! pData->isQuitting) | |||
pData->idle(idleTimeInMs); | |||
#endif | |||
} | |||
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) | |||
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||
standalone ? PUGL_WORLD_THREADS : 0x0)), | |||
@@ -60,11 +67,8 @@ Application::PrivateData::PrivateData(const bool standalone) | |||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||
puglSetWorldHandle(world, this); | |||
#ifndef __EMSCRIPTEN__ | |||
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | |||
#ifdef DISTRHO_OS_MAC | |||
if (standalone) | |||
puglMacOSActivateApp(); | |||
#endif | |||
} | |||
@@ -22,6 +22,9 @@ | |||
#include <list> | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
typedef HANDLE d_ThreadHandle; | |||
@@ -55,9 +55,9 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||
# ifdef DGL_USE_NANOVG_FBO | |||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | |||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | |||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | |||
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | |||
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | |||
@@ -82,7 +82,9 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
// Include NanoVG OpenGL implementation | |||
//#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 | |||
#else | |||
# define NANOVG_GL2_IMPLEMENTATION | |||
@@ -138,6 +140,11 @@ NVGcontext* nvgCreateGL(int flags) | |||
# define DGL_EXT(PROC, func) \ | |||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | |||
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(PFNGLATTACHSHADERPROC, glAttachShader) | |||
DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) | |||
@@ -167,16 +174,16 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||
# ifdef DGL_USE_NANOVG_FBO | |||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | |||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | |||
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 | |||
# ifdef DGL_USE_OPENGL3 | |||
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) | |||
@@ -188,6 +195,7 @@ DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) | |||
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
# endif | |||
# undef DGL_EXT | |||
# undef DGL_EXT2 | |||
needsInit = false; | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic pop | |||
@@ -314,11 +322,14 @@ NanoVG::Paint::operator NVGpaint() const noexcept | |||
NanoVG::NanoVG(int flags) | |||
: fContext(nvgCreateGL(flags)), | |||
fInFrame(false), | |||
fIsSubWidget(false) {} | |||
fIsSubWidget(false) | |||
{ | |||
DISTRHO_CUSTOM_SAFE_ASSERT("Failed to create NanoVG context, expect a black screen", fContext != nullptr); | |||
} | |||
NanoVG::~NanoVG() | |||
{ | |||
DISTRHO_SAFE_ASSERT(! fInFrame); | |||
DISTRHO_CUSTOM_SAFE_ASSERT("Destroying NanoVG context with still active frame", ! fInFrame); | |||
if (fContext != nullptr && ! fIsSubWidget) | |||
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) | |||
{ | |||
d_stderr2("OpenGL3 function not implemented: %s", name); | |||
} | |||
#else | |||
# define DGL_USE_COMPAT_OPENGL | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
@@ -47,7 +59,7 @@ static void notImplemented(const char* const name) | |||
void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
if (includeAlpha) | |||
glColor4f(red, green, blue, alpha); | |||
else | |||
@@ -62,7 +74,7 @@ void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
// ----------------------------------------------------------------------- | |||
// Line | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
template<typename T> | |||
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> | |||
void Line<T>::draw(const GraphicsContext&, const T width) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||
glLineWidth(static_cast<GLfloat>(width)); | |||
@@ -96,7 +108,7 @@ void Line<T>::draw(const GraphicsContext&, const T width) | |||
template<typename T> | |||
void Line<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawLine<T>(posStart, posEnd); | |||
#else | |||
notImplemented("Line::draw"); | |||
@@ -113,7 +125,7 @@ template class Line<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Circle | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
template<typename T> | |||
static void drawCircle(const Point<T>& pos, | |||
const uint numSegments, | |||
@@ -146,7 +158,7 @@ static void drawCircle(const Point<T>& pos, | |||
template<typename T> | |||
void Circle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
#else | |||
notImplemented("Circle::draw"); | |||
@@ -159,7 +171,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
#else | |||
notImplemented("Circle::drawOutline"); | |||
@@ -170,7 +182,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
template<typename T> | |||
void Circle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
#else | |||
notImplemented("Circle::draw"); | |||
@@ -180,7 +192,7 @@ void Circle<T>::draw() | |||
template<typename T> | |||
void Circle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
#else | |||
notImplemented("Circle::drawOutline"); | |||
@@ -197,7 +209,7 @@ template class Circle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Triangle | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
template<typename T> | |||
static void drawTriangle(const Point<T>& pos1, | |||
const Point<T>& pos2, | |||
@@ -221,7 +233,7 @@ static void drawTriangle(const Point<T>& pos1, | |||
template<typename T> | |||
void Triangle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawTriangle<T>(pos1, pos2, pos3, false); | |||
#else | |||
notImplemented("Triangle::draw"); | |||
@@ -234,7 +246,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawTriangle<T>(pos1, pos2, pos3, true); | |||
#else | |||
notImplemented("Triangle::drawOutline"); | |||
@@ -245,7 +257,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
template<typename T> | |||
void Triangle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawTriangle<T>(pos1, pos2, pos3, false); | |||
#else | |||
notImplemented("Triangle::draw"); | |||
@@ -255,7 +267,7 @@ void Triangle<T>::draw() | |||
template<typename T> | |||
void Triangle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawTriangle<T>(pos1, pos2, pos3, true); | |||
#else | |||
notImplemented("Triangle::drawOutline"); | |||
@@ -272,7 +284,7 @@ template class Triangle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Rectangle | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
template<typename T> | |||
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> | |||
void Rectangle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawRectangle<T>(*this, false); | |||
#else | |||
notImplemented("Rectangle::draw"); | |||
@@ -319,7 +331,7 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawRectangle<T>(*this, true); | |||
#else | |||
notImplemented("Rectangle::drawOutline"); | |||
@@ -330,7 +342,7 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
template<typename T> | |||
void Rectangle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawRectangle<T>(*this, false); | |||
#else | |||
notImplemented("Rectangle::draw"); | |||
@@ -340,7 +352,7 @@ void Rectangle<T>::draw() | |||
template<typename T> | |||
void Rectangle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
drawRectangle<T>(*this, true); | |||
#else | |||
notImplemented("Rectangle::drawOutline"); | |||
@@ -395,14 +407,14 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, con | |||
setupCalled = true; | |||
} | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
#endif | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, textureId); | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
glBegin(GL_QUADS); | |||
{ | |||
@@ -616,21 +628,21 @@ void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
if (pData->rotationAngle != 0) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
glPushMatrix(); | |||
#endif | |||
const int w2 = w/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); | |||
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||
#endif | |||
Rectangle<int>(-w2, -h2, w, h).draw(context); | |||
#ifndef DGL_USE_OPENGL3 | |||
#ifdef DGL_USE_COMPAT_OPENGL | |||
glPopMatrix(); | |||
#endif | |||
} | |||
@@ -60,14 +60,14 @@ void TopLevelWidget::setSize(const Size<uint>& 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) | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* 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); | |||
} | |||
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 | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
@@ -247,7 +289,7 @@ void Window::setSize(uint width, uint height) | |||
} | |||
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); | |||
} | |||
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) | |||
@@ -324,7 +362,7 @@ const GraphicsContext& Window::getGraphicsContext() const noexcept | |||
uintptr_t Window::getNativeWindowHandle() const noexcept | |||
{ | |||
return puglGetNativeWindow(pData->view); | |||
return puglGetNativeView(pData->view); | |||
} | |||
double Window::getScaleFactor() const noexcept | |||
@@ -358,10 +396,10 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||
return; | |||
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) | |||
{ | |||
@@ -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() | |||
{ | |||
return true; | |||
@@ -451,13 +526,6 @@ void Window::onFileSelected(const char*) | |||
} | |||
#endif | |||
#if 0 | |||
void Window::setTransientWinId(const uintptr_t winId) | |||
{ | |||
puglSetTransientFor(pData->view, winId); | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* 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 | |||
if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | |||
return std::max(1.0, std::atof(scale)); | |||
if (view != nullptr) | |||
return puglGetDesktopScaleFactor(view); | |||
return puglGetScaleFactorFromParent(view); | |||
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) | |||
: app(a), | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(nullptr), | |||
view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), | |||
topLevelWidgets(), | |||
isClosed(true), | |||
isVisible(false), | |||
isEmbed(false), | |||
usesSizeRequest(false), | |||
scaleFactor(getDesktopScaleFactor(view)), | |||
scaleFactor(getScaleFactorFromParent(view)), | |||
autoScaling(false), | |||
autoScaleFactor(1.0), | |||
minWidth(0), | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
ignoreEvents(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
fileBrowserHandle(nullptr), | |||
@@ -98,8 +125,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||
: app(a), | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(ppData->view), | |||
view(puglNewViewWithTransientParent(appData->world, ppData->view)), | |||
topLevelWidgets(), | |||
isClosed(true), | |||
isVisible(false), | |||
@@ -112,15 +138,15 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
ignoreEvents(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
fileBrowserHandle(nullptr), | |||
#endif | |||
modal(ppData) | |||
{ | |||
puglSetTransientFor(view, puglGetNativeWindow(transientParentView)); | |||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | |||
} | |||
@@ -130,30 +156,28 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
: app(a), | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(nullptr), | |||
view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |||
topLevelWidgets(), | |||
isClosed(parentWindowHandle == 0), | |||
isVisible(parentWindowHandle != 0), | |||
isEmbed(parentWindowHandle != 0), | |||
usesSizeRequest(false), | |||
scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), | |||
scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |||
autoScaling(false), | |||
autoScaleFactor(1.0), | |||
minWidth(0), | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
ignoreEvents(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
fileBrowserHandle(nullptr), | |||
#endif | |||
modal() | |||
{ | |||
if (isEmbed) | |||
puglSetParentWindow(view, parentWindowHandle); | |||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); | |||
} | |||
@@ -164,21 +188,22 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
: app(a), | |||
appData(a.pData), | |||
self(s), | |||
view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), | |||
transientParentView(nullptr), | |||
view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), | |||
topLevelWidgets(), | |||
isClosed(parentWindowHandle == 0), | |||
isVisible(parentWindowHandle != 0 && view != nullptr), | |||
isEmbed(parentWindowHandle != 0), | |||
usesSizeRequest(isVST3), | |||
scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), | |||
scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), | |||
autoScaling(false), | |||
autoScaleFactor(1.0), | |||
minWidth(0), | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
ignoreEvents(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
fileBrowserHandle(nullptr), | |||
@@ -230,11 +255,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||
} | |||
puglSetMatchingBackendForCurrentBuild(view); | |||
puglClearMinSize(view); | |||
puglSetWindowSize(view, width, height); | |||
puglSetHandle(view, this); | |||
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | |||
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); | |||
#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); | |||
#endif | |||
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_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 | |||
// PUGL_SAMPLES ?? | |||
puglSetEventFunc(view, puglEventCallback); | |||
// setting default size triggers system-level calls, do it last | |||
puglSetSizeHint(view, PUGL_DEFAULT_SIZE, width, height); | |||
} | |||
bool Window::PrivateData::initPost() | |||
@@ -314,8 +347,8 @@ void Window::PrivateData::show() | |||
appData->oneWindowShown(); | |||
// 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) | |||
puglWin32ShowCentered(view); | |||
@@ -378,11 +411,7 @@ void Window::PrivateData::focus() | |||
if (! isEmbed) | |||
puglRaiseWindow(view); | |||
#ifdef HAVE_X11 | |||
puglX11GrabFocus(view); | |||
#else | |||
puglGrabFocus(view); | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------- | |||
@@ -393,10 +422,7 @@ void Window::PrivateData::setResizable(const bool resizable) | |||
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); | |||
fileBrowserHandle = fileBrowserCreate(isEmbed, | |||
puglGetNativeWindow(view), | |||
puglGetNativeView(view), | |||
autoScaling ? autoScaleFactor : scaleFactor, | |||
options2); | |||
@@ -588,7 +614,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh | |||
void Window::PrivateData::onPuglExpose() | |||
{ | |||
DGL_DBGp("PUGL: onPuglExpose\n"); | |||
DGL_DBG("PUGL: onPuglExpose\n"); | |||
puglOnDisplayPrepare(view); | |||
@@ -744,6 +770,78 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||
#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) | |||
static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); | |||
#endif | |||
@@ -757,6 +855,36 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
} | |||
#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) | |||
{ | |||
///< No event | |||
@@ -765,10 +893,10 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< View created, a #PuglEventCreate | |||
case PUGL_CREATE: | |||
#ifdef HAVE_X11 | |||
#ifdef DGL_USING_X11 | |||
if (! pData->isEmbed) | |||
puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |||
#endif | |||
#endif | |||
break; | |||
///< View destroyed, a #PuglEventDestroy | |||
@@ -795,8 +923,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< View must be drawn, a #PuglEventExpose | |||
case PUGL_EXPOSE: | |||
if (pData->ignoreEvents) | |||
break; | |||
// unused x, y, width, height (double) | |||
pData->onPuglExpose(); | |||
break; | |||
@@ -810,8 +936,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
case PUGL_FOCUS_IN: | |||
///< Keyboard focus left view, a #PuglEventFocus | |||
case PUGL_FOCUS_OUT: | |||
if (pData->ignoreEvents) | |||
break; | |||
pData->onPuglFocus(event->type == PUGL_FOCUS_IN, | |||
static_cast<CrossingMode>(event->focus.mode)); | |||
break; | |||
@@ -821,8 +945,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Key released, a #PuglEventKey | |||
case PUGL_KEY_RELEASE: | |||
{ | |||
if (pData->ignoreEvents) | |||
break; | |||
// unused x, y, xRoot, yRoot (double) | |||
Widget::KeyboardEvent ev; | |||
ev.mod = event->key.state; | |||
@@ -846,8 +968,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Character entered, a #PuglEventText | |||
case PUGL_TEXT: | |||
{ | |||
if (pData->ignoreEvents) | |||
break; | |||
// unused x, y, xRoot, yRoot (double) | |||
Widget::CharacterInputEvent ev; | |||
ev.mod = event->text.state; | |||
@@ -872,13 +992,11 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Mouse button released, a #PuglEventButton | |||
case PUGL_BUTTON_RELEASE: | |||
{ | |||
if (pData->ignoreEvents) | |||
break; | |||
Widget::MouseEvent ev; | |||
ev.mod = event->button.state; | |||
ev.flags = event->button.flags; | |||
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.pos = Point<double>(event->button.x, event->button.y); | |||
ev.absolutePos = ev.pos; | |||
@@ -889,8 +1007,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Pointer moved, a #PuglEventMotion | |||
case PUGL_MOTION: | |||
{ | |||
if (pData->ignoreEvents) | |||
break; | |||
Widget::MotionEvent ev; | |||
ev.mod = event->motion.state; | |||
ev.flags = event->motion.flags; | |||
@@ -904,8 +1020,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Scrolled, a #PuglEventScroll | |||
case PUGL_SCROLL: | |||
{ | |||
if (pData->ignoreEvents) | |||
break; | |||
Widget::ScrollEvent ev; | |||
ev.mod = event->scroll.state; | |||
ev.flags = event->scroll.flags; | |||
@@ -924,8 +1038,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Timer triggered, a #PuglEventTimer | |||
case PUGL_TIMER: | |||
if (pData->ignoreEvents) | |||
break; | |||
if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id)) | |||
idleCallback->idleCallback(); | |||
break; | |||
@@ -937,6 +1049,17 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< Recursive loop left, a #PuglEventLoopLeave | |||
case PUGL_LOOP_LEAVE: | |||
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; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* 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. */ | |||
PuglView* view; | |||
/** Pugl view instance of the transient parent window. */ | |||
PuglView* const transientParentView; | |||
/** Reserved space for graphics context. */ | |||
mutable uint8_t graphicsContext[sizeof(void*)]; | |||
@@ -80,15 +77,19 @@ struct Window::PrivateData : IdleCallback { | |||
/** Whether to ignore idle callback requests, useful for temporary windows. */ | |||
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. */ | |||
char* filenameToRenderInto; | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** Handle for file browser dialog operations. */ | |||
FileBrowserHandle fileBrowserHandle; | |||
DGL_NAMESPACE::FileBrowserHandle fileBrowserHandle; | |||
#endif | |||
/** Modal window setup. */ | |||
@@ -165,7 +166,7 @@ struct Window::PrivateData : IdleCallback { | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
// file handling | |||
bool openFileBrowser(const FileBrowserOptions& options); | |||
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options); | |||
#endif | |||
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 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 | |||
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); | |||
if (idx == FONS_INVALID) | |||
{ | |||
if (freeData && data) free(data); | |||
return FONS_INVALID; | |||
} | |||
font = stash->fonts[idx]; | |||
@@ -18,6 +18,28 @@ | |||
#ifndef 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 | |||
extern "C" { | |||
#endif | |||
@@ -151,6 +173,9 @@ struct GLNVGtexture { | |||
int width, height; | |||
int type; | |||
int flags; | |||
#if defined NANOVG_GLES2 | |||
unsigned char* data; | |||
#endif | |||
}; | |||
typedef struct GLNVGtexture GLNVGtexture; | |||
@@ -399,7 +424,12 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||
for (i = 0; i < gl->textureContext->ntextures; i++) { | |||
if (gl->textureContext->textures[i].id == id) { | |||
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |||
{ | |||
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])); | |||
return 1; | |||
} | |||
@@ -753,7 +783,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
} | |||
// No mips. | |||
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; | |||
} | |||
} | |||
@@ -783,10 +813,37 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
switch (type) | |||
{ | |||
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; | |||
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); | |||
#endif | |||
break; | |||
case NVG_TEXTURE_RGB: | |||
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_DEPRECATED_BY | |||
- PUGL_UNUSED | |||
- PUGL_WARN_UNUSED_RESULT | |||
- _Pragma | |||
... |
@@ -1,15 +1,7 @@ | |||
[ | |||
{ "include": [ "<ext/alloc_traits.h>", "private", "<string>", "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": [ "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 (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 | |||
platform-specific details from applications. | |||
@@ -16,18 +16,18 @@ distinguishing features: | |||
* Support for embedding in native windows, for example as a plugin or | |||
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 | |||
--------- | |||
@@ -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 | |||
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 | |||
------------- | |||
@@ -62,37 +66,7 @@ all the tests at once via ninja: | |||
cd build | |||
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> |
@@ -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, | |||
-*-uppercase-literal-suffix, | |||
-altera-struct-pack-align, | |||
-altera*, | |||
-clang-diagnostic-unused-function, | |||
-clang-diagnostic-unused-macros, | |||
-llvmlibc-*, | |||
@@ -1,3 +1,6 @@ | |||
# Copyright 2021 David Robillard <d@drobilla.net> | |||
# SPDX-License-Identifier: CC0-1.0 OR ISC | |||
c_headers = [ | |||
'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 | |||
#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 | |||
#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 | |||
#define PUGL_PUGL_H | |||
#include <stdbool.h> | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#ifndef __cplusplus | |||
# include <stdbool.h> | |||
#endif | |||
#ifndef PUGL_API | |||
# if defined(_WIN32) && !defined(PUGL_STATIC) && defined(PUGL_INTERNAL) | |||
# define PUGL_API __declspec(dllexport) | |||
@@ -53,13 +43,8 @@ | |||
PUGL_API \ | |||
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 | |||
@@ -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 { | |||
double x; | |||
double y; | |||
double width; | |||
double height; | |||
PuglCoord x; | |||
PuglCoord y; | |||
PuglSpan width; | |||
PuglSpan height; | |||
} PuglRect; | |||
/** | |||
@@ -192,6 +201,8 @@ typedef enum { | |||
PUGL_TIMER, ///< Timer triggered, a #PuglTimerEvent | |||
PUGL_LOOP_ENTER, ///< Recursive loop entered, a #PuglLoopEnterEvent | |||
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; | |||
/// Common flags for all event types | |||
@@ -269,10 +280,10 @@ typedef PuglAnyEvent PuglDestroyEvent; | |||
typedef struct { | |||
PuglEventType type; ///< #PUGL_CONFIGURE | |||
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; | |||
/** | |||
@@ -315,10 +326,10 @@ typedef PuglAnyEvent PuglUpdateEvent; | |||
typedef struct { | |||
PuglEventType type; ///< #PUGL_EXPOSE | |||
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; | |||
/** | |||
@@ -419,6 +430,21 @@ typedef struct { | |||
/** | |||
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 { | |||
PuglEventType type; ///< #PUGL_BUTTON_PRESS or #PUGL_BUTTON_RELEASE | |||
@@ -429,7 +455,7 @@ typedef struct { | |||
double xRoot; ///< Root-relative X coordinate | |||
double yRoot; ///< Root-relative Y coordinate | |||
PuglMods state; ///< Bitwise OR of #PuglMod flags | |||
uint32_t button; ///< Button number starting from 1 | |||
uint32_t button; ///< Button number starting from 0 | |||
} PuglButtonEvent; | |||
/** | |||
@@ -499,6 +525,34 @@ typedef struct { | |||
uintptr_t id; ///< Timer ID | |||
} 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. | |||
@@ -556,6 +610,8 @@ typedef union { | |||
PuglFocusEvent focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT | |||
PuglClientEvent client; ///< #PUGL_CLIENT | |||
PuglTimerEvent timer; ///< #PUGL_TIMER | |||
PuglDataOfferEvent offer; ///< #PUGL_DATA_OFFER | |||
PuglDataEvent data; ///< #PUGL_DATA | |||
} PuglEvent; | |||
/** | |||
@@ -580,7 +636,8 @@ typedef enum { | |||
PUGL_REALIZE_FAILED, ///< System view realization failed | |||
PUGL_SET_FORMAT_FAILED, ///< Failed to set pixel format | |||
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; | |||
/// Return a string describing a status code | |||
@@ -626,7 +683,7 @@ typedef enum { | |||
/** | |||
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 | |||
} PuglWorldFlag; | |||
@@ -693,6 +750,11 @@ PUGL_API | |||
PuglStatus | |||
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. | |||
@@ -792,10 +854,11 @@ typedef enum { | |||
PUGL_RESIZABLE, ///< True if view should be resizable | |||
PUGL_IGNORE_KEY_REPEAT, ///< True if key repeat events are ignored | |||
PUGL_REFRESH_RATE, ///< Refresh rate in Hz | |||
PUGL_NUM_VIEW_HINTS | |||
} PuglViewHint; | |||
/// The number of #PuglViewHint values | |||
#define PUGL_NUM_VIEW_HINTS ((unsigned)PUGL_REFRESH_RATE + 1u) | |||
/// A special view hint value | |||
typedef enum { | |||
PUGL_DONT_CARE = -1, ///< Use best available value | |||
@@ -803,6 +866,46 @@ typedef enum { | |||
PUGL_TRUE = 1 ///< Explicitly true | |||
} 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 | |||
typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); | |||
@@ -872,6 +975,10 @@ PUGL_API | |||
PuglStatus | |||
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 | |||
PUGL_API | |||
PuglStatus | |||
@@ -897,6 +1004,22 @@ PUGL_API | |||
int | |||
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 | |||
@@ -926,64 +1049,43 @@ PuglStatus | |||
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 | |||
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 | |||
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 | |||
not yet realized. | |||
*/ | |||
PUGL_API | |||
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 | |||
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. | |||
@@ -1012,6 +1119,11 @@ PUGL_API | |||
PuglStatus | |||
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. | |||
@@ -1024,13 +1136,23 @@ puglSetParentWindow(PuglView* view, PuglNativeView parent); | |||
*/ | |||
PUGL_API | |||
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. | |||
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 | |||
with the view when puglFreeView() is called. | |||
@@ -1067,7 +1189,7 @@ puglGetVisible(const PuglView* view); | |||
/// Return the native window handle | |||
PUGL_API | |||
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 | |||
} 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 | |||
PuglStatus | |||
puglGrabFocus(PuglView* view); | |||
@@ -1149,6 +1281,59 @@ PUGL_API | |||
bool | |||
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. | |||
@@ -1174,13 +1359,13 @@ puglSetClipboard(PuglView* view, | |||
puglSetClipboard() or copied from another application. | |||
@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. | |||
@return The clipboard contents, or null. | |||
*/ | |||
PUGL_API | |||
const void* | |||
puglGetClipboard(PuglView* view, const char** type, size_t* len); | |||
puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); | |||
/** | |||
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, | |||
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 | |||
PuglStatus | |||
@@ -1348,6 +1533,7 @@ typedef PuglLoopLeaveEvent PuglEventLoopLeave; | |||
Windows: This is a `HWND`. | |||
*/ | |||
PUGL_DEPRECATED_BY("PuglNativeView") | |||
typedef uintptr_t PuglNativeWindow; | |||
/** | |||
@@ -1408,8 +1594,8 @@ puglInitWindowSize(PuglView* view, int width, int height) | |||
{ | |||
PuglRect frame = puglGetFrame(view); | |||
frame.width = width; | |||
frame.height = height; | |||
frame.width = (PuglSpan)width; | |||
frame.height = (PuglSpan)height; | |||
puglSetFrame(view, frame); | |||
} | |||
@@ -1421,7 +1607,7 @@ static inline PUGL_DEPRECATED_BY("puglSetMinSize") | |||
void | |||
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. | |||
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") | |||
void | |||
@@ -1442,7 +1628,8 @@ puglInitWindowAspectRatio(PuglView* view, | |||
int maxX, | |||
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 OSX, parent must be an NSView*. | |||
*/ | |||
static inline PUGL_DEPRECATED_BY("puglSetTransientFor") | |||
static inline PUGL_DEPRECATED_BY("puglSetTransientParent") | |||
void | |||
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(). | |||
*/ | |||
PUGL_API | |||
PUGL_DEPRECATED_BY("puglUpdate") | |||
static inline PUGL_DEPRECATED_BY("puglUpdate") | |||
PuglStatus | |||
puglPollEvents(PuglWorld* world, double timeout); | |||
puglPollEvents(PuglWorld* world, double timeout) | |||
{ | |||
return puglUpdate(world, timeout); | |||
} | |||
/** | |||
Dispatch any pending events to views. | |||
@@ -1609,20 +1810,115 @@ puglPollEvents(PuglWorld* world, double timeout); | |||
@deprecated Use puglUpdate(). | |||
*/ | |||
PUGL_API | |||
PUGL_DEPRECATED_BY("puglUpdate") | |||
static inline PUGL_DEPRECATED_BY("puglUpdate") | |||
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 | |||
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 | |||
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 | |||
@@ -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 | |||
#define PUGL_STUB_H | |||