@@ -0,0 +1,50 @@ | |||||
# DISTRHO Plugin Framework (DPF) | |||||
# Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||||
# | |||||
# SPDX-License-Identifier: ISC | |||||
cmake_minimum_required(VERSION 3.7) | |||||
project(DPF) | |||||
# ensure c++11 at minimum, the parent project can override | |||||
if(NOT CMAKE_CXX_STANDARD) | |||||
set(CMAKE_CXX_STANDARD 11) | |||||
endif() | |||||
# check if we are building from this project, or are imported by another | |||||
if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | |||||
set(DPF_BUILD_FROM_HERE TRUE) | |||||
else() | |||||
set(DPF_BUILD_FROM_HERE FALSE) | |||||
endif() | |||||
option(DPF_LIBRARIES "Build the libraries" "${DPF_BUILD_FROM_HERE}") | |||||
option(DPF_EXAMPLES "Build the examples" "${DPF_BUILD_FROM_HERE}") | |||||
set(DPF_ROOT_DIR "${PROJECT_SOURCE_DIR}" CACHE INTERNAL | |||||
"Root directory of the DISTRHO Plugin Framework.") | |||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") | |||||
include(DPF-plugin) | |||||
if(DPF_LIBRARIES) | |||||
if(NOT (MSVC OR APPLE)) # TODO skip this one for now | |||||
dpf__add_dgl_cairo() | |||||
endif() | |||||
dpf__add_dgl_opengl() | |||||
endif() | |||||
if(DPF_EXAMPLES) | |||||
if(NOT (MSVC OR APPLE)) # TODO skip this one for now | |||||
add_subdirectory("examples/CairoUI") | |||||
endif() | |||||
#add_subdirectory("examples/ExternalUI") | |||||
add_subdirectory("examples/FileHandling") | |||||
add_subdirectory("examples/Info") | |||||
add_subdirectory("examples/Latency") | |||||
add_subdirectory("examples/Meters") | |||||
add_subdirectory("examples/MidiThrough") | |||||
add_subdirectory("examples/Parameters") | |||||
add_subdirectory("examples/States") | |||||
endif() |
@@ -1,5 +1,5 @@ | |||||
DISTRHO Plugin Framework (DPF) | DISTRHO Plugin Framework (DPF) | ||||
Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any purpose with | Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
or without fee is hereby granted, provided that the above copyright notice and this | or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -38,6 +38,9 @@ endif | |||||
ifneq (,$(findstring mingw,$(TARGET_MACHINE))) | ifneq (,$(findstring mingw,$(TARGET_MACHINE))) | ||||
WINDOWS=true | WINDOWS=true | ||||
endif | endif | ||||
ifneq (,$(findstring windows,$(TARGET_MACHINE))) | |||||
WINDOWS=true | |||||
endif | |||||
endif | endif | ||||
endif | endif | ||||
@@ -148,6 +151,9 @@ endif | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
# MacOS linker flags | # MacOS linker flags | ||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs | LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs | ||||
ifneq ($(SKIP_STRIPPING),true) | |||||
LINK_OPTS += -Wl,-x | |||||
endif | |||||
else | else | ||||
# Common linker flags | # Common linker flags | ||||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed | LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed | ||||
@@ -157,19 +163,13 @@ endif | |||||
endif | endif | ||||
ifeq ($(NOOPT),true) | ifeq ($(NOOPT),true) | ||||
# No CPU-specific optimization flags | |||||
# Non-CPU-specific optimization flags | |||||
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | ||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
# mingw has issues with this specific optimization | |||||
# See https://github.com/falkTX/Carla/issues/696 | |||||
BASE_OPTS += -fno-rerun-cse-after-loop | |||||
# See https://github.com/falkTX/Carla/issues/855 | |||||
# Needed for windows, see https://github.com/falkTX/Carla/issues/855 | |||||
BASE_OPTS += -mstackrealign | BASE_OPTS += -mstackrealign | ||||
ifeq ($(BUILDING_FOR_WINDOWS),true) | |||||
BASE_FLAGS += -DBUILDING_CARLA_FOR_WINDOWS | |||||
endif | |||||
else | else | ||||
# Not needed for Windows | # Not needed for Windows | ||||
BASE_FLAGS += -fPIC -DPIC | BASE_FLAGS += -fPIC -DPIC | ||||
@@ -206,7 +206,7 @@ endif | |||||
ifeq ($(TESTBUILD),true) | ifeq ($(TESTBUILD),true) | ||||
BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings | BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings | ||||
BASE_FLAGS += -Wpointer-arith -Wabi -Winit-self -Wuninitialized -Wstrict-overflow=5 | |||||
BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5 | |||||
# BASE_FLAGS += -Wfloat-equal | # BASE_FLAGS += -Wfloat-equal | ||||
ifeq ($(CC),clang) | ifeq ($(CC),clang) | ||||
BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command | BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command | ||||
@@ -228,20 +228,28 @@ endif | |||||
# Check for required libraries | # Check for required libraries | ||||
HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) | ||||
HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) | |||||
ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
ifeq ($(MACOS_OR_WINDOWS),true) | |||||
HAVE_OPENGL = true | HAVE_OPENGL = true | ||||
else | else | ||||
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | ||||
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) | |||||
ifneq ($(HAIKU),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 | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Check for optional libraries | # Check for optional libraries | ||||
HAVE_JACK = $(shell $(PKG_CONFIG) --exists jack && echo true) | |||||
HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | ||||
# backwards compat | |||||
HAVE_JACK = true | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set Generic DGL stuff | # Set Generic DGL stuff | ||||
@@ -250,7 +258,7 @@ DGL_SYSTEM_LIBS += -lbe | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
DGL_SYSTEM_LIBS += -framework Cocoa | |||||
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo | |||||
endif | endif | ||||
ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
@@ -259,8 +267,21 @@ endif | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | ||||
ifeq ($(HAVE_X11),true) | ifeq ($(HAVE_X11),true) | ||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) | |||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 | |||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) | DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) | ||||
ifeq ($(HAVE_XCURSOR),true) | |||||
# TODO -DHAVE_XCURSOR | |||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor) | |||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xcursor) | |||||
endif | |||||
ifeq ($(HAVE_XEXT),true) | |||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xext) -DHAVE_XEXT -DHAVE_XSYNC | |||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xext) | |||||
endif | |||||
ifeq ($(HAVE_XRANDR),true) | |||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR | |||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) | |||||
endif | |||||
endif | endif | ||||
endif | endif | ||||
@@ -291,7 +312,7 @@ OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl) | |||||
endif | endif | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 | |||||
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations | |||||
OPENGL_LIBS = -framework OpenGL | OPENGL_LIBS = -framework OpenGL | ||||
endif | endif | ||||
@@ -308,6 +329,52 @@ HAVE_CAIRO_OR_OPENGL = true | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set Stub specific stuff | |||||
ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
HAVE_STUB = true | |||||
else | |||||
HAVE_STUB = $(HAVE_X11) | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set Vulkan specific stuff | |||||
ifeq ($(HAVE_VULKAN),true) | |||||
DGL_FLAGS += -DHAVE_VULKAN | |||||
VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan) | |||||
VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan) | |||||
ifneq ($(WINDOWS),true) | |||||
VULKAN_LIBS += -ldl | |||||
endif | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set optional libraries specific stuff | |||||
ifeq ($(HAVE_LIBLO),true) | |||||
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo) | |||||
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo) | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Backwards-compatible HAVE_DGL | |||||
ifeq ($(MACOS_OR_WINDOWS),true) | |||||
HAVE_DGL = true | |||||
else ifeq ($(HAVE_OPENGL),true) | |||||
ifeq ($(HAIKU),true) | |||||
HAVE_DGL = true | |||||
else | |||||
HAVE_DGL = $(HAVE_X11) | |||||
endif | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set app extension | # Set app extension | ||||
@@ -7,37 +7,62 @@ | |||||
# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | ||||
ifeq ($(DPF_CUSTOM_PATH),) | |||||
ifeq (,$(wildcard ../../Makefile.base.mk)) | ifeq (,$(wildcard ../../Makefile.base.mk)) | ||||
DPF_PATH=../../dpf | DPF_PATH=../../dpf | ||||
else | else | ||||
DPF_PATH=../.. | DPF_PATH=../.. | ||||
endif | endif | ||||
else | |||||
DPF_PATH = $(DPF_CUSTOM_PATH) | |||||
endif | |||||
include $(DPF_PATH)/Makefile.base.mk | include $(DPF_PATH)/Makefile.base.mk | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Basic setup | # Basic setup | ||||
ifeq ($(DPF_CUSTOM_PATH),) | |||||
TARGET_DIR = ../../bin | TARGET_DIR = ../../bin | ||||
BUILD_DIR = ../../build/$(NAME) | BUILD_DIR = ../../build/$(NAME) | ||||
else | |||||
ifeq ($(DPF_CUSTOM_TARGET_DIR),) | |||||
$(error DPF_CUSTOM_TARGET_DIR is not set) | |||||
else | |||||
TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR) | |||||
endif | |||||
ifeq ($(DPF_CUSTOM_BUILD_DIR),) | |||||
$(error DPF_CUSTOM_BUILD_DIR is not set) | |||||
else | |||||
BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR) | |||||
endif | |||||
endif | |||||
BUILD_C_FLAGS += -I. | BUILD_C_FLAGS += -I. | ||||
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | ||||
ifeq ($(HAVE_CAIRO),true) | |||||
DGL_FLAGS += -DHAVE_CAIRO | |||||
ifeq ($(HAVE_LIBLO),true) | |||||
BASE_FLAGS += -DHAVE_LIBLO | |||||
endif | endif | ||||
ifeq ($(HAVE_OPENGL),true) | |||||
DGL_FLAGS += -DHAVE_OPENGL | |||||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||||
JACK_LIBS = -ldl | |||||
endif | endif | ||||
ifeq ($(HAVE_JACK),true) | |||||
# backwards compat | |||||
BASE_FLAGS += -DHAVE_JACK | BASE_FLAGS += -DHAVE_JACK | ||||
endif | |||||
ifeq ($(HAVE_LIBLO),true) | |||||
BASE_FLAGS += -DHAVE_LIBLO | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html | |||||
ifeq ($(LINUX),true) | |||||
VST3_FILENAME = $(TARGET_PROCESSOR)-linux/$(NAME).so | |||||
endif | |||||
ifeq ($(MACOS),true) | |||||
VST3_FILENAME = MacOS/$(NAME) | |||||
endif | |||||
ifeq ($(WINDOWS),true) | |||||
VST3_FILENAME = $(TARGET_PROCESSOR)-win/$(NAME).vst3 | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -46,6 +71,10 @@ endif | |||||
OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) | OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) | ||||
OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) | OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) | ||||
ifeq ($(MACOS),true) | |||||
OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set plugin binary file targets | # Set plugin binary file targets | ||||
@@ -56,12 +85,28 @@ dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) | |||||
lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) | lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) | ||||
lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) | lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) | ||||
lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) | lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) | ||||
vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) | |||||
vst2 = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) | |||||
ifneq ($(VST3_FILENAME),) | |||||
vst3 = $(TARGET_DIR)/$(NAME).vst3/Contents/$(VST3_FILENAME) | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# Set plugin symbols to export | |||||
ifeq ($(MACOS),true) | |||||
SYMBOLS_LADSPA = -Wl,-exported_symbol,_ladspa_descriptor | |||||
SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_dssi_descriptor | |||||
SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl | |||||
SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor | |||||
SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain | |||||
SYMBOLS_VST3 = -Wl,-exported_symbol,_GetPluginFactory -Wl,-exported_symbol,_bundleEntry -Wl,-exported_symbol,_bundleExit | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Handle UI stuff, disable UI support automatically | # Handle UI stuff, disable UI support automatically | ||||
ifeq ($(FILES_UI),) | ifeq ($(FILES_UI),) | ||||
HAVE_DGL = false | |||||
UI_TYPE = none | UI_TYPE = none | ||||
endif | endif | ||||
@@ -69,9 +114,17 @@ ifeq ($(UI_TYPE),) | |||||
UI_TYPE = opengl | UI_TYPE = opengl | ||||
endif | endif | ||||
ifeq ($(UI_TYPE),generic) | |||||
ifeq ($(HAVE_OPENGL),true) | |||||
UI_TYPE = opengl | |||||
else ifeq ($(HAVE_CAIRO),true) | |||||
UI_TYPE = cairo | |||||
endif | |||||
endif | |||||
ifeq ($(UI_TYPE),cairo) | ifeq ($(UI_TYPE),cairo) | ||||
DGL_FLAGS += -DDGL_CAIRO | |||||
ifeq ($(HAVE_CAIRO),true) | ifeq ($(HAVE_CAIRO),true) | ||||
DGL_FLAGS += -DDGL_CAIRO | |||||
DGL_FLAGS += $(CAIRO_FLAGS) | DGL_FLAGS += $(CAIRO_FLAGS) | ||||
DGL_LIBS += $(CAIRO_LIBS) | DGL_LIBS += $(CAIRO_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a | DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a | ||||
@@ -82,8 +135,8 @@ endif | |||||
endif | endif | ||||
ifeq ($(UI_TYPE),opengl) | ifeq ($(UI_TYPE),opengl) | ||||
DGL_FLAGS += -DDGL_OPENGL | |||||
ifeq ($(HAVE_OPENGL),true) | ifeq ($(HAVE_OPENGL),true) | ||||
DGL_FLAGS += -DDGL_OPENGL | |||||
DGL_FLAGS += $(OPENGL_FLAGS) | DGL_FLAGS += $(OPENGL_FLAGS) | ||||
DGL_LIBS += $(OPENGL_LIBS) | DGL_LIBS += $(OPENGL_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a | DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a | ||||
@@ -93,11 +146,32 @@ HAVE_DGL = false | |||||
endif | endif | ||||
endif | endif | ||||
ifeq ($(UI_TYPE),vulkan) | |||||
ifeq ($(HAVE_VULKAN),true) | |||||
DGL_FLAGS += -DDGL_VULKAN | |||||
DGL_FLAGS += $(VULKAN_FLAGS) | |||||
DGL_LIBS += $(VULKAN_LIBS) | |||||
DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a | |||||
HAVE_DGL = true | |||||
else | |||||
HAVE_DGL = false | |||||
endif | |||||
endif | |||||
ifeq ($(UI_TYPE),external) | ifeq ($(UI_TYPE),external) | ||||
DGL_FLAGS += -DDGL_EXTERNAL | DGL_FLAGS += -DDGL_EXTERNAL | ||||
HAVE_DGL = true | HAVE_DGL = true | ||||
endif | endif | ||||
ifeq ($(UI_TYPE),stub) | |||||
ifeq ($(HAVE_STUB),true) | |||||
DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a | |||||
HAVE_DGL = true | |||||
else | |||||
HAVE_DGL = false | |||||
endif | |||||
endif | |||||
DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm | DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm | ||||
ifneq ($(HAVE_DGL),true) | ifneq ($(HAVE_DGL),true) | ||||
@@ -107,6 +181,10 @@ DGL_LIBS = | |||||
OBJS_UI = | OBJS_UI = | ||||
endif | endif | ||||
ifneq ($(HAVE_LIBLO),true) | |||||
dssi_ui = | |||||
endif | |||||
# TODO split dsp and ui object build flags | # TODO split dsp and ui object build flags | ||||
BASE_FLAGS += $(DGL_FLAGS) | BASE_FLAGS += $(DGL_FLAGS) | ||||
@@ -118,6 +196,11 @@ all: | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Common | # Common | ||||
$(BUILD_DIR)/%.S.o: %.S | |||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
@echo "Compiling $<" | |||||
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||||
$(BUILD_DIR)/%.c.o: %.c | $(BUILD_DIR)/%.c.o: %.c | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | ||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
@@ -138,6 +221,23 @@ clean: | |||||
rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 | rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# DGL | |||||
$(DPF_PATH)/build/libdgl-cairo.a: | |||||
$(MAKE) -C $(DPF_PATH)/dgl cairo | |||||
$(DPF_PATH)/build/libdgl-opengl.a: | |||||
$(MAKE) -C $(DPF_PATH)/dgl opengl | |||||
$(DPF_PATH)/build/libdgl-stub.a: | |||||
$(MAKE) -C $(DPF_PATH)/dgl stub | |||||
$(DPF_PATH)/build/libdgl-vulkan.a: | |||||
$(MAKE) -C $(DPF_PATH)/dgl vulkan | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
AS_PUGL_NAMESPACE = $(subst -,_,$(1)) | |||||
$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@@ -149,15 +249,20 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | |||||
@echo "Compiling DistrhoUIMain.cpp ($*)" | @echo "Compiling DistrhoUIMain.cpp ($*)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ | ||||
$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm | |||||
-@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 $@ | |||||
$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoPluginMain.cpp (JACK)" | @echo "Compiling DistrhoPluginMain.cpp (JACK)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags jack) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@ | |||||
$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | ||||
-@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUIMain.cpp (DSSI)" | @echo "Compiling DistrhoUIMain.cpp (DSSI)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(shell $(PKG_CONFIG) --cflags liblo) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# JACK | # JACK | ||||
@@ -171,7 +276,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating JACK standalone for $(NAME)" | @echo "Creating JACK standalone for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs jack) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(JACK_LIBS) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# LADSPA | # LADSPA | ||||
@@ -181,7 +286,7 @@ ladspa: $(ladspa_dsp) | |||||
$(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o | $(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LADSPA plugin for $(NAME)" | @echo "Creating LADSPA plugin for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# DSSI | # DSSI | ||||
@@ -193,12 +298,12 @@ dssi_ui: $(dssi_ui) | |||||
$(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o | $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating DSSI plugin library for $(NAME)" | @echo "Creating DSSI plugin library for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@ | |||||
$(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) | $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating DSSI UI for $(NAME)" | @echo "Creating DSSI UI for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs liblo) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# LV2 | # LV2 | ||||
@@ -210,36 +315,46 @@ lv2_sep: $(lv2_dsp) $(lv2_ui) | |||||
$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin for $(NAME)" | @echo "Creating LV2 plugin for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) $(SYMBOLS_LV2UI) -o $@ | |||||
$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin library for $(NAME)" | @echo "Creating LV2 plugin library for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2) -o $@ | |||||
$(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin UI for $(NAME)" | @echo "Creating LV2 plugin UI for $(NAME)" | ||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# VST | |||||
# VST2 | |||||
vst: $(vst) | |||||
vst2 vst: $(vst2) | |||||
ifeq ($(HAVE_DGL),true) | ifeq ($(HAVE_DGL),true) | ||||
$(vst): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST.cpp.o $(DGL_LIB) | |||||
$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) | |||||
else | else | ||||
$(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o | |||||
$(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating VST plugin for $(NAME)" | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ | |||||
@echo "Creating VST2 plugin for $(NAME)" | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
# VST3 | |||||
vst3: $(vst3) | |||||
$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o | |||||
-@mkdir -p $(shell dirname $@) | |||||
@echo "Creating VST3 plugin for $(NAME)" | |||||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
-include $(OBJS_DSP:%.o=%.d) | -include $(OBJS_DSP:%.o=%.d) | ||||
ifeq ($(HAVE_DGL),true) | |||||
ifneq ($(UI_TYPE),) | |||||
-include $(OBJS_UI:%.o=%.d) | -include $(OBJS_UI:%.o=%.d) | ||||
endif | endif | ||||
@@ -247,11 +362,13 @@ endif | |||||
-include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d | -include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d | -include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d | -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.d | |||||
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | |||||
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d | |||||
-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d | -include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d | ||||
-include $(BUILD_DIR)/DistrhoUIMain_VST.cpp.d | |||||
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | |||||
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- |
@@ -45,12 +45,14 @@ Online help and discussion about DPF happens in the [KXStudio chat DPF room](htt | |||||
- [Stone Phaser](https://github.com/jpcima/stone-phaser) | - [Stone Phaser](https://github.com/jpcima/stone-phaser) | ||||
- [String-machine](https://github.com/jpcima/string-machine) | - [String-machine](https://github.com/jpcima/string-machine) | ||||
- [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins) | - [Uhhyou Plugins](https://github.com/ryukau/LV2Plugins) | ||||
- [VL1-emulator](https://github.com/linuxmao-org/VL1-emulator) | |||||
- [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper) | - [Wolf Shaper](https://github.com/pdesaulniers/wolf-shaper) | ||||
- [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | - [Wolf Spectrum](https://github.com/pdesaulniers/wolf-spectrum) | ||||
- [YK Chorus](https://github.com/SpotlightKid/ykchorus) | - [YK Chorus](https://github.com/SpotlightKid/ykchorus) | ||||
- [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | - [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | ||||
## Work in progress | ## Work in progress | ||||
- [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2) | - [CV-LFO-blender-LV2](https://github.com/BramGiesen/cv-lfo-blender-lv2) | ||||
- [fverb](https://github.com/jpcima/fverb) | |||||
- [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) | - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) | ||||
- [gunshot](https://github.com/soerenbnoergaard/gunshot) | - [gunshot](https://github.com/soerenbnoergaard/gunshot) | ||||
- [midiomatic](https://github.com/SpotlightKid/midiomatic) | - [midiomatic](https://github.com/SpotlightKid/midiomatic) | ||||
@@ -0,0 +1,660 @@ | |||||
# DISTRHO Plugin Framework (DPF) | |||||
# Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||||
# | |||||
# SPDX-License-Identifier: ISC | |||||
# ------------------------------------------------------------------------------ | |||||
# CMake support module for the DISTRHO Plugin Framework | |||||
# | |||||
# The purpose of this module is to help building music plugins easily, when the | |||||
# project uses CMake as its build system. | |||||
# | |||||
# In order to use the helpers provided by this module, a plugin author should | |||||
# add DPF as a subproject, making the function `dpf_add_plugin` available. | |||||
# The usage of this function is documented below in greater detail. | |||||
# | |||||
# Example project `CMakeLists.txt`: | |||||
# | |||||
# ``` | |||||
# cmake_minimum_required(VERSION 3.7) | |||||
# project(MyPlugin) | |||||
# | |||||
# add_subdirectory(DPF) | |||||
# | |||||
# dpf_add_plugin(MyPlugin | |||||
# TARGETS lv2 vst2 | |||||
# UI_TYPE opengl | |||||
# FILES_DSP | |||||
# src/MyPlugin.cpp | |||||
# FILES_UI | |||||
# src/MyUI.cpp) | |||||
# | |||||
# target_include_directories(MyPlugin | |||||
# PUBLIC src) | |||||
# ``` | |||||
# | |||||
# Important: note that properties, such as include directories, definitions, | |||||
# and linked libraries *must* be marked with `PUBLIC` so they take effect and | |||||
# propagate into all the plugin targets. | |||||
include(CMakeParseArguments) | |||||
# ------------------------------------------------------------------------------ | |||||
# DPF public functions | |||||
# ------------------------------------------------------------------------------ | |||||
# dpf_add_plugin(name <args...>) | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add a plugin built using the DISTRHO Plugin Framework. | |||||
# | |||||
# ------------------------------------------------------------------------------ | |||||
# Created targets: | |||||
# | |||||
# `<name>` | |||||
# static library: the common part of the plugin | |||||
# The public properties set on this target apply to both DSP and UI. | |||||
# | |||||
# `<name>-dsp` | |||||
# static library: the DSP part of the plugin | |||||
# The public properties set on this target apply to the DSP only. | |||||
# | |||||
# `<name>-ui` | |||||
# static library: the UI part of the plugin | |||||
# The public properties set on this target apply to the UI only. | |||||
# | |||||
# `<name>-<target>` for each target specified with the `TARGETS` argument. | |||||
# This is target-dependent and not intended for public use. | |||||
# | |||||
# ------------------------------------------------------------------------------ | |||||
# Arguments: | |||||
# | |||||
# `TARGETS` <tgt1>...<tgtN> | |||||
# a list of one of more of the following target types: | |||||
# `jack`, `ladspa`, `dssi`, `lv2`, `vst2` | |||||
# | |||||
# `UI_TYPE` <type> | |||||
# the user interface type: `opengl` (default), `cairo` | |||||
# | |||||
# `MONOLITHIC` | |||||
# build LV2 as a single binary for UI and DSP | |||||
# | |||||
# `FILES_DSP` <file1>...<fileN> | |||||
# list of sources which are part of the DSP | |||||
# | |||||
# `FILES_UI` <file1>...<fileN> | |||||
# list of sources which are part of the UI | |||||
# empty indicates the plugin does not have UI | |||||
# | |||||
# `FILES_COMMON` <file1>...<fileN> | |||||
# list of sources which are part of both DSP and UI | |||||
# | |||||
function(dpf_add_plugin NAME) | |||||
set(options MONOLITHIC) | |||||
set(oneValueArgs UI_TYPE) | |||||
set(multiValueArgs TARGETS FILES_DSP FILES_UI FILES_COMMON) | |||||
cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | |||||
if("${_dpf_plugin_UI_TYPE}" STREQUAL "") | |||||
set(_dpf_plugin_UI_TYPE "opengl") | |||||
endif() | |||||
set(_dgl_library) | |||||
if(_dpf_plugin_FILES_UI) | |||||
if(_dpf_plugin_UI_TYPE STREQUAL "cairo") | |||||
dpf__add_dgl_cairo() | |||||
set(_dgl_library dgl-cairo) | |||||
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl") | |||||
dpf__add_dgl_opengl() | |||||
set(_dgl_library dgl-opengl) | |||||
else() | |||||
message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}") | |||||
endif() | |||||
endif() | |||||
### | |||||
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_COMMON) | |||||
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP) | |||||
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_UI) | |||||
### | |||||
dpf__add_static_library("${NAME}" ${_dpf_plugin_FILES_COMMON}) | |||||
target_include_directories("${NAME}" PUBLIC | |||||
"${DPF_ROOT_DIR}/distrho") | |||||
if(_dgl_library) | |||||
# make sure that all code will see DGL_* definitions | |||||
target_link_libraries("${NAME}" PUBLIC | |||||
"${_dgl_library}-definitions" | |||||
dgl-system-libs-definitions) | |||||
endif() | |||||
dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP}) | |||||
target_link_libraries("${NAME}-dsp" PUBLIC "${NAME}") | |||||
if(_dgl_library) | |||||
dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI}) | |||||
target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library}) | |||||
# add the files containing Objective-C classes, recompiled under namespace | |||||
dpf__add_plugin_specific_ui_sources("${NAME}-ui") | |||||
else() | |||||
add_library("${NAME}-ui" INTERFACE) | |||||
endif() | |||||
### | |||||
foreach(_target ${_dpf_plugin_TARGETS}) | |||||
if(_target STREQUAL "jack") | |||||
dpf__build_jack("${NAME}" "${_dgl_library}") | |||||
elseif(_target STREQUAL "ladspa") | |||||
dpf__build_ladspa("${NAME}") | |||||
elseif(_target STREQUAL "dssi") | |||||
dpf__build_dssi("${NAME}" "${_dgl_library}") | |||||
elseif(_target STREQUAL "lv2") | |||||
dpf__build_lv2("${NAME}" "${_dgl_library}" "${_dpf_plugin_MONOLITHIC}") | |||||
elseif(_target STREQUAL "vst2") | |||||
dpf__build_vst2("${NAME}" "${_dgl_library}") | |||||
else() | |||||
message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") | |||||
endif() | |||||
endforeach() | |||||
endfunction() | |||||
# ------------------------------------------------------------------------------ | |||||
# DPF private functions (prefixed with `dpf__`) | |||||
# ------------------------------------------------------------------------------ | |||||
# Note: The $<0:> trick is to prevent MSVC from appending the build type | |||||
# to the output directory. | |||||
# | |||||
# dpf__build_jack | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add build rules for a JACK program. | |||||
# | |||||
function(dpf__build_jack NAME DGL_LIBRARY) | |||||
dpf__create_dummy_source_list(_no_srcs) | |||||
dpf__add_executable("${NAME}-jack" ${_no_srcs}) | |||||
dpf__add_plugin_main("${NAME}-jack" "jack") | |||||
dpf__add_ui_main("${NAME}-jack" "jack" "${DGL_LIBRARY}") | |||||
target_link_libraries("${NAME}-jack" PRIVATE "${NAME}-dsp" "${NAME}-ui") | |||||
set_target_properties("${NAME}-jack" PROPERTIES | |||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | |||||
OUTPUT_NAME "${NAME}") | |||||
# Note: libjack will be linked at runtime | |||||
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | |||||
target_link_libraries("${NAME}-jack" PRIVATE "dl") | |||||
endif() | |||||
endfunction() | |||||
# dpf__build_ladspa | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add build rules for a DSSI plugin. | |||||
# | |||||
function(dpf__build_ladspa NAME) | |||||
dpf__create_dummy_source_list(_no_srcs) | |||||
dpf__add_module("${NAME}-ladspa" ${_no_srcs}) | |||||
dpf__add_plugin_main("${NAME}-ladspa" "ladspa") | |||||
target_link_libraries("${NAME}-ladspa" PRIVATE "${NAME}-dsp") | |||||
set_target_properties("${NAME}-ladspa" PROPERTIES | |||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | |||||
OUTPUT_NAME "${NAME}-ladspa" | |||||
PREFIX "") | |||||
endfunction() | |||||
# dpf__build_dssi | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add build rules for a DSSI plugin. | |||||
# | |||||
function(dpf__build_dssi NAME DGL_LIBRARY) | |||||
find_package(PkgConfig) | |||||
pkg_check_modules(LIBLO "liblo") | |||||
if(NOT LIBLO_FOUND) | |||||
dpf__warn_once_only(missing_liblo | |||||
"liblo is not found, skipping the `dssi` plugin targets") | |||||
return() | |||||
endif() | |||||
link_directories(${LIBLO_LIBRARY_DIRS}) | |||||
dpf__create_dummy_source_list(_no_srcs) | |||||
dpf__add_module("${NAME}-dssi" ${_no_srcs}) | |||||
dpf__add_plugin_main("${NAME}-dssi" "dssi") | |||||
target_link_libraries("${NAME}-dssi" PRIVATE "${NAME}-dsp") | |||||
set_target_properties("${NAME}-dssi" PROPERTIES | |||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | |||||
OUTPUT_NAME "${NAME}-dssi" | |||||
PREFIX "") | |||||
if(DGL_LIBRARY) | |||||
dpf__add_executable("${NAME}-dssi-ui" ${_no_srcs}) | |||||
dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${DGL_LIBRARY}") | |||||
target_link_libraries("${NAME}-dssi-ui" PRIVATE "${NAME}-ui") | |||||
set_target_properties("${NAME}-dssi-ui" PROPERTIES | |||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}-dssi/$<0:>" | |||||
OUTPUT_NAME "${NAME}_ui") | |||||
target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS}) | |||||
target_link_libraries("${NAME}-dssi-ui" PRIVATE ${LIBLO_LIBRARIES}) | |||||
endif() | |||||
endfunction() | |||||
# dpf__build_lv2 | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add build rules for a LV2 plugin. | |||||
# | |||||
function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) | |||||
dpf__create_dummy_source_list(_no_srcs) | |||||
dpf__add_module("${NAME}-lv2" ${_no_srcs}) | |||||
dpf__add_plugin_main("${NAME}-lv2" "lv2") | |||||
target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-dsp") | |||||
set_target_properties("${NAME}-lv2" PROPERTIES | |||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>" | |||||
OUTPUT_NAME "${NAME}_dsp" | |||||
PREFIX "") | |||||
if(DGL_LIBRARY) | |||||
if(MONOLITHIC) | |||||
dpf__add_ui_main("${NAME}-lv2" "lv2" "${DGL_LIBRARY}") | |||||
target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-ui") | |||||
set_target_properties("${NAME}-lv2" PROPERTIES | |||||
OUTPUT_NAME "${NAME}") | |||||
else() | |||||
dpf__add_module("${NAME}-lv2-ui" ${_no_srcs}) | |||||
dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${DGL_LIBRARY}") | |||||
target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui") | |||||
set_target_properties("${NAME}-lv2-ui" PROPERTIES | |||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>" | |||||
OUTPUT_NAME "${NAME}_ui" | |||||
PREFIX "") | |||||
endif() | |||||
endif() | |||||
dpf__add_lv2_ttl_generator() | |||||
add_dependencies("${NAME}-lv2" lv2_ttl_generator) | |||||
add_custom_command(TARGET "${NAME}-lv2" POST_BUILD | |||||
COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} | |||||
"$<TARGET_FILE:lv2_ttl_generator>" | |||||
"$<TARGET_FILE:${NAME}-lv2>" | |||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2" | |||||
DEPENDS lv2_ttl_generator) | |||||
endfunction() | |||||
# dpf__build_vst2 | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add build rules for a VST2 plugin. | |||||
# | |||||
function(dpf__build_vst2 NAME DGL_LIBRARY) | |||||
dpf__create_dummy_source_list(_no_srcs) | |||||
dpf__add_module("${NAME}-vst2" ${_no_srcs}) | |||||
dpf__add_plugin_main("${NAME}-vst2" "vst2") | |||||
dpf__add_ui_main("${NAME}-vst2" "vst2" "${DGL_LIBRARY}") | |||||
target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui") | |||||
set_target_properties("${NAME}-vst2" PROPERTIES | |||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | |||||
OUTPUT_NAME "${NAME}-vst2" | |||||
PREFIX "") | |||||
endfunction() | |||||
# dpf__add_dgl_cairo | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add the Cairo variant of DGL, if not already available. | |||||
# | |||||
function(dpf__add_dgl_cairo) | |||||
if(TARGET dgl-cairo) | |||||
return() | |||||
endif() | |||||
find_package(PkgConfig) | |||||
pkg_check_modules(CAIRO "cairo" REQUIRED) | |||||
link_directories(${CAIRO_LIBRARY_DIRS}) | |||||
dpf__add_static_library(dgl-cairo STATIC | |||||
"${DPF_ROOT_DIR}/dgl/src/Application.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Color.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Resources.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Window.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Cairo.cpp") | |||||
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") | |||||
endif() | |||||
target_include_directories(dgl-cairo PUBLIC | |||||
"${DPF_ROOT_DIR}/dgl") | |||||
target_include_directories(dgl-cairo PUBLIC | |||||
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include") | |||||
dpf__add_dgl_system_libs() | |||||
target_link_libraries(dgl-cairo PRIVATE dgl-system-libs) | |||||
add_library(dgl-cairo-definitions INTERFACE) | |||||
target_compile_definitions(dgl-cairo-definitions INTERFACE "DGL_CAIRO" "HAVE_CAIRO") | |||||
target_include_directories(dgl-cairo PUBLIC ${CAIRO_INCLUDE_DIRS}) | |||||
if(MINGW) | |||||
target_link_libraries(dgl-cairo PRIVATE ${CAIRO_STATIC_LIBRARIES}) | |||||
else() | |||||
target_link_libraries(dgl-cairo PRIVATE ${CAIRO_LIBRARIES}) | |||||
endif() | |||||
target_link_libraries(dgl-cairo PRIVATE dgl-cairo-definitions) | |||||
endfunction() | |||||
# dpf__add_dgl_opengl | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Add the OpenGL variant of DGL, if not already available. | |||||
# | |||||
function(dpf__add_dgl_opengl) | |||||
if(TARGET dgl-opengl) | |||||
return() | |||||
endif() | |||||
if(NOT OpenGL_GL_PREFERENCE) | |||||
set(OpenGL_GL_PREFERENCE "LEGACY") | |||||
endif() | |||||
find_package(OpenGL REQUIRED) | |||||
dpf__add_static_library(dgl-opengl STATIC | |||||
"${DPF_ROOT_DIR}/dgl/src/Application.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Color.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Resources.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/Window.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp" | |||||
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp") | |||||
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") | |||||
endif() | |||||
target_include_directories(dgl-opengl PUBLIC | |||||
"${DPF_ROOT_DIR}/dgl") | |||||
target_include_directories(dgl-opengl PUBLIC | |||||
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include") | |||||
if(APPLE) | |||||
target_compile_definitions(dgl-opengl PUBLIC "GL_SILENCE_DEPRECATION") | |||||
endif() | |||||
dpf__add_dgl_system_libs() | |||||
target_link_libraries(dgl-opengl PRIVATE dgl-system-libs) | |||||
add_library(dgl-opengl-definitions INTERFACE) | |||||
target_compile_definitions(dgl-opengl-definitions INTERFACE "DGL_OPENGL" "HAVE_OPENGL") | |||||
target_include_directories(dgl-opengl PUBLIC "${OPENGL_INCLUDE_DIR}") | |||||
target_link_libraries(dgl-opengl PRIVATE dgl-opengl-definitions "${OPENGL_gl_LIBRARY}") | |||||
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. | |||||
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() | |||||
# dpf__add_dgl_system_libs | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Find system libraries required by DGL and add them as an interface target. | |||||
# | |||||
function(dpf__add_dgl_system_libs) | |||||
if(TARGET dgl-system-libs) | |||||
return() | |||||
endif() | |||||
add_library(dgl-system-libs INTERFACE) | |||||
add_library(dgl-system-libs-definitions INTERFACE) | |||||
if(HAIKU) | |||||
target_link_libraries(dgl-system-libs INTERFACE "be") | |||||
elseif(WIN32) | |||||
target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32") | |||||
elseif(APPLE) | |||||
find_library(APPLE_COCOA_FRAMEWORK "Cocoa") | |||||
find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo") | |||||
target_link_libraries(dgl-system-libs INTERFACE "${APPLE_COCOA_FRAMEWORK}" "${APPLE_COREVIDEO_FRAMEWORK}") | |||||
else() | |||||
find_package(X11 REQUIRED) | |||||
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_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() | |||||
endif() | |||||
if(MSVC) | |||||
file(MAKE_DIRECTORY "${DPF_ROOT_DIR}/khronos/GL") | |||||
foreach(_gl_header "glext.h") | |||||
if(NOT EXISTS "${DPF_ROOT_DIR}/khronos/GL/${_gl_header}") | |||||
file(DOWNLOAD "https://www.khronos.org/registry/OpenGL/api/GL/${_gl_header}" "${DPF_ROOT_DIR}/khronos/GL/${_gl_header}" SHOW_PROGRESS) | |||||
endif() | |||||
endforeach() | |||||
foreach(_khr_header "khrplatform.h") | |||||
if(NOT EXISTS "${DPF_ROOT_DIR}/khronos/KHR/${_khr_header}") | |||||
file(DOWNLOAD "https://www.khronos.org/registry/EGL/api/KHR/${_khr_header}" "${DPF_ROOT_DIR}/khronos/KHR/${_khr_header}" SHOW_PROGRESS) | |||||
endif() | |||||
endforeach() | |||||
target_include_directories(dgl-system-libs-definitions INTERFACE "${DPF_ROOT_DIR}/khronos") | |||||
endif() | |||||
target_link_libraries(dgl-system-libs INTERFACE dgl-system-libs-definitions) | |||||
endfunction() | |||||
# dpf__add_executable | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds an executable target, and set some default properties on the target. | |||||
# | |||||
function(dpf__add_executable NAME) | |||||
add_executable("${NAME}" ${ARGN}) | |||||
dpf__set_target_defaults("${NAME}") | |||||
if(MINGW) | |||||
target_link_libraries("${NAME}" PRIVATE "-static") | |||||
endif() | |||||
endfunction() | |||||
# dpf__add_module | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds a module target, and set some default properties on the target. | |||||
# | |||||
function(dpf__add_module NAME) | |||||
add_library("${NAME}" MODULE ${ARGN}) | |||||
dpf__set_target_defaults("${NAME}") | |||||
if(MINGW) | |||||
target_link_libraries("${NAME}" PRIVATE "-static") | |||||
endif() | |||||
endfunction() | |||||
# dpf__add_static_library | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds a static library target, and set some default properties on the target. | |||||
# | |||||
function(dpf__add_static_library NAME) | |||||
add_library("${NAME}" STATIC ${ARGN}) | |||||
dpf__set_target_defaults("${NAME}") | |||||
endfunction() | |||||
# dpf__set_target_defaults | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Set default properties which must apply to all DPF-defined targets. | |||||
# | |||||
function(dpf__set_target_defaults NAME) | |||||
set_target_properties("${NAME}" PROPERTIES | |||||
POSITION_INDEPENDENT_CODE TRUE | |||||
C_VISIBILITY_PRESET "hidden" | |||||
CXX_VISIBILITY_PRESET "hidden" | |||||
VISIBILITY_INLINES_HIDDEN TRUE) | |||||
if(WIN32) | |||||
target_compile_definitions("${NAME}" PUBLIC "NOMINMAX") | |||||
endif() | |||||
if (MINGW) | |||||
target_compile_options("${NAME}" PUBLIC "-mstackrealign") | |||||
endif() | |||||
if (MSVC) | |||||
target_compile_options("${NAME}" PUBLIC "/UTF-8") | |||||
target_compile_definitions("${NAME}" PUBLIC "_CRT_SECURE_NO_WARNINGS") | |||||
endif() | |||||
endfunction() | |||||
# dpf__add_plugin_main | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds plugin code to the given target. | |||||
# | |||||
function(dpf__add_plugin_main NAME TARGET) | |||||
target_sources("${NAME}" PRIVATE | |||||
"${DPF_ROOT_DIR}/distrho/DistrhoPluginMain.cpp") | |||||
dpf__add_plugin_target_definition("${NAME}" "${TARGET}") | |||||
endfunction() | |||||
# dpf__add_ui_main | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds UI code to the given target (only if the target has UI). | |||||
# | |||||
function(dpf__add_ui_main NAME TARGET HAS_UI) | |||||
if(HAS_UI) | |||||
target_sources("${NAME}" PRIVATE | |||||
"${DPF_ROOT_DIR}/distrho/DistrhoUIMain.cpp") | |||||
dpf__add_plugin_target_definition("${NAME}" "${TARGET}") | |||||
endif() | |||||
endfunction() | |||||
# dpf__add_plugin_target_definition | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Adds the plugins target macro definition. | |||||
# This selects which entry file is compiled according to the target type. | |||||
# | |||||
function(dpf__add_plugin_target_definition NAME TARGET) | |||||
string(TOUPPER "${TARGET}" _upperTarget) | |||||
target_compile_definitions("${NAME}" PRIVATE "DISTRHO_PLUGIN_TARGET_${_upperTarget}") | |||||
endfunction() | |||||
# dpf__add_lv2_ttl_generator | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Build the LV2 TTL generator. | |||||
# | |||||
function(dpf__add_lv2_ttl_generator) | |||||
if(TARGET lv2_ttl_generator) | |||||
return() | |||||
endif() | |||||
add_executable(lv2_ttl_generator "${DPF_ROOT_DIR}/utils/lv2-ttl-generator/lv2_ttl_generator.c") | |||||
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | |||||
target_link_libraries(lv2_ttl_generator PRIVATE "dl") | |||||
endif() | |||||
endfunction() | |||||
# dpf__ensure_sources_non_empty | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Ensure the given source list contains at least one file. | |||||
# The function appends an empty source file to the list if necessary. | |||||
# This is useful when CMake does not permit to add targets without sources. | |||||
# | |||||
function(dpf__ensure_sources_non_empty VAR) | |||||
if(NOT "" STREQUAL "${${VAR}}") | |||||
return() | |||||
endif() | |||||
set(_file "${CMAKE_CURRENT_BINARY_DIR}/_dpf_empty.c") | |||||
if(NOT EXISTS "${_file}") | |||||
file(WRITE "${_file}" "") | |||||
endif() | |||||
set("${VAR}" "${_file}" PARENT_SCOPE) | |||||
endfunction() | |||||
# dpf__create_dummy_source_list | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Create a dummy source list which is equivalent to compiling nothing. | |||||
# This is only for compatibility with older CMake versions, which refuse to add | |||||
# targets without any sources. | |||||
# | |||||
macro(dpf__create_dummy_source_list VAR) | |||||
set("${VAR}") | |||||
if(CMAKE_VERSION VERSION_LESS "3.11") | |||||
dpf__ensure_sources_non_empty("${VAR}") | |||||
endif() | |||||
endmacro() | |||||
# dpf__warn_once | |||||
# ------------------------------------------------------------------------------ | |||||
# | |||||
# Prints a warning message once only. | |||||
# | |||||
function(dpf__warn_once_only TOKEN MESSAGE) | |||||
get_property(_warned GLOBAL PROPERTY "dpf__have_warned_${TOKEN}") | |||||
if(NOT _warned) | |||||
set_property(GLOBAL PROPERTY "dpf__have_warned_${TOKEN}" TRUE) | |||||
message(WARNING "${MESSAGE}") | |||||
endif() | |||||
endfunction() |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -21,22 +21,15 @@ | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// Forward class names | |||||
class Window; | |||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
Base DGL Application class. | Base DGL Application class. | ||||
One application instance is required for creating a window. | One application instance is required for creating a window. | ||||
There's no single/global application instance in DGL, and multiple | |||||
windows can share the same app instance. | |||||
There's no single/global application instance in DGL, and multiple windows can share the same app instance. | |||||
In standalone mode an application will automatically quit its | |||||
event-loop when all its windows are closed. | |||||
In standalone mode an application will automatically quit its event-loop when all its windows are closed. | |||||
*/ | */ | ||||
class Application | class Application | ||||
{ | { | ||||
@@ -44,7 +37,8 @@ public: | |||||
/** | /** | ||||
Constructor. | Constructor. | ||||
*/ | */ | ||||
Application(); | |||||
// NOTE: the default value is not yet passed, so we catch where we use this | |||||
Application(bool isStandalone = true); | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
@@ -62,11 +56,12 @@ public: | |||||
idle() is called at regular intervals. | idle() is called at regular intervals. | ||||
@note This function is meant for standalones only, *never* call this from plugins. | @note This function is meant for standalones only, *never* call this from plugins. | ||||
*/ | */ | ||||
void exec(int idleTime = 10); | |||||
void exec(uint idleTimeInMs = 30); | |||||
/** | /** | ||||
Quit the application. | Quit the application. | ||||
This stops the event-loop and closes all Windows. | This stops the event-loop and closes all Windows. | ||||
@note This function is meant for standalones only, *never* call this from plugins. | |||||
*/ | */ | ||||
void quit(); | void quit(); | ||||
@@ -76,6 +71,30 @@ public: | |||||
*/ | */ | ||||
bool isQuiting() const noexcept; | bool isQuiting() const noexcept; | ||||
/** | |||||
Add a callback function to be triggered on every idle cycle. | |||||
You can add more than one, and remove them at anytime with removeIdleCallback(). | |||||
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle). | |||||
There are no guarantees in terms of timing. | |||||
*/ | |||||
void addIdleCallback(IdleCallback* callback); | |||||
/** | |||||
Remove an idle callback previously added via addIdleCallback(). | |||||
*/ | |||||
void removeIdleCallback(IdleCallback* callback); | |||||
/** | |||||
Set the class name of the application. | |||||
This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. | |||||
It is not displayed to the user, but can be used in scripts and by window managers, | |||||
so it should be the same for every instance of the application, but different from other applications. | |||||
Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. | |||||
*/ | |||||
void setClassName(const char* name); | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
@@ -84,7 +103,7 @@ private: | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -20,7 +20,7 @@ | |||||
#include "../distrho/extra/LeakDetector.hpp" | #include "../distrho/extra/LeakDetector.hpp" | ||||
#include "../distrho/extra/ScopedPointer.hpp" | #include "../distrho/extra/ScopedPointer.hpp" | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Define namespace | // Define namespace | ||||
#ifndef DGL_NAMESPACE | #ifndef DGL_NAMESPACE | ||||
@@ -33,33 +33,46 @@ | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Base DGL enums | // Base DGL enums | ||||
/** | |||||
Convenience symbols for ASCII control characters. | |||||
*/ | |||||
enum Char { | |||||
kCharBackspace = 0x08, | |||||
kCharEscape = 0x1B, | |||||
kCharDelete = 0x7F | |||||
}; | |||||
/** | /** | ||||
Keyboard modifier flags. | Keyboard modifier flags. | ||||
*/ | */ | ||||
enum Modifier { | enum Modifier { | ||||
kModifierShift = 1 << 0, /**< Shift key */ | |||||
kModifierControl = 1 << 1, /**< Control key */ | |||||
kModifierAlt = 1 << 2, /**< Alt/Option key */ | |||||
kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */ | |||||
kModifierShift = 1u << 0u, ///< Shift key | |||||
kModifierControl = 1u << 1u, ///< Control key | |||||
kModifierAlt = 1u << 2u, ///< Alt/Option key | |||||
kModifierSuper = 1u << 3u ///< Mod4/Command/Windows key | |||||
}; | }; | ||||
/** | /** | ||||
Special (non-Unicode) keyboard keys. | |||||
Keyboard key codepoints. | |||||
All keys are identified by a Unicode code point in PuglEventKey::key. This | |||||
enumeration defines constants for special keys that do not have a standard | |||||
code point, and some convenience constants for control characters. Note | |||||
that all keys are handled in the same way, this enumeration is just for | |||||
convenience when writing hard-coded key bindings. | |||||
Keys that do not have a standard code point use values in the Private Use | |||||
Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications | |||||
must take care to not interpret these values beyond key detection, the | |||||
mapping used here is arbitrary and specific to DPF. | |||||
*/ | */ | ||||
enum Key { | enum Key { | ||||
kKeyF1 = 1, | |||||
// Convenience symbols for ASCII control characters | |||||
kKeyBackspace = 0x08, | |||||
kKeyEscape = 0x1B, | |||||
kKeyDelete = 0x7F, | |||||
// Backwards compatibility with old DPF | |||||
kCharBackspace DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace, | |||||
kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape, | |||||
kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete, | |||||
// Unicode Private Use Area | |||||
kKeyF1 = 0xE000, | |||||
kKeyF2, | kKeyF2, | ||||
kKeyF3, | kKeyF3, | ||||
kKeyF4, | kKeyF4, | ||||
@@ -81,30 +94,75 @@ enum Key { | |||||
kKeyEnd, | kKeyEnd, | ||||
kKeyInsert, | kKeyInsert, | ||||
kKeyShift, | kKeyShift, | ||||
kKeyShiftL = kKeyShift, | |||||
kKeyShiftR, | |||||
kKeyControl, | kKeyControl, | ||||
kKeyControlL = kKeyControl, | |||||
kKeyControlR, | |||||
kKeyAlt, | kKeyAlt, | ||||
kKeySuper | |||||
kKeyAltL = kKeyAlt, | |||||
kKeyAltR, | |||||
kKeySuper, | |||||
kKeySuperL = kKeySuper, | |||||
kKeySuperR, | |||||
kKeyMenu, | |||||
kKeyCapsLock, | |||||
kKeyScrollLock, | |||||
kKeyNumLock, | |||||
kKeyPrintScreen, | |||||
kKeyPause | |||||
}; | |||||
/** | |||||
Common flags for all events. | |||||
*/ | |||||
enum Flag { | |||||
kFlagSendEvent = 1, ///< Event is synthetic | |||||
kFlagIsHint = 2 ///< Event is a hint (not direct user input) | |||||
}; | |||||
/** | |||||
Reason for a crossing event. | |||||
*/ | |||||
enum CrossingMode { | |||||
kCrossingNormal, ///< Crossing due to pointer motion | |||||
kCrossingGrab, ///< Crossing due to a grab | |||||
kCrossingUngrab ///< Crossing due to a grab release | |||||
}; | |||||
/** | |||||
Scroll direction. | |||||
Describes the direction of a scroll event along with whether the scroll is a "smooth" scroll. | |||||
The discrete directions are for devices like mouse wheels with constrained axes, | |||||
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 | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Base DGL classes | // Base DGL classes | ||||
/** | /** | ||||
Graphics context, definition depends on build type. | Graphics context, definition depends on build type. | ||||
*/ | */ | ||||
struct GraphicsContext; | |||||
struct GraphicsContext {}; | |||||
/** | /** | ||||
Idle callback. | Idle callback. | ||||
*/ | */ | ||||
class IdleCallback | |||||
struct IdleCallback | |||||
{ | { | ||||
public: | |||||
virtual ~IdleCallback() {} | virtual ~IdleCallback() {} | ||||
virtual void idleCallback() = 0; | virtual void idleCallback() = 0; | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -114,6 +172,6 @@ END_NAMESPACE_DGL | |||||
using namespace DGL_NAMESPACE; | using namespace DGL_NAMESPACE; | ||||
#endif | #endif | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
#endif // DGL_BASE_HPP_INCLUDED | #endif // DGL_BASE_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,23 +17,176 @@ | |||||
#ifndef DGL_CAIRO_HPP_INCLUDED | #ifndef DGL_CAIRO_HPP_INCLUDED | ||||
#define DGL_CAIRO_HPP_INCLUDED | #define DGL_CAIRO_HPP_INCLUDED | ||||
#include "Base.hpp" | |||||
#include "ImageBase.hpp" | |||||
#include "ImageBaseWidgets.hpp" | |||||
#include <cairo/cairo.h> | #include <cairo/cairo.h> | ||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
Graphics context. | |||||
Cairo Graphics context. | |||||
*/ | */ | ||||
struct GraphicsContext | |||||
struct CairoGraphicsContext : GraphicsContext | |||||
{ | { | ||||
cairo_t* cairo; // FIXME proper name.. | |||||
cairo_t* handle; | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | |||||
Cairo Image class. | |||||
TODO ... | |||||
*/ | |||||
class CairoImage : public ImageBase | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor for a null Image. | |||||
*/ | |||||
CairoImage(); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
CairoImage(const char* rawData, uint width, uint height, ImageFormat format); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
CairoImage(const char* rawData, const Size<uint>& size, ImageFormat format); | |||||
/** | |||||
Constructor using another image data. | |||||
*/ | |||||
CairoImage(const CairoImage& image); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
~CairoImage() override; | |||||
/** | |||||
Load raw image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* rawData, | |||||
const Size<uint>& size, | |||||
ImageFormat format = kImageFormatBGRA) noexcept override; | |||||
/** | |||||
Load PNG image from memory. | |||||
Image size is read from PNG contents. | |||||
@note @a pngData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromPNG(const char* pngData, uint dataSize) noexcept; | |||||
/** | |||||
Draw this image at position @a pos using the graphics context @a context. | |||||
*/ | |||||
void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | |||||
/** | |||||
Get the cairo surface currently associated with this image. | |||||
FIXME might be removed | |||||
*/ | |||||
inline cairo_surface_t* getSurface() const noexcept | |||||
{ | |||||
return surface; | |||||
} | |||||
/** | |||||
TODO document this. | |||||
*/ | |||||
CairoImage& operator=(const CairoImage& image) noexcept; | |||||
// 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); }; | |||||
inline void draw(const GraphicsContext& context) | |||||
{ drawAt(context, Point<int>(0, 0)); }; | |||||
inline void drawAt(const GraphicsContext& context, int x, int y) | |||||
{ drawAt(context, Point<int>(x, y)); }; | |||||
private: | |||||
cairo_surface_t* surface; | |||||
uchar* surfacedata; | |||||
int* datarefcount; | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | |||||
CairoWidget, handy class that takes graphics context during onDisplay and passes it in a new function. | |||||
*/ | |||||
template <class BaseWidget> | |||||
class CairoBaseWidget : public BaseWidget | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor for a CairoSubWidget. | |||||
*/ | |||||
explicit CairoBaseWidget(Widget* const parentGroupWidget); | |||||
/** | |||||
Constructor for a CairoTopLevelWidget. | |||||
*/ | |||||
explicit CairoBaseWidget(Window& windowToMapTo); | |||||
/** | |||||
Constructor for a CairoStandaloneWindow without parent window. | |||||
*/ | |||||
explicit CairoBaseWidget(Application& app); | |||||
/** | |||||
Constructor for a CairoStandaloneWindow with parent window. | |||||
*/ | |||||
explicit CairoBaseWidget(Application& app, Window& parentWindow); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
virtual ~CairoBaseWidget() {} | |||||
protected: | |||||
/** | |||||
New virtual onDisplay function. | |||||
@see onDisplay | |||||
*/ | |||||
virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; | |||||
private: | |||||
/** | |||||
Widget display function. | |||||
Implemented internally to pass context into the drawing function. | |||||
*/ | |||||
void onDisplay() override | |||||
{ | |||||
const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext()); | |||||
onCairoDisplay(context); | |||||
} | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget); | |||||
}; | |||||
typedef CairoBaseWidget<SubWidget> CairoSubWidget; | |||||
typedef CairoBaseWidget<TopLevelWidget> CairoTopLevelWidget; | |||||
typedef CairoBaseWidget<StandaloneWindow> CairoStandaloneWindow; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
typedef ImageBaseAboutWindow<CairoImage> CairoImageAboutWindow; | |||||
typedef ImageBaseButton<CairoImage> CairoImageButton; | |||||
typedef ImageBaseKnob<CairoImage> CairoImageKnob; | |||||
typedef ImageBaseSlider<CairoImage> CairoImageSlider; | |||||
typedef ImageBaseSwitch<CairoImage> CairoImageSwitch; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -23,7 +23,7 @@ struct NVGcolor; | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
A color made from red, green, blue and alpha floating-point values in [0..1] range. | A color made from red, green, blue and alpha floating-point values in [0..1] range. | ||||
@@ -44,13 +44,13 @@ struct Color { | |||||
/** | /** | ||||
Create a color from red, green, blue and alpha numeric values. | Create a color from red, green, blue and alpha numeric values. | ||||
Values must be in [0..255] range. | |||||
All values except alpha must be in [0..255] range, with alpha in [0..1] range. | |||||
*/ | */ | ||||
Color(int red, int green, int blue, int alpha = 255) noexcept; | |||||
Color(int red, int green, int blue, float alpha = 1.0f) noexcept; | |||||
/** | /** | ||||
Create a color from red, green, blue and alpha floating-point values. | Create a color from red, green, blue and alpha floating-point values. | ||||
Values must in [0..1] range. | |||||
All values must in [0..1] range. | |||||
*/ | */ | ||||
Color(float red, float green, float blue, float alpha = 1.0f) noexcept; | Color(float red, float green, float blue, float alpha = 1.0f) noexcept; | ||||
@@ -74,7 +74,7 @@ struct Color { | |||||
/** | /** | ||||
Create a color from a HTML string like "#333" or "#112233". | Create a color from a HTML string like "#333" or "#112233". | ||||
*/ | */ | ||||
static Color fromHTML(const char* rgb, float alpha = 1.0f); | |||||
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; | |||||
/** | /** | ||||
Linearly interpolate this color against another. | Linearly interpolate this color against another. | ||||
@@ -83,7 +83,7 @@ struct Color { | |||||
/** | /** | ||||
Check if this color matches another. | Check if this color matches another. | ||||
@note Comparison is forced within 8-bit color values. | |||||
@note Comparison is done within 8-bit color space. | |||||
*/ | */ | ||||
bool isEqual(const Color& color, bool withAlpha = true) noexcept; | bool isEqual(const Color& color, bool withAlpha = true) noexcept; | ||||
bool isNotEqual(const Color& color, bool withAlpha = true) noexcept; | bool isNotEqual(const Color& color, bool withAlpha = true) noexcept; | ||||
@@ -95,6 +95,11 @@ struct Color { | |||||
*/ | */ | ||||
void fixBounds() noexcept; | void fixBounds() noexcept; | ||||
/** | |||||
Set this color for use in the next drawing operation for the provided context. | |||||
*/ | |||||
void setFor(const GraphicsContext& context, bool includeAlpha = false); | |||||
/** | /** | ||||
@internal | @internal | ||||
Needed for NanoVG compatibility. | Needed for NanoVG compatibility. | ||||
@@ -103,7 +108,7 @@ struct Color { | |||||
operator NVGcolor() const noexcept; | operator NVGcolor() const noexcept; | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -21,7 +21,7 @@ | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Forward class names | // Forward class names | ||||
template<typename> class Line; | template<typename> class Line; | ||||
@@ -29,7 +29,7 @@ template<typename> class Circle; | |||||
template<typename> class Triangle; | template<typename> class Triangle; | ||||
template<typename> class Rectangle; | template<typename> class Rectangle; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
DGL Point class. | DGL Point class. | ||||
@@ -114,14 +114,14 @@ public: | |||||
bool operator!=(const Point<T>& pos) const noexcept; | bool operator!=(const Point<T>& pos) const noexcept; | ||||
private: | private: | ||||
T fX, fY; | |||||
T x, y; | |||||
template<typename> friend class Line; | template<typename> friend class Line; | ||||
template<typename> friend class Circle; | template<typename> friend class Circle; | ||||
template<typename> friend class Triangle; | template<typename> friend class Triangle; | ||||
template<typename> friend class Rectangle; | template<typename> friend class Rectangle; | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
DGL Size class. | DGL Size class. | ||||
@@ -195,7 +195,7 @@ public: | |||||
/** | /** | ||||
Return true if size is not null (0x0). | Return true if size is not null (0x0). | ||||
A non-null size is still invalid if its width or height is negative. | |||||
A non-null size is still invalid if its width or height are negative. | |||||
*/ | */ | ||||
bool isNotNull() const noexcept; | bool isNotNull() const noexcept; | ||||
@@ -210,6 +210,8 @@ public: | |||||
*/ | */ | ||||
bool isInvalid() const noexcept; | bool isInvalid() const noexcept; | ||||
Size<int> toInt() const noexcept; | |||||
Size<T> operator+(const Size<T>& size) noexcept; | Size<T> operator+(const Size<T>& size) noexcept; | ||||
Size<T> operator-(const Size<T>& size) noexcept; | Size<T> operator-(const Size<T>& size) noexcept; | ||||
Size<T>& operator=(const Size<T>& size) noexcept; | Size<T>& operator=(const Size<T>& size) noexcept; | ||||
@@ -217,6 +219,8 @@ public: | |||||
Size<T>& operator-=(const Size<T>& size) noexcept; | Size<T>& operator-=(const Size<T>& size) noexcept; | ||||
Size<T>& operator*=(double m) noexcept; | Size<T>& operator*=(double m) noexcept; | ||||
Size<T>& operator/=(double d) noexcept; | Size<T>& operator/=(double d) noexcept; | ||||
Size<T> operator*(double m) const noexcept; | |||||
Size<T> operator/(double m) const noexcept; | |||||
bool operator==(const Size<T>& size) const noexcept; | bool operator==(const Size<T>& size) const noexcept; | ||||
bool operator!=(const Size<T>& size) const noexcept; | bool operator!=(const Size<T>& size) const noexcept; | ||||
@@ -346,11 +350,6 @@ public: | |||||
*/ | */ | ||||
void moveBy(const Point<T>& pos) noexcept; | void moveBy(const Point<T>& pos) noexcept; | ||||
/** | |||||
Draw this line using the current OpenGL state. | |||||
*/ | |||||
void draw(); | |||||
/** | /** | ||||
Return true if line is null (start and end pos are equal). | Return true if line is null (start and end pos are equal). | ||||
*/ | */ | ||||
@@ -361,12 +360,28 @@ public: | |||||
*/ | */ | ||||
bool isNotNull() const noexcept; | bool isNotNull() const noexcept; | ||||
#ifndef DPF_TEST_POINT_CPP | |||||
/** | |||||
Draw this line using the provided graphics context, optionally specifying line width. | |||||
*/ | |||||
void draw(const GraphicsContext& context, T width = 1); | |||||
#endif | |||||
Line<T>& operator=(const Line<T>& line) noexcept; | Line<T>& operator=(const Line<T>& line) noexcept; | ||||
bool operator==(const Line<T>& line) const noexcept; | bool operator==(const Line<T>& line) const noexcept; | ||||
bool operator!=(const Line<T>& line) const noexcept; | bool operator!=(const Line<T>& line) const noexcept; | ||||
#ifndef DPF_TEST_POINT_CPP | |||||
/** | |||||
Draw this line using the current OpenGL state.@n | |||||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||||
void draw(); | |||||
#endif | |||||
private: | private: | ||||
Point<T> fPosStart, fPosEnd; | |||||
Point<T> posStart, posEnd; | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -461,19 +476,35 @@ public: | |||||
void setNumSegments(const uint num); | void setNumSegments(const uint num); | ||||
/** | /** | ||||
Draw this circle using the current OpenGL state. | |||||
Draw this circle using the provided graphics context. | |||||
*/ | */ | ||||
void draw(); | |||||
void draw(const GraphicsContext& context); | |||||
/** | /** | ||||
Draw lines (outline of this circle) using the current OpenGL state. | |||||
Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width. | |||||
*/ | */ | ||||
void drawOutline(); | |||||
void drawOutline(const GraphicsContext& context, T lineWidth = 1); | |||||
Circle<T>& operator=(const Circle<T>& cir) noexcept; | Circle<T>& operator=(const Circle<T>& cir) noexcept; | ||||
bool operator==(const Circle<T>& cir) const noexcept; | bool operator==(const Circle<T>& cir) const noexcept; | ||||
bool operator!=(const Circle<T>& cir) const noexcept; | bool operator!=(const Circle<T>& cir) const noexcept; | ||||
#ifndef DPF_TEST_POINT_CPP | |||||
/** | |||||
Draw this circle using the current OpenGL state.@n | |||||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||||
void draw(); | |||||
/** | |||||
Draw lines (outline of this circle) using the current OpenGL state.@n | |||||
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||||
void drawOutline(); | |||||
#endif | |||||
private: | private: | ||||
Point<T> fPos; | Point<T> fPos; | ||||
float fSize; | float fSize; | ||||
@@ -481,9 +512,6 @@ private: | |||||
// cached values | // cached values | ||||
float fTheta, fCos, fSin; | float fTheta, fCos, fSin; | ||||
/** @internal */ | |||||
void _draw(const bool outline); | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -541,24 +569,37 @@ public: | |||||
bool isInvalid() const noexcept; | bool isInvalid() const noexcept; | ||||
/** | /** | ||||
Draw this triangle using the current OpenGL state. | |||||
Draw this triangle using the provided graphics context. | |||||
*/ | */ | ||||
void draw(); | |||||
void draw(const GraphicsContext& context); | |||||
/** | /** | ||||
Draw lines (outline of this triangle) using the current OpenGL state. | |||||
Draw lines (outline of this triangle) using the provided graphics context, optionally specifying line width. | |||||
*/ | */ | ||||
void drawOutline(); | |||||
void drawOutline(const GraphicsContext& context, T lineWidth = 1); | |||||
Triangle<T>& operator=(const Triangle<T>& tri) noexcept; | Triangle<T>& operator=(const Triangle<T>& tri) noexcept; | ||||
bool operator==(const Triangle<T>& tri) const noexcept; | bool operator==(const Triangle<T>& tri) const noexcept; | ||||
bool operator!=(const Triangle<T>& tri) const noexcept; | bool operator!=(const Triangle<T>& tri) const noexcept; | ||||
private: | |||||
Point<T> fPos1, fPos2, fPos3; | |||||
#ifndef DPF_TEST_POINT_CPP | |||||
/** | |||||
Draw this triangle using the current OpenGL state.@n | |||||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||||
void draw(); | |||||
/** @internal */ | |||||
void _draw(const bool outline); | |||||
/** | |||||
Draw lines (outline of this triangle) using the current OpenGL state.@n | |||||
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||||
void drawOutline(); | |||||
#endif | |||||
private: | |||||
Point<T> pos1, pos2, pos3; | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -712,6 +753,12 @@ public: | |||||
*/ | */ | ||||
bool contains(const Point<T>& pos) const noexcept; | bool contains(const Point<T>& pos) const noexcept; | ||||
/** | |||||
Check if this rectangle contains the point @a pos of another type. | |||||
*/ | |||||
template<typename T2> | |||||
bool contains(const Point<T2>& pos) const noexcept; | |||||
/** | /** | ||||
Check if this rectangle contains X. | Check if this rectangle contains X. | ||||
*/ | */ | ||||
@@ -723,14 +770,37 @@ public: | |||||
bool containsY(const T& y) const noexcept; | bool containsY(const T& y) const noexcept; | ||||
/** | /** | ||||
Draw this rectangle using the current OpenGL state. | |||||
Return true if size is null (0x0). | |||||
An null size is also invalid. | |||||
*/ | */ | ||||
void draw(); | |||||
bool isNull() const noexcept; | |||||
/** | /** | ||||
Draw lines (outline of this rectangle) using the current OpenGL state. | |||||
Return true if size is not null (0x0). | |||||
A non-null size is still invalid if its width or height are negative. | |||||
*/ | */ | ||||
void drawOutline(); | |||||
bool isNotNull() const noexcept; | |||||
/** | |||||
Return true if size is valid (width and height are higher than zero). | |||||
*/ | |||||
bool isValid() const noexcept; | |||||
/** | |||||
Return true if size is invalid (width or height are lower or equal to zero). | |||||
An invalid size might not be null under some circumstances. | |||||
*/ | |||||
bool isInvalid() const noexcept; | |||||
/** | |||||
Draw this rectangle using the provided graphics context. | |||||
*/ | |||||
void draw(const GraphicsContext& context); | |||||
/** | |||||
Draw lines (outline of this rectangle) using the provided graphics context, optionally specifying line width. | |||||
*/ | |||||
void drawOutline(const GraphicsContext& context, T lineWidth = 1); | |||||
Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; | Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; | ||||
Rectangle<T>& operator*=(double m) noexcept; | Rectangle<T>& operator*=(double m) noexcept; | ||||
@@ -738,12 +808,23 @@ public: | |||||
bool operator==(const Rectangle<T>& size) const noexcept; | bool operator==(const Rectangle<T>& size) const noexcept; | ||||
bool operator!=(const Rectangle<T>& size) const noexcept; | bool operator!=(const Rectangle<T>& size) const noexcept; | ||||
private: | |||||
Point<T> fPos; | |||||
Size<T> fSize; | |||||
/** | |||||
Draw this rectangle using the current OpenGL state.@n | |||||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||||
void draw(); | |||||
/** @internal */ | |||||
void _draw(const bool outline); | |||||
/** | |||||
Draw lines (outline of this rectangle) using the current OpenGL state.@n | |||||
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||||
void drawOutline(); | |||||
private: | |||||
Point<T> pos; | |||||
Size<T> size; | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,108 +17,19 @@ | |||||
#ifndef DGL_IMAGE_HPP_INCLUDED | #ifndef DGL_IMAGE_HPP_INCLUDED | ||||
#define DGL_IMAGE_HPP_INCLUDED | #define DGL_IMAGE_HPP_INCLUDED | ||||
#include "ImageBase.hpp" | |||||
#ifdef DGL_CAIRO | |||||
#include "Cairo.hpp" | |||||
#else | |||||
#include "OpenGL.hpp" | #include "OpenGL.hpp" | ||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
/** | |||||
OpenGL Image class. | |||||
This is an Image class that handles raw image data in pixels. | |||||
You can init the image data on the contructor or later on by calling loadFromMemory(). | |||||
To generate raw data useful for this class see the utils/png2rgba.py script. | |||||
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR' | |||||
instead of the default 'GL_BGRA'. | |||||
Images are drawn on screen via 2D textures. | |||||
*/ | |||||
class Image : public ImageBase | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor for a null Image. | |||||
*/ | |||||
Image(); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
Image(const char* const rawData, | |||||
const uint width, | |||||
const uint height, | |||||
const GLenum format = GL_BGRA, | |||||
const GLenum type = GL_UNSIGNED_BYTE); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
Image(const char* const rawData, | |||||
const Size<uint>& size, | |||||
const GLenum format = GL_BGRA, | |||||
const GLenum type = GL_UNSIGNED_BYTE); | |||||
/** | |||||
Constructor using another image data. | |||||
*/ | |||||
Image(const Image& image); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
~Image() override; | |||||
/** | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* const rawData, | |||||
const uint width, | |||||
const uint height, | |||||
const GLenum format = GL_BGRA, | |||||
const GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||||
/** | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* const rawData, | |||||
const Size<uint>& size, | |||||
const GLenum format = GL_BGRA, | |||||
const GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||||
/** | |||||
Get the image format. | |||||
*/ | |||||
GLenum getFormat() const noexcept; | |||||
/** | |||||
Get the image type. | |||||
*/ | |||||
GLenum getType() const noexcept; | |||||
/** | |||||
TODO document this. | |||||
*/ | |||||
Image& operator=(const Image& image) noexcept; | |||||
protected: | |||||
/** @internal */ | |||||
void _drawAt(const Point<int>& pos) override; | |||||
private: | |||||
GLenum fFormat; | |||||
GLenum fType; | |||||
GLuint fTextureId; | |||||
bool fIsReady; | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
#ifdef DGL_CAIRO | |||||
typedef CairoImage Image; | |||||
#else | |||||
typedef OpenGLImage Image; | |||||
#endif | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -21,7 +21,16 @@ | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
enum ImageFormat { | |||||
kImageFormatNull, | |||||
kImageFormatGrayscale, | |||||
kImageFormatBGR, | |||||
kImageFormatBGRA, | |||||
kImageFormatRGB, | |||||
kImageFormatRGBA, | |||||
}; | |||||
/** | /** | ||||
Base DGL Image class. | Base DGL Image class. | ||||
@@ -44,13 +53,13 @@ protected: | |||||
Constructor using raw image data. | Constructor using raw image data. | ||||
@note @a rawData must remain valid for the lifetime of this Image. | @note @a rawData must remain valid for the lifetime of this Image. | ||||
*/ | */ | ||||
ImageBase(const char* const rawData, const uint width, const uint height); | |||||
ImageBase(const char* rawData, uint width, uint height, ImageFormat format); | |||||
/** | /** | ||||
Constructor using raw image data. | Constructor using raw image data. | ||||
@note @a rawData must remain valid for the lifetime of this Image. | @note @a rawData must remain valid for the lifetime of this Image. | ||||
*/ | */ | ||||
ImageBase(const char* const rawData, const Size<uint>& size); | |||||
ImageBase(const char* rawData, const Size<uint>& size, ImageFormat format); | |||||
/** | /** | ||||
Constructor using another image data. | Constructor using another image data. | ||||
@@ -68,6 +77,11 @@ public: | |||||
*/ | */ | ||||
bool isValid() const noexcept; | bool isValid() const noexcept; | ||||
/** | |||||
Check if this image is not valid. | |||||
*/ | |||||
bool isInvalid() const noexcept; | |||||
/** | /** | ||||
Get width. | Get width. | ||||
*/ | */ | ||||
@@ -89,19 +103,38 @@ public: | |||||
const char* getRawData() const noexcept; | const char* getRawData() const noexcept; | ||||
/** | /** | ||||
Draw this image at (0, 0) point. | |||||
Get the image format. | |||||
*/ | */ | ||||
void draw(); | |||||
ImageFormat getFormat() const noexcept; | |||||
/** | /** | ||||
Draw this image at (x, y) point. | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | */ | ||||
void drawAt(const int x, const int y); | |||||
void loadFromMemory(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA) noexcept; | |||||
/** | /** | ||||
Draw this image at position @a pos. | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | */ | ||||
void drawAt(const Point<int>& pos); | |||||
virtual void loadFromMemory(const char* rawData, | |||||
const Size<uint>& size, | |||||
ImageFormat format = kImageFormatBGRA) noexcept; | |||||
/** | |||||
Draw this image at (0, 0) point using the current OpenGL context. | |||||
*/ | |||||
void draw(const GraphicsContext& context); | |||||
/** | |||||
Draw this image at (x, y) point using the current OpenGL context. | |||||
*/ | |||||
void drawAt(const GraphicsContext& context, int x, int y); | |||||
/** | |||||
Draw this image at position @a pos using the current OpenGL context. | |||||
*/ | |||||
virtual void drawAt(const GraphicsContext& context, const Point<int>& pos) = 0; | |||||
/** | /** | ||||
TODO document this. | TODO document this. | ||||
@@ -111,14 +144,12 @@ public: | |||||
bool operator!=(const ImageBase& image) const noexcept; | bool operator!=(const ImageBase& image) const noexcept; | ||||
protected: | protected: | ||||
/** @internal */ | |||||
virtual void _drawAt(const Point<int>& pos) = 0; | |||||
const char* fRawData; | |||||
Size<uint> fSize; | |||||
const char* rawData; | |||||
Size<uint> size; | |||||
ImageFormat format; | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -0,0 +1,223 @@ | |||||
/* | |||||
* 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_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||||
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||||
#include "StandaloneWindow.hpp" | |||||
#include "SubWidget.hpp" | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
class ImageBaseAboutWindow : public StandaloneWindow | |||||
{ | |||||
public: | |||||
explicit ImageBaseAboutWindow(Window& parentWindow, const ImageType& image = ImageType()); | |||||
explicit ImageBaseAboutWindow(TopLevelWidget* parentTopLevelWidget, const ImageType& image = ImageType()); | |||||
void setImage(const ImageType& image); | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onKeyboard(const KeyboardEvent&) override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
private: | |||||
ImageType img; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
class ImageBaseButton : public SubWidget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageButtonClicked(ImageBaseButton* imageButton, int button) = 0; | |||||
}; | |||||
explicit ImageBaseButton(Widget* parentWidget, const ImageType& image); | |||||
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown); | |||||
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown); | |||||
~ImageBaseButton() override; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseButton) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
class ImageBaseKnob : public SubWidget | |||||
{ | |||||
public: | |||||
enum Orientation { | |||||
Horizontal, | |||||
Vertical | |||||
}; | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0; | |||||
virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0; | |||||
virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0; | |||||
}; | |||||
explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept; | |||||
explicit ImageBaseKnob(const ImageBaseKnob& imageKnob); | |||||
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | |||||
~ImageBaseKnob() override; | |||||
float getValue() const noexcept; | |||||
void setDefault(float def) noexcept; | |||||
void setRange(float min, float max) noexcept; | |||||
void setStep(float step) noexcept; | |||||
void setValue(float value, bool sendCallback = false) noexcept; | |||||
void setUsingLogScale(bool yesNo) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
void setOrientation(Orientation orientation) noexcept; | |||||
void setRotationAngle(int angle); | |||||
void setImageLayerCount(uint count) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
bool onScroll(const ScrollEvent&) override; | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
DISTRHO_LEAK_DETECTOR(ImageBaseKnob) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// note set range and step before setting the value | |||||
template <class ImageType> | |||||
class ImageBaseSlider : public SubWidget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0; | |||||
virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0; | |||||
virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0; | |||||
}; | |||||
explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept; | |||||
~ImageBaseSlider() override; | |||||
float getValue() const noexcept; | |||||
void setValue(float value, bool sendCallback = false) noexcept; | |||||
void setDefault(float def) noexcept; | |||||
void setStartPos(const Point<int>& startPos) noexcept; | |||||
void setStartPos(int x, int y) noexcept; | |||||
void setEndPos(const Point<int>& endPos) noexcept; | |||||
void setEndPos(int x, int y) noexcept; | |||||
void setInverted(bool inverted) noexcept; | |||||
void setRange(float min, float max) noexcept; | |||||
void setStep(float step) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
// these should not be used | |||||
void setAbsoluteX(int) const noexcept {} | |||||
void setAbsoluteY(int) const noexcept {} | |||||
void setAbsolutePos(int, int) const noexcept {} | |||||
void setAbsolutePos(const Point<int>&) const noexcept {} | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
class ImageBaseSwitch : public SubWidget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0; | |||||
}; | |||||
explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept; | |||||
explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept; | |||||
ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept; | |||||
~ImageBaseSwitch() override; | |||||
bool isDown() const noexcept; | |||||
void setDown(bool down) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
DISTRHO_LEAK_DETECTOR(ImageBaseSwitch) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,266 +18,24 @@ | |||||
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED | #define DGL_IMAGE_WIDGETS_HPP_INCLUDED | ||||
#include "Image.hpp" | #include "Image.hpp" | ||||
#include "Widget.hpp" | |||||
#include "Window.hpp" | |||||
#include "ImageBaseWidgets.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
#ifndef DISTRHO_OS_HAIKU | |||||
class ImageAboutWindow : public Window, | |||||
public Widget | |||||
#else | |||||
// crash when creating or opening 2nd window | |||||
class ImageAboutWindow | |||||
#endif | |||||
{ | |||||
public: | |||||
explicit ImageAboutWindow(Window& parent, const Image& image = Image()); | |||||
explicit ImageAboutWindow(Widget* widget, const Image& image = Image()); | |||||
void setImage(const Image& image); | |||||
#ifndef DISTRHO_OS_HAIKU | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onKeyboard(const KeyboardEvent&) override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
void onReshape(uint width, uint height) override; | |||||
#ifdef DGL_CAIRO | |||||
typedef CairoImageAboutWindow ImageAboutWindow; | |||||
typedef CairoImageButton ImageButton; | |||||
typedef CairoImageKnob ImageKnob; | |||||
typedef CairoImageSlider ImageSlider; | |||||
typedef CairoImageSwitch ImageSwitch; | |||||
#else | #else | ||||
void exec() {} | |||||
typedef OpenGLImageAboutWindow ImageAboutWindow; | |||||
typedef OpenGLImageButton ImageButton; | |||||
typedef OpenGLImageKnob ImageKnob; | |||||
typedef OpenGLImageSlider ImageSlider; | |||||
typedef OpenGLImageSwitch ImageSwitch; | |||||
#endif | #endif | ||||
private: | |||||
Image fImgBackground; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
class ImageButton : public Widget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; | |||||
}; | |||||
explicit ImageButton(Window& parent, const Image& image); | |||||
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown); | |||||
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||||
explicit ImageButton(Widget* widget, const Image& image); | |||||
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown); | |||||
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||||
~ImageButton() override; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageButton) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
class ImageKnob : public Widget | |||||
{ | |||||
public: | |||||
enum Orientation { | |||||
Horizontal, | |||||
Vertical | |||||
}; | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0; | |||||
virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0; | |||||
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; | |||||
}; | |||||
explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept; | |||||
explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept; | |||||
explicit ImageKnob(const ImageKnob& imageKnob); | |||||
ImageKnob& operator=(const ImageKnob& imageKnob); | |||||
~ImageKnob() override; | |||||
float getValue() const noexcept; | |||||
void setDefault(float def) noexcept; | |||||
void setRange(float min, float max) noexcept; | |||||
void setStep(float step) noexcept; | |||||
void setValue(float value, bool sendCallback = false) noexcept; | |||||
void setUsingLogScale(bool yesNo) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
void setOrientation(Orientation orientation) noexcept; | |||||
void setRotationAngle(int angle); | |||||
void setImageLayerCount(uint count) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
bool onScroll(const ScrollEvent&) override; | |||||
private: | |||||
Image fImage; | |||||
float fMinimum; | |||||
float fMaximum; | |||||
float fStep; | |||||
float fValue; | |||||
float fValueDef; | |||||
float fValueTmp; | |||||
bool fUsingDefault; | |||||
bool fUsingLog; | |||||
Orientation fOrientation; | |||||
int fRotationAngle; | |||||
bool fDragging; | |||||
int fLastX; | |||||
int fLastY; | |||||
Callback* fCallback; | |||||
bool fIsImgVertical; | |||||
uint fImgLayerWidth; | |||||
uint fImgLayerHeight; | |||||
uint fImgLayerCount; | |||||
bool fIsReady; | |||||
GLuint fTextureId; | |||||
float _logscale(float value) const; | |||||
float _invlogscale(float value) const; | |||||
DISTRHO_LEAK_DETECTOR(ImageKnob) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
// note set range and step before setting the value | |||||
class ImageSlider : public Widget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0; | |||||
virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0; | |||||
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; | |||||
}; | |||||
explicit ImageSlider(Window& parent, const Image& image) noexcept; | |||||
explicit ImageSlider(Widget* widget, const Image& image) noexcept; | |||||
float getValue() const noexcept; | |||||
void setValue(float value, bool sendCallback = false) noexcept; | |||||
void setDefault(float def) noexcept; | |||||
void setStartPos(const Point<int>& startPos) noexcept; | |||||
void setStartPos(int x, int y) noexcept; | |||||
void setEndPos(const Point<int>& endPos) noexcept; | |||||
void setEndPos(int x, int y) noexcept; | |||||
void setInverted(bool inverted) noexcept; | |||||
void setRange(float min, float max) noexcept; | |||||
void setStep(float step) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
bool onMotion(const MotionEvent&) override; | |||||
private: | |||||
Image fImage; | |||||
float fMinimum; | |||||
float fMaximum; | |||||
float fStep; | |||||
float fValue; | |||||
float fValueDef; | |||||
float fValueTmp; | |||||
bool fUsingDefault; | |||||
bool fDragging; | |||||
bool fInverted; | |||||
bool fValueIsSet; | |||||
int fStartedX; | |||||
int fStartedY; | |||||
Callback* fCallback; | |||||
Point<int> fStartPos; | |||||
Point<int> fEndPos; | |||||
Rectangle<int> fSliderArea; | |||||
void _recheckArea() noexcept; | |||||
// these should not be used | |||||
void setAbsoluteX(int) const noexcept {} | |||||
void setAbsoluteY(int) const noexcept {} | |||||
void setAbsolutePos(int, int) const noexcept {} | |||||
void setAbsolutePos(const Point<int>&) const noexcept {} | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
class ImageSwitch : public Widget | |||||
{ | |||||
public: | |||||
class Callback | |||||
{ | |||||
public: | |||||
virtual ~Callback() {} | |||||
virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0; | |||||
}; | |||||
explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; | |||||
explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept; | |||||
explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept; | |||||
ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept; | |||||
bool isDown() const noexcept; | |||||
void setDown(bool down) noexcept; | |||||
void setCallback(Callback* callback) noexcept; | |||||
protected: | |||||
void onDisplay() override; | |||||
bool onMouse(const MouseEvent&) override; | |||||
private: | |||||
Image fImageNormal; | |||||
Image fImageDown; | |||||
bool fIsDown; | |||||
Callback* fCallback; | |||||
DISTRHO_LEAK_DETECTOR(ImageSwitch) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
#endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED | #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED |
@@ -10,8 +10,12 @@ include ../Makefile.base.mk | |||||
BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc | BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc | ||||
BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter | BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter | ||||
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include | |||||
LINK_FLAGS += $(DGL_LIBS) | LINK_FLAGS += $(DGL_LIBS) | ||||
# TODO fix these after pugl-upstream is done | |||||
BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -Wno-narrowing | |||||
# ifneq ($(MACOS_OLD),true) | # ifneq ($(MACOS_OLD),true) | ||||
# needed by sofd right now, fix later | # needed by sofd right now, fix later | ||||
# BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive | # BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive | ||||
@@ -21,39 +25,64 @@ LINK_FLAGS += $(DGL_LIBS) | |||||
OBJS_common = \ | OBJS_common = \ | ||||
../build/dgl/Application.cpp.o \ | ../build/dgl/Application.cpp.o \ | ||||
../build/dgl/ApplicationPrivateData.cpp.o \ | |||||
../build/dgl/Color.cpp.o \ | ../build/dgl/Color.cpp.o \ | ||||
../build/dgl/Geometry.cpp.o \ | ../build/dgl/Geometry.cpp.o \ | ||||
../build/dgl/ImageBase.cpp.o \ | ../build/dgl/ImageBase.cpp.o \ | ||||
../build/dgl/ImageBaseWidgets.cpp.o \ | |||||
../build/dgl/Resources.cpp.o \ | ../build/dgl/Resources.cpp.o \ | ||||
../build/dgl/Widget.cpp.o | |||||
# TODO: ImageWidgets.cpp | |||||
../build/dgl/SubWidget.cpp.o \ | |||||
../build/dgl/SubWidgetPrivateData.cpp.o \ | |||||
../build/dgl/TopLevelWidget.cpp.o \ | |||||
../build/dgl/TopLevelWidgetPrivateData.cpp.o \ | |||||
../build/dgl/Widget.cpp.o \ | |||||
../build/dgl/WidgetPrivateData.cpp.o \ | |||||
../build/dgl/Window.cpp.o \ | |||||
../build/dgl/WindowPrivateData.cpp.o | |||||
# ../build/dgl/WindowFileBrowser.cpp.o | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
OBJS_cairo = $(OBJS_common) \ | OBJS_cairo = $(OBJS_common) \ | ||||
../build/dgl/Cairo.cpp.cairo.o \ | |||||
../build/dgl/WidgetPrivateData.cpp.cairo.o | |||||
../build/dgl/Cairo.cpp.cairo.o | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OBJS_cairo += ../build/dgl/Window.mm.cairo.o | |||||
OBJS_cairo += ../build/dgl/pugl.mm.cairo.o | |||||
else | else | ||||
OBJS_cairo += ../build/dgl/Window.cpp.cairo.o | |||||
OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
OBJS_opengl = $(OBJS_common) \ | OBJS_opengl = $(OBJS_common) \ | ||||
../build/dgl/OpenGL.cpp.opengl.o \ | ../build/dgl/OpenGL.cpp.opengl.o \ | ||||
../build/dgl/Image.cpp.opengl.o \ | |||||
../build/dgl/ImageWidgets.cpp.opengl.o \ | |||||
../build/dgl/NanoVG.cpp.opengl.o \ | |||||
../build/dgl/WidgetPrivateData.cpp.opengl.o | |||||
../build/dgl/NanoVG.cpp.opengl.o | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OBJS_opengl += ../build/dgl/Window.mm.opengl.o | |||||
OBJS_opengl += ../build/dgl/pugl.mm.opengl.o | |||||
else | else | ||||
OBJS_opengl += ../build/dgl/Window.cpp.opengl.o | |||||
OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
OBJS_stub = $(OBJS_common) | |||||
ifeq ($(MACOS),true) | |||||
OBJS_stub += ../build/dgl/pugl.mm.o | |||||
else | |||||
OBJS_stub += ../build/dgl/pugl.cpp.o | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
OBJS_vulkan = $(OBJS_common) \ | |||||
../build/dgl/Vulkan.cpp.vulkan.o | |||||
ifeq ($(MACOS),true) | |||||
OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o | |||||
else | |||||
OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -68,10 +97,23 @@ TARGETS += ../build/libdgl-opengl.a | |||||
TARGETS += ../build/libdgl.a | TARGETS += ../build/libdgl.a | ||||
endif | endif | ||||
ifeq ($(HAVE_STUB),true) | |||||
TARGETS += ../build/libdgl-stub.a | |||||
endif | |||||
ifeq ($(HAVE_VULKAN),true) | |||||
TARGETS += ../build/libdgl-vulkan.a | |||||
endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
all: $(TARGETS) | all: $(TARGETS) | ||||
cairo: ../build/libdgl-cairo.a | |||||
opengl: ../build/libdgl-opengl.a | |||||
stub: ../build/libdgl-stub.a | |||||
vulkan: ../build/libdgl-vulkan.a | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/libdgl-cairo.a: $(OBJS_cairo) | ../build/libdgl-cairo.a: $(OBJS_cairo) | ||||
@@ -86,6 +128,18 @@ all: $(TARGETS) | |||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(SILENT)$(AR) crs $@ $^ | ||||
../build/libdgl-stub.a: $(OBJS_stub) | |||||
-@mkdir -p ../build | |||||
@echo "Creating libdgl-stub.a" | |||||
$(SILENT)rm -f $@ | |||||
$(SILENT)$(AR) crs $@ $^ | |||||
../build/libdgl-vulkan.a: $(OBJS_vulkan) | |||||
-@mkdir -p ../build | |||||
@echo "Creating libdgl-vulkan.a" | |||||
$(SILENT)rm -f $@ | |||||
$(SILENT)$(AR) crs $@ $^ | |||||
# Compat name, to be removed soon | # Compat name, to be removed soon | ||||
../build/libdgl.a: ../build/libdgl-opengl.a | ../build/libdgl.a: ../build/libdgl-opengl.a | ||||
@echo "Symlinking libdgl.a" | @echo "Symlinking libdgl.a" | ||||
@@ -103,6 +157,11 @@ all: $(TARGETS) | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | ||||
../build/dgl/%.mm.o: src/%.mm | |||||
-@mkdir -p ../build/dgl | |||||
@echo "Compiling $<" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
../build/dgl/%.cpp.cairo.o: src/%.cpp | ../build/dgl/%.cpp.cairo.o: src/%.cpp | ||||
@@ -110,15 +169,10 @@ all: $(TARGETS) | |||||
@echo "Compiling $< (Cairo variant)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | ||||
../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl/* | |||||
../build/dgl/%.mm.cairo.o: src/%.mm | |||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (Cairo variant)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | |||||
../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl/* | |||||
-@mkdir -p ../build/dgl | |||||
@echo "Compiling $< (Cairo variant)" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -127,15 +181,22 @@ all: $(TARGETS) | |||||
@echo "Compiling $< (OpenGL variant)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | ||||
../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl/* | |||||
../build/dgl/%.mm.opengl.o: src/%.mm | |||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL variant)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ | |||||
../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl/* | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
../build/dgl/%.cpp.vulkan.o: src/%.cpp | |||||
-@mkdir -p ../build/dgl | -@mkdir -p ../build/dgl | ||||
@echo "Compiling $< (OpenGL variant)" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@ | |||||
@echo "Compiling $< (Vulkan variant)" | |||||
$(SILENT)$(CXX) $< $(BUILD_CXX_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 $@ | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -150,5 +211,7 @@ debug: | |||||
-include $(OBJS_common:%.o=%.d) | -include $(OBJS_common:%.o=%.d) | ||||
-include $(OBJS_cairo:%.o=%.d) | -include $(OBJS_cairo:%.o=%.d) | ||||
-include $(OBJS_opengl:%.o=%.d) | -include $(OBJS_opengl:%.o=%.d) | ||||
-include $(OBJS_stub:%.o=%.d) | |||||
-include $(OBJS_vulkan:%.o=%.d) | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -19,7 +19,9 @@ | |||||
#include "Color.hpp" | #include "Color.hpp" | ||||
#include "OpenGL.hpp" | #include "OpenGL.hpp" | ||||
#include "Widget.hpp" | |||||
#include "SubWidget.hpp" | |||||
#include "TopLevelWidget.hpp" | |||||
#include "StandaloneWindow.hpp" | |||||
#ifndef DGL_NO_SHARED_RESOURCES | #ifndef DGL_NO_SHARED_RESOURCES | ||||
# define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" | # define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" | ||||
@@ -33,7 +35,6 @@ START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Forward class names | // Forward class names | ||||
class BlendishWidget; | |||||
class NanoVG; | class NanoVG; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -305,7 +306,9 @@ public: | |||||
/** | /** | ||||
Constructor reusing a NanoVG context, used for subwidgets. | Constructor reusing a NanoVG context, used for subwidgets. | ||||
*/ | */ | ||||
/* | |||||
NanoVG(NanoWidget* groupWidget); | NanoVG(NanoWidget* groupWidget); | ||||
*/ | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
@@ -851,14 +854,13 @@ public: | |||||
/** | /** | ||||
Load DPF's internal shared resources for this NanoVG class. | Load DPF's internal shared resources for this NanoVG class. | ||||
*/ | */ | ||||
virtual void loadSharedResources(); | |||||
virtual bool loadSharedResources(); | |||||
#endif | #endif | ||||
private: | private: | ||||
NVGcontext* const fContext; | NVGcontext* const fContext; | ||||
bool fInFrame; | bool fInFrame; | ||||
bool fIsSubWidget; | bool fIsSubWidget; | ||||
friend class BlendishWidget; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG) | ||||
}; | }; | ||||
@@ -873,30 +875,39 @@ private: | |||||
The drawing function onDisplay() is implemented internally but a | The drawing function onDisplay() is implemented internally but a | ||||
new onNanoDisplay() needs to be overridden instead. | new onNanoDisplay() needs to be overridden instead. | ||||
*/ | */ | ||||
class NanoWidget : public Widget, | |||||
public NanoVG | |||||
template <class BaseWidget> | |||||
class NanoBaseWidget : public BaseWidget, | |||||
public NanoVG | |||||
{ | { | ||||
public: | public: | ||||
/** | /** | ||||
Constructor. | |||||
Constructor for a NanoSubWidget. | |||||
@see CreateFlags | @see CreateFlags | ||||
*/ | */ | ||||
explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS); | |||||
explicit NanoBaseWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS); | |||||
/** | /** | ||||
Constructor for a subwidget. | |||||
Constructor for a NanoTopLevelWidget. | |||||
@see CreateFlags | |||||
*/ | */ | ||||
explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS); | |||||
explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); | |||||
/** | /** | ||||
Constructor for a subwidget, reusing a NanoVG context. | |||||
Constructor for a NanoStandaloneWindow without parent window. | |||||
@see CreateFlags | |||||
*/ | */ | ||||
explicit NanoWidget(NanoWidget* groupWidget); | |||||
explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS); | |||||
/** | |||||
Constructor for a NanoStandaloneWindow with parent window. | |||||
@see CreateFlags | |||||
*/ | |||||
explicit NanoBaseWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS); | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
virtual ~NanoWidget(); | |||||
virtual ~NanoBaseWidget() {} | |||||
protected: | protected: | ||||
/** | /** | ||||
@@ -906,14 +917,16 @@ protected: | |||||
virtual void onNanoDisplay() = 0; | virtual void onNanoDisplay() = 0; | ||||
private: | private: | ||||
struct PrivateData; | |||||
PrivateData* const nData; | |||||
/** | /** | ||||
Widget display function. | Widget display function. | ||||
Implemented internally to wrap begin/endFrame() automatically. | Implemented internally to wrap begin/endFrame() automatically. | ||||
*/ | */ | ||||
void onDisplay() override; | |||||
inline void onDisplay() override | |||||
{ | |||||
NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); | |||||
onNanoDisplay(); | |||||
NanoVG::endFrame(); | |||||
} | |||||
// these should not be used | // these should not be used | ||||
void beginFrame(uint,uint) {} | void beginFrame(uint,uint) {} | ||||
@@ -922,9 +935,16 @@ private: | |||||
void cancelFrame() {} | void cancelFrame() {} | ||||
void endFrame() {} | void endFrame() {} | ||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget) | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget) | |||||
}; | }; | ||||
typedef NanoBaseWidget<SubWidget> NanoSubWidget; | |||||
typedef NanoBaseWidget<TopLevelWidget> NanoTopLevelWidget; | |||||
typedef NanoBaseWidget<StandaloneWindow> NanoStandaloneWindow; | |||||
DISTRHO_DEPRECATED_BY("NanoSubWidget") | |||||
typedef NanoSubWidget NanoWidget; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,6 +18,7 @@ | |||||
#define DGL_OPENGL_HPP_INCLUDED | #define DGL_OPENGL_HPP_INCLUDED | ||||
#include "ImageBase.hpp" | #include "ImageBase.hpp" | ||||
#include "ImageBaseWidgets.hpp" | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Fix OpenGL includes for Windows, based on glfw code (part 1) | // Fix OpenGL includes for Windows, based on glfw code (part 1) | ||||
@@ -108,14 +109,185 @@ START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
/** | /** | ||||
Graphics context. | |||||
OpenGL Graphics context. | |||||
*/ | */ | ||||
struct GraphicsContext | |||||
struct OpenGLGraphicsContext : GraphicsContext | |||||
{ | { | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
static inline | |||||
ImageFormat asDISTRHOImageFormat(const GLenum format) | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case GL_LUMINANCE: | |||||
return kImageFormatGrayscale; | |||||
case GL_BGR: | |||||
return kImageFormatBGR; | |||||
case GL_BGRA: | |||||
return kImageFormatBGRA; | |||||
case GL_RGB: | |||||
return kImageFormatRGB; | |||||
case GL_RGBA: | |||||
return kImageFormatRGBA; | |||||
} | |||||
return kImageFormatNull; | |||||
} | |||||
static inline | |||||
GLenum asOpenGLImageFormat(const ImageFormat format) | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatGrayscale: | |||||
return GL_LUMINANCE; | |||||
case kImageFormatBGR: | |||||
return GL_BGR; | |||||
case kImageFormatBGRA: | |||||
return GL_BGRA; | |||||
case kImageFormatRGB: | |||||
return GL_RGB; | |||||
case kImageFormatRGBA: | |||||
return GL_RGBA; | |||||
} | |||||
return 0x0; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
/** | |||||
OpenGL Image class. | |||||
This is an Image class that handles raw image data in pixels. | |||||
You can init the image data on the contructor or later on by calling loadFromMemory(). | |||||
To generate raw data useful for this class see the utils/png2rgba.py script. | |||||
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR' | |||||
instead of the default 'GL_BGRA'. | |||||
Images are drawn on screen via 2D textures. | |||||
*/ | |||||
class OpenGLImage : public ImageBase | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor for a null Image. | |||||
*/ | |||||
OpenGLImage(); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
OpenGLImage(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
OpenGLImage(const char* rawData, const Size<uint>& size, ImageFormat format = kImageFormatBGRA); | |||||
/** | |||||
Constructor using another image data. | |||||
*/ | |||||
OpenGLImage(const OpenGLImage& image); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
~OpenGLImage() override; | |||||
/** | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* rawData, | |||||
const Size<uint>& size, | |||||
ImageFormat format = kImageFormatBGRA) noexcept override; | |||||
/** | |||||
Draw this image at position @a pos using the graphics context @a context. | |||||
*/ | |||||
void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | |||||
/** | |||||
TODO document this. | |||||
*/ | |||||
OpenGLImage& operator=(const OpenGLImage& image) noexcept; | |||||
// 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); }; | |||||
inline void draw(const GraphicsContext& context) | |||||
{ drawAt(context, Point<int>(0, 0)); }; | |||||
inline void drawAt(const GraphicsContext& context, int x, int y) | |||||
{ drawAt(context, Point<int>(x, y)); }; | |||||
/** | |||||
Constructor using raw image data, specifying an OpenGL image format. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, uint, uint, ImageFormat)") | |||||
explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum glFormat); | |||||
/** | |||||
Constructor using raw image data, specifying an OpenGL image format. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, const Size<uint>&, ImageFormat)") | |||||
explicit OpenGLImage(const char* rawData, const Size<uint>& size, GLenum glFormat); | |||||
/** | |||||
Draw this image at (0, 0) point using the current OpenGL context. | |||||
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||||
void draw(); | |||||
/** | |||||
Draw this image at (x, y) point using the current OpenGL context. | |||||
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, int, int)") | |||||
void drawAt(int x, int y); | |||||
/** | |||||
Draw this image at position @a pos using the current OpenGL context. | |||||
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. | |||||
*/ | |||||
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, const Point<int>&)") | |||||
void drawAt(const Point<int>& pos); | |||||
/** | |||||
Get the image type. | |||||
DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE. | |||||
*/ | |||||
DISTRHO_DEPRECATED | |||||
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | |||||
private: | |||||
GLuint textureId; | |||||
bool setupCalled; | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | |||||
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | |||||
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||||
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | |||||
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
#endif | #endif |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,39 +17,54 @@ | |||||
#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED | #ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED | ||||
#define DGL_STANDALONE_WINDOW_HPP_INCLUDED | #define DGL_STANDALONE_WINDOW_HPP_INCLUDED | ||||
#include "Application.hpp" | |||||
#include "Widget.hpp" | |||||
#include "TopLevelWidget.hpp" | |||||
#include "Window.hpp" | #include "Window.hpp" | ||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
class StandaloneWindow : public Application, | |||||
public Window | |||||
class StandaloneWindow : public Window, | |||||
public TopLevelWidget | |||||
{ | { | ||||
public: | public: | ||||
/** | /** | ||||
Constructor. | |||||
Constructor without parent. | |||||
*/ | */ | ||||
StandaloneWindow(); | |||||
StandaloneWindow(Application& app) | |||||
: Window(app), | |||||
TopLevelWidget((Window&)*this) {} | |||||
/** | /** | ||||
Show window and execute application. | |||||
Constructor with parent window, typically used to run as modal. | |||||
*/ | */ | ||||
void exec(); | |||||
StandaloneWindow(Application& app, Window& parentWindow) | |||||
: Window(app, parentWindow), | |||||
TopLevelWidget((Window&)*this) {} | |||||
private: | |||||
Widget* fWidget; | |||||
/** @internal */ | |||||
void onReshape(uint width, uint height) override; | |||||
/** @internal */ | |||||
void _addWidget(Widget* widget) override; | |||||
/** @internal */ | |||||
void _removeWidget(Widget* widget) override; | |||||
/** | |||||
Overloaded functions to ensure they apply to the Window class. | |||||
*/ | |||||
bool isVisible() const noexcept { return Window::isVisible(); } | |||||
void setVisible(bool yesNo) { Window::setVisible(yesNo); } | |||||
void hide() { Window::hide(); } | |||||
void show() { Window::show(); } | |||||
uint getWidth() const noexcept { return Window::getWidth(); } | |||||
uint getHeight() const noexcept { return Window::getHeight(); } | |||||
const Size<uint> getSize() const noexcept { return Window::getSize(); } | |||||
void repaint() noexcept { Window::repaint(); } | |||||
void setWidth(uint width) { Window::setWidth(width); } | |||||
void setHeight(uint height) { Window::setHeight(height); } | |||||
void setSize(uint width, uint height) { Window::setSize(width, height); } | |||||
void setSize(const Size<uint>& size) { Window::setSize(size); } | |||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) | |||||
{ return Window::addIdleCallback(callback, timerFrequencyInMs); } | |||||
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | |||||
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | |||||
double getScaleFactor() const noexcept { return Window::getScaleFactor(); } | |||||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | |||||
bool keepAspectRatio = false, bool automaticallyScale = false) | |||||
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); } | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | ||||
}; | }; | ||||
@@ -0,0 +1,163 @@ | |||||
/* | |||||
* 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_SUBWIDGET_HPP_INCLUDED | |||||
#define DGL_SUBWIDGET_HPP_INCLUDED | |||||
#include "Widget.hpp" | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | |||||
Sub-Widget class. | |||||
This class is the main entry point for creating any reusable widgets from within DGL. | |||||
It can be freely positioned from within a parent widget, thus being named subwidget. | |||||
Many subwidgets can share the same parent, and subwidgets themselves can also have its own subwidgets. | |||||
It is subwidgets all the way down. | |||||
TODO check absolute vs relative position and see what makes more sense. | |||||
@see CairoSubWidget | |||||
*/ | |||||
class SubWidget : public Widget | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor. | |||||
*/ | |||||
explicit SubWidget(Widget* parentWidget); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
virtual ~SubWidget(); | |||||
/** | |||||
Check if this widget contains the point defined by @a x and @a y. | |||||
*/ | |||||
// TODO rename as containsRelativePos | |||||
template<typename T> | |||||
bool contains(T x, T y) const noexcept; | |||||
/** | |||||
Check if this widget contains the point @a pos. | |||||
*/ | |||||
// TODO rename as containsRelativePos | |||||
template<typename T> | |||||
bool contains(const Point<T>& pos) const noexcept; | |||||
/** | |||||
Get absolute X. | |||||
*/ | |||||
int getAbsoluteX() const noexcept; | |||||
/** | |||||
Get absolute Y. | |||||
*/ | |||||
int getAbsoluteY() const noexcept; | |||||
/** | |||||
Get absolute position. | |||||
*/ | |||||
Point<int> getAbsolutePos() const noexcept; | |||||
/** | |||||
Get absolute area of this subwidget. | |||||
This is the same as `Rectangle<int>(getAbsolutePos(), getSize());` | |||||
@see getConstrainedAbsoluteArea() | |||||
*/ | |||||
Rectangle<int> getAbsoluteArea() const noexcept; | |||||
/** | |||||
Get absolute area of this subwidget, with special consideration for not allowing negative values. | |||||
@see getAbsoluteArea() | |||||
*/ | |||||
Rectangle<uint> getConstrainedAbsoluteArea() const noexcept; | |||||
/** | |||||
Set absolute X. | |||||
*/ | |||||
void setAbsoluteX(int x) noexcept; | |||||
/** | |||||
Set absolute Y. | |||||
*/ | |||||
void setAbsoluteY(int y) noexcept; | |||||
/** | |||||
Set absolute position using @a x and @a y values. | |||||
*/ | |||||
void setAbsolutePos(int x, int y) noexcept; | |||||
/** | |||||
Set absolute position. | |||||
*/ | |||||
void setAbsolutePos(const Point<int>& pos) noexcept; | |||||
/** | |||||
Get parent Widget, as passed in the constructor. | |||||
*/ | |||||
Widget* getParentWidget() const noexcept; | |||||
/** | |||||
Request repaint of this subwidget's area to the window this widget belongs to. | |||||
*/ | |||||
void repaint() noexcept override; | |||||
/** | |||||
Bring this widget to the "front" of the parent widget. | |||||
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front". | |||||
*/ | |||||
virtual void toFront(); | |||||
/** | |||||
Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing. | |||||
*/ | |||||
void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true); | |||||
/** | |||||
Indicate that this subwidget will always draw at its own internal size and needs scaling to fit target size. | |||||
*/ | |||||
void setNeedsViewportScaling(bool needsViewportScaling = true, double autoScaleFactor = 0.0); | |||||
/** | |||||
Indicate that this subwidget should not be drawn on screen, typically because it is managed by something else. | |||||
*/ | |||||
void setSkipDrawing(bool skipDrawing = true); | |||||
protected: | |||||
/** | |||||
A function called when the subwidget's absolute position is changed. | |||||
*/ | |||||
virtual void onPositionChanged(const PositionChangedEvent&); | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
friend class Widget; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif // DGL_SUBWIDGET_HPP_INCLUDED | |||||
@@ -0,0 +1,125 @@ | |||||
/* | |||||
* 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_TOP_LEVEL_WIDGET_HPP_INCLUDED | |||||
#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED | |||||
#include "Widget.hpp" | |||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
START_NAMESPACE_DISTRHO | |||||
class UI; | |||||
END_NAMESPACE_DISTRHO | |||||
#endif | |||||
START_NAMESPACE_DGL | |||||
class Window; | |||||
// ----------------------------------------------------------------------- | |||||
/** | |||||
Top-Level Widget class. | |||||
This is the only Widget class that is allowed to be used directly on a Window. | |||||
This widget takes the full size of the Window it is mapped to. | |||||
Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent. | |||||
Doing so allows for custom position and sizes. | |||||
This class is used as the type for DPF Plugin UIs. | |||||
So anything that a plugin UI might need that does not belong in a simple Widget will go here. | |||||
*/ | |||||
class TopLevelWidget : public Widget | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor. | |||||
*/ | |||||
explicit TopLevelWidget(Window& windowToMapTo); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
virtual ~TopLevelWidget(); | |||||
/** | |||||
Get the application associated with this top-level widget's window. | |||||
*/ | |||||
Application& getApp() const noexcept; | |||||
/** | |||||
Get the window associated with this top-level widget. | |||||
*/ | |||||
Window& getWindow() const noexcept; | |||||
/** | |||||
Set width of this widget's window. | |||||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||||
*/ | |||||
void setWidth(uint width); | |||||
/** | |||||
Set height of this widget's window. | |||||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||||
*/ | |||||
void setHeight(uint height); | |||||
/** | |||||
Set size of this widget's window, using @a width and @a height values. | |||||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||||
*/ | |||||
void setSize(uint width, uint height); | |||||
/** | |||||
Set size of this widget's window. | |||||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||||
*/ | |||||
void setSize(const Size<uint>& size); | |||||
// TODO group stuff after here, convenience functions present in Window class | |||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | |||||
bool removeIdleCallback(IdleCallback* callback); | |||||
double getScaleFactor() const noexcept; | |||||
void repaint() noexcept; | |||||
void repaint(const Rectangle<uint>& rect) noexcept; | |||||
void setGeometryConstraints(uint minimumWidth, | |||||
uint minimumHeight, | |||||
bool keepAspectRatio = false, | |||||
bool automaticallyScale = false); | |||||
DISTRHO_DEPRECATED_BY("getApp()") | |||||
Application& getParentApp() const noexcept { return getApp(); } | |||||
DISTRHO_DEPRECATED_BY("getWindow()") | |||||
Window& getParentWindow() const noexcept { return getWindow(); } | |||||
private: | |||||
struct PrivateData; | |||||
PrivateData* const pData; | |||||
friend class Window; | |||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
friend class DISTRHO_NAMESPACE::UI; | |||||
#endif | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED |
@@ -0,0 +1,103 @@ | |||||
/* | |||||
* 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_VULKAN_HPP_INCLUDED | |||||
#define DGL_VULKAN_HPP_INCLUDED | |||||
#include "ImageBase.hpp" | |||||
#include <vulkan/vulkan_core.h> | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | |||||
Vulkan Graphics context. | |||||
*/ | |||||
struct VulkanGraphicsContext : GraphicsContext | |||||
{ | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | |||||
Vulkan Image class. | |||||
TODO ... | |||||
*/ | |||||
class VulkanImage : public ImageBase | |||||
{ | |||||
public: | |||||
/** | |||||
Constructor for a null Image. | |||||
*/ | |||||
VulkanImage(); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
VulkanImage(const char* rawData, uint width, uint height, ImageFormat format); | |||||
/** | |||||
Constructor using raw image data. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
VulkanImage(const char* rawData, const Size<uint>& size, ImageFormat format); | |||||
/** | |||||
Constructor using another image data. | |||||
*/ | |||||
VulkanImage(const VulkanImage& image); | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
~VulkanImage() override; | |||||
/** | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* rawData, | |||||
const Size<uint>& size, | |||||
ImageFormat format = kImageFormatBGRA) noexcept override; | |||||
/** | |||||
Draw this image at position @a pos using the graphics context @a context. | |||||
*/ | |||||
void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | |||||
/** | |||||
TODO document this. | |||||
*/ | |||||
VulkanImage& operator=(const VulkanImage& image) noexcept; | |||||
// 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); }; | |||||
inline void draw(const GraphicsContext& context) | |||||
{ drawAt(context, Point<int>(0, 0)); }; | |||||
inline void drawAt(const GraphicsContext& context, int x, int y) | |||||
{ drawAt(context, Point<int>(x, y)); }; | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -19,82 +19,97 @@ | |||||
#include "Geometry.hpp" | #include "Geometry.hpp" | ||||
#include <vector> | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Forward class names | // Forward class names | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
START_NAMESPACE_DISTRHO | |||||
class UI; | |||||
END_NAMESPACE_DISTRHO | |||||
#endif | |||||
START_NAMESPACE_DGL | |||||
class Application; | class Application; | ||||
class ImageSlider; | |||||
class NanoWidget; | |||||
class SubWidget; | |||||
class TopLevelWidget; | |||||
class Window; | class Window; | ||||
class StandaloneWindow; | |||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
/** | /** | ||||
Base DGL Widget class. | Base DGL Widget class. | ||||
This is the base Widget class, from which all widgets are built. | This is the base Widget class, from which all widgets are built. | ||||
All widgets have a parent Window where they'll be drawn. | |||||
This parent is never changed during the widget lifetime. | |||||
All widgets have a parent widget where they'll be drawn, this can be the top-level widget or a group widget. | |||||
This parent is never changed during a widget's lifetime. | |||||
Widgets receive events in relative coordinates. | |||||
(0, 0) means its top-left position. | |||||
Widgets receive events in relative coordinates. (0, 0) means its top-left position. | |||||
Windows paint widgets in the order they are constructed. | |||||
Early widgets are drawn first, at the bottom, then newer ones on top. | |||||
Events are sent in the inverse order so that the top-most widget gets | |||||
The top-level widget will draw subwidgets in the order they are constructed. | |||||
Early subwidgets are drawn first, at the bottom, then newer ones on top. | |||||
Events are sent in the inverse order so that the top-most widgets get | |||||
a chance to catch the event and stop its propagation. | a chance to catch the event and stop its propagation. | ||||
All widget event callbacks do nothing by default. | |||||
All widget event callbacks do nothing by default and onDisplay MUST be reimplemented by subclasses. | |||||
@note It is not possible to subclass this Widget class directly, you must use SubWidget or TopLevelWidget instead. | |||||
*/ | */ | ||||
class Widget | class Widget | ||||
{ | { | ||||
public: | public: | ||||
/** | /** | ||||
Base event data. | Base event data. | ||||
@a mod The currently active keyboard modifiers, @see Modifier. | |||||
@a time The timestamp (if any). | |||||
These are the fields present on all Widget events. | |||||
@a mod Currently active keyboard modifiers, @see Modifier. | |||||
@a mod Event flags, @see Flag. | |||||
@a time Event timestamp (if any). | |||||
*/ | */ | ||||
struct BaseEvent { | struct BaseEvent { | ||||
uint mod; | |||||
uint32_t time; | |||||
uint mod; | |||||
uint flags; | |||||
uint time; | |||||
/** Constuctor */ | /** Constuctor */ | ||||
BaseEvent() noexcept : mod(0x0), time(0) {} | |||||
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | |||||
/** Destuctor */ | /** Destuctor */ | ||||
virtual ~BaseEvent() noexcept {} | virtual ~BaseEvent() noexcept {} | ||||
}; | }; | ||||
/** | /** | ||||
Keyboard event. | Keyboard event. | ||||
@a press True if the key was pressed, false if released. | |||||
@a key Unicode point of the key pressed. | |||||
This event represents low-level key presses and releases. | |||||
This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. | |||||
Keys are represented portably as Unicode code points, using the "natural" code point for the key. | |||||
The @a key field is the code for the pressed key, without any modifiers applied. | |||||
For example, a press or release of the 'A' key will have `key` 97 ('a') | |||||
regardless of whether shift or control are being held. | |||||
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 | @see onKeyboard | ||||
*/ | */ | ||||
struct KeyboardEvent : BaseEvent { | struct KeyboardEvent : BaseEvent { | ||||
bool press; | bool press; | ||||
uint key; | uint key; | ||||
uint keycode; | |||||
/** Constuctor */ | /** Constuctor */ | ||||
KeyboardEvent() noexcept | KeyboardEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
press(false), | press(false), | ||||
key(0) {} | |||||
key(0), | |||||
keycode(0) {} | |||||
}; | }; | ||||
/** | /** | ||||
Special keyboard event. | Special keyboard event. | ||||
This event allows the use of keys that do not have unicode points. | |||||
Note that some are non-printable keys. | |||||
@a press True if the key was pressed, false if released. | @a press True if the key was pressed, false if released. | ||||
@a key The key pressed. | @a key The key pressed. | ||||
@see onSpecial | @see onSpecial | ||||
@@ -111,54 +126,103 @@ public: | |||||
}; | }; | ||||
/** | /** | ||||
Mouse event. | |||||
@a button The button number (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. | |||||
Character input event. | |||||
This event represents text input, usually as the result of a key press. | |||||
The text is given both as a Unicode character code and a UTF-8 string. | |||||
Note that this event is generated by the platform's input system, | |||||
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 { | |||||
uint keycode; | |||||
uint character; | |||||
char string[8]; | |||||
/** Constuctor */ | |||||
CharacterInputEvent() noexcept | |||||
: BaseEvent(), | |||||
keycode(0), | |||||
character(0), | |||||
string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} | |||||
}; | |||||
/** | |||||
Mouse press or release event. | |||||
@a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). | |||||
@a press True if the button was pressed, false if released. | |||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a absolutePos The absolute coordinates of the pointer. | |||||
@see onMouse | @see onMouse | ||||
*/ | */ | ||||
struct MouseEvent : BaseEvent { | struct MouseEvent : BaseEvent { | ||||
int button; | |||||
uint button; | |||||
bool press; | bool press; | ||||
Point<int> pos; | |||||
Point<double> pos; | |||||
Point<double> absolutePos; | |||||
/** Constuctor */ | /** Constuctor */ | ||||
MouseEvent() noexcept | MouseEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
button(0), | button(0), | ||||
press(false), | press(false), | ||||
pos(0, 0) {} | |||||
pos(0.0, 0.0), | |||||
absolutePos(0.0, 0.0) {} | |||||
}; | }; | ||||
/** | /** | ||||
Mouse motion event. | Mouse motion event. | ||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a absolutePos The absolute coordinates of the pointer. | |||||
@see onMotion | @see onMotion | ||||
*/ | */ | ||||
struct MotionEvent : BaseEvent { | struct MotionEvent : BaseEvent { | ||||
Point<int> pos; | |||||
Point<double> pos; | |||||
Point<double> absolutePos; | |||||
/** Constuctor */ | /** Constuctor */ | ||||
MotionEvent() noexcept | MotionEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
pos(0, 0) {} | |||||
pos(0.0, 0.0), | |||||
absolutePos(0.0, 0.0) {} | |||||
}; | }; | ||||
/** | /** | ||||
Mouse scroll event. | Mouse scroll event. | ||||
@a pos The widget-relative coordinates of the pointer. | |||||
@a delta The scroll distance. | |||||
The scroll distance is expressed in "lines", | |||||
an arbitrary unit that corresponds to a single tick of a detented mouse wheel. | |||||
For example, `delta.y` = 1.0 scrolls 1 line up. | |||||
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 | @see onScroll | ||||
*/ | */ | ||||
struct ScrollEvent : BaseEvent { | struct ScrollEvent : BaseEvent { | ||||
Point<int> pos; | |||||
Point<float> delta; | |||||
Point<double> pos; | |||||
Point<double> absolutePos; | |||||
Point<double> delta; | |||||
ScrollDirection direction; | |||||
/** Constuctor */ | /** Constuctor */ | ||||
ScrollEvent() noexcept | ScrollEvent() noexcept | ||||
: BaseEvent(), | : BaseEvent(), | ||||
pos(0, 0), | |||||
delta(0.0f, 0.0f) {} | |||||
pos(0.0, 0.0), | |||||
absolutePos(0.0, 0.0), | |||||
delta(0.0, 0.0), | |||||
direction(kScrollSmooth) {} | |||||
}; | }; | ||||
/** | /** | ||||
@@ -193,16 +257,18 @@ public: | |||||
oldPos(0, 0) {} | oldPos(0, 0) {} | ||||
}; | }; | ||||
private: | |||||
/** | /** | ||||
Constructor. | |||||
Private constructor, reserved for TopLevelWidget class. | |||||
*/ | */ | ||||
explicit Widget(Window& parent); | |||||
explicit Widget(TopLevelWidget* topLevelWidget); | |||||
/** | /** | ||||
Constructor for a subwidget. | |||||
Private constructor, reserved for SubWidget class. | |||||
*/ | */ | ||||
explicit Widget(Widget* groupWidget); | |||||
explicit Widget(Widget* widgetToGroupTo); | |||||
public: | |||||
/** | /** | ||||
Destructor. | Destructor. | ||||
*/ | */ | ||||
@@ -215,9 +281,9 @@ public: | |||||
bool isVisible() const noexcept; | bool isVisible() const noexcept; | ||||
/** | /** | ||||
Set widget visible (or not) according to @a yesNo. | |||||
Set widget visible (or not) according to @a visible. | |||||
*/ | */ | ||||
void setVisible(bool yesNo); | |||||
void setVisible(bool visible); | |||||
/** | /** | ||||
Show widget. | Show widget. | ||||
@@ -244,7 +310,7 @@ public: | |||||
/** | /** | ||||
Get size. | Get size. | ||||
*/ | */ | ||||
const Size<uint>& getSize() const noexcept; | |||||
const Size<uint> getSize() const noexcept; | |||||
/** | /** | ||||
Set width. | Set width. | ||||
@@ -267,81 +333,58 @@ public: | |||||
void setSize(const Size<uint>& size) noexcept; | void setSize(const Size<uint>& size) noexcept; | ||||
/** | /** | ||||
Get absolute X. | |||||
*/ | |||||
int getAbsoluteX() const noexcept; | |||||
/** | |||||
Get absolute Y. | |||||
*/ | |||||
int getAbsoluteY() const noexcept; | |||||
/** | |||||
Get absolute position. | |||||
*/ | |||||
const Point<int>& getAbsolutePos() const noexcept; | |||||
/** | |||||
Set absolute X. | |||||
*/ | |||||
void setAbsoluteX(int x) noexcept; | |||||
/** | |||||
Set absolute Y. | |||||
*/ | |||||
void setAbsoluteY(int y) noexcept; | |||||
/** | |||||
Set absolute position using @a x and @a y values. | |||||
Get the Id associated with this widget. | |||||
@see setId | |||||
*/ | */ | ||||
void setAbsolutePos(int x, int y) noexcept; | |||||
uint getId() const noexcept; | |||||
/** | /** | ||||
Set absolute position. | |||||
Set an Id to be associated with this widget. | |||||
@see getId | |||||
*/ | */ | ||||
void setAbsolutePos(const Point<int>& pos) noexcept; | |||||
void setId(uint id) noexcept; | |||||
/** | /** | ||||
Get this widget's window application. | |||||
Same as calling getParentWindow().getApp(). | |||||
Get the application associated with this widget's window. | |||||
This is the same as calling `getTopLevelWidget()->getApp()`. | |||||
*/ | */ | ||||
Application& getParentApp() const noexcept; | |||||
Application& getApp() const noexcept; | |||||
/** | /** | ||||
Get parent window, as passed in the constructor. | |||||
Get the window associated with this widget. | |||||
This is the same as calling `getTopLevelWidget()->getWindow()`. | |||||
*/ | */ | ||||
Window& getParentWindow() const noexcept; | |||||
Window& getWindow() const noexcept; | |||||
/** | /** | ||||
Check if this widget contains the point defined by @a x and @a y. | |||||
Get the graphics context associated with this widget's window. | |||||
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, | |||||
for example GraphicsContext. | |||||
@see CairoSubWidget, CairoTopLevelWidget | |||||
*/ | */ | ||||
bool contains(int x, int y) const noexcept; | |||||
const GraphicsContext& getGraphicsContext() const noexcept; | |||||
/** | /** | ||||
Check if this widget contains the point @a pos. | |||||
Get top-level widget, as passed directly in the constructor | |||||
or going up the chain of group widgets until it finds the top-level one. | |||||
*/ | */ | ||||
bool contains(const Point<int>& pos) const noexcept; | |||||
TopLevelWidget* getTopLevelWidget() const noexcept; | |||||
/** | /** | ||||
Tell this widget's window to repaint itself. | |||||
Request repaint of this widget's area to the window this widget belongs to. | |||||
On the raw Widget class this function does nothing. | |||||
*/ | */ | ||||
void repaint() noexcept; | |||||
virtual void repaint() noexcept; | |||||
/** | |||||
Get the Id associated with this widget. | |||||
@see setId | |||||
*/ | |||||
uint getId() const noexcept; | |||||
DISTRHO_DEPRECATED_BY("getApp()") | |||||
Application& getParentApp() const noexcept { return getApp(); } | |||||
/** | |||||
Set an Id to be associated with this widget. | |||||
@see getId | |||||
*/ | |||||
void setId(uint id) noexcept; | |||||
DISTRHO_DEPRECATED_BY("getWindow()") | |||||
Window& getParentWindow() const noexcept { return getWindow(); } | |||||
protected: | protected: | ||||
/** | /** | ||||
A function called to draw the view contents with OpenGL. | |||||
A function called to draw the widget contents. | |||||
*/ | */ | ||||
virtual void onDisplay() = 0; | virtual void onDisplay() = 0; | ||||
@@ -357,6 +400,12 @@ protected: | |||||
*/ | */ | ||||
virtual bool onSpecial(const SpecialEvent&); | virtual bool onSpecial(const SpecialEvent&); | ||||
/** | |||||
A function called when an UTF-8 character is received. | |||||
@return True to stop event propagation, false otherwise. | |||||
*/ | |||||
virtual bool onCharacterInput(const CharacterInputEvent&); | |||||
/** | /** | ||||
A function called when a mouse button is pressed or released. | A function called when a mouse button is pressed or released. | ||||
@return True to stop event propagation, false otherwise. | @return True to stop event propagation, false otherwise. | ||||
@@ -380,30 +429,16 @@ protected: | |||||
*/ | */ | ||||
virtual void onResize(const ResizeEvent&); | virtual void onResize(const ResizeEvent&); | ||||
/** | |||||
A function called when the widget's absolute position is changed. | |||||
*/ | |||||
virtual void onPositionChanged(const PositionChangedEvent&); | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
/** @internal */ | |||||
explicit Widget(Widget* groupWidget, bool addToSubWidgets); | |||||
friend class ImageSlider; | |||||
friend class NanoWidget; | |||||
friend class Window; | |||||
friend class StandaloneWindow; | |||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
friend class DISTRHO_NAMESPACE::UI; | |||||
#endif | |||||
friend class SubWidget; | |||||
friend class TopLevelWidget; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | ||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -19,50 +19,79 @@ | |||||
#include "Geometry.hpp" | #include "Geometry.hpp" | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
START_NAMESPACE_DISTRHO | |||||
class UIExporter; | |||||
END_NAMESPACE_DISTRHO | |||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
class Application; | |||||
class TopLevelWidget; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
class Application; | |||||
class Widget; | |||||
class StandaloneWindow; | |||||
/** | |||||
DGL Window class. | |||||
This is the where all OS-related events initially happen, before being propagated to any widgets. | |||||
A Window MUST have an Application instance tied to it. | |||||
It is not possible to swap Application instances from within the lifetime of a Window. | |||||
But it is possible to completely change the Widgets that a Window contains during its lifetime. | |||||
Typically the event handling functions as following: | |||||
Application -> Window -> Top-Level-Widget -> SubWidgets | |||||
Please note that, unlike many other graphical toolkits out there, | |||||
DGL makes a clear distinction between a Window and a Widget. | |||||
You cannot directly draw in a Window, you need to create a Widget for that. | |||||
Also, a Window MUST have a single top-level Widget. | |||||
The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle. | |||||
... | |||||
*/ | |||||
class Window | class Window | ||||
{ | { | ||||
public: | public: | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
/** | /** | ||||
File browser options. | File browser options. | ||||
@see Window::openFileBrowser | |||||
*/ | */ | ||||
struct FileBrowserOptions { | struct FileBrowserOptions { | ||||
/** | |||||
File browser button state. | |||||
This allows to customize the behaviour of the file browse dialog buttons. | |||||
*/ | |||||
enum ButtonState { | |||||
kButtonInvisible, | |||||
kButtonVisibleUnchecked, | |||||
kButtonVisibleChecked, | |||||
}; | |||||
/** Start directory, uses current working directory if null */ | |||||
const char* startDir; | const char* startDir; | ||||
/** File browser dialog window title, uses "FileBrowser" if null */ | |||||
const char* title; | const char* title; | ||||
/** File browser dialog window width */ | |||||
uint width; | uint width; | ||||
/** File browser dialog window height */ | |||||
uint height; | uint height; | ||||
// TODO file filter | |||||
/** | |||||
File browser buttons. | |||||
0 means hidden. | |||||
1 means visible and unchecked. | |||||
2 means visible and checked. | |||||
/** | |||||
File browser buttons. | |||||
*/ | */ | ||||
struct Buttons { | struct Buttons { | ||||
uint listAllFiles; | |||||
uint showHidden; | |||||
uint showPlaces; | |||||
/** Whether to list all files vs only those with matching file extension */ | |||||
ButtonState listAllFiles; | |||||
/** Whether to show hidden files */ | |||||
ButtonState showHidden; | |||||
/** Whether to show list of places (bookmarks) */ | |||||
ButtonState showPlaces; | |||||
/** Constuctor for default values */ | /** Constuctor for default values */ | ||||
Buttons() | Buttons() | ||||
: listAllFiles(2), | |||||
showHidden(1), | |||||
showPlaces(1) {} | |||||
: listAllFiles(kButtonVisibleChecked), | |||||
showHidden(kButtonVisibleUnchecked), | |||||
showPlaces(kButtonVisibleUnchecked) {} | |||||
} buttons; | } buttons; | ||||
/** Constuctor for default values */ | /** Constuctor for default values */ | ||||
@@ -75,91 +104,334 @@ public: | |||||
}; | }; | ||||
#endif // DGL_FILE_BROWSER_DISABLED | #endif // DGL_FILE_BROWSER_DISABLED | ||||
/** | |||||
Constructor for a regular, standalone window. | |||||
*/ | |||||
explicit Window(Application& app); | explicit Window(Application& app); | ||||
/** | |||||
Constructor for a modal window, by having another window as its parent. | |||||
The Application instance must be the same between the 2 windows. | |||||
*/ | |||||
explicit Window(Application& app, Window& parent); | explicit Window(Application& app, Window& parent); | ||||
explicit Window(Application& app, intptr_t parentId, double scaling, bool resizable); | |||||
virtual ~Window(); | |||||
void show(); | |||||
void hide(); | |||||
void close(); | |||||
void exec(bool lockWait = false); | |||||
/** | |||||
Constructor for an embed Window without known size, | |||||
typically used in modules or plugins that run inside another host. | |||||
*/ | |||||
explicit Window(Application& app, | |||||
uintptr_t parentWindowHandle, | |||||
double scaleFactor, | |||||
bool resizable); | |||||
void focus(); | |||||
void repaint() noexcept; | |||||
/** | |||||
Constructor for an embed Window with known size, | |||||
typically used in modules or plugins that run inside another host. | |||||
*/ | |||||
explicit Window(Application& app, | |||||
uintptr_t parentWindowHandle, | |||||
uint width, | |||||
uint height, | |||||
double scaleFactor, | |||||
bool resizable); | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
bool openFileBrowser(const FileBrowserOptions& options); | |||||
#endif | |||||
/** | |||||
Destructor. | |||||
*/ | |||||
virtual ~Window(); | |||||
/** | |||||
Whether this Window is embed into another (usually not DGL-controlled) Window. | |||||
*/ | |||||
bool isEmbed() const noexcept; | bool isEmbed() const noexcept; | ||||
/** | |||||
Check if this window is visible / mapped. | |||||
Invisible windows do not receive events except resize. | |||||
@see setVisible(bool) | |||||
*/ | |||||
bool isVisible() const noexcept; | bool isVisible() const noexcept; | ||||
void setVisible(bool yesNo); | |||||
/** | |||||
Set windows visible (or not) according to @a visible. | |||||
Only valid for standalones, embed windows are always visible. | |||||
@see isVisible(), hide(), show() | |||||
*/ | |||||
void setVisible(bool visible); | |||||
/** | |||||
Show window. | |||||
This is the same as calling setVisible(true). | |||||
@see isVisible(), setVisible(bool) | |||||
*/ | |||||
void show(); | |||||
/** | |||||
Hide window. | |||||
This is the same as calling setVisible(false). | |||||
@see isVisible(), setVisible(bool) | |||||
*/ | |||||
void hide(); | |||||
/** | |||||
Hide window and notify application of a window close event. | |||||
The application event-loop will stop when all windows have been closed. | |||||
For standalone windows only, has no effect if window is embed. | |||||
@see isEmbed() | |||||
@note It is possible to hide the window while not stopping the event-loop. | |||||
A closed window is always hidden, but the reverse is not always true. | |||||
*/ | |||||
void close(); | |||||
/** | |||||
Check if this window is resizable (by the user or window manager). | |||||
@see setResizable | |||||
*/ | |||||
bool isResizable() const noexcept; | bool isResizable() const noexcept; | ||||
void setResizable(bool yesNo); | |||||
/** | |||||
Set window as resizable (by the user or window manager). | |||||
It is always possible to resize a window programmatically, which is not the same as the user being allowed to it. | |||||
@note This function does nothing for plugins, where the resizable state is set via macro. | |||||
@see DISTRHO_UI_USER_RESIZABLE | |||||
*/ | |||||
void setResizable(bool resizable); | |||||
/** | |||||
Get width. | |||||
*/ | |||||
uint getWidth() const noexcept; | uint getWidth() const noexcept; | ||||
/** | |||||
Get height. | |||||
*/ | |||||
uint getHeight() const noexcept; | uint getHeight() const noexcept; | ||||
/** | |||||
Get size. | |||||
*/ | |||||
Size<uint> getSize() const noexcept; | Size<uint> getSize() const noexcept; | ||||
/** | |||||
Set width. | |||||
*/ | |||||
void setWidth(uint width); | |||||
/** | |||||
Set height. | |||||
*/ | |||||
void setHeight(uint height); | |||||
/** | |||||
Set size using @a width and @a height values. | |||||
*/ | |||||
void setSize(uint width, uint height); | void setSize(uint width, uint height); | ||||
void setSize(Size<uint> size); | |||||
/** | |||||
Set size. | |||||
*/ | |||||
void setSize(const Size<uint>& size); | |||||
/** | |||||
Get the title of the window previously set with setTitle(). | |||||
*/ | |||||
const char* getTitle() const noexcept; | const char* getTitle() const noexcept; | ||||
void setTitle(const char* title); | |||||
void setGeometryConstraints(uint width, uint height, bool aspect); | |||||
void setTransientWinId(uintptr_t winId); | |||||
/** | |||||
Set the title of the window, typically displayed in the title bar or in window switchers. | |||||
This only makes sense for non-embedded windows. | |||||
*/ | |||||
void setTitle(const char* title); | |||||
double getScaling() const noexcept; | |||||
/** | |||||
Check if key repeat events are ignored. | |||||
*/ | |||||
bool isIgnoringKeyRepeat() const noexcept; | |||||
bool getIgnoringKeyRepeat() const noexcept; | |||||
/** | |||||
Set to ignore (or not) key repeat events according to @a ignore. | |||||
*/ | |||||
void setIgnoringKeyRepeat(bool ignore) noexcept; | void setIgnoringKeyRepeat(bool ignore) noexcept; | ||||
/** | |||||
Add a callback function to be triggered on every idle cycle or on a specific timer frequency. | |||||
You can add more than one, and remove them at anytime with removeIdleCallback(). | |||||
This can be used to perform some action at a regular interval with relatively low frequency. | |||||
If providing a timer frequency, there are a few things to note: | |||||
1. There is a platform-specific limit to the number of supported timers, and overhead associated with each, | |||||
so you should create only a few timers and perform several tasks in one if necessary. | |||||
2. This timer frequency 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. | |||||
*/ | |||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | |||||
/** | |||||
Remove an idle callback previously added via addIdleCallback(). | |||||
*/ | |||||
bool removeIdleCallback(IdleCallback* callback); | |||||
/** | |||||
Get the application associated with this window. | |||||
*/ | |||||
Application& getApp() const noexcept; | Application& getApp() const noexcept; | ||||
intptr_t getWindowId() const noexcept; | |||||
/** | |||||
Get the graphics context associated with this window. | |||||
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, | |||||
for example GraphicsContext. | |||||
@see CairoSubWidget, CairoTopLevelWidget | |||||
*/ | |||||
const GraphicsContext& getGraphicsContext() const noexcept; | const GraphicsContext& getGraphicsContext() const noexcept; | ||||
void addIdleCallback(IdleCallback* const callback); | |||||
void removeIdleCallback(IdleCallback* const callback); | |||||
/** | |||||
Get the "native" window handle. | |||||
Returned value depends on the platform: | |||||
- HaikuOS: This is a pointer to a `BView`. | |||||
- MacOS: This is a pointer to an `NSView*`. | |||||
- Windows: This is a `HWND`. | |||||
- Everything else: This is an [X11] `Window`. | |||||
*/ | |||||
uintptr_t getNativeWindowHandle() const noexcept; | |||||
/** | |||||
Get the scale factor requested for this window. | |||||
This is purely informational, and up to developers to choose what to do with it. | |||||
If you do not want to deal with this yourself, | |||||
consider using setGeometryConstraints() where you can specify to automatically scale the window contents. | |||||
@see setGeometryConstraints | |||||
*/ | |||||
double getScaleFactor() const noexcept; | |||||
/** | |||||
Grab the keyboard input focus. | |||||
*/ | |||||
void focus(); | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
/** | |||||
Open a file browser dialog with this window as parent. | |||||
A few options can be specified to setup the dialog. | |||||
If a path is selected, onFileSelected() will be called with the user chosen path. | |||||
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename. | |||||
This function does not block the event loop. | |||||
*/ | |||||
bool openFileBrowser(const FileBrowserOptions& options); | |||||
#endif | |||||
/** | |||||
Request repaint of this window, for the entire area. | |||||
*/ | |||||
void repaint() noexcept; | |||||
/** | |||||
Request partial repaint of this window, with bounds according to @a rect. | |||||
*/ | |||||
void repaint(const Rectangle<uint>& rect) noexcept; | |||||
/** | |||||
Run this window as a modal, blocking input events from the parent. | |||||
Only valid for windows that have been created with another window as parent (as passed in the constructor). | |||||
Can optionally block-wait, but such option is only available if the application is running as standalone. | |||||
*/ | |||||
void runAsModal(bool blockWait = false); | |||||
/** | |||||
Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. | |||||
*/ | |||||
void setGeometryConstraints(uint minimumWidth, | |||||
uint minimumHeight, | |||||
bool keepAspectRatio = false, | |||||
bool automaticallyScale = false); | |||||
/** DEPRECATED Use isIgnoringKeyRepeat(). */ | |||||
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") | |||||
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } | |||||
/** DEPRECATED Use getScaleFactor(). */ | |||||
DISTRHO_DEPRECATED_BY("getScaleFactor()") | |||||
inline double getScaling() const noexcept { return getScaleFactor(); } | |||||
/** DEPRECATED Use runAsModal(bool). */ | |||||
DISTRHO_DEPRECATED_BY("runAsModal(bool)") | |||||
inline void exec(bool blockWait = false) { runAsModal(blockWait); } | |||||
// TESTING, DO NOT USE | |||||
void leaveContext(); | |||||
protected: | protected: | ||||
virtual void onDisplayBefore(); | |||||
virtual void onDisplayAfter(); | |||||
/** | |||||
A function called when the window is attempted to be closed. | |||||
Returning true closes the window, which is the default behaviour. | |||||
Override this method and return false to prevent the window from being closed by the user. | |||||
This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI. | |||||
For embed windows, closing is handled by the host/parent process and we have no control over it. | |||||
As such, a close action on embed windows will always succeed and cannot be cancelled. | |||||
*/ | |||||
virtual bool onClose(); | |||||
/** | |||||
A function called when the window gains or loses the keyboard focus. | |||||
The default implementation does nothing. | |||||
*/ | |||||
virtual void onFocus(bool focus, CrossingMode mode); | |||||
/** | |||||
A function called when the window is resized. | |||||
If there is a top-level widget associated with this window, its size will be set right after this function. | |||||
The default implementation sets up drawing context where necessary. | |||||
*/ | |||||
virtual void onReshape(uint width, uint height); | virtual void onReshape(uint width, uint height); | ||||
virtual void onClose(); | |||||
/** | |||||
A function called when scale factor requested for this window changes. | |||||
The default implementation does nothing. | |||||
WARNING function needs a proper name | |||||
*/ | |||||
virtual void onScaleFactorChanged(double scaleFactor); | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
virtual void fileBrowserSelected(const char* filename); | |||||
#endif | |||||
/** | |||||
A function called when a path is selected by the user, as triggered by openFileBrowser(). | |||||
This action happens after the user confirms the action, so the file browser dialog will be closed at this point. | |||||
The default implementation does nothing. | |||||
*/ | |||||
virtual void onFileSelected(const char* filename); | |||||
// internal | |||||
void _setAutoScaling(double scaling) noexcept; | |||||
/** DEPRECATED Use onFileSelected(). */ | |||||
DISTRHO_DEPRECATED_BY("onFileSelected(const char*)") | |||||
inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); } | |||||
#endif | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
friend class Application; | friend class Application; | ||||
friend class Widget; | |||||
friend class StandaloneWindow; | |||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
friend class DISTRHO_NAMESPACE::UIExporter; | |||||
#endif | |||||
virtual void _addWidget(Widget* const widget); | |||||
virtual void _removeWidget(Widget* const widget); | |||||
void _idle(); | |||||
bool handlePluginKeyboard(const bool press, const uint key); | |||||
bool handlePluginSpecial(const bool press, const Key key); | |||||
friend class TopLevelWidget; | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
/* TODO | |||||
* add eventcrossing/enter-leave event | |||||
*/ | |||||
#if 0 | |||||
protected: | |||||
bool handlePluginKeyboard(const bool press, const uint key); | |||||
bool handlePluginSpecial(const bool press, const Key key); | |||||
#endif | |||||
// ----------------------------------------------------------------------- | |||||
#endif // DGL_WINDOW_HPP_INCLUDED | #endif // DGL_WINDOW_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -15,14 +15,13 @@ | |||||
*/ | */ | ||||
#include "ApplicationPrivateData.hpp" | #include "ApplicationPrivateData.hpp" | ||||
#include "../Window.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
Application::Application() | |||||
: pData(new PrivateData()) {} | |||||
Application::Application(const bool isStandalone) | |||||
: pData(new PrivateData(isStandalone)) {} | |||||
Application::~Application() | Application::~Application() | ||||
{ | { | ||||
@@ -31,44 +30,48 @@ Application::~Application() | |||||
void Application::idle() | void Application::idle() | ||||
{ | { | ||||
for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it) | |||||
{ | |||||
Window* const window(*it); | |||||
window->_idle(); | |||||
} | |||||
for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it) | |||||
{ | |||||
IdleCallback* const idleCallback(*it); | |||||
idleCallback->idleCallback(); | |||||
} | |||||
pData->idle(0); | |||||
} | } | ||||
void Application::exec(int idleTime) | |||||
void Application::exec(const uint idleTimeInMs) | |||||
{ | { | ||||
for (; pData->doLoop;) | |||||
{ | |||||
idle(); | |||||
d_msleep(idleTime); | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | |||||
while (! pData->isQuitting) | |||||
pData->idle(idleTimeInMs); | |||||
} | } | ||||
void Application::quit() | void Application::quit() | ||||
{ | { | ||||
pData->doLoop = false; | |||||
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | |||||
for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit) | |||||
{ | |||||
Window* const window(*rit); | |||||
window->close(); | |||||
} | |||||
pData->quit(); | |||||
} | } | ||||
bool Application::isQuiting() const noexcept | bool Application::isQuiting() const noexcept | ||||
{ | { | ||||
return !pData->doLoop; | |||||
return pData->isQuitting || pData->isQuittingInNextCycle; | |||||
} | |||||
void Application::addIdleCallback(IdleCallback* const callback) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | |||||
pData->idleCallbacks.push_back(callback); | |||||
} | |||||
void Application::removeIdleCallback(IdleCallback* const callback) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | |||||
pData->idleCallbacks.remove(callback); | |||||
} | |||||
void Application::setClassName(const char* const name) | |||||
{ | |||||
pData->setClassName(name); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -0,0 +1,159 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "ApplicationPrivateData.hpp" | |||||
#include "../Window.hpp" | |||||
#include "pugl.hpp" | |||||
#include <ctime> | |||||
START_NAMESPACE_DGL | |||||
typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator; | |||||
static ThreadHandle getCurrentThreadHandle() noexcept | |||||
{ | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
return GetCurrentThread(); | |||||
#else | |||||
return pthread_self(); | |||||
#endif | |||||
} | |||||
static bool isThisMainThread(const ThreadHandle mainThreadHandle) noexcept | |||||
{ | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ? | |||||
#else | |||||
return pthread_equal(getCurrentThreadHandle(), mainThreadHandle) != 0; | |||||
#endif | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
Application::PrivateData::PrivateData(const bool standalone) | |||||
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||||
standalone ? PUGL_WORLD_THREADS : 0x0)), | |||||
isStandalone(standalone), | |||||
isQuitting(false), | |||||
isQuittingInNextCycle(false), | |||||
isStarting(true), | |||||
visibleWindows(0), | |||||
mainThreadHandle(getCurrentThreadHandle()), | |||||
windows(), | |||||
idleCallbacks() | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||||
puglSetWorldHandle(world, this); | |||||
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | |||||
#ifdef HAVE_X11 | |||||
sofdFileDialogSetup(world); | |||||
#endif | |||||
} | |||||
Application::PrivateData::~PrivateData() | |||||
{ | |||||
DISTRHO_SAFE_ASSERT(isStarting || isQuitting); | |||||
DISTRHO_SAFE_ASSERT(visibleWindows == 0); | |||||
windows.clear(); | |||||
idleCallbacks.clear(); | |||||
if (world != nullptr) | |||||
puglFreeWorld(world); | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
void Application::PrivateData::oneWindowShown() noexcept | |||||
{ | |||||
if (++visibleWindows == 1) | |||||
{ | |||||
isQuitting = false; | |||||
isStarting = false; | |||||
} | |||||
} | |||||
void Application::PrivateData::oneWindowClosed() noexcept | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,); | |||||
if (--visibleWindows == 0) | |||||
isQuitting = true; | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
void Application::PrivateData::idle(const uint timeoutInMs) | |||||
{ | |||||
if (isQuittingInNextCycle) | |||||
{ | |||||
quit(); | |||||
isQuittingInNextCycle = false; | |||||
} | |||||
if (world != nullptr) | |||||
{ | |||||
const double timeoutInSeconds = timeoutInMs != 0 | |||||
? static_cast<double>(timeoutInMs) / 1000.0 | |||||
: 0.0; | |||||
puglUpdate(world, timeoutInSeconds); | |||||
} | |||||
for (std::list<IdleCallback*>::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) | |||||
{ | |||||
IdleCallback* const idleCallback(*it); | |||||
idleCallback->idleCallback(); | |||||
} | |||||
} | |||||
void Application::PrivateData::quit() | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); | |||||
if (! isThisMainThread(mainThreadHandle)) | |||||
{ | |||||
if (! isQuittingInNextCycle) | |||||
{ | |||||
isQuittingInNextCycle = true; | |||||
return; | |||||
} | |||||
} | |||||
isQuitting = true; | |||||
#ifndef DPF_TEST_APPLICATION_CPP | |||||
for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) | |||||
{ | |||||
DGL_NAMESPACE::Window* const window(*rit); | |||||
window->close(); | |||||
} | |||||
#endif | |||||
} | |||||
void Application::PrivateData::setClassName(const char* const name) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |||||
puglSetClassName(world, name); | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,53 +18,83 @@ | |||||
#define DGL_APP_PRIVATE_DATA_HPP_INCLUDED | #define DGL_APP_PRIVATE_DATA_HPP_INCLUDED | ||||
#include "../Application.hpp" | #include "../Application.hpp" | ||||
#include "../../distrho/extra/Sleep.hpp" | |||||
#include <list> | #include <list> | ||||
#ifdef DISTRHO_OS_WINDOWS | |||||
# include <winsock2.h> | |||||
# include <windows.h> | |||||
typedef HANDLE ThreadHandle; | |||||
#else | |||||
# include <pthread.h> | |||||
typedef pthread_t ThreadHandle; | |||||
#endif | |||||
typedef struct PuglWorldImpl PuglWorld; | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
class Window; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
struct Application::PrivateData { | struct Application::PrivateData { | ||||
bool doLoop; | |||||
/** Pugl world instance. */ | |||||
PuglWorld* const world; | |||||
/** Whether the application is running as standalone, otherwise it is part of a plugin. */ | |||||
const bool isStandalone; | |||||
/** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | |||||
bool isQuitting; | |||||
/** Helper for safely close everything from main thread. */ | |||||
bool isQuittingInNextCycle; | |||||
/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||||
bool isStarting; | |||||
/** Counter of visible windows, only used in standalone mode. | |||||
If 0->1, application is starting. If 1->0, application is quitting/stopping. */ | |||||
uint visibleWindows; | uint visibleWindows; | ||||
std::list<Window*> windows; | |||||
std::list<IdleCallback*> idleCallbacks; | |||||
PrivateData() | |||||
: doLoop(true), | |||||
visibleWindows(0), | |||||
windows(), | |||||
idleCallbacks() {} | |||||
~PrivateData() | |||||
{ | |||||
DISTRHO_SAFE_ASSERT(! doLoop); | |||||
DISTRHO_SAFE_ASSERT(visibleWindows == 0); | |||||
windows.clear(); | |||||
idleCallbacks.clear(); | |||||
} | |||||
void oneShown() noexcept | |||||
{ | |||||
if (++visibleWindows == 1) | |||||
doLoop = true; | |||||
} | |||||
void oneHidden() noexcept | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,); | |||||
if (--visibleWindows == 0) | |||||
doLoop = false; | |||||
} | |||||
DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||||
/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */ | |||||
ThreadHandle mainThreadHandle; | |||||
/** List of windows for this application. Only used during `close`. */ | |||||
std::list<DGL_NAMESPACE::Window*> windows; | |||||
/** List of idle callbacks for this application. */ | |||||
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | |||||
/** Constructor and destructor */ | |||||
explicit PrivateData(bool standalone); | |||||
~PrivateData(); | |||||
/** Flag one window as shown, which increments @a visibleWindows. | |||||
Sets @a isQuitting and @a isStarting as false if this is the first window. | |||||
For standalone mode only. */ | |||||
void oneWindowShown() noexcept; | |||||
/** Flag one window as closed, which decrements @a visibleWindows. | |||||
Sets @a isQuitting as true if this is the last window. | |||||
For standalone mode only. */ | |||||
void oneWindowClosed() noexcept; | |||||
/** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */ | |||||
void idle(uint timeoutInMs); | |||||
/** Set flag indicating application is quitting, and close all windows in reverse order of registration. | |||||
For standalone mode only. */ | |||||
void quit(); | |||||
/** Set pugl world class name. */ | |||||
void setClassName(const char* name); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -1,6 +1,7 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -14,77 +15,148 @@ | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include "../Geometry.hpp" | |||||
#ifdef _MSC_VER | |||||
// instantiated template classes whose methods are defined elsewhere | |||||
# pragma warning(disable:4661) | |||||
#endif | |||||
#include "../Cairo.hpp" | #include "../Cairo.hpp" | ||||
#include "../Color.hpp" | |||||
#include "../ImageBaseWidgets.hpp" | |||||
#include "Common.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "TopLevelWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
#include "WindowPrivateData.hpp" | |||||
// templated classes | |||||
#include "ImageBaseWidgets.cpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
static void notImplemented(const char *name) | |||||
static void notImplemented(const char* const name) | |||||
{ | { | ||||
d_stderr2("cairo function not implemented: %s", name); | d_stderr2("cairo function not implemented: %s", name); | ||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// Color | |||||
void Color::setFor(const GraphicsContext& context, const bool includeAlpha) | |||||
{ | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
if (includeAlpha) | |||||
cairo_set_source_rgba(handle, red, green, blue, alpha); | |||||
else | |||||
cairo_set_source_rgb(handle, red, green, blue); | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Line | // Line | ||||
template<typename T> | |||||
void Line<T>::draw(const GraphicsContext& context, const T width) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
cairo_set_line_width(handle, width); | |||||
cairo_move_to(handle, posStart.getX(), posStart.getY()); | |||||
cairo_line_to(handle, posEnd.getX(), posEnd.getY()); | |||||
cairo_stroke(handle); | |||||
} | |||||
template<typename T> | template<typename T> | ||||
void Line<T>::draw() | void Line<T>::draw() | ||||
{ | { | ||||
notImplemented("Line::draw"); | notImplemented("Line::draw"); | ||||
} | } | ||||
template class Line<double>; | |||||
template class Line<float>; | |||||
template class Line<int>; | |||||
template class Line<uint>; | |||||
template class Line<short>; | |||||
template class Line<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Circle | // Circle | ||||
template<typename T> | template<typename T> | ||||
void Circle<T>::_draw(const bool outline) | |||||
static void drawCircle(cairo_t* const handle, | |||||
const Point<T>& pos, | |||||
const uint numSegments, | |||||
const float size, | |||||
const float sin, | |||||
const float cos, | |||||
const bool outline) | |||||
{ | { | ||||
notImplemented("Circle::draw"); | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); | |||||
// ----------------------------------------------------------------------- | |||||
// Triangle | |||||
const T origx = pos.getX(); | |||||
const T origy = pos.getY(); | |||||
double t, x = size, y = 0.0; | |||||
// TODO use arc | |||||
/* | |||||
cairo_arc(handle, origx, origy, size, sin, cos); | |||||
*/ | |||||
cairo_move_to(handle, x + origx, y + origy); | |||||
for (uint i=1; i<numSegments; ++i) | |||||
{ | |||||
cairo_line_to(handle, x + origx, y + origy); | |||||
t = x; | |||||
x = cos * x - sin * y; | |||||
y = sin * t + cos * y; | |||||
} | |||||
cairo_line_to(handle, x + origx, y + origy); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | |||||
template<typename T> | template<typename T> | ||||
void Triangle<T>::_draw(const bool outline) | |||||
void Circle<T>::draw(const GraphicsContext& context) | |||||
{ | { | ||||
notImplemented("Triangle::draw"); | |||||
} | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
// ----------------------------------------------------------------------- | |||||
// Rectangle | |||||
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
} | |||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::_draw(const bool outline) | |||||
void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||||
{ | { | ||||
notImplemented("Rectangle::draw"); | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
// ----------------------------------------------------------------------- | |||||
// Possible template data types | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
template class Point<double>; | |||||
template class Point<float>; | |||||
template class Point<int>; | |||||
template class Point<uint>; | |||||
template class Point<short>; | |||||
template class Point<ushort>; | |||||
cairo_set_line_width(handle, lineWidth); | |||||
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
} | |||||
template class Size<double>; | |||||
template class Size<float>; | |||||
template class Size<int>; | |||||
template class Size<uint>; | |||||
template class Size<short>; | |||||
template class Size<ushort>; | |||||
template<typename T> | |||||
void Circle<T>::draw() | |||||
{ | |||||
notImplemented("Circle::draw"); | |||||
} | |||||
template class Line<double>; | |||||
template class Line<float>; | |||||
template class Line<int>; | |||||
template class Line<uint>; | |||||
template class Line<short>; | |||||
template class Line<ushort>; | |||||
template<typename T> | |||||
void Circle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Circle::drawOutline"); | |||||
} | |||||
template class Circle<double>; | template class Circle<double>; | ||||
template class Circle<float>; | template class Circle<float>; | ||||
@@ -93,6 +165,60 @@ template class Circle<uint>; | |||||
template class Circle<short>; | template class Circle<short>; | ||||
template class Circle<ushort>; | template class Circle<ushort>; | ||||
// ----------------------------------------------------------------------- | |||||
// Triangle | |||||
template<typename T> | |||||
static void drawTriangle(cairo_t* const handle, | |||||
const Point<T>& pos1, | |||||
const Point<T>& pos2, | |||||
const Point<T>& pos3, | |||||
const bool outline) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |||||
cairo_move_to(handle, pos1.getX(), pos1.getY()); | |||||
cairo_line_to(handle, pos2.getX(), pos2.getY()); | |||||
cairo_line_to(handle, pos3.getX(), pos3.getY()); | |||||
cairo_line_to(handle, pos1.getX(), pos1.getY()); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::draw(const GraphicsContext& context) | |||||
{ | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
drawTriangle<T>(handle, pos1, pos2, pos3, false); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
cairo_set_line_width(handle, lineWidth); | |||||
drawTriangle<T>(handle, pos1, pos2, pos3, true); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::draw() | |||||
{ | |||||
notImplemented("Triangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Triangle::drawOutline"); | |||||
} | |||||
template class Triangle<double>; | template class Triangle<double>; | ||||
template class Triangle<float>; | template class Triangle<float>; | ||||
template class Triangle<int>; | template class Triangle<int>; | ||||
@@ -100,6 +226,54 @@ template class Triangle<uint>; | |||||
template class Triangle<short>; | template class Triangle<short>; | ||||
template class Triangle<ushort>; | template class Triangle<ushort>; | ||||
// ----------------------------------------------------------------------- | |||||
// Rectangle | |||||
template<typename T> | |||||
static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline) | |||||
{ | |||||
cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::draw(const GraphicsContext& context) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
drawRectangle(handle, *this, false); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
cairo_set_line_width(handle, lineWidth); | |||||
drawRectangle(handle, *this, true); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::draw() | |||||
{ | |||||
notImplemented("Rectangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Rectangle::drawOutline"); | |||||
} | |||||
template class Rectangle<double>; | template class Rectangle<double>; | ||||
template class Rectangle<float>; | template class Rectangle<float>; | ||||
template class Rectangle<int>; | template class Rectangle<int>; | ||||
@@ -107,6 +281,529 @@ template class Rectangle<uint>; | |||||
template class Rectangle<short>; | template class Rectangle<short>; | ||||
template class Rectangle<ushort>; | template class Rectangle<ushort>; | ||||
// ----------------------------------------------------------------------- | |||||
// CairoImage | |||||
static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatGrayscale: | |||||
return CAIRO_FORMAT_A8; | |||||
case kImageFormatBGR: | |||||
case kImageFormatRGB: | |||||
return CAIRO_FORMAT_RGB24; | |||||
case kImageFormatBGRA: | |||||
case kImageFormatRGBA: | |||||
return CAIRO_FORMAT_ARGB32; | |||||
} | |||||
return CAIRO_FORMAT_INVALID; | |||||
} | |||||
/* | |||||
static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case CAIRO_FORMAT_INVALID: | |||||
break; | |||||
case CAIRO_FORMAT_ARGB32: | |||||
break; | |||||
case CAIRO_FORMAT_RGB24: | |||||
break; | |||||
case CAIRO_FORMAT_A8: | |||||
break; | |||||
case CAIRO_FORMAT_A1: | |||||
break; | |||||
case CAIRO_FORMAT_RGB16_565: | |||||
break; | |||||
case CAIRO_FORMAT_RGB30: | |||||
break; | |||||
} | |||||
return kImageFormatNull; | |||||
} | |||||
*/ | |||||
CairoImage::CairoImage() | |||||
: ImageBase(), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) {} | |||||
CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) | |||||
: ImageBase(rdata, w, h, fmt), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) | |||||
{ | |||||
loadFromMemory(rdata, w, h, fmt); | |||||
} | |||||
CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||||
: ImageBase(rdata, s, fmt), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) | |||||
{ | |||||
loadFromMemory(rdata, s, fmt); | |||||
} | |||||
CairoImage::CairoImage(const CairoImage& image) | |||||
: ImageBase(image.rawData, image.size, image.format), | |||||
surface(cairo_surface_reference(image.surface)), | |||||
surfacedata(image.surfacedata), | |||||
datarefcount(image.datarefcount) | |||||
{ | |||||
if (datarefcount != nullptr) | |||||
++(*datarefcount); | |||||
} | |||||
CairoImage::~CairoImage() | |||||
{ | |||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
{ | |||||
std::free(surfacedata); | |||||
std::free(datarefcount); | |||||
} | |||||
} | |||||
void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||||
{ | |||||
const cairo_format_t cairoformat = asCairoImageFormat(fmt); | |||||
const int width = static_cast<int>(s.getWidth()); | |||||
const int height = static_cast<int>(s.getHeight()); | |||||
const int stride = cairo_format_stride_for_width(cairoformat, width); | |||||
uchar* const newdata = (uchar*)std::malloc(static_cast<size_t>(width * height * stride * 4)); | |||||
DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,); | |||||
cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); | |||||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),); | |||||
DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),); | |||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
std::free(surfacedata); | |||||
else | |||||
datarefcount = (int*)malloc(sizeof(*datarefcount)); | |||||
surface = newsurface; | |||||
surfacedata = newdata; | |||||
*datarefcount = 1; | |||||
switch (fmt) | |||||
{ | |||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatGrayscale: | |||||
// Grayscale to A8 | |||||
// TODO | |||||
break; | |||||
case kImageFormatBGR: | |||||
// BGR8 to CAIRO_FORMAT_RGB24 | |||||
for (int h = 0; h < height; ++h) | |||||
{ | |||||
for (int w = 0; w < width; ++w) | |||||
{ | |||||
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*3+w*3+0]); | |||||
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*3+w*3+1]); | |||||
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*3+w*3+2]); | |||||
newdata[h*width*4+w*4+3] = 0; | |||||
} | |||||
} | |||||
break; | |||||
case kImageFormatBGRA: | |||||
// BGRA8 to CAIRO_FORMAT_ARGB32 | |||||
// FIXME something is wrong here... | |||||
for (int h = 0, t; h < height; ++h) | |||||
{ | |||||
for (int w = 0; w < width; ++w) | |||||
{ | |||||
if ((t = rdata[h*width*4+w*4+3]) != 0) | |||||
{ | |||||
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*4+w*4+0]); | |||||
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*4+w*4+1]); | |||||
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*4+w*4+2]); | |||||
newdata[h*width*4+w*4+3] = static_cast<uchar>(t); | |||||
} | |||||
else | |||||
{ | |||||
// make all pixels zero, cairo does not render full transparency otherwise | |||||
memset(&newdata[h*width*4+w*4], 0, 4); | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
case kImageFormatRGB: | |||||
// RGB8 to CAIRO_FORMAT_RGB24 | |||||
// TODO | |||||
break; | |||||
case kImageFormatRGBA: | |||||
// RGBA8 to CAIRO_FORMAT_ARGB32 | |||||
// TODO | |||||
break; | |||||
} | |||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
} | |||||
// const GraphicsContext& context | |||||
void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept | |||||
{ | |||||
struct PngReaderData | |||||
{ | |||||
const char* dataPtr; | |||||
uint sizeLeft; | |||||
static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept | |||||
{ | |||||
PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure); | |||||
if (readerData.sizeLeft < length) | |||||
return CAIRO_STATUS_READ_ERROR; | |||||
std::memcpy(data, readerData.dataPtr, length); | |||||
readerData.dataPtr += length; | |||||
readerData.sizeLeft -= length; | |||||
return CAIRO_STATUS_SUCCESS; | |||||
} | |||||
}; | |||||
PngReaderData readerData; | |||||
readerData.dataPtr = pngData; | |||||
readerData.sizeLeft = pngSize; | |||||
cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData); | |||||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||||
const int newwidth = cairo_image_surface_get_width(newsurface); | |||||
const int newheight = cairo_image_surface_get_height(newsurface); | |||||
DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,); | |||||
DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,); | |||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
std::free(surfacedata); | |||||
else | |||||
datarefcount = (int*)malloc(sizeof(*datarefcount)); | |||||
surface = newsurface; | |||||
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface); | |||||
*datarefcount = 1; | |||||
rawData = nullptr; | |||||
format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface)); | |||||
size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight)); | |||||
} | |||||
void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos) | |||||
{ | |||||
if (surface == nullptr) | |||||
return; | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); | |||||
cairo_paint(handle); | |||||
} | |||||
CairoImage& CairoImage::operator=(const CairoImage& image) noexcept | |||||
{ | |||||
cairo_surface_t* newsurface = cairo_surface_reference(image.surface); | |||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
{ | |||||
std::free(surfacedata); | |||||
std::free(datarefcount); | |||||
} | |||||
surface = newsurface; | |||||
rawData = image.rawData; | |||||
size = image.size; | |||||
format = image.format; | |||||
surfacedata = image.surfacedata; | |||||
datarefcount = image.datarefcount; | |||||
if (datarefcount != nullptr) | |||||
++(*datarefcount); | |||||
return *this; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// CairoSubWidget | |||||
template <> | |||||
CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent) | |||||
: SubWidget(parent) {} | |||||
template class CairoBaseWidget<SubWidget>; | |||||
// ----------------------------------------------------------------------- | |||||
// CairoTopLevelWidget | |||||
template <> | |||||
CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo) | |||||
: TopLevelWidget(windowToMapTo) {} | |||||
template class CairoBaseWidget<TopLevelWidget>; | |||||
// ----------------------------------------------------------------------- | |||||
// CairoStandaloneWindow | |||||
template <> | |||||
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app) | |||||
: StandaloneWindow(app) {} | |||||
template <> | |||||
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow) | |||||
: StandaloneWindow(app, parentWindow) {} | |||||
template class CairoBaseWidget<StandaloneWindow>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseAboutWindow | |||||
#if 0 | |||||
template <> | |||||
void ImageBaseAboutWindow<CairoImage>::onDisplay() | |||||
{ | |||||
img.draw(getGraphicsContext()); | |||||
} | |||||
#endif | |||||
template class ImageBaseAboutWindow<CairoImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseButton | |||||
template class ImageBaseButton<CairoImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseKnob | |||||
template <> | |||||
void ImageBaseKnob<CairoImage>::PrivateData::init() | |||||
{ | |||||
alwaysRepaint = true; | |||||
cairoSurface = nullptr; | |||||
} | |||||
template <> | |||||
void ImageBaseKnob<CairoImage>::PrivateData::cleanup() | |||||
{ | |||||
cairo_surface_destroy((cairo_surface_t*)cairoSurface); | |||||
cairoSurface = nullptr; | |||||
} | |||||
/** | |||||
Get the pixel size in bytes. | |||||
@return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes. | |||||
*/ | |||||
static int getBytesPerPixel(const cairo_format_t format) noexcept | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case CAIRO_FORMAT_ARGB32: | |||||
case CAIRO_FORMAT_RGB24: | |||||
case CAIRO_FORMAT_RGB30: | |||||
return 4; | |||||
case CAIRO_FORMAT_RGB16_565: | |||||
return 2; | |||||
case CAIRO_FORMAT_A8: | |||||
return 1; | |||||
case CAIRO_FORMAT_A1: | |||||
return 0; | |||||
default: | |||||
DISTRHO_SAFE_ASSERT(false); | |||||
return 0; | |||||
} | |||||
} | |||||
static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept | |||||
{ | |||||
const cairo_format_t format = cairo_image_surface_get_format(origsurface); | |||||
const int bpp = getBytesPerPixel(format); | |||||
if (bpp == 0) | |||||
return nullptr; | |||||
const int fullWidth = cairo_image_surface_get_width(origsurface); | |||||
const int fullHeight = cairo_image_surface_get_height(origsurface); | |||||
const int stride = cairo_image_surface_get_stride(origsurface); | |||||
uchar* const fullData = cairo_image_surface_get_data(origsurface); | |||||
x = (x < fullWidth) ? x : fullWidth; | |||||
y = (y < fullHeight) ? y : fullHeight; | |||||
width = (x + width < fullWidth) ? width : (fullWidth - x); | |||||
height = (x + height < fullHeight) ? height : (fullHeight - x); | |||||
uchar* const data = fullData + (x * bpp + y * stride); | |||||
return cairo_image_surface_create_for_data(data, format, width, height, stride); | |||||
} | |||||
template <> | |||||
void ImageBaseKnob<CairoImage>::onDisplay() | |||||
{ | |||||
const GraphicsContext& context(getGraphicsContext()); | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||||
/ (pData->maximum - pData->minimum); | |||||
cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | |||||
if (! pData->isReady) | |||||
{ | |||||
const int layerW = static_cast<int>(pData->imgLayerWidth); | |||||
const int layerH = static_cast<int>(pData->imgLayerHeight); | |||||
int layerNum = 0; | |||||
if (pData->rotationAngle == 0) | |||||
layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5); | |||||
const int layerX = pData->isImgVertical ? 0 : layerNum * layerW; | |||||
const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH; | |||||
cairo_surface_t* newsurface; | |||||
if (pData->rotationAngle == 0) | |||||
{ | |||||
newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); | |||||
} | |||||
else | |||||
{ | |||||
newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH); | |||||
cairo_t* const cr = cairo_create(newsurface); | |||||
cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); | |||||
cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180)); | |||||
cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH); | |||||
cairo_paint(cr); | |||||
cairo_destroy(cr); | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||||
cairo_surface_destroy(surface); | |||||
pData->cairoSurface = surface = newsurface; | |||||
pData->isReady = true; | |||||
} | |||||
if (surface != nullptr) | |||||
{ | |||||
cairo_set_source_surface(handle, surface, 0, 0); | |||||
cairo_paint(handle); | |||||
} | |||||
} | |||||
template class ImageBaseKnob<CairoImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseSlider | |||||
template class ImageBaseSlider<CairoImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseSwitch | |||||
template class ImageBaseSwitch<CairoImage>; | |||||
// ----------------------------------------------------------------------- | |||||
void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |||||
{ | |||||
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle; | |||||
bool needsResetClip = false; | |||||
cairo_matrix_t matrix; | |||||
cairo_get_matrix(handle, &matrix); | |||||
if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height))) | |||||
{ | |||||
// full viewport size | |||||
cairo_translate(handle, 0, 0); | |||||
} | |||||
else if (needsViewportScaling) | |||||
{ | |||||
// limit viewport to widget bounds | |||||
// NOTE only used for nanovg for now, which is not relevant here | |||||
cairo_translate(handle, 0, 0); | |||||
} | |||||
else | |||||
{ | |||||
// set viewport pos | |||||
cairo_translate(handle, absolutePos.getX(), absolutePos.getY()); | |||||
// then cut the outer bounds | |||||
cairo_rectangle(handle, | |||||
0, | |||||
0, | |||||
std::round(self->getWidth() * autoScaleFactor), | |||||
std::round(self->getHeight() * autoScaleFactor)); | |||||
cairo_clip(handle); | |||||
needsResetClip = true; | |||||
} | |||||
// display widget | |||||
self->onDisplay(); | |||||
if (needsResetClip) | |||||
cairo_reset_clip(handle); | |||||
cairo_set_matrix(handle, &matrix); | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
void TopLevelWidget::PrivateData::display() | |||||
{ | |||||
if (! selfw->pData->visible) | |||||
return; | |||||
const Size<uint> size(window.getSize()); | |||||
const uint width = size.getWidth(); | |||||
const uint height = size.getHeight(); | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
// FIXME anything needed here? | |||||
#if 0 | |||||
// full viewport size | |||||
if (window.pData->autoScaling) | |||||
glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); | |||||
else | |||||
glViewport(0, 0, width, height); | |||||
#endif | |||||
// main widget drawing | |||||
self->onDisplay(); | |||||
// now draw subwidgets if there are any | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||||
{ | |||||
GraphicsContext& context((GraphicsContext&)graphicsContext); | |||||
((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); | |||||
return context; | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -16,10 +16,6 @@ | |||||
#include "../Color.hpp" | #include "../Color.hpp" | ||||
#ifndef HAVE_DCAIRO | |||||
#include "nanovg/nanovg.h" | |||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -61,27 +57,27 @@ static uchar getFixedRange2(const float& value) | |||||
return 0; | return 0; | ||||
if (value2 >= 255.0f) | if (value2 >= 255.0f) | ||||
return 255; | return 255; | ||||
return static_cast<uchar>(value2); | |||||
return static_cast<uchar>(value2 + 0.5f); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
Color::Color() noexcept | Color::Color() noexcept | ||||
: red(1.0f), | |||||
green(1.0f), | |||||
blue(1.0f), | |||||
: red(0.0f), | |||||
green(0.0f), | |||||
blue(0.0f), | |||||
alpha(1.0f) {} | alpha(1.0f) {} | ||||
Color::Color(int r, int g, int b, int a) noexcept | |||||
Color::Color(const int r, const int g, const int b, const float a) noexcept | |||||
: red(static_cast<float>(r)/255.0f), | : red(static_cast<float>(r)/255.0f), | ||||
green(static_cast<float>(g)/255.0f), | green(static_cast<float>(g)/255.0f), | ||||
blue(static_cast<float>(b)/255.0f), | blue(static_cast<float>(b)/255.0f), | ||||
alpha(static_cast<float>(a)/255.0f) | |||||
alpha(a) | |||||
{ | { | ||||
fixBounds(); | fixBounds(); | ||||
} | } | ||||
Color::Color(float r, float g, float b, float a) noexcept | |||||
Color::Color(const float r, const float g, const float b, const float a) noexcept | |||||
: red(r), | : red(r), | ||||
green(g), | green(g), | ||||
blue(b), | blue(b), | ||||
@@ -109,7 +105,7 @@ Color& Color::operator=(const Color& color) noexcept | |||||
return *this; | return *this; | ||||
} | } | ||||
Color::Color(const Color& color1, const Color& color2, float u) noexcept | |||||
Color::Color(const Color& color1, const Color& color2, const float u) noexcept | |||||
: red(color1.red), | : red(color1.red), | ||||
green(color1.green), | green(color1.green), | ||||
blue(color1.blue), | blue(color1.blue), | ||||
@@ -136,65 +132,66 @@ Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | |||||
return col; | return col; | ||||
} | } | ||||
Color Color::fromHTML(const char* rgb, float alpha) | |||||
Color Color::fromHTML(const char* rgb, const float alpha) noexcept | |||||
{ | { | ||||
Color fallback; | Color fallback; | ||||
DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback); | DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback); | ||||
if (rgb[0] == '#') ++rgb; | |||||
if (rgb[0] == '#') | |||||
++rgb; | |||||
DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback); | DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback); | ||||
std::size_t rgblen(std::strlen(rgb)); | |||||
std::size_t rgblen = std::strlen(rgb); | |||||
DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback); | DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback); | ||||
char rgbtmp[3] = { '\0', '\0', '\0' }; | |||||
char rgbtmp[5] = { '0', 'x', '\0', '\0', '\0' }; | |||||
int r, g, b; | int r, g, b; | ||||
if (rgblen == 3) | if (rgblen == 3) | ||||
{ | { | ||||
rgbtmp[0] = rgb[0]; | |||||
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
rgbtmp[2] = rgb[0]; | |||||
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17; | |||||
rgbtmp[0] = rgb[1]; | |||||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
rgbtmp[2] = rgb[1]; | |||||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17; | |||||
rgbtmp[0] = rgb[2]; | |||||
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
rgbtmp[2] = rgb[2]; | |||||
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
rgbtmp[0] = rgb[0]; | |||||
rgbtmp[1] = rgb[1]; | |||||
rgbtmp[2] = rgb[0]; | |||||
rgbtmp[3] = rgb[1]; | |||||
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | ||||
rgbtmp[0] = rgb[2]; | |||||
rgbtmp[1] = rgb[3]; | |||||
rgbtmp[2] = rgb[2]; | |||||
rgbtmp[3] = rgb[3]; | |||||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | ||||
rgbtmp[0] = rgb[4]; | |||||
rgbtmp[1] = rgb[5]; | |||||
rgbtmp[2] = rgb[4]; | |||||
rgbtmp[3] = rgb[5]; | |||||
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | ||||
} | } | ||||
return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f)); | |||||
return Color(r, g, b, alpha); | |||||
} | } | ||||
void Color::interpolate(const Color& other, float u) noexcept | void Color::interpolate(const Color& other, float u) noexcept | ||||
{ | { | ||||
fixRange(u); | fixRange(u); | ||||
const float oneMinusU(1.0f - u); | |||||
const float oneMinusU = 1.0f - u; | |||||
red = red * oneMinusU + other.red * u; | |||||
green = green * oneMinusU + other.green * u; | |||||
blue = blue * oneMinusU + other.blue * u; | |||||
alpha = alpha * oneMinusU + other.alpha * u; | |||||
red = (red * oneMinusU) + (other.red * u); | |||||
green = (green * oneMinusU) + (other.green * u); | |||||
blue = (blue * oneMinusU) + (other.blue * u); | |||||
alpha = (alpha * oneMinusU) + (other.alpha * u); | |||||
fixBounds(); | fixBounds(); | ||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
bool Color::isEqual(const Color& color, bool withAlpha) noexcept | |||||
bool Color::isEqual(const Color& color, const bool withAlpha) noexcept | |||||
{ | { | ||||
const uchar r1 = getFixedRange2(rgba[0]); | const uchar r1 = getFixedRange2(rgba[0]); | ||||
const uchar g1 = getFixedRange2(rgba[1]); | const uchar g1 = getFixedRange2(rgba[1]); | ||||
@@ -212,7 +209,7 @@ bool Color::isEqual(const Color& color, bool withAlpha) noexcept | |||||
return (r1 == r2 && g1 == g2 && b1 == b2); | return (r1 == r2 && g1 == g2 && b1 == b2); | ||||
} | } | ||||
bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept | |||||
bool Color::isNotEqual(const Color& color, const bool withAlpha) noexcept | |||||
{ | { | ||||
const uchar r1 = getFixedRange2(rgba[0]); | const uchar r1 = getFixedRange2(rgba[0]); | ||||
const uchar g1 = getFixedRange2(rgba[1]); | const uchar g1 = getFixedRange2(rgba[1]); | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,12 +17,13 @@ | |||||
#ifndef DGL_COMMON_HPP_INCLUDED | #ifndef DGL_COMMON_HPP_INCLUDED | ||||
#define DGL_COMMON_HPP_INCLUDED | #define DGL_COMMON_HPP_INCLUDED | ||||
#include "../ImageWidgets.hpp" | |||||
#include "../ImageBaseWidgets.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
template <class ImageType> | |||||
struct ButtonImpl { | struct ButtonImpl { | ||||
enum State { | enum State { | ||||
kStateNormal = 0, | kStateNormal = 0, | ||||
@@ -32,11 +33,11 @@ struct ButtonImpl { | |||||
int button; | int button; | ||||
int state; | int state; | ||||
Widget* self; | |||||
ImageBaseButton<ImageType>* const self; | |||||
ImageButton::Callback* callback_img; | |||||
typename ImageBaseButton<ImageType>::Callback* callback_img; | |||||
ButtonImpl(Widget* const s) noexcept | |||||
explicit ButtonImpl(ImageBaseButton<ImageType>* const s) noexcept | |||||
: button(-1), | : button(-1), | ||||
state(kStateNormal), | state(kStateNormal), | ||||
self(s), | self(s), | ||||
@@ -66,7 +67,7 @@ struct ButtonImpl { | |||||
self->repaint(); | self->repaint(); | ||||
if (callback_img != nullptr) | if (callback_img != nullptr) | ||||
callback_img->imageButtonClicked((ImageButton*)self, button2); | |||||
callback_img->imageButtonClicked(self, button2); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -74,7 +75,7 @@ struct ButtonImpl { | |||||
// button was pressed, wait for release | // button was pressed, wait for release | ||||
if (ev.press && self->contains(ev.pos)) | if (ev.press && self->contains(ev.pos)) | ||||
{ | { | ||||
button = ev.button; | |||||
button = static_cast<int>(ev.button); | |||||
state = kStateDown; | state = kStateDown; | ||||
self->repaint(); | self->repaint(); | ||||
return true; | return true; | ||||
@@ -114,7 +115,70 @@ struct ButtonImpl { | |||||
} | } | ||||
DISTRHO_PREVENT_HEAP_ALLOCATION | DISTRHO_PREVENT_HEAP_ALLOCATION | ||||
DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl) | |||||
DISTRHO_DECLARE_NON_COPYABLE(ButtonImpl) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
template <class ImageType> | |||||
struct ImageBaseKnob<ImageType>::PrivateData { | |||||
ImageType image; | |||||
float minimum; | |||||
float maximum; | |||||
float step; | |||||
float value; | |||||
float valueDef; | |||||
float valueTmp; | |||||
bool usingDefault; | |||||
bool usingLog; | |||||
Orientation orientation; | |||||
int rotationAngle; | |||||
bool dragging; | |||||
double lastX; | |||||
double lastY; | |||||
Callback* callback; | |||||
bool alwaysRepaint; | |||||
bool isImgVertical; | |||||
uint imgLayerWidth; | |||||
uint imgLayerHeight; | |||||
uint imgLayerCount; | |||||
bool isReady; | |||||
union { | |||||
uint glTextureId; | |||||
void* cairoSurface; | |||||
}; | |||||
explicit PrivateData(const ImageType& img, const Orientation o); | |||||
explicit PrivateData(PrivateData* const other); | |||||
void assignFrom(PrivateData* const other); | |||||
~PrivateData() | |||||
{ | |||||
cleanup(); | |||||
} | |||||
void init(); | |||||
void cleanup(); | |||||
inline float logscale(const float v) const | |||||
{ | |||||
const float b = std::log(maximum/minimum)/(maximum-minimum); | |||||
const float a = maximum/std::exp(maximum*b); | |||||
return a * std::exp(b*v); | |||||
} | |||||
inline float invlogscale(const float v) const | |||||
{ | |||||
const float b = std::log(maximum/minimum)/(maximum-minimum); | |||||
const float a = maximum/std::exp(maximum*b); | |||||
return std::log(v/a)/b; | |||||
} | |||||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -14,6 +14,11 @@ | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#ifdef _MSC_VER | |||||
// instantiated template classes whose methods are defined elsewhere | |||||
# pragma warning(disable:4661) | |||||
#endif | |||||
#include "../Geometry.hpp" | #include "../Geometry.hpp" | ||||
#include <cmath> | #include <cmath> | ||||
@@ -27,129 +32,129 @@ static const float M_2PIf = 3.14159265358979323846f*2.0f; | |||||
template<typename T> | template<typename T> | ||||
Point<T>::Point() noexcept | Point<T>::Point() noexcept | ||||
: fX(0), | |||||
fY(0) {} | |||||
: x(0), | |||||
y(0) {} | |||||
template<typename T> | template<typename T> | ||||
Point<T>::Point(const T& x, const T& y) noexcept | |||||
: fX(x), | |||||
fY(y) {} | |||||
Point<T>::Point(const T& x2, const T& y2) noexcept | |||||
: x(x2), | |||||
y(y2) {} | |||||
template<typename T> | template<typename T> | ||||
Point<T>::Point(const Point<T>& pos) noexcept | Point<T>::Point(const Point<T>& pos) noexcept | ||||
: fX(pos.fX), | |||||
fY(pos.fY) {} | |||||
: x(pos.x), | |||||
y(pos.y) {} | |||||
template<typename T> | template<typename T> | ||||
const T& Point<T>::getX() const noexcept | const T& Point<T>::getX() const noexcept | ||||
{ | { | ||||
return fX; | |||||
return x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Point<T>::getY() const noexcept | const T& Point<T>::getY() const noexcept | ||||
{ | { | ||||
return fY; | |||||
return y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::setX(const T& x) noexcept | |||||
void Point<T>::setX(const T& x2) noexcept | |||||
{ | { | ||||
fX = x; | |||||
x = x2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::setY(const T& y) noexcept | |||||
void Point<T>::setY(const T& y2) noexcept | |||||
{ | { | ||||
fY = y; | |||||
y = y2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::setPos(const T& x, const T& y) noexcept | |||||
void Point<T>::setPos(const T& x2, const T& y2) noexcept | |||||
{ | { | ||||
fX = x; | |||||
fY = y; | |||||
x = x2; | |||||
y = y2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::setPos(const Point<T>& pos) noexcept | void Point<T>::setPos(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fX = pos.fX; | |||||
fY = pos.fY; | |||||
x = pos.x; | |||||
y = pos.y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::moveBy(const T& x, const T& y) noexcept | |||||
void Point<T>::moveBy(const T& x2, const T& y2) noexcept | |||||
{ | { | ||||
fX = static_cast<T>(fX+x); | |||||
fY = static_cast<T>(fY+y); | |||||
x = static_cast<T>(x+x2); | |||||
y = static_cast<T>(y+y2); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Point<T>::moveBy(const Point<T>& pos) noexcept | void Point<T>::moveBy(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fX = static_cast<T>(fX+pos.fX); | |||||
fY = static_cast<T>(fY+pos.fY); | |||||
x = static_cast<T>(x+pos.x); | |||||
y = static_cast<T>(y+pos.y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Point<T>::isZero() const noexcept | bool Point<T>::isZero() const noexcept | ||||
{ | { | ||||
return fX == 0 && fY == 0; | |||||
return x == 0 && y == 0; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Point<T>::isNotZero() const noexcept | bool Point<T>::isNotZero() const noexcept | ||||
{ | { | ||||
return fX != 0 || fY != 0; | |||||
return x != 0 || y != 0; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Point<T> Point<T>::operator+(const Point<T>& pos) noexcept | Point<T> Point<T>::operator+(const Point<T>& pos) noexcept | ||||
{ | { | ||||
return Point<T>(fX+pos.fX, fY+pos.fY); | |||||
return Point<T>(x+pos.x, y+pos.y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Point<T> Point<T>::operator-(const Point<T>& pos) noexcept | Point<T> Point<T>::operator-(const Point<T>& pos) noexcept | ||||
{ | { | ||||
return Point<T>(fX-pos.fX, fY-pos.fY); | |||||
return Point<T>(x-pos.x, y-pos.y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept | Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fX = pos.fX; | |||||
fY = pos.fY; | |||||
x = pos.x; | |||||
y = pos.y; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept | Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fX = static_cast<T>(fX+pos.fX); | |||||
fY = static_cast<T>(fY+pos.fY); | |||||
x = static_cast<T>(x+pos.x); | |||||
y = static_cast<T>(y+pos.y); | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept | Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fX = static_cast<T>(fX-pos.fX); | |||||
fY = static_cast<T>(fY-pos.fY); | |||||
x = static_cast<T>(x-pos.x); | |||||
y = static_cast<T>(y-pos.y); | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Point<T>::operator==(const Point<T>& pos) const noexcept | bool Point<T>::operator==(const Point<T>& pos) const noexcept | ||||
{ | { | ||||
return (fX == pos.fX && fY == pos.fY); | |||||
return (x == pos.x && y == pos.y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Point<T>::operator!=(const Point<T>& pos) const noexcept | bool Point<T>::operator!=(const Point<T>& pos) const noexcept | ||||
{ | { | ||||
return (fX != pos.fX || fY != pos.fY); | |||||
return (x != pos.x || y != pos.y); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -246,6 +251,27 @@ bool Size<T>::isInvalid() const noexcept | |||||
return fWidth <= 0 || fHeight <= 0; | return fWidth <= 0 || fHeight <= 0; | ||||
} | } | ||||
template<typename T> | |||||
Size<int> Size<T>::toInt() const noexcept | |||||
{ | |||||
return Size<int>(static_cast<int>(fWidth), | |||||
static_cast<int>(fHeight)); | |||||
} | |||||
template<> | |||||
Size<int> Size<double>::toInt() const noexcept | |||||
{ | |||||
return Size<int>(static_cast<int>(fWidth + 0.5), | |||||
static_cast<int>(fHeight + 0.5)); | |||||
} | |||||
template<> | |||||
Size<int> Size<float>::toInt() const noexcept | |||||
{ | |||||
return Size<int>(static_cast<int>(fWidth + 0.5f), | |||||
static_cast<int>(fHeight + 0.5f)); | |||||
} | |||||
template<typename T> | template<typename T> | ||||
Size<T> Size<T>::operator+(const Size<T>& size) noexcept | Size<T> Size<T>::operator+(const Size<T>& size) noexcept | ||||
{ | { | ||||
@@ -298,6 +324,22 @@ Size<T>& Size<T>::operator/=(double d) noexcept | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | |||||
Size<T> Size<T>::operator*(const double m) const noexcept | |||||
{ | |||||
Size<T> size(fWidth, fHeight); | |||||
size *= m; | |||||
return size; | |||||
} | |||||
template<typename T> | |||||
Size<T> Size<T>::operator/(const double m) const noexcept | |||||
{ | |||||
Size<T> size(fWidth, fHeight); | |||||
size /= m; | |||||
return size; | |||||
} | |||||
template<typename T> | template<typename T> | ||||
bool Size<T>::operator==(const Size<T>& size) const noexcept | bool Size<T>::operator==(const Size<T>& size) const noexcept | ||||
{ | { | ||||
@@ -315,162 +357,162 @@ bool Size<T>::operator!=(const Size<T>& size) const noexcept | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line() noexcept | Line<T>::Line() noexcept | ||||
: fPosStart(0, 0), | |||||
fPosEnd(0, 0) {} | |||||
: posStart(0, 0), | |||||
posEnd(0, 0) {} | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept | Line<T>::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept | ||||
: fPosStart(startX, startY), | |||||
fPosEnd(endX, endY) {} | |||||
: posStart(startX, startY), | |||||
posEnd(endX, endY) {} | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept | Line<T>::Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept | ||||
: fPosStart(startX, startY), | |||||
fPosEnd(endPos) {} | |||||
: posStart(startX, startY), | |||||
posEnd(endPos) {} | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept | Line<T>::Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept | ||||
: fPosStart(startPos), | |||||
fPosEnd(endX, endY) {} | |||||
: posStart(startPos), | |||||
posEnd(endX, endY) {} | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line(const Point<T>& startPos, const Point<T>& endPos) noexcept | Line<T>::Line(const Point<T>& startPos, const Point<T>& endPos) noexcept | ||||
: fPosStart(startPos), | |||||
fPosEnd(endPos) {} | |||||
: posStart(startPos), | |||||
posEnd(endPos) {} | |||||
template<typename T> | template<typename T> | ||||
Line<T>::Line(const Line<T>& line) noexcept | Line<T>::Line(const Line<T>& line) noexcept | ||||
: fPosStart(line.fPosStart), | |||||
fPosEnd(line.fPosEnd) {} | |||||
: posStart(line.posStart), | |||||
posEnd(line.posEnd) {} | |||||
template<typename T> | template<typename T> | ||||
const T& Line<T>::getStartX() const noexcept | const T& Line<T>::getStartX() const noexcept | ||||
{ | { | ||||
return fPosStart.fX; | |||||
return posStart.x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Line<T>::getStartY() const noexcept | const T& Line<T>::getStartY() const noexcept | ||||
{ | { | ||||
return fPosStart.fY; | |||||
return posStart.y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Line<T>::getEndX() const noexcept | const T& Line<T>::getEndX() const noexcept | ||||
{ | { | ||||
return fPosEnd.fX; | |||||
return posEnd.x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Line<T>::getEndY() const noexcept | const T& Line<T>::getEndY() const noexcept | ||||
{ | { | ||||
return fPosEnd.fY; | |||||
return posEnd.y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const Point<T>& Line<T>::getStartPos() const noexcept | const Point<T>& Line<T>::getStartPos() const noexcept | ||||
{ | { | ||||
return fPosStart; | |||||
return posStart; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const Point<T>& Line<T>::getEndPos() const noexcept | const Point<T>& Line<T>::getEndPos() const noexcept | ||||
{ | { | ||||
return fPosEnd; | |||||
return posEnd; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setStartX(const T& x) noexcept | void Line<T>::setStartX(const T& x) noexcept | ||||
{ | { | ||||
fPosStart.fX = x; | |||||
posStart.x = x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setStartY(const T& y) noexcept | void Line<T>::setStartY(const T& y) noexcept | ||||
{ | { | ||||
fPosStart.fY = y; | |||||
posStart.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setStartPos(const T& x, const T& y) noexcept | void Line<T>::setStartPos(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPosStart = Point<T>(x, y); | |||||
posStart = Point<T>(x, y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setStartPos(const Point<T>& pos) noexcept | void Line<T>::setStartPos(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fPosStart = pos; | |||||
posStart = pos; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setEndX(const T& x) noexcept | void Line<T>::setEndX(const T& x) noexcept | ||||
{ | { | ||||
fPosEnd.fX = x; | |||||
posEnd.x = x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setEndY(const T& y) noexcept | void Line<T>::setEndY(const T& y) noexcept | ||||
{ | { | ||||
fPosEnd.fY = y; | |||||
posEnd.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setEndPos(const T& x, const T& y) noexcept | void Line<T>::setEndPos(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPosEnd = Point<T>(x, y); | |||||
posEnd = Point<T>(x, y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::setEndPos(const Point<T>& pos) noexcept | void Line<T>::setEndPos(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fPosEnd = pos; | |||||
posEnd = pos; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::moveBy(const T& x, const T& y) noexcept | void Line<T>::moveBy(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPosStart.moveBy(x, y); | |||||
fPosEnd.moveBy(x, y); | |||||
posStart.moveBy(x, y); | |||||
posEnd.moveBy(x, y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::moveBy(const Point<T>& pos) noexcept | void Line<T>::moveBy(const Point<T>& pos) noexcept | ||||
{ | { | ||||
fPosStart.moveBy(pos); | |||||
fPosEnd.moveBy(pos); | |||||
posStart.moveBy(pos); | |||||
posEnd.moveBy(pos); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Line<T>::isNull() const noexcept | bool Line<T>::isNull() const noexcept | ||||
{ | { | ||||
return fPosStart == fPosEnd; | |||||
return posStart == posEnd; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Line<T>::isNotNull() const noexcept | bool Line<T>::isNotNull() const noexcept | ||||
{ | { | ||||
return fPosStart != fPosEnd; | |||||
return posStart != posEnd; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Line<T>& Line<T>::operator=(const Line<T>& line) noexcept | Line<T>& Line<T>::operator=(const Line<T>& line) noexcept | ||||
{ | { | ||||
fPosStart = line.fPosStart; | |||||
fPosEnd = line.fPosEnd; | |||||
posStart = line.posStart; | |||||
posEnd = line.posEnd; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Line<T>::operator==(const Line<T>& line) const noexcept | bool Line<T>::operator==(const Line<T>& line) const noexcept | ||||
{ | { | ||||
return (fPosStart == line.fPosStart && fPosEnd == line.fPosEnd); | |||||
return (posStart == line.posStart && posEnd == line.posEnd); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Line<T>::operator!=(const Line<T>& line) const noexcept | bool Line<T>::operator!=(const Line<T>& line) const noexcept | ||||
{ | { | ||||
return (fPosStart != line.fPosStart || fPosEnd != line.fPosEnd); | |||||
return (posStart != line.posStart || posEnd != line.posEnd); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -524,13 +566,13 @@ Circle<T>::Circle(const Circle<T>& cir) noexcept | |||||
template<typename T> | template<typename T> | ||||
const T& Circle<T>::getX() const noexcept | const T& Circle<T>::getX() const noexcept | ||||
{ | { | ||||
return fPos.fX; | |||||
return fPos.x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Circle<T>::getY() const noexcept | const T& Circle<T>::getY() const noexcept | ||||
{ | { | ||||
return fPos.fY; | |||||
return fPos.y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -542,20 +584,20 @@ const Point<T>& Circle<T>::getPos() const noexcept | |||||
template<typename T> | template<typename T> | ||||
void Circle<T>::setX(const T& x) noexcept | void Circle<T>::setX(const T& x) noexcept | ||||
{ | { | ||||
fPos.fX = x; | |||||
fPos.x = x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Circle<T>::setY(const T& y) noexcept | void Circle<T>::setY(const T& y) noexcept | ||||
{ | { | ||||
fPos.fY = y; | |||||
fPos.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Circle<T>::setPos(const T& x, const T& y) noexcept | void Circle<T>::setPos(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPos.fX = x; | |||||
fPos.fY = y; | |||||
fPos.x = x; | |||||
fPos.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -599,18 +641,6 @@ void Circle<T>::setNumSegments(const uint num) | |||||
fSin = std::sin(fTheta); | fSin = std::sin(fTheta); | ||||
} | } | ||||
template<typename T> | |||||
void Circle<T>::draw() | |||||
{ | |||||
_draw(false); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::drawOutline() | |||||
{ | |||||
_draw(true); | |||||
} | |||||
template<typename T> | template<typename T> | ||||
Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept | Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept | ||||
{ | { | ||||
@@ -640,83 +670,71 @@ bool Circle<T>::operator!=(const Circle<T>& cir) const noexcept | |||||
template<typename T> | template<typename T> | ||||
Triangle<T>::Triangle() noexcept | Triangle<T>::Triangle() noexcept | ||||
: fPos1(0, 0), | |||||
fPos2(0, 0), | |||||
fPos3(0, 0) {} | |||||
: pos1(0, 0), | |||||
pos2(0, 0), | |||||
pos3(0, 0) {} | |||||
template<typename T> | template<typename T> | ||||
Triangle<T>::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept | Triangle<T>::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept | ||||
: fPos1(x1, y1), | |||||
fPos2(x2, y2), | |||||
fPos3(x3, y3) {} | |||||
: pos1(x1, y1), | |||||
pos2(x2, y2), | |||||
pos3(x3, y3) {} | |||||
template<typename T> | template<typename T> | ||||
Triangle<T>::Triangle(const Point<T>& pos1, const Point<T>& pos2, const Point<T>& pos3) noexcept | |||||
: fPos1(pos1), | |||||
fPos2(pos2), | |||||
fPos3(pos3) {} | |||||
Triangle<T>::Triangle(const Point<T>& p1, const Point<T>& p2, const Point<T>& p3) noexcept | |||||
: pos1(p1), | |||||
pos2(p2), | |||||
pos3(p3) {} | |||||
template<typename T> | template<typename T> | ||||
Triangle<T>::Triangle(const Triangle<T>& tri) noexcept | Triangle<T>::Triangle(const Triangle<T>& tri) noexcept | ||||
: fPos1(tri.fPos1), | |||||
fPos2(tri.fPos2), | |||||
fPos3(tri.fPos3) {} | |||||
: pos1(tri.pos1), | |||||
pos2(tri.pos2), | |||||
pos3(tri.pos3) {} | |||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::isNull() const noexcept | bool Triangle<T>::isNull() const noexcept | ||||
{ | { | ||||
return fPos1 == fPos2 && fPos1 == fPos3; | |||||
return pos1 == pos2 && pos1 == pos3; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::isNotNull() const noexcept | bool Triangle<T>::isNotNull() const noexcept | ||||
{ | { | ||||
return fPos1 != fPos2 || fPos1 != fPos3; | |||||
return pos1 != pos2 || pos1 != pos3; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::isValid() const noexcept | bool Triangle<T>::isValid() const noexcept | ||||
{ | { | ||||
return fPos1 != fPos2 && fPos1 != fPos3; | |||||
return pos1 != pos2 && pos1 != pos3; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::isInvalid() const noexcept | bool Triangle<T>::isInvalid() const noexcept | ||||
{ | { | ||||
return fPos1 == fPos2 || fPos1 == fPos3; | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::draw() | |||||
{ | |||||
_draw(false); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline() | |||||
{ | |||||
_draw(true); | |||||
return pos1 == pos2 || pos1 == pos3; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept | Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept | ||||
{ | { | ||||
fPos1 = tri.fPos1; | |||||
fPos2 = tri.fPos2; | |||||
fPos3 = tri.fPos3; | |||||
pos1 = tri.pos1; | |||||
pos2 = tri.pos2; | |||||
pos3 = tri.pos3; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::operator==(const Triangle<T>& tri) const noexcept | bool Triangle<T>::operator==(const Triangle<T>& tri) const noexcept | ||||
{ | { | ||||
return (fPos1 == tri.fPos1 && fPos2 == tri.fPos2 && fPos3 == tri.fPos3); | |||||
return (pos1 == tri.pos1 && pos2 == tri.pos2 && pos3 == tri.pos3); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept | bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept | ||||
{ | { | ||||
return (fPos1 != tri.fPos1 || fPos2 != tri.fPos2 || fPos3 != tri.fPos3); | |||||
return (pos1 != tri.pos1 || pos2 != tri.pos2 || pos3 != tri.pos3); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -724,226 +742,257 @@ bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle() noexcept | Rectangle<T>::Rectangle() noexcept | ||||
: fPos(0, 0), | |||||
fSize(0, 0) {} | |||||
: pos(0, 0), | |||||
size(0, 0) {} | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept | |||||
: fPos(x, y), | |||||
fSize(width, height) {} | |||||
Rectangle<T>::Rectangle(const T& x, const T& y, const T& w, const T& h) noexcept | |||||
: pos(x, y), | |||||
size(w, h) {} | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& size) noexcept | |||||
: fPos(x, y), | |||||
fSize(size) {} | |||||
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& s) noexcept | |||||
: pos(x, y), | |||||
size(s) {} | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle(const Point<T>& pos, const T& width, const T& height) noexcept | |||||
: fPos(pos), | |||||
fSize(width, height) {} | |||||
Rectangle<T>::Rectangle(const Point<T>& p, const T& w, const T& h) noexcept | |||||
: pos(p), | |||||
size(w, h) {} | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle(const Point<T>& pos, const Size<T>& size) noexcept | |||||
: fPos(pos), | |||||
fSize(size) {} | |||||
Rectangle<T>::Rectangle(const Point<T>& p, const Size<T>& s) noexcept | |||||
: pos(p), | |||||
size(s) {} | |||||
template<typename T> | template<typename T> | ||||
Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept | Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept | ||||
: fPos(rect.fPos), | |||||
fSize(rect.fSize) {} | |||||
: pos(rect.pos), | |||||
size(rect.size) {} | |||||
template<typename T> | template<typename T> | ||||
const T& Rectangle<T>::getX() const noexcept | const T& Rectangle<T>::getX() const noexcept | ||||
{ | { | ||||
return fPos.fX; | |||||
return pos.x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Rectangle<T>::getY() const noexcept | const T& Rectangle<T>::getY() const noexcept | ||||
{ | { | ||||
return fPos.fY; | |||||
return pos.y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Rectangle<T>::getWidth() const noexcept | const T& Rectangle<T>::getWidth() const noexcept | ||||
{ | { | ||||
return fSize.fWidth; | |||||
return size.fWidth; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const T& Rectangle<T>::getHeight() const noexcept | const T& Rectangle<T>::getHeight() const noexcept | ||||
{ | { | ||||
return fSize.fHeight; | |||||
return size.fHeight; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const Point<T>& Rectangle<T>::getPos() const noexcept | const Point<T>& Rectangle<T>::getPos() const noexcept | ||||
{ | { | ||||
return fPos; | |||||
return pos; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
const Size<T>& Rectangle<T>::getSize() const noexcept | const Size<T>& Rectangle<T>::getSize() const noexcept | ||||
{ | { | ||||
return fSize; | |||||
return size; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setX(const T& x) noexcept | void Rectangle<T>::setX(const T& x) noexcept | ||||
{ | { | ||||
fPos.fX = x; | |||||
pos.x = x; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setY(const T& y) noexcept | void Rectangle<T>::setY(const T& y) noexcept | ||||
{ | { | ||||
fPos.fY = y; | |||||
pos.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setPos(const T& x, const T& y) noexcept | void Rectangle<T>::setPos(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPos.fX = x; | |||||
fPos.fY = y; | |||||
pos.x = x; | |||||
pos.y = y; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setPos(const Point<T>& pos) noexcept | |||||
void Rectangle<T>::setPos(const Point<T>& pos2) noexcept | |||||
{ | { | ||||
fPos = pos; | |||||
pos = pos2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::moveBy(const T& x, const T& y) noexcept | void Rectangle<T>::moveBy(const T& x, const T& y) noexcept | ||||
{ | { | ||||
fPos.moveBy(x, y); | |||||
pos.moveBy(x, y); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::moveBy(const Point<T>& pos) noexcept | |||||
void Rectangle<T>::moveBy(const Point<T>& pos2) noexcept | |||||
{ | { | ||||
fPos.moveBy(pos); | |||||
pos.moveBy(pos2); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setWidth(const T& width) noexcept | void Rectangle<T>::setWidth(const T& width) noexcept | ||||
{ | { | ||||
fSize.fWidth = width; | |||||
size.fWidth = width; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setHeight(const T& height) noexcept | void Rectangle<T>::setHeight(const T& height) noexcept | ||||
{ | { | ||||
fSize.fHeight = height; | |||||
size.fHeight = height; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setSize(const T& width, const T& height) noexcept | void Rectangle<T>::setSize(const T& width, const T& height) noexcept | ||||
{ | { | ||||
fSize.fWidth = width; | |||||
fSize.fHeight = height; | |||||
size.fWidth = width; | |||||
size.fHeight = height; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setSize(const Size<T>& size) noexcept | |||||
void Rectangle<T>::setSize(const Size<T>& size2) noexcept | |||||
{ | { | ||||
fSize = size; | |||||
size = size2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::growBy(double multiplier) noexcept | void Rectangle<T>::growBy(double multiplier) noexcept | ||||
{ | { | ||||
fSize.growBy(multiplier); | |||||
size.growBy(multiplier); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::shrinkBy(double divider) noexcept | void Rectangle<T>::shrinkBy(double divider) noexcept | ||||
{ | { | ||||
fSize.shrinkBy(divider); | |||||
size.shrinkBy(divider); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setRectangle(const Point<T>& pos, const Size<T>& size) noexcept | |||||
void Rectangle<T>::setRectangle(const Point<T>& pos2, const Size<T>& size2) noexcept | |||||
{ | { | ||||
fPos = pos; | |||||
fSize = size; | |||||
pos = pos2; | |||||
size = size2; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::setRectangle(const Rectangle<T>& rect) noexcept | void Rectangle<T>::setRectangle(const Rectangle<T>& rect) noexcept | ||||
{ | { | ||||
fPos = rect.fPos; | |||||
fSize = rect.fSize; | |||||
pos = rect.pos; | |||||
size = rect.size; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::contains(const T& x, const T& y) const noexcept | bool Rectangle<T>::contains(const T& x, const T& y) const noexcept | ||||
{ | { | ||||
return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight); | |||||
return (x >= pos.x && y >= pos.y && x <= pos.x+size.fWidth && y <= pos.y+size.fHeight); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::contains(const Point<T>& pos) const noexcept | |||||
bool Rectangle<T>::contains(const Point<T>& p) const noexcept | |||||
{ | { | ||||
return contains(pos.fX, pos.fY); | |||||
return contains(p.x, p.y); | |||||
} | |||||
template<typename T> | |||||
template<typename T2> | |||||
bool Rectangle<T>::contains(const Point<T2>& p) const noexcept | |||||
{ | |||||
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); | |||||
} | |||||
template<> template<> | |||||
bool Rectangle<int>::contains(const Point<double>& p) const noexcept | |||||
{ | |||||
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); | |||||
} | |||||
template<> template<> | |||||
bool Rectangle<uint>::contains(const Point<double>& p) const noexcept | |||||
{ | |||||
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::containsX(const T& x) const noexcept | bool Rectangle<T>::containsX(const T& x) const noexcept | ||||
{ | { | ||||
return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth); | |||||
return (x >= pos.x && x <= pos.x + size.fWidth); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::containsY(const T& y) const noexcept | bool Rectangle<T>::containsY(const T& y) const noexcept | ||||
{ | { | ||||
return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight); | |||||
return (y >= pos.y && y <= pos.y + size.fHeight); | |||||
} | |||||
template<typename T> | |||||
bool Rectangle<T>::isNull() const noexcept | |||||
{ | |||||
return size.isNull(); | |||||
} | |||||
template<typename T> | |||||
bool Rectangle<T>::isNotNull() const noexcept | |||||
{ | |||||
return size.isNotNull(); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::draw() | |||||
bool Rectangle<T>::isValid() const noexcept | |||||
{ | { | ||||
_draw(false); | |||||
return size.isValid(); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::drawOutline() | |||||
bool Rectangle<T>::isInvalid() const noexcept | |||||
{ | { | ||||
_draw(true); | |||||
return size.isInvalid(); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept | Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept | ||||
{ | { | ||||
fPos = rect.fPos; | |||||
fSize = rect.fSize; | |||||
pos = rect.pos; | |||||
size = rect.size; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept | Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept | ||||
{ | { | ||||
fSize *= m; | |||||
size *= m; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept | Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept | ||||
{ | { | ||||
fSize /= d; | |||||
size /= d; | |||||
return *this; | return *this; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::operator==(const Rectangle<T>& rect) const noexcept | bool Rectangle<T>::operator==(const Rectangle<T>& rect) const noexcept | ||||
{ | { | ||||
return (fPos == rect.fPos && fSize == rect.fSize); | |||||
return (pos == rect.pos && size == rect.size); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept | bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept | ||||
{ | { | ||||
return (fPos != rect.fPos || fSize != rect.fSize); | |||||
return (pos != rect.pos || size != rect.size); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,150 +0,0 @@ | |||||
/* | |||||
* DISTRHO Plugin Framework (DPF) | |||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
* or without fee is hereby granted, provided that the above copyright notice and this | |||||
* permission notice appear in all copies. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#include "../Image.hpp" | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
Image::Image() | |||||
: ImageBase(), | |||||
fFormat(0), | |||||
fType(0), | |||||
fTextureId(0), | |||||
fIsReady(false) | |||||
{ | |||||
glGenTextures(1, &fTextureId); | |||||
} | |||||
Image::Image(const Image& image) | |||||
: ImageBase(image), | |||||
fFormat(image.fFormat), | |||||
fType(image.fType), | |||||
fTextureId(0), | |||||
fIsReady(false) | |||||
{ | |||||
glGenTextures(1, &fTextureId); | |||||
} | |||||
Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) | |||||
: ImageBase(rawData, width, height), | |||||
fFormat(format), | |||||
fType(type), | |||||
fTextureId(0), | |||||
fIsReady(false) | |||||
{ | |||||
glGenTextures(1, &fTextureId); | |||||
} | |||||
Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) | |||||
: ImageBase(rawData, size), | |||||
fFormat(format), | |||||
fType(type), | |||||
fTextureId(0), | |||||
fIsReady(false) | |||||
{ | |||||
glGenTextures(1, &fTextureId); | |||||
} | |||||
Image::~Image() | |||||
{ | |||||
if (fTextureId != 0) | |||||
{ | |||||
#ifndef DISTRHO_OS_MAC // FIXME | |||||
glDeleteTextures(1, &fTextureId); | |||||
#endif | |||||
fTextureId = 0; | |||||
} | |||||
} | |||||
void Image::loadFromMemory(const char* const rawData, | |||||
const uint width, | |||||
const uint height, | |||||
const GLenum format, | |||||
const GLenum type) noexcept | |||||
{ | |||||
loadFromMemory(rawData, Size<uint>(width, height), format, type); | |||||
} | |||||
void Image::loadFromMemory(const char* const rawData, | |||||
const Size<uint>& size, | |||||
const GLenum format, | |||||
const GLenum type) noexcept | |||||
{ | |||||
fRawData = rawData; | |||||
fSize = size; | |||||
fFormat = format; | |||||
fType = type; | |||||
fIsReady = false; | |||||
} | |||||
GLenum Image::getFormat() const noexcept | |||||
{ | |||||
return fFormat; | |||||
} | |||||
GLenum Image::getType() const noexcept | |||||
{ | |||||
return fType; | |||||
} | |||||
Image& Image::operator=(const Image& image) noexcept | |||||
{ | |||||
fRawData = image.fRawData; | |||||
fSize = image.fSize; | |||||
fFormat = image.fFormat; | |||||
fType = image.fType; | |||||
fIsReady = false; | |||||
return *this; | |||||
} | |||||
void Image::_drawAt(const Point<int>& pos) | |||||
{ | |||||
if (fTextureId == 0 || ! isValid()) | |||||
return; | |||||
glEnable(GL_TEXTURE_2D); | |||||
glBindTexture(GL_TEXTURE_2D, fTextureId); | |||||
if (! fIsReady) | |||||
{ | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||||
static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||||
glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||||
static_cast<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0, | |||||
fFormat, fType, fRawData); | |||||
fIsReady = true; | |||||
} | |||||
Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw(); | |||||
glBindTexture(GL_TEXTURE_2D, 0); | |||||
glDisable(GL_TEXTURE_2D); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,82 +18,108 @@ | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// protected constructors | |||||
ImageBase::ImageBase() | ImageBase::ImageBase() | ||||
: fRawData(nullptr), | |||||
fSize(0, 0) {} | |||||
: rawData(nullptr), | |||||
size(0, 0), | |||||
format(kImageFormatNull) {} | |||||
ImageBase::ImageBase(const char* const rawData, const uint width, const uint height) | |||||
: fRawData(rawData), | |||||
fSize(width, height) {} | |||||
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) | |||||
: rawData(rdata), | |||||
size(width, height), | |||||
format(fmt) {} | |||||
ImageBase::ImageBase(const char* const rawData, const Size<uint>& size) | |||||
: fRawData(rawData), | |||||
fSize(size) {} | |||||
ImageBase::ImageBase(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||||
: rawData(rdata), | |||||
size(s), | |||||
format(fmt) {} | |||||
ImageBase::ImageBase(const ImageBase& image) | ImageBase::ImageBase(const ImageBase& image) | ||||
: fRawData(image.fRawData), | |||||
fSize(image.fSize) {} | |||||
: rawData(image.rawData), | |||||
size(image.size), | |||||
format(image.format) {} | |||||
ImageBase::~ImageBase() {} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// public methods | |||||
// ----------------------------------------------------------------------- | |||||
ImageBase::~ImageBase() {} | |||||
bool ImageBase::isValid() const noexcept | bool ImageBase::isValid() const noexcept | ||||
{ | { | ||||
return (fRawData != nullptr && fSize.isValid()); | |||||
return (rawData != nullptr && size.isValid()); | |||||
} | |||||
bool ImageBase::isInvalid() const noexcept | |||||
{ | |||||
return (rawData == nullptr || size.isInvalid()); | |||||
} | } | ||||
uint ImageBase::getWidth() const noexcept | uint ImageBase::getWidth() const noexcept | ||||
{ | { | ||||
return fSize.getWidth(); | |||||
return size.getWidth(); | |||||
} | } | ||||
uint ImageBase::getHeight() const noexcept | uint ImageBase::getHeight() const noexcept | ||||
{ | { | ||||
return fSize.getHeight(); | |||||
return size.getHeight(); | |||||
} | } | ||||
const Size<uint>& ImageBase::getSize() const noexcept | const Size<uint>& ImageBase::getSize() const noexcept | ||||
{ | { | ||||
return fSize; | |||||
return size; | |||||
} | } | ||||
const char* ImageBase::getRawData() const noexcept | const char* ImageBase::getRawData() const noexcept | ||||
{ | { | ||||
return fRawData; | |||||
return rawData; | |||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
ImageFormat ImageBase::getFormat() const noexcept | |||||
{ | |||||
return format; | |||||
} | |||||
void ImageBase::loadFromMemory(const char* const rdata, | |||||
const uint width, | |||||
const uint height, | |||||
const ImageFormat fmt) noexcept | |||||
{ | |||||
loadFromMemory(rdata, Size<uint>(width, height), fmt); | |||||
} | |||||
void ImageBase::draw() | |||||
void ImageBase::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||||
{ | { | ||||
_drawAt(Point<int>()); | |||||
rawData = rdata; | |||||
size = s; | |||||
format = fmt; | |||||
} | } | ||||
void ImageBase::drawAt(const int x, const int y) | |||||
void ImageBase::draw(const GraphicsContext& context) | |||||
{ | { | ||||
_drawAt(Point<int>(x, y)); | |||||
drawAt(context, Point<int>(0, 0)); | |||||
} | } | ||||
void ImageBase::drawAt(const Point<int>& pos) | |||||
void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y) | |||||
{ | { | ||||
_drawAt(pos); | |||||
drawAt(context, Point<int>(x, y)); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// public operators | |||||
ImageBase& ImageBase::operator=(const ImageBase& image) noexcept | ImageBase& ImageBase::operator=(const ImageBase& image) noexcept | ||||
{ | { | ||||
fRawData = image.fRawData; | |||||
fSize = image.fSize; | |||||
rawData = image.rawData; | |||||
size = image.size; | |||||
format = image.format; | |||||
return *this; | return *this; | ||||
} | } | ||||
bool ImageBase::operator==(const ImageBase& image) const noexcept | bool ImageBase::operator==(const ImageBase& image) const noexcept | ||||
{ | { | ||||
return (fRawData == image.fRawData && fSize == image.fSize); | |||||
return (rawData == image.rawData && size == image.size && format == image.format); | |||||
} | } | ||||
bool ImageBase::operator!=(const ImageBase& image) const noexcept | bool ImageBase::operator!=(const ImageBase& image) const noexcept | ||||
@@ -101,6 +127,6 @@ bool ImageBase::operator!=(const ImageBase& image) const noexcept | |||||
return !operator==(image); | return !operator==(image); | ||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -15,7 +15,7 @@ | |||||
*/ | */ | ||||
#include "../NanoVG.hpp" | #include "../NanoVG.hpp" | ||||
#include "WidgetPrivateData.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#ifndef DGL_NO_SHARED_RESOURCES | #ifndef DGL_NO_SHARED_RESOURCES | ||||
# include "Resources.hpp" | # include "Resources.hpp" | ||||
@@ -53,6 +53,7 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
# undef DGL_EXT | # undef DGL_EXT | ||||
#endif | #endif | ||||
@@ -66,20 +67,32 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||||
#if defined(NANOVG_GL2) | #if defined(NANOVG_GL2) | ||||
# define nvgCreateGL nvgCreateGL2 | # define nvgCreateGL nvgCreateGL2 | ||||
# define nvgDeleteGL nvgDeleteGL2 | # define nvgDeleteGL nvgDeleteGL2 | ||||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2 | |||||
# define nvglImageHandle nvglImageHandleGL2 | |||||
#elif defined(NANOVG_GL3) | #elif defined(NANOVG_GL3) | ||||
# define nvgCreateGL nvgCreateGL3 | # define nvgCreateGL nvgCreateGL3 | ||||
# define nvgDeleteGL nvgDeleteGL3 | # define nvgDeleteGL nvgDeleteGL3 | ||||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3 | |||||
# define nvglImageHandle nvglImageHandleGL3 | |||||
#elif defined(NANOVG_GLES2) | #elif defined(NANOVG_GLES2) | ||||
# define nvgCreateGL nvgCreateGLES2 | # define nvgCreateGL nvgCreateGLES2 | ||||
# define nvgDeleteGL nvgDeleteGLES2 | # define nvgDeleteGL nvgDeleteGLES2 | ||||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2 | |||||
# define nvglImageHandle nvglImageHandleGLES2 | |||||
#elif defined(NANOVG_GLES3) | #elif defined(NANOVG_GLES3) | ||||
# define nvgCreateGL nvgCreateGLES3 | # define nvgCreateGL nvgCreateGLES3 | ||||
# define nvgDeleteGL nvgDeleteGLES3 | # define nvgDeleteGL nvgDeleteGLES3 | ||||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3 | |||||
# define nvglImageHandle nvglImageHandleGLES3 | |||||
#endif | #endif | ||||
static NVGcontext* nvgCreateGL_helper(int flags) | static NVGcontext* nvgCreateGL_helper(int flags) | ||||
{ | { | ||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||||
# pragma GCC diagnostic push | |||||
# pragma GCC diagnostic ignored "-Wcast-function-type" | |||||
# endif | |||||
static bool needsInit = true; | static bool needsInit = true; | ||||
# define DGL_EXT(PROC, func) \ | # define DGL_EXT(PROC, func) \ | ||||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | ||||
@@ -111,8 +124,12 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
# undef DGL_EXT | # undef DGL_EXT | ||||
needsInit = false; | needsInit = false; | ||||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||||
# pragma GCC diagnostic pop | |||||
# endif | |||||
#endif | #endif | ||||
return nvgCreateGL(flags); | return nvgCreateGL(flags); | ||||
} | } | ||||
@@ -240,11 +257,6 @@ NanoVG::NanoVG(int flags) | |||||
fInFrame(false), | fInFrame(false), | ||||
fIsSubWidget(false) {} | fIsSubWidget(false) {} | ||||
NanoVG::NanoVG(NanoWidget* groupWidget) | |||||
: fContext(groupWidget->fContext), | |||||
fInFrame(false), | |||||
fIsSubWidget(true) {} | |||||
NanoVG::~NanoVG() | NanoVG::~NanoVG() | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT(! fInFrame); | DISTRHO_SAFE_ASSERT(! fInFrame); | ||||
@@ -260,8 +272,8 @@ void NanoVG::beginFrame(const uint width, const uint height, const float scaleFa | |||||
if (fContext == nullptr) return; | if (fContext == nullptr) return; | ||||
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); | DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); | ||||
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | ||||
fInFrame = true; | fInFrame = true; | ||||
nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); | nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); | ||||
} | } | ||||
@@ -269,14 +281,13 @@ void NanoVG::beginFrame(Widget* const widget) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); | ||||
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | ||||
fInFrame = true; | fInFrame = true; | ||||
if (fContext == nullptr) | if (fContext == nullptr) | ||||
return; | return; | ||||
Window& window(widget->getParentWindow()); | |||||
nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f); | |||||
if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) | |||||
nvgBeginFrame(fContext, static_cast<int>(tlw->getWidth()), static_cast<int>(tlw->getHeight()), 1.0f); | |||||
} | } | ||||
void NanoVG::cancelFrame() | void NanoVG::cancelFrame() | ||||
@@ -772,26 +783,26 @@ void NanoVG::stroke() | |||||
NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) | NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) | ||||
{ | { | ||||
if (fContext == nullptr) return -1; | |||||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | ||||
DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); | DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); | ||||
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); | |||||
return nvgCreateFont(fContext, name, filename); | return nvgCreateFont(fContext, name, filename); | ||||
} | } | ||||
NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) | NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) | ||||
{ | { | ||||
if (fContext == nullptr) return -1; | |||||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | ||||
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); | DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); | ||||
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); | |||||
return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData); | return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData); | ||||
} | } | ||||
NanoVG::FontId NanoVG::findFont(const char* name) | NanoVG::FontId NanoVG::findFont(const char* name) | ||||
{ | { | ||||
if (fContext == nullptr) return -1; | |||||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | ||||
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); | |||||
return nvgFindFont(fContext, name); | return nvgFindFont(fContext, name); | ||||
} | } | ||||
@@ -913,79 +924,56 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi | |||||
} | } | ||||
#ifndef DGL_NO_SHARED_RESOURCES | #ifndef DGL_NO_SHARED_RESOURCES | ||||
void NanoVG::loadSharedResources() | |||||
bool NanoVG::loadSharedResources() | |||||
{ | { | ||||
if (fContext == nullptr) return; | |||||
if (fContext == nullptr) return false; | |||||
if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) | if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) | ||||
return; | |||||
return true; | |||||
using namespace dpf_resources; | using namespace dpf_resources; | ||||
nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0); | |||||
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; | |||||
} | } | ||||
#endif | #endif | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// NanoSubWidget | |||||
struct NanoWidget::PrivateData { | |||||
NanoWidget* const self; | |||||
std::vector<NanoWidget*> subWidgets; | |||||
PrivateData(NanoWidget* const s) | |||||
: self(s), | |||||
subWidgets() {} | |||||
~PrivateData() | |||||
{ | |||||
subWidgets.clear(); | |||||
} | |||||
}; | |||||
NanoWidget::NanoWidget(Window& parent, int flags) | |||||
: Widget(parent), | |||||
NanoVG(flags), | |||||
nData(new PrivateData(this)) | |||||
template <> | |||||
NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parent, int flags) | |||||
: SubWidget(parent), | |||||
NanoVG(flags) | |||||
{ | { | ||||
pData->needsScaling = true; | |||||
setNeedsViewportScaling(); | |||||
} | } | ||||
NanoWidget::NanoWidget(Widget* groupWidget, int flags) | |||||
: Widget(groupWidget, true), | |||||
NanoVG(flags), | |||||
nData(new PrivateData(this)) | |||||
{ | |||||
pData->needsScaling = true; | |||||
} | |||||
template class NanoBaseWidget<SubWidget>; | |||||
NanoWidget::NanoWidget(NanoWidget* groupWidget) | |||||
: Widget(groupWidget, false), | |||||
NanoVG(groupWidget), | |||||
nData(new PrivateData(this)) | |||||
{ | |||||
pData->needsScaling = true; | |||||
pData->skipDisplay = true; | |||||
groupWidget->nData->subWidgets.push_back(this); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// NanoTopLevelWidget | |||||
NanoWidget::~NanoWidget() | |||||
{ | |||||
delete nData; | |||||
} | |||||
template <> | |||||
NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags) | |||||
: TopLevelWidget(windowToMapTo), | |||||
NanoVG(flags) {} | |||||
void NanoWidget::onDisplay() | |||||
{ | |||||
NanoVG::beginFrame(getWidth(), getHeight()); | |||||
onNanoDisplay(); | |||||
template class NanoBaseWidget<TopLevelWidget>; | |||||
for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it) | |||||
{ | |||||
NanoWidget* const widget(*it); | |||||
widget->onNanoDisplay(); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// NanoStandaloneWindow | |||||
NanoVG::endFrame(); | |||||
} | |||||
template <> | |||||
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags) | |||||
: StandaloneWindow(app), | |||||
NanoVG(flags) {} | |||||
template <> | |||||
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags) | |||||
: StandaloneWindow(app, parentWindow), | |||||
NanoVG(flags) {} | |||||
template class NanoBaseWidget<StandaloneWindow>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -14,136 +14,260 @@ | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include "../Geometry.hpp" | |||||
#ifdef _MSC_VER | |||||
// instantiated template classes whose methods are defined elsewhere | |||||
# pragma warning(disable:4661) | |||||
#endif | |||||
#include "../OpenGL.hpp" | #include "../OpenGL.hpp" | ||||
#include "../Color.hpp" | |||||
#include "../ImageWidgets.hpp" | |||||
#include "Common.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "TopLevelWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
#include "WindowPrivateData.hpp" | |||||
// templated classes | |||||
#include "ImageBaseWidgets.cpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// Color | |||||
void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||||
{ | |||||
if (includeAlpha) | |||||
glColor4f(red, green, blue, alpha); | |||||
else | |||||
glColor3f(red, green, blue); | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Line | // Line | ||||
template<typename T> | template<typename T> | ||||
void Line<T>::draw() | |||||
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fPosStart != fPosEnd,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||||
glBegin(GL_LINES); | glBegin(GL_LINES); | ||||
{ | { | ||||
glVertex2d(fPosStart.fX, fPosStart.fY); | |||||
glVertex2d(fPosEnd.fX, fPosEnd.fY); | |||||
glVertex2d(posStart.getX(), posStart.getY()); | |||||
glVertex2d(posEnd.getX(), posEnd.getY()); | |||||
} | } | ||||
glEnd(); | glEnd(); | ||||
} | } | ||||
template<typename T> | |||||
void Line<T>::draw(const GraphicsContext&, const T width) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||||
glLineWidth(static_cast<GLfloat>(width)); | |||||
drawLine<T>(posStart, posEnd); | |||||
} | |||||
// deprecated calls | |||||
template<typename T> | |||||
void Line<T>::draw() | |||||
{ | |||||
drawLine<T>(posStart, posEnd); | |||||
} | |||||
template class Line<double>; | |||||
template class Line<float>; | |||||
template class Line<int>; | |||||
template class Line<uint>; | |||||
template class Line<short>; | |||||
template class Line<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Circle | // Circle | ||||
template<typename T> | template<typename T> | ||||
void Circle<T>::_draw(const bool outline) | |||||
static void drawCircle(const Point<T>& pos, | |||||
const uint numSegments, | |||||
const float size, | |||||
const float sin, | |||||
const float cos, | |||||
const bool outline) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fNumSegments >= 3 && fSize > 0.0f,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); | |||||
double t, x = fSize, y = 0.0; | |||||
const T origx = pos.getX(); | |||||
const T origy = pos.getY(); | |||||
double t, x = size, y = 0.0; | |||||
glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); | glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); | ||||
for (uint i=0; i<fNumSegments; ++i) | |||||
for (uint i=0; i<numSegments; ++i) | |||||
{ | { | ||||
glVertex2d(x + fPos.fX, y + fPos.fY); | |||||
glVertex2d(x + origx, y + origy); | |||||
t = x; | t = x; | ||||
x = fCos * x - fSin * y; | |||||
y = fSin * t + fCos * y; | |||||
x = cos * x - sin * y; | |||||
y = sin * t + cos * y; | |||||
} | } | ||||
glEnd(); | glEnd(); | ||||
} | } | ||||
template<typename T> | |||||
void Circle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
} | |||||
// deprecated calls | |||||
template<typename T> | |||||
void Circle<T>::draw() | |||||
{ | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::drawOutline() | |||||
{ | |||||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
} | |||||
template class Circle<double>; | |||||
template class Circle<float>; | |||||
template class Circle<int>; | |||||
template class Circle<uint>; | |||||
template class Circle<short>; | |||||
template class Circle<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Triangle | // Triangle | ||||
template<typename T> | template<typename T> | ||||
void Triangle<T>::_draw(const bool outline) | |||||
static void drawTriangle(const Point<T>& pos1, | |||||
const Point<T>& pos2, | |||||
const Point<T>& pos3, | |||||
const bool outline) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fPos1 != fPos2 && fPos1 != fPos3,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |||||
glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); | glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); | ||||
{ | { | ||||
glVertex2d(fPos1.fX, fPos1.fY); | |||||
glVertex2d(fPos2.fX, fPos2.fY); | |||||
glVertex2d(fPos3.fX, fPos3.fY); | |||||
glVertex2d(pos1.getX(), pos1.getY()); | |||||
glVertex2d(pos2.getX(), pos2.getY()); | |||||
glVertex2d(pos3.getX(), pos3.getY()); | |||||
} | } | ||||
glEnd(); | glEnd(); | ||||
} | } | ||||
template<typename T> | |||||
void Triangle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
drawTriangle<T>(pos1, pos2, pos3, false); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
drawTriangle<T>(pos1, pos2, pos3, true); | |||||
} | |||||
// deprecated calls | |||||
template<typename T> | |||||
void Triangle<T>::draw() | |||||
{ | |||||
drawTriangle<T>(pos1, pos2, pos3, false); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline() | |||||
{ | |||||
drawTriangle<T>(pos1, pos2, pos3, true); | |||||
} | |||||
template class Triangle<double>; | |||||
template class Triangle<float>; | |||||
template class Triangle<int>; | |||||
template class Triangle<uint>; | |||||
template class Triangle<short>; | |||||
template class Triangle<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Rectangle | // Rectangle | ||||
template<typename T> | template<typename T> | ||||
void Rectangle<T>::_draw(const bool outline) | |||||
static void drawRectangle(const Rectangle<T>& rect, const bool outline) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fSize.isValid(),); | |||||
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); | |||||
glBegin(outline ? GL_LINE_LOOP : GL_QUADS); | glBegin(outline ? GL_LINE_LOOP : GL_QUADS); | ||||
{ | { | ||||
const T x = rect.getX(); | |||||
const T y = rect.getY(); | |||||
const T w = rect.getWidth(); | |||||
const T h = rect.getHeight(); | |||||
glTexCoord2f(0.0f, 0.0f); | glTexCoord2f(0.0f, 0.0f); | ||||
glVertex2d(fPos.fX, fPos.fY); | |||||
glVertex2d(x, y); | |||||
glTexCoord2f(1.0f, 0.0f); | glTexCoord2f(1.0f, 0.0f); | ||||
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY); | |||||
glVertex2d(x+w, y); | |||||
glTexCoord2f(1.0f, 1.0f); | glTexCoord2f(1.0f, 1.0f); | ||||
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight); | |||||
glVertex2d(x+w, y+h); | |||||
glTexCoord2f(0.0f, 1.0f); | glTexCoord2f(0.0f, 1.0f); | ||||
glVertex2d(fPos.fX, fPos.fY+fSize.fHeight); | |||||
glVertex2d(x, y+h); | |||||
} | } | ||||
glEnd(); | glEnd(); | ||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// Possible template data types | |||||
template class Point<double>; | |||||
template class Point<float>; | |||||
template class Point<int>; | |||||
template class Point<uint>; | |||||
template class Point<short>; | |||||
template class Point<ushort>; | |||||
template class Size<double>; | |||||
template class Size<float>; | |||||
template class Size<int>; | |||||
template class Size<uint>; | |||||
template class Size<short>; | |||||
template class Size<ushort>; | |||||
template<typename T> | |||||
void Rectangle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
drawRectangle<T>(*this, false); | |||||
} | |||||
template class Line<double>; | |||||
template class Line<float>; | |||||
template class Line<int>; | |||||
template class Line<uint>; | |||||
template class Line<short>; | |||||
template class Line<ushort>; | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
template class Circle<double>; | |||||
template class Circle<float>; | |||||
template class Circle<int>; | |||||
template class Circle<uint>; | |||||
template class Circle<short>; | |||||
template class Circle<ushort>; | |||||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
drawRectangle<T>(*this, true); | |||||
} | |||||
template class Triangle<double>; | |||||
template class Triangle<float>; | |||||
template class Triangle<int>; | |||||
template class Triangle<uint>; | |||||
template class Triangle<short>; | |||||
template class Triangle<ushort>; | |||||
// deprecated calls | |||||
template<typename T> | |||||
void Rectangle<T>::draw() | |||||
{ | |||||
drawRectangle<T>(*this, false); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline() | |||||
{ | |||||
drawRectangle<T>(*this, true); | |||||
} | |||||
template class Rectangle<double>; | template class Rectangle<double>; | ||||
template class Rectangle<float>; | template class Rectangle<float>; | ||||
@@ -152,6 +276,399 @@ template class Rectangle<uint>; | |||||
template class Rectangle<short>; | template class Rectangle<short>; | ||||
template class Rectangle<ushort>; | template class Rectangle<ushort>; | ||||
// ----------------------------------------------------------------------- | |||||
// OpenGLImage | |||||
static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); | |||||
glEnable(GL_TEXTURE_2D); | |||||
glBindTexture(GL_TEXTURE_2D, textureId); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||||
static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||||
glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||||
static_cast<GLsizei>(image.getWidth()), | |||||
static_cast<GLsizei>(image.getHeight()), | |||||
0, | |||||
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData()); | |||||
glBindTexture(GL_TEXTURE_2D, 0); | |||||
glDisable(GL_TEXTURE_2D); | |||||
} | |||||
static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled) | |||||
{ | |||||
if (textureId == 0 || image.isInvalid()) | |||||
return; | |||||
if (! setupCalled) | |||||
{ | |||||
setupOpenGLImage(image, textureId); | |||||
setupCalled = true; | |||||
} | |||||
glEnable(GL_TEXTURE_2D); | |||||
glBindTexture(GL_TEXTURE_2D, textureId); | |||||
glBegin(GL_QUADS); | |||||
{ | |||||
const int x = pos.getX(); | |||||
const int y = pos.getY(); | |||||
const int w = static_cast<int>(image.getWidth()); | |||||
const int h = static_cast<int>(image.getHeight()); | |||||
glTexCoord2f(0.0f, 0.0f); | |||||
glVertex2d(x, y); | |||||
glTexCoord2f(1.0f, 0.0f); | |||||
glVertex2d(x+w, y); | |||||
glTexCoord2f(1.0f, 1.0f); | |||||
glVertex2d(x+w, y+h); | |||||
glTexCoord2f(0.0f, 1.0f); | |||||
glVertex2d(x, y+h); | |||||
} | |||||
glEnd(); | |||||
glBindTexture(GL_TEXTURE_2D, 0); | |||||
glDisable(GL_TEXTURE_2D); | |||||
} | |||||
OpenGLImage::OpenGLImage() | |||||
: ImageBase(), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) | |||||
: ImageBase(rdata, w, h, fmt), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||||
: ImageBase(rdata, s, fmt), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
OpenGLImage::OpenGLImage(const OpenGLImage& image) | |||||
: ImageBase(image), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
OpenGLImage::~OpenGLImage() | |||||
{ | |||||
if (textureId != 0) | |||||
glDeleteTextures(1, &textureId); | |||||
} | |||||
void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||||
{ | |||||
setupCalled = false; | |||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
} | |||||
void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||||
{ | |||||
drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
} | |||||
OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||||
{ | |||||
rawData = image.rawData; | |||||
size = image.size; | |||||
format = image.format; | |||||
setupCalled = false; | |||||
return *this; | |||||
} | |||||
// deprecated calls | |||||
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) | |||||
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt) | |||||
: ImageBase(rdata, s, asDISTRHOImageFormat(fmt)), | |||||
textureId(0), | |||||
setupCalled(false) | |||||
{ | |||||
glGenTextures(1, &textureId); | |||||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||||
} | |||||
void OpenGLImage::draw() | |||||
{ | |||||
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||||
} | |||||
void OpenGLImage::drawAt(const int x, const int y) | |||||
{ | |||||
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||||
} | |||||
void OpenGLImage::drawAt(const Point<int>& pos) | |||||
{ | |||||
drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseAboutWindow | |||||
#if 0 | |||||
template <> | |||||
void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||||
{ | |||||
const GraphicsContext& context(getGraphicsContext()); | |||||
img.draw(context); | |||||
} | |||||
#endif | |||||
template class ImageBaseAboutWindow<OpenGLImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseButton | |||||
template class ImageBaseButton<OpenGLImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseKnob | |||||
template <> | |||||
void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||||
{ | |||||
glTextureId = 0; | |||||
glGenTextures(1, &glTextureId); | |||||
} | |||||
template <> | |||||
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||||
{ | |||||
if (glTextureId == 0) | |||||
return; | |||||
glDeleteTextures(1, &glTextureId); | |||||
glTextureId = 0; | |||||
} | |||||
template <> | |||||
void ImageBaseKnob<OpenGLImage>::onDisplay() | |||||
{ | |||||
const GraphicsContext& context(getGraphicsContext()); | |||||
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||||
/ (pData->maximum - pData->minimum); | |||||
glEnable(GL_TEXTURE_2D); | |||||
glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||||
if (! pData->isReady) | |||||
{ | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||||
static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||||
glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||||
uint imageDataOffset = 0; | |||||
if (pData->rotationAngle == 0) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); | |||||
const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); | |||||
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); | |||||
// TODO kImageFormatGreyscale | |||||
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || | |||||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||||
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); | |||||
} | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||||
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||||
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||||
pData->isReady = true; | |||||
} | |||||
const int w = static_cast<int>(getWidth()); | |||||
const int h = static_cast<int>(getHeight()); | |||||
if (pData->rotationAngle != 0) | |||||
{ | |||||
glPushMatrix(); | |||||
const int w2 = w/2; | |||||
const int h2 = h/2; | |||||
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||||
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||||
Rectangle<int>(-w2, -h2, w, h).draw(context); | |||||
glPopMatrix(); | |||||
} | |||||
else | |||||
{ | |||||
Rectangle<int>(0, 0, w, h).draw(context); | |||||
} | |||||
glBindTexture(GL_TEXTURE_2D, 0); | |||||
glDisable(GL_TEXTURE_2D); | |||||
} | |||||
template class ImageBaseKnob<OpenGLImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseSlider | |||||
template class ImageBaseSlider<OpenGLImage>; | |||||
// ----------------------------------------------------------------------- | |||||
// ImageBaseSwitch | |||||
template class ImageBaseSwitch<OpenGLImage>; | |||||
// ----------------------------------------------------------------------- | |||||
void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |||||
{ | |||||
if (skipDrawing) | |||||
return; | |||||
bool needsDisableScissor = false; | |||||
if (needsViewportScaling) | |||||
{ | |||||
// limit viewport to widget bounds | |||||
const int x = absolutePos.getX(); | |||||
const int w = static_cast<int>(self->getWidth()); | |||||
const int h = static_cast<int>(self->getHeight()); | |||||
if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0) | |||||
{ | |||||
glViewport(x, | |||||
-static_cast<int>(height * viewportScaleFactor - height + absolutePos.getY() + 0.5), | |||||
static_cast<int>(width * viewportScaleFactor + 0.5), | |||||
static_cast<int>(height * viewportScaleFactor + 0.5)); | |||||
} | |||||
else | |||||
{ | |||||
const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY(); | |||||
glViewport(x, y, w, h); | |||||
} | |||||
} | |||||
else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height))) | |||||
{ | |||||
// full viewport size | |||||
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height)); | |||||
} | |||||
else | |||||
{ | |||||
// set viewport pos | |||||
glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5), | |||||
-static_cast<int>(std::round((height * autoScaleFactor - height) | |||||
+ (absolutePos.getY() * autoScaleFactor))), | |||||
static_cast<int>(std::round(width * autoScaleFactor)), | |||||
static_cast<int>(std::round(height * autoScaleFactor))); | |||||
// then cut the outer bounds | |||||
glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5), | |||||
static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY()) | |||||
* autoScaleFactor)), | |||||
static_cast<int>(std::round(self->getWidth() * autoScaleFactor)), | |||||
static_cast<int>(std::round(self->getHeight() * autoScaleFactor))); | |||||
glEnable(GL_SCISSOR_TEST); | |||||
needsDisableScissor = true; | |||||
} | |||||
// display widget | |||||
self->onDisplay(); | |||||
if (needsDisableScissor) | |||||
glDisable(GL_SCISSOR_TEST); | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
void TopLevelWidget::PrivateData::display() | |||||
{ | |||||
if (! selfw->pData->visible) | |||||
return; | |||||
const Size<uint> size(window.getSize()); | |||||
const uint width = size.getWidth(); | |||||
const uint height = size.getHeight(); | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
// full viewport size | |||||
if (window.pData->autoScaling) | |||||
{ | |||||
glViewport(0, | |||||
-static_cast<int>(height * autoScaleFactor - height), | |||||
static_cast<int>(width * autoScaleFactor), | |||||
static_cast<int>(height * autoScaleFactor)); | |||||
} | |||||
else | |||||
{ | |||||
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height)); | |||||
} | |||||
// main widget drawing | |||||
self->onDisplay(); | |||||
// now draw subwidgets if there are any | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||||
{ | |||||
return (const GraphicsContext&)graphicsContext; | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -0,0 +1,163 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
#include "../TopLevelWidget.hpp" | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
SubWidget::SubWidget(Widget* const parentWidget) | |||||
: Widget(parentWidget), | |||||
pData(new PrivateData(this, parentWidget)) {} | |||||
SubWidget::~SubWidget() | |||||
{ | |||||
delete pData; | |||||
} | |||||
template<typename T> | |||||
bool SubWidget::contains(T x, T y) const noexcept | |||||
{ | |||||
return Rectangle<double>(0, 0, getWidth(), getHeight()).contains(x, y); | |||||
} | |||||
template<typename T> | |||||
bool SubWidget::contains(const Point<T>& pos) const noexcept | |||||
{ | |||||
return contains(pos.getX(), pos.getY()); | |||||
} | |||||
int SubWidget::getAbsoluteX() const noexcept | |||||
{ | |||||
return pData->absolutePos.getX(); | |||||
} | |||||
int SubWidget::getAbsoluteY() const noexcept | |||||
{ | |||||
return pData->absolutePos.getY(); | |||||
} | |||||
Point<int> SubWidget::getAbsolutePos() const noexcept | |||||
{ | |||||
return pData->absolutePos; | |||||
} | |||||
Rectangle<int> SubWidget::getAbsoluteArea() const noexcept | |||||
{ | |||||
return Rectangle<int>(getAbsolutePos(), getSize().toInt()); | |||||
} | |||||
Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept | |||||
{ | |||||
return Rectangle<uint>(static_cast<uint>(std::max(0, getAbsoluteX())), | |||||
static_cast<uint>(std::max(0, getAbsoluteY())), | |||||
getSize()); | |||||
} | |||||
void SubWidget::setAbsoluteX(int x) noexcept | |||||
{ | |||||
setAbsolutePos(Point<int>(x, getAbsoluteY())); | |||||
} | |||||
void SubWidget::setAbsoluteY(int y) noexcept | |||||
{ | |||||
setAbsolutePos(Point<int>(getAbsoluteX(), y)); | |||||
} | |||||
void SubWidget::setAbsolutePos(int x, int y) noexcept | |||||
{ | |||||
setAbsolutePos(Point<int>(x, y)); | |||||
} | |||||
void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept | |||||
{ | |||||
if (pData->absolutePos == pos) | |||||
return; | |||||
PositionChangedEvent ev; | |||||
ev.oldPos = pData->absolutePos; | |||||
ev.pos = pos; | |||||
pData->absolutePos = pos; | |||||
onPositionChanged(ev); | |||||
repaint(); | |||||
} | |||||
Widget* SubWidget::getParentWidget() const noexcept | |||||
{ | |||||
return pData->parentWidget; | |||||
} | |||||
void SubWidget::repaint() noexcept | |||||
{ | |||||
if (! isVisible()) | |||||
return; | |||||
if (TopLevelWidget* const topw = getTopLevelWidget()) | |||||
{ | |||||
if (pData->needsFullViewportForDrawing) | |||||
topw->repaint(); | |||||
else | |||||
topw->repaint(getConstrainedAbsoluteArea()); | |||||
} | |||||
} | |||||
void SubWidget::toFront() | |||||
{ | |||||
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets); | |||||
subwidgets.remove(this); | |||||
subwidgets.push_back(this); | |||||
} | |||||
void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing) | |||||
{ | |||||
pData->needsFullViewportForDrawing = needsFullViewportForDrawing; | |||||
} | |||||
void SubWidget::setNeedsViewportScaling(const bool needsViewportScaling, const double autoScaleFactor) | |||||
{ | |||||
pData->needsViewportScaling = needsViewportScaling; | |||||
pData->viewportScaleFactor = autoScaleFactor; | |||||
} | |||||
void SubWidget::setSkipDrawing(const bool skipDrawing) | |||||
{ | |||||
pData->skipDrawing = skipDrawing; | |||||
} | |||||
void SubWidget::onPositionChanged(const PositionChangedEvent&) | |||||
{ | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Possible template data types | |||||
template<> | |||||
bool SubWidget::contains(const Point<double>& pos) const noexcept | |||||
{ | |||||
return contains(pos.getX(), pos.getY()); | |||||
} | |||||
// float, int, uint, short, ushort | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -0,0 +1,44 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) | |||||
: self(s), | |||||
selfw((Widget*)s), | |||||
parentWidget(pw), | |||||
absolutePos(), | |||||
needsFullViewportForDrawing(false), | |||||
needsViewportScaling(false), | |||||
skipDrawing(false), | |||||
viewportScaleFactor(0.0) | |||||
{ | |||||
parentWidget->pData->subWidgets.push_back(self); | |||||
} | |||||
SubWidget::PrivateData::~PrivateData() | |||||
{ | |||||
parentWidget->pData->subWidgets.remove(self); | |||||
} | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -0,0 +1,49 @@ | |||||
/* | |||||
* 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_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
#include "../SubWidget.hpp" | |||||
START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
struct SubWidget::PrivateData { | |||||
SubWidget* const self; | |||||
Widget* const selfw; | |||||
Widget* const parentWidget; | |||||
Point<int> absolutePos; | |||||
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds | |||||
bool needsViewportScaling; // needed for NanoVG | |||||
bool skipDrawing; // for context reuse in NanoVG based guis | |||||
double viewportScaleFactor; // auto-scaling for NanoVG | |||||
explicit PrivateData(SubWidget* const s, Widget* const pw); | |||||
~PrivateData(); | |||||
// NOTE display function is different depending on build type, must call displaySubWidgets at the end | |||||
void display(uint width, uint height, double autoScaleFactor); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED |
@@ -0,0 +1,98 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "TopLevelWidgetPrivateData.hpp" | |||||
#include "../Window.hpp" | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
TopLevelWidget::TopLevelWidget(Window& windowToMapTo) | |||||
: Widget(this), | |||||
pData(new PrivateData(this, windowToMapTo)) {} | |||||
TopLevelWidget::~TopLevelWidget() | |||||
{ | |||||
delete pData; | |||||
} | |||||
Application& TopLevelWidget::getApp() const noexcept | |||||
{ | |||||
return pData->window.getApp(); | |||||
} | |||||
Window& TopLevelWidget::getWindow() const noexcept | |||||
{ | |||||
return pData->window; | |||||
} | |||||
void TopLevelWidget::setWidth(const uint width) | |||||
{ | |||||
pData->window.setWidth(width); | |||||
} | |||||
void TopLevelWidget::setHeight(const uint height) | |||||
{ | |||||
pData->window.setHeight(height); | |||||
} | |||||
void TopLevelWidget::setSize(const uint width, const uint height) | |||||
{ | |||||
pData->window.setSize(width, height); | |||||
} | |||||
void TopLevelWidget::setSize(const Size<uint>& size) | |||||
{ | |||||
pData->window.setSize(size); | |||||
} | |||||
bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) | |||||
{ | |||||
return pData->window.addIdleCallback(callback, timerFrequencyInMs); | |||||
} | |||||
bool TopLevelWidget::removeIdleCallback(IdleCallback* const callback) | |||||
{ | |||||
return pData->window.removeIdleCallback(callback); | |||||
} | |||||
double TopLevelWidget::getScaleFactor() const noexcept | |||||
{ | |||||
return pData->window.getScaleFactor(); | |||||
} | |||||
void TopLevelWidget::repaint() noexcept | |||||
{ | |||||
pData->window.repaint(); | |||||
} | |||||
void TopLevelWidget::repaint(const Rectangle<uint>& rect) noexcept | |||||
{ | |||||
pData->window.repaint(rect); | |||||
} | |||||
void TopLevelWidget::setGeometryConstraints(const uint minimumWidth, | |||||
const uint minimumHeight, | |||||
const bool keepAspectRatio, | |||||
const bool automaticallyScale) | |||||
{ | |||||
pData->window.setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -0,0 +1,168 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "TopLevelWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
#include "WindowPrivateData.hpp" | |||||
#include "pugl.hpp" | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) | |||||
: self(s), | |||||
selfw(s), | |||||
window(w) | |||||
{ | |||||
window.pData->topLevelWidgets.push_back(self); | |||||
} | |||||
TopLevelWidget::PrivateData::~PrivateData() | |||||
{ | |||||
window.pData->topLevelWidgets.remove(self); | |||||
} | |||||
bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onKeyboard(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveKeyboardEventForSubWidgets(ev); | |||||
} | |||||
bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onSpecial(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveSpecialEventForSubWidgets(ev); | |||||
} | |||||
bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onCharacterInput(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveCharacterInputEventForSubWidgets(ev); | |||||
} | |||||
bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
MouseEvent rev = ev; | |||||
if (window.pData->autoScaling) | |||||
{ | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||||
} | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onMouse(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveMouseEventForSubWidgets(rev); | |||||
} | |||||
bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
MotionEvent rev = ev; | |||||
if (window.pData->autoScaling) | |||||
{ | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||||
} | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onMotion(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveMotionEventForSubWidgets(rev); | |||||
} | |||||
bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev) | |||||
{ | |||||
// ignore event if we are not visible | |||||
if (! selfw->pData->visible) | |||||
return false; | |||||
ScrollEvent rev = ev; | |||||
if (window.pData->autoScaling) | |||||
{ | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||||
rev.delta.setX(ev.delta.getX() / autoScaleFactor); | |||||
rev.delta.setY(ev.delta.getY() / autoScaleFactor); | |||||
} | |||||
// give top-level widget chance to catch this event first | |||||
if (self->onScroll(ev)) | |||||
return true; | |||||
// propagate event to all subwidgets recursively | |||||
return selfw->pData->giveScrollEventForSubWidgets(rev); | |||||
} | |||||
void TopLevelWidget::PrivateData::fallbackOnResize() | |||||
{ | |||||
puglFallbackOnResize(window.pData->view); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL |
@@ -0,0 +1,51 @@ | |||||
/* | |||||
* 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_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
#include "../TopLevelWidget.hpp" | |||||
#include <list> | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
struct TopLevelWidget::PrivateData { | |||||
TopLevelWidget* const self; | |||||
Widget* const selfw; | |||||
Window& window; | |||||
explicit PrivateData(TopLevelWidget* const s, Window& w); | |||||
~PrivateData(); | |||||
void display(); | |||||
bool keyboardEvent(const KeyboardEvent& ev); | |||||
bool specialEvent(const SpecialEvent& ev); | |||||
bool characterInputEvent(const CharacterInputEvent& ev); | |||||
bool mouseEvent(const MouseEvent& ev); | |||||
bool motionEvent(const MotionEvent& ev); | |||||
bool scrollEvent(const ScrollEvent& ev); | |||||
void fallbackOnResize(); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED |
@@ -0,0 +1,243 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#include "../Vulkan.hpp" | |||||
#include "../Color.hpp" | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "TopLevelWidgetPrivateData.hpp" | |||||
#include "WidgetPrivateData.hpp" | |||||
#include "WindowPrivateData.hpp" | |||||
START_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- | |||||
static void notImplemented(const char* const name) | |||||
{ | |||||
d_stderr2("vulkan function not implemented: %s", name); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Color | |||||
void Color::setFor(const GraphicsContext&, bool) | |||||
{ | |||||
notImplemented("Color::setFor"); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
// Line | |||||
template<typename T> | |||||
void Line<T>::draw(const GraphicsContext&, T) | |||||
{ | |||||
notImplemented("Line::draw"); | |||||
} | |||||
template<typename T> | |||||
void Line<T>::draw() | |||||
{ | |||||
notImplemented("Line::draw"); | |||||
} | |||||
template class Line<double>; | |||||
template class Line<float>; | |||||
template class Line<int>; | |||||
template class Line<uint>; | |||||
template class Line<short>; | |||||
template class Line<ushort>; | |||||
// ----------------------------------------------------------------------- | |||||
// Circle | |||||
template<typename T> | |||||
void Circle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
notImplemented("Circle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::drawOutline(const GraphicsContext&, T) | |||||
{ | |||||
notImplemented("Circle::drawOutline"); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::draw() | |||||
{ | |||||
notImplemented("Circle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Circle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Circle::drawOutline"); | |||||
} | |||||
template class Circle<double>; | |||||
template class Circle<float>; | |||||
template class Circle<int>; | |||||
template class Circle<uint>; | |||||
template class Circle<short>; | |||||
template class Circle<ushort>; | |||||
// ----------------------------------------------------------------------- | |||||
// Triangle | |||||
template<typename T> | |||||
void Triangle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
notImplemented("Triangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline(const GraphicsContext&, T) | |||||
{ | |||||
notImplemented("Triangle::drawOutline"); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::draw() | |||||
{ | |||||
notImplemented("Triangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Triangle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Triangle::drawOutline"); | |||||
} | |||||
template class Triangle<double>; | |||||
template class Triangle<float>; | |||||
template class Triangle<int>; | |||||
template class Triangle<uint>; | |||||
template class Triangle<short>; | |||||
template class Triangle<ushort>; | |||||
// ----------------------------------------------------------------------- | |||||
// Rectangle | |||||
template<typename T> | |||||
void Rectangle<T>::draw(const GraphicsContext&) | |||||
{ | |||||
notImplemented("Rectangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline(const GraphicsContext&, T) | |||||
{ | |||||
notImplemented("Rectangle::drawOutline"); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::draw() | |||||
{ | |||||
notImplemented("Rectangle::draw"); | |||||
} | |||||
template<typename T> | |||||
void Rectangle<T>::drawOutline() | |||||
{ | |||||
notImplemented("Rectangle::drawOutline"); | |||||
} | |||||
template class Rectangle<double>; | |||||
template class Rectangle<float>; | |||||
template class Rectangle<int>; | |||||
template class Rectangle<uint>; | |||||
template class Rectangle<short>; | |||||
template class Rectangle<ushort>; | |||||
// ----------------------------------------------------------------------- | |||||
// VulkanImage | |||||
VulkanImage::VulkanImage() | |||||
: ImageBase() {} | |||||
VulkanImage::VulkanImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) | |||||
: ImageBase(rdata, w, h, fmt) {} | |||||
VulkanImage::VulkanImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||||
: ImageBase(rdata, s, fmt) {} | |||||
VulkanImage::VulkanImage(const VulkanImage& image) | |||||
: ImageBase(image.rawData, image.size, image.format) {} | |||||
VulkanImage::~VulkanImage() {} | |||||
void VulkanImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||||
{ | |||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
} | |||||
void VulkanImage::drawAt(const GraphicsContext&, const Point<int>&) | |||||
{ | |||||
} | |||||
VulkanImage& VulkanImage::operator=(const VulkanImage& image) noexcept | |||||
{ | |||||
rawData = image.rawData; | |||||
size = image.size; | |||||
format = image.format; | |||||
return *this; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |||||
{ | |||||
// TODO | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
void TopLevelWidget::PrivateData::display() | |||||
{ | |||||
if (! selfw->pData->visible) | |||||
return; | |||||
const Size<uint> size(window.getSize()); | |||||
const uint width = size.getWidth(); | |||||
const uint height = size.getHeight(); | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||||
// TODO | |||||
// main widget drawing | |||||
self->onDisplay(); | |||||
// now draw subwidgets if there are any | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||||
{ | |||||
return (const GraphicsContext&)graphicsContext; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
// ----------------------------------------------------------------------- |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -15,33 +15,22 @@ | |||||
*/ | */ | ||||
#include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
#include "../TopLevelWidget.hpp" | |||||
#include "../Window.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Widget | // Widget | ||||
Widget::Widget(Window& parent) | |||||
: pData(new PrivateData(this, parent, nullptr, false)) | |||||
{ | |||||
parent._addWidget(this); | |||||
} | |||||
Widget::Widget(TopLevelWidget* const topLevelWidget) | |||||
: pData(new PrivateData(this, topLevelWidget)) {} | |||||
Widget::Widget(Widget* groupWidget) | |||||
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)) | |||||
{ | |||||
pData->parent._addWidget(this); | |||||
} | |||||
Widget::Widget(Widget* groupWidget, bool addToSubWidgets) | |||||
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)) | |||||
{ | |||||
pData->parent._addWidget(this); | |||||
} | |||||
Widget::Widget(Widget* const parentWidget) | |||||
: pData(new PrivateData(this, parentWidget)) {} | |||||
Widget::~Widget() | Widget::~Widget() | ||||
{ | { | ||||
pData->parent._removeWidget(this); | |||||
delete pData; | delete pData; | ||||
} | } | ||||
@@ -50,13 +39,15 @@ bool Widget::isVisible() const noexcept | |||||
return pData->visible; | return pData->visible; | ||||
} | } | ||||
void Widget::setVisible(bool yesNo) | |||||
void Widget::setVisible(bool visible) | |||||
{ | { | ||||
if (pData->visible == yesNo) | |||||
if (pData->visible == visible) | |||||
return; | return; | ||||
pData->visible = yesNo; | |||||
pData->parent.repaint(); | |||||
pData->visible = visible; | |||||
repaint(); | |||||
// FIXME check case of hiding a previously visible widget, does it trigger a repaint? | |||||
} | } | ||||
void Widget::show() | void Widget::show() | ||||
@@ -79,7 +70,7 @@ uint Widget::getHeight() const noexcept | |||||
return pData->size.getHeight(); | return pData->size.getHeight(); | ||||
} | } | ||||
const Size<uint>& Widget::getSize() const noexcept | |||||
const Size<uint> Widget::getSize() const noexcept | |||||
{ | { | ||||
return pData->size; | return pData->size; | ||||
} | } | ||||
@@ -96,7 +87,7 @@ void Widget::setWidth(uint width) noexcept | |||||
pData->size.setWidth(width); | pData->size.setWidth(width); | ||||
onResize(ev); | onResize(ev); | ||||
pData->parent.repaint(); | |||||
repaint(); | |||||
} | } | ||||
void Widget::setHeight(uint height) noexcept | void Widget::setHeight(uint height) noexcept | ||||
@@ -111,7 +102,7 @@ void Widget::setHeight(uint height) noexcept | |||||
pData->size.setHeight(height); | pData->size.setHeight(height); | ||||
onResize(ev); | onResize(ev); | ||||
pData->parent.repaint(); | |||||
repaint(); | |||||
} | } | ||||
void Widget::setSize(uint width, uint height) noexcept | void Widget::setSize(uint width, uint height) noexcept | ||||
@@ -131,77 +122,34 @@ void Widget::setSize(const Size<uint>& size) noexcept | |||||
pData->size = size; | pData->size = size; | ||||
onResize(ev); | onResize(ev); | ||||
pData->parent.repaint(); | |||||
} | |||||
int Widget::getAbsoluteX() const noexcept | |||||
{ | |||||
return pData->absolutePos.getX(); | |||||
} | |||||
int Widget::getAbsoluteY() const noexcept | |||||
{ | |||||
return pData->absolutePos.getY(); | |||||
repaint(); | |||||
} | } | ||||
const Point<int>& Widget::getAbsolutePos() const noexcept | |||||
Application& Widget::getApp() const noexcept | |||||
{ | { | ||||
return pData->absolutePos; | |||||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||||
return pData->topLevelWidget->getApp(); | |||||
} | } | ||||
void Widget::setAbsoluteX(int x) noexcept | |||||
Window& Widget::getWindow() const noexcept | |||||
{ | { | ||||
setAbsolutePos(Point<int>(x, getAbsoluteY())); | |||||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||||
return pData->topLevelWidget->getWindow(); | |||||
} | } | ||||
void Widget::setAbsoluteY(int y) noexcept | |||||
const GraphicsContext& Widget::getGraphicsContext() const noexcept | |||||
{ | { | ||||
setAbsolutePos(Point<int>(getAbsoluteX(), y)); | |||||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||||
return pData->topLevelWidget->getWindow().getGraphicsContext(); | |||||
} | } | ||||
void Widget::setAbsolutePos(int x, int y) noexcept | |||||
TopLevelWidget* Widget::getTopLevelWidget() const noexcept | |||||
{ | { | ||||
setAbsolutePos(Point<int>(x, y)); | |||||
} | |||||
void Widget::setAbsolutePos(const Point<int>& pos) noexcept | |||||
{ | |||||
if (pData->absolutePos == pos) | |||||
return; | |||||
PositionChangedEvent ev; | |||||
ev.oldPos = pData->absolutePos; | |||||
ev.pos = pos; | |||||
pData->absolutePos = pos; | |||||
onPositionChanged(ev); | |||||
pData->parent.repaint(); | |||||
} | |||||
Application& Widget::getParentApp() const noexcept | |||||
{ | |||||
return pData->parent.getApp(); | |||||
} | |||||
Window& Widget::getParentWindow() const noexcept | |||||
{ | |||||
return pData->parent; | |||||
} | |||||
bool Widget::contains(int x, int y) const noexcept | |||||
{ | |||||
return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight()); | |||||
} | |||||
bool Widget::contains(const Point<int>& pos) const noexcept | |||||
{ | |||||
return contains(pos.getX(), pos.getY()); | |||||
return pData->topLevelWidget; | |||||
} | } | ||||
void Widget::repaint() noexcept | void Widget::repaint() noexcept | ||||
{ | { | ||||
pData->parent.repaint(); | |||||
} | } | ||||
uint Widget::getId() const noexcept | uint Widget::getId() const noexcept | ||||
@@ -214,39 +162,43 @@ void Widget::setId(uint id) noexcept | |||||
pData->id = id; | pData->id = id; | ||||
} | } | ||||
bool Widget::onKeyboard(const KeyboardEvent&) | |||||
bool Widget::onKeyboard(const KeyboardEvent& ev) | |||||
{ | { | ||||
return false; | |||||
return pData->giveKeyboardEventForSubWidgets(ev); | |||||
} | } | ||||
bool Widget::onSpecial(const SpecialEvent&) | |||||
bool Widget::onSpecial(const SpecialEvent& ev) | |||||
{ | { | ||||
return false; | |||||
return pData->giveSpecialEventForSubWidgets(ev); | |||||
} | } | ||||
bool Widget::onMouse(const MouseEvent&) | |||||
bool Widget::onCharacterInput(const CharacterInputEvent& ev) | |||||
{ | { | ||||
return false; | |||||
return pData->giveCharacterInputEventForSubWidgets(ev); | |||||
} | } | ||||
bool Widget::onMotion(const MotionEvent&) | |||||
bool Widget::onMouse(const MouseEvent& ev) | |||||
{ | { | ||||
return false; | |||||
MouseEvent rev = ev; | |||||
return pData->giveMouseEventForSubWidgets(rev); | |||||
} | } | ||||
bool Widget::onScroll(const ScrollEvent&) | |||||
bool Widget::onMotion(const MotionEvent& ev) | |||||
{ | { | ||||
return false; | |||||
MotionEvent rev = ev; | |||||
return pData->giveMotionEventForSubWidgets(rev); | |||||
} | } | ||||
void Widget::onResize(const ResizeEvent&) | |||||
bool Widget::onScroll(const ScrollEvent& ev) | |||||
{ | { | ||||
ScrollEvent rev = ev; | |||||
return pData->giveScrollEventForSubWidgets(rev); | |||||
} | } | ||||
void Widget::onPositionChanged(const PositionChangedEvent&) | |||||
void Widget::onResize(const ResizeEvent&) | |||||
{ | { | ||||
} | } | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -15,91 +15,240 @@ | |||||
*/ | */ | ||||
#include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
#ifdef DGL_CAIRO | |||||
# include "../Cairo.hpp" | |||||
#endif | |||||
#ifdef DGL_OPENGL | |||||
# include "../OpenGL.hpp" | |||||
#endif | |||||
#include "SubWidgetPrivateData.hpp" | |||||
#include "../TopLevelWidget.hpp" | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
#define FOR_EACH_SUBWIDGET(it) \ | |||||
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||||
#define FOR_EACH_SUBWIDGET_INV(rit) \ | |||||
for (std::list<SubWidget*>::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit) | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
void Widget::PrivateData::display(const uint width, | |||||
const uint height, | |||||
const double scaling, | |||||
const bool renderingSubWidget) | |||||
Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) | |||||
: self(s), | |||||
topLevelWidget(tlw), | |||||
parentWidget(nullptr), | |||||
id(0), | |||||
needsScaling(false), | |||||
visible(true), | |||||
size(0, 0), | |||||
subWidgets() {} | |||||
Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) | |||||
: self(s), | |||||
topLevelWidget(findTopLevelWidget(pw)), | |||||
parentWidget(pw), | |||||
id(0), | |||||
needsScaling(false), | |||||
visible(true), | |||||
size(0, 0), | |||||
subWidgets() {} | |||||
Widget::PrivateData::~PrivateData() | |||||
{ | { | ||||
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) | |||||
subWidgets.clear(); | |||||
} | |||||
void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) | |||||
{ | |||||
if (subWidgets.size() == 0) | |||||
return; | return; | ||||
#ifdef DGL_OPENGL | |||||
bool needsDisableScissor = false; | |||||
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||||
{ | |||||
SubWidget* const subwidget(*it); | |||||
if (subwidget->isVisible()) | |||||
subwidget->pData->display(width, height, autoScaleFactor); | |||||
} | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | |||||
SubWidget* const widget(*rit); | |||||
if (widget->isVisible() && widget->onKeyboard(ev)) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool Widget::PrivateData::giveSpecialEventForSubWidgets(const SpecialEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | |||||
SubWidget* const widget(*rit); | |||||
if (widget->isVisible() && widget->onSpecial(ev)) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | |||||
SubWidget* const widget(*rit); | |||||
if (widget->isVisible() && widget->onCharacterInput(ev)) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
// reset color | |||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||||
bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height))) | |||||
double x = ev.absolutePos.getX(); | |||||
double y = ev.absolutePos.getY(); | |||||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||||
{ | { | ||||
// full viewport size | |||||
glViewport(0, | |||||
-(height * scaling - height), | |||||
width * scaling, | |||||
height * scaling); | |||||
if (selfw->pData->needsViewportScaling) | |||||
{ | |||||
x -= selfw->getAbsoluteX(); | |||||
y -= selfw->getAbsoluteY(); | |||||
ev.absolutePos.setX(x); | |||||
ev.absolutePos.setY(y); | |||||
} | |||||
} | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | |||||
SubWidget* const widget(*rit); | |||||
if (! widget->isVisible()) | |||||
continue; | |||||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||||
y - widget->getAbsoluteY()); | |||||
if (widget->onMouse(ev)) | |||||
return true; | |||||
} | } | ||||
else if (needsScaling) | |||||
return false; | |||||
} | |||||
bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
double x = ev.absolutePos.getX(); | |||||
double y = ev.absolutePos.getY(); | |||||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||||
{ | { | ||||
// limit viewport to widget bounds | |||||
glViewport(absolutePos.getX(), | |||||
height - self->getHeight() - absolutePos.getY(), | |||||
self->getWidth(), | |||||
self->getHeight()); | |||||
if (selfw->pData->needsViewportScaling) | |||||
{ | |||||
x -= selfw->getAbsoluteX(); | |||||
y -= selfw->getAbsoluteY(); | |||||
ev.absolutePos.setX(x); | |||||
ev.absolutePos.setY(y); | |||||
} | |||||
} | } | ||||
else | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | { | ||||
// only set viewport pos | |||||
glViewport(absolutePos.getX() * scaling, | |||||
-std::round((height * scaling - height) + (absolutePos.getY() * scaling)), | |||||
std::round(width * scaling), | |||||
std::round(height * scaling)); | |||||
// then cut the outer bounds | |||||
glScissor(absolutePos.getX() * scaling, | |||||
height - std::round((self->getHeight() + absolutePos.getY()) * scaling), | |||||
std::round(self->getWidth() * scaling), | |||||
std::round(self->getHeight() * scaling)); | |||||
glEnable(GL_SCISSOR_TEST); | |||||
needsDisableScissor = true; | |||||
SubWidget* const widget(*rit); | |||||
if (! widget->isVisible()) | |||||
continue; | |||||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||||
y - widget->getAbsoluteY()); | |||||
if (widget->onMotion(ev)) | |||||
return true; | |||||
} | } | ||||
#endif | |||||
#ifdef DGL_CAIRO | |||||
cairo_t* cr = parent.getGraphicsContext().cairo; | |||||
cairo_matrix_t matrix; | |||||
cairo_get_matrix(cr, &matrix); | |||||
cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); | |||||
// TODO: scaling and cropping | |||||
#endif | |||||
return false; | |||||
} | |||||
// display widget | |||||
self->onDisplay(); | |||||
bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) | |||||
{ | |||||
if (! visible) | |||||
return false; | |||||
if (subWidgets.size() == 0) | |||||
return false; | |||||
double x = ev.absolutePos.getX(); | |||||
double y = ev.absolutePos.getY(); | |||||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||||
{ | |||||
if (selfw->pData->needsViewportScaling) | |||||
{ | |||||
x -= selfw->getAbsoluteX(); | |||||
y -= selfw->getAbsoluteY(); | |||||
#ifdef DGL_CAIRO | |||||
cairo_set_matrix(cr, &matrix); | |||||
#endif | |||||
ev.absolutePos.setX(x); | |||||
ev.absolutePos.setY(y); | |||||
} | |||||
} | |||||
#ifdef DGL_OPENGL | |||||
if (needsDisableScissor) | |||||
FOR_EACH_SUBWIDGET_INV(rit) | |||||
{ | { | ||||
glDisable(GL_SCISSOR_TEST); | |||||
needsDisableScissor = false; | |||||
SubWidget* const widget(*rit); | |||||
if (! widget->isVisible()) | |||||
continue; | |||||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||||
y - widget->getAbsoluteY()); | |||||
if (widget->onScroll(ev)) | |||||
return true; | |||||
} | } | ||||
#endif | |||||
displaySubWidgets(width, height, scaling); | |||||
return false; | |||||
} | |||||
// ----------------------------------------------------------------------- | |||||
TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw) | |||||
{ | |||||
if (pw->pData->topLevelWidget != nullptr) | |||||
return pw->pData->topLevelWidget; | |||||
if (pw->pData->parentWidget != nullptr) | |||||
return findTopLevelWidget(pw->pData->parentWidget); | |||||
return nullptr; | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,69 +18,44 @@ | |||||
#define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | #define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | ||||
#include "../Widget.hpp" | #include "../Widget.hpp" | ||||
#include "../Window.hpp" | |||||
#include <vector> | |||||
#include <list> | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
struct Widget::PrivateData { | struct Widget::PrivateData { | ||||
Widget* const self; | Widget* const self; | ||||
Window& parent; | |||||
Point<int> absolutePos; | |||||
Size<uint> size; | |||||
std::vector<Widget*> subWidgets; | |||||
TopLevelWidget* const topLevelWidget; | |||||
Widget* const parentWidget; | |||||
uint id; | uint id; | ||||
bool needsFullViewport; | |||||
bool needsScaling; | bool needsScaling; | ||||
bool skipDisplay; | |||||
bool visible; | bool visible; | ||||
Size<uint> size; | |||||
std::list<SubWidget*> subWidgets; | |||||
PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets) | |||||
: self(s), | |||||
parent(p), | |||||
absolutePos(0, 0), | |||||
size(0, 0), | |||||
subWidgets(), | |||||
id(0), | |||||
needsFullViewport(false), | |||||
needsScaling(false), | |||||
skipDisplay(false), | |||||
visible(true) | |||||
{ | |||||
if (addToSubWidgets && groupWidget != nullptr) | |||||
{ | |||||
skipDisplay = true; | |||||
groupWidget->pData->subWidgets.push_back(self); | |||||
} | |||||
} | |||||
~PrivateData() | |||||
{ | |||||
subWidgets.clear(); | |||||
} | |||||
// called via TopLevelWidget | |||||
explicit PrivateData(Widget* const s, TopLevelWidget* const tlw); | |||||
// called via SubWidget | |||||
explicit PrivateData(Widget* const s, Widget* const pw); | |||||
~PrivateData(); | |||||
// display function is different depending on build type | |||||
void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget); | |||||
void displaySubWidgets(uint width, uint height, double autoScaleFactor); | |||||
void displaySubWidgets(const uint width, const uint height, const double scaling) | |||||
{ | |||||
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||||
{ | |||||
Widget* const widget(*it); | |||||
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | |||||
bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev); | |||||
bool giveSpecialEventForSubWidgets(const SpecialEvent& ev); | |||||
bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev); | |||||
bool giveMouseEventForSubWidgets(MouseEvent& ev); | |||||
bool giveMotionEventForSubWidgets(MotionEvent& ev); | |||||
bool giveScrollEventForSubWidgets(ScrollEvent& ev); | |||||
widget->pData->display(width, height, scaling, true); | |||||
} | |||||
} | |||||
static TopLevelWidget* findTopLevelWidget(Widget* const w); | |||||
DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | |||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -0,0 +1,309 @@ | |||||
/* | |||||
* 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_WINDOW_PRIVATE_DATA_HPP_INCLUDED | |||||
#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED | |||||
#include "../Window.hpp" | |||||
#include "../Widget.hpp" | |||||
#include "ApplicationPrivateData.hpp" | |||||
#include "pugl.hpp" | |||||
#include <list> | |||||
START_NAMESPACE_DGL | |||||
class TopLevelWidget; | |||||
// ----------------------------------------------------------------------- | |||||
struct Window::PrivateData : IdleCallback { | |||||
/** Reference to the DGL Application class this (private data) window associates with. */ | |||||
Application& app; | |||||
/** Direct access to the DGL Application private data where we registers ourselves in. */ | |||||
Application::PrivateData* const appData; | |||||
/** Pointer to the the DGL Window class that this private data belongs to. */ | |||||
Window* const self; | |||||
/** Pugl view instance. */ | |||||
PuglView* const view; | |||||
/** Reserved space for graphics context. */ | |||||
mutable uint8_t graphicsContext[sizeof(void*)]; | |||||
/** The top-level widgets associated with this Window. */ | |||||
std::list<TopLevelWidget*> topLevelWidgets; | |||||
/** Whether this Window is closed (not visible or counted in the Application it is tied to). | |||||
Defaults to true unless embed (embed windows are never closed). */ | |||||
bool isClosed; | |||||
/** Whether this Window is currently visible/mapped. Defaults to false. */ | |||||
bool isVisible; | |||||
/** Whether this Window is embed into another (usually not DGL-controlled) Window. */ | |||||
const bool isEmbed; | |||||
/** Scale factor to report to widgets on request, purely informational. */ | |||||
double scaleFactor; | |||||
/** Automatic scaling to apply on widgets, implemented internally. */ | |||||
bool autoScaling; | |||||
double autoScaleFactor; | |||||
/** Pugl geometry constraints access. */ | |||||
uint minWidth, minHeight; | |||||
bool keepAspectRatio; | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
/** Selected file for openFileBrowser on windows, stored for fake async operation. */ | |||||
const char* win32SelectedFile; | |||||
#endif | |||||
/** Modal window setup. */ | |||||
struct Modal { | |||||
PrivateData* parent; // parent of this window (so we can become modal) | |||||
PrivateData* child; // child window to give focus to when modal mode is enabled | |||||
bool enabled; // wherever modal mode is enabled (only possible if parent != null) | |||||
/** Constructor for a non-modal window. */ | |||||
Modal() noexcept | |||||
: parent(nullptr), | |||||
child(nullptr), | |||||
enabled(false) {} | |||||
/** Constructor for a modal window (with a parent). */ | |||||
Modal(PrivateData* const p) noexcept | |||||
: parent(p), | |||||
child(nullptr), | |||||
enabled(false) {} | |||||
/** Destructor. */ | |||||
~Modal() noexcept | |||||
{ | |||||
DISTRHO_SAFE_ASSERT(! enabled); | |||||
} | |||||
DISTRHO_DECLARE_NON_COPYABLE(Modal) | |||||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
} modal; | |||||
/** Constructor for a regular, standalone window. */ | |||||
explicit PrivateData(Application& app, Window* self); | |||||
/** Constructor for a modal window. */ | |||||
explicit PrivateData(Application& app, Window* self, PrivateData* ppData); | |||||
/** Constructor for an embed Window, with a few extra hints from the host side. */ | |||||
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); | |||||
/** Constructor for an embed Window, with a few extra hints from the host side. */ | |||||
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, | |||||
uint width, uint height, double scaling, bool resizable); | |||||
/** Destructor. */ | |||||
~PrivateData() override; | |||||
/** Helper initialization function called at the end of all this class constructors. */ | |||||
void initPre(uint width, uint height, bool resizable); | |||||
/** Helper initialization function called on the Window constructor after we are done. */ | |||||
void initPost(); | |||||
/** Hide window and notify application of a window close event. | |||||
* Does nothing if window is embed (that is, not standalone). | |||||
* The application event-loop will stop when all windows have been closed. | |||||
* | |||||
* @note It is possible to hide the window while not stopping the event-loop. | |||||
* A closed window is always hidden, but the reverse is not always true. | |||||
*/ | |||||
void close(); | |||||
void show(); | |||||
void hide(); | |||||
void focus(); | |||||
void setResizable(bool resizable); | |||||
const GraphicsContext& getGraphicsContext() const noexcept; | |||||
// idle callback stuff | |||||
void idleCallback() override; | |||||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs); | |||||
bool removeIdleCallback(IdleCallback* callback); | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
// file handling | |||||
bool openFileBrowser(const Window::FileBrowserOptions& options); | |||||
# ifdef DISTRHO_OS_MAC | |||||
static void openPanelCallback(PuglView* view, const char* path); | |||||
# endif | |||||
#endif | |||||
// modal handling | |||||
void startModal(); | |||||
void stopModal(); | |||||
void runAsModal(bool blockWait); | |||||
// TESTING | |||||
void leaveContext(); | |||||
// pugl events | |||||
void onPuglConfigure(double width, double height); | |||||
void onPuglExpose(); | |||||
void onPuglClose(); | |||||
void onPuglFocus(bool focus, CrossingMode mode); | |||||
void onPuglKey(const Widget::KeyboardEvent& ev); | |||||
void onPuglSpecial(const Widget::SpecialEvent& ev); | |||||
void onPuglText(const Widget::CharacterInputEvent& ev); | |||||
void onPuglMouse(const Widget::MouseEvent& ev); | |||||
void onPuglMotion(const Widget::MotionEvent& ev); | |||||
void onPuglScroll(const Widget::ScrollEvent& ev); | |||||
// Pugl event handling entry point | |||||
static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
END_NAMESPACE_DGL | |||||
#if 0 | |||||
// #if defined(DISTRHO_OS_HAIKU) | |||||
// BApplication* bApplication; | |||||
// BView* bView; | |||||
// BWindow* bWindow; | |||||
#if defined(DISTRHO_OS_MAC) | |||||
// NSView<PuglGenericView>* mView; | |||||
// id mWindow; | |||||
// id mParentWindow; | |||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
NSOpenPanel* fOpenFilePanel; | |||||
id fFilePanelDelegate; | |||||
# endif | |||||
#elif defined(DISTRHO_OS_WINDOWS) | |||||
// HWND hwnd; | |||||
// HWND hwndParent; | |||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
String fSelectedFile; | |||||
# endif | |||||
#endif | |||||
#endif | |||||
#if 0 | |||||
// ----------------------------------------------------------------------- | |||||
// Window Private | |||||
struct Window::PrivateData { | |||||
// ------------------------------------------------------------------- | |||||
bool handlePluginSpecial(const bool press, const Key key) | |||||
{ | |||||
DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); | |||||
if (fModal.childFocus != nullptr) | |||||
{ | |||||
fModal.childFocus->focus(); | |||||
return true; | |||||
} | |||||
int mods = 0x0; | |||||
switch (key) | |||||
{ | |||||
case kKeyShift: | |||||
mods |= kModifierShift; | |||||
break; | |||||
case kKeyControl: | |||||
mods |= kModifierControl; | |||||
break; | |||||
case kKeyAlt: | |||||
mods |= kModifierAlt; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
if (mods != 0x0) | |||||
{ | |||||
if (press) | |||||
fView->mods |= mods; | |||||
else | |||||
fView->mods &= ~(mods); | |||||
} | |||||
Widget::SpecialEvent ev; | |||||
ev.press = press; | |||||
ev.key = key; | |||||
ev.mod = static_cast<Modifier>(fView->mods); | |||||
ev.time = 0; | |||||
FOR_EACH_WIDGET_INV(rit) | |||||
{ | |||||
Widget* const widget(*rit); | |||||
if (widget->isVisible() && widget->onSpecial(ev)) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) | |||||
static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) | |||||
{ | |||||
PrivateData* pData = (PrivateData*)userData; | |||||
if (returnCode == NSOKButton) | |||||
{ | |||||
NSArray* urls = [panel URLs]; | |||||
NSURL* fileUrl = nullptr; | |||||
for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) | |||||
{ | |||||
NSURL* url = (NSURL*)[urls objectAtIndex:i]; | |||||
if ([url isFileURL]) | |||||
fileUrl = url; | |||||
} | |||||
if (fileUrl) | |||||
{ | |||||
PuglView* view = pData->fView; | |||||
if (view->fileSelectedFunc) | |||||
{ | |||||
const char* fileName = [fileUrl.path UTF8String]; | |||||
view->fileSelectedFunc(view, fileName); | |||||
} | |||||
} | |||||
} | |||||
[pData->fOpenFilePanel release]; | |||||
pData->fOpenFilePanel = nullptr; | |||||
} | |||||
#endif | |||||
// ------------------------------------------------------------------- | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
}; | |||||
#endif | |||||
#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED |
@@ -38,10 +38,15 @@ enum FONSalign { | |||||
FONS_ALIGN_BASELINE = 1<<6, // Default | FONS_ALIGN_BASELINE = 1<<6, // Default | ||||
}; | }; | ||||
enum FONSglyphBitmap { | |||||
FONS_GLYPH_BITMAP_OPTIONAL = 1, | |||||
FONS_GLYPH_BITMAP_REQUIRED = 2, | |||||
}; | |||||
enum FONSerrorCode { | enum FONSerrorCode { | ||||
// Font atlas is full. | // Font atlas is full. | ||||
FONS_ATLAS_FULL = 1, | FONS_ATLAS_FULL = 1, | ||||
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||||
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||||
FONS_SCRATCH_FULL = 2, | FONS_SCRATCH_FULL = 2, | ||||
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. | // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. | ||||
FONS_STATES_OVERFLOW = 3, | FONS_STATES_OVERFLOW = 3, | ||||
@@ -78,6 +83,7 @@ struct FONStextIter { | |||||
const char* next; | const char* next; | ||||
const char* end; | const char* end; | ||||
unsigned int utf8state; | unsigned int utf8state; | ||||
int bitmapOption; | |||||
}; | }; | ||||
typedef struct FONStextIter FONStextIter; | typedef struct FONStextIter FONStextIter; | ||||
@@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s); | |||||
void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | ||||
// Returns current atlas size. | // Returns current atlas size. | ||||
void fonsGetAtlasSize(FONScontext* s, int* width, int* height); | void fonsGetAtlasSize(FONScontext* s, int* width, int* height); | ||||
// Expands the atlas size. | |||||
// Expands the atlas size. | |||||
int fonsExpandAtlas(FONScontext* s, int width, int height); | int fonsExpandAtlas(FONScontext* s, int width, int height); | ||||
// Resets the whole stash. | // Resets the whole stash. | ||||
int fonsResetAtlas(FONScontext* stash, int width, int height); | int fonsResetAtlas(FONScontext* stash, int width, int height); | ||||
// Add fonts | // Add fonts | ||||
int fonsAddFont(FONScontext* s, const char* name, const char* path); | |||||
int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData); | |||||
int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); | |||||
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); | |||||
int fonsGetFontByName(FONScontext* s, const char* name); | int fonsGetFontByName(FONScontext* s, const char* name); | ||||
// State handling | // State handling | ||||
@@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); | |||||
void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); | void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); | ||||
// Text iterator | // Text iterator | ||||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); | |||||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption); | |||||
int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); | ||||
// Pull texture changes | // Pull texture changes | ||||
@@ -154,20 +160,28 @@ typedef struct FONSttFontImpl FONSttFontImpl; | |||||
static FT_Library ftLibrary; | static FT_Library ftLibrary; | ||||
int fons__tt_init(FONScontext *context) | int fons__tt_init(FONScontext *context) | ||||
{ | |||||
{ | |||||
FT_Error ftError; | FT_Error ftError; | ||||
FONS_NOTUSED(context); | |||||
FONS_NOTUSED(context); | |||||
ftError = FT_Init_FreeType(&ftLibrary); | ftError = FT_Init_FreeType(&ftLibrary); | ||||
return ftError == 0; | return ftError == 0; | ||||
} | } | ||||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||||
int fons__tt_done(FONScontext *context) | |||||
{ | |||||
FT_Error ftError; | |||||
FONS_NOTUSED(context); | |||||
ftError = FT_Done_FreeType(ftLibrary); | |||||
return ftError == 0; | |||||
} | |||||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||||
{ | { | ||||
FT_Error ftError; | FT_Error ftError; | ||||
FONS_NOTUSED(context); | FONS_NOTUSED(context); | ||||
//font->font.userdata = stash; | //font->font.userdata = stash; | ||||
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); | |||||
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); | |||||
return ftError == 0; | return ftError == 0; | ||||
} | } | ||||
@@ -180,7 +194,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i | |||||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | ||||
{ | { | ||||
#if 1 | |||||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
return size / (font->font->ascender - font->font->descender); | return size / (font->font->ascender - font->font->descender); | ||||
#else | |||||
return size / font->font->units_per_EM; | |||||
#endif | |||||
} | } | ||||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | ||||
@@ -193,16 +212,28 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float | |||||
{ | { | ||||
FT_Error ftError; | FT_Error ftError; | ||||
FT_GlyphSlot ftGlyph; | FT_GlyphSlot ftGlyph; | ||||
FT_Fixed advFixed; | |||||
FONS_NOTUSED(scale); | FONS_NOTUSED(scale); | ||||
#if 1 | |||||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | ||||
#else | |||||
ftError = FT_Set_Pixel_Sizes(font->font, 0, size); | |||||
#endif | |||||
if (ftError) return 0; | if (ftError) return 0; | ||||
#if 1 | |||||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | ||||
#else | |||||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); | |||||
#endif | |||||
if (ftError) return 0; | if (ftError) return 0; | ||||
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance); | |||||
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); | |||||
if (ftError) return 0; | if (ftError) return 0; | ||||
ftGlyph = font->font->glyph; | ftGlyph = font->font->glyph; | ||||
*lsb = ftGlyph->metrics.horiBearingX; | |||||
*advance = (int)advFixed; | |||||
*lsb = (int)ftGlyph->metrics.horiBearingX; | |||||
*x0 = ftGlyph->bitmap_left; | *x0 = ftGlyph->bitmap_left; | ||||
*x1 = *x0 + ftGlyph->bitmap.width; | *x1 = *x0 + ftGlyph->bitmap.width; | ||||
*y0 = -ftGlyph->bitmap_top; | *y0 = -ftGlyph->bitmap_top; | ||||
@@ -215,7 +246,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int | |||||
{ | { | ||||
FT_GlyphSlot ftGlyph = font->font->glyph; | FT_GlyphSlot ftGlyph = font->font->glyph; | ||||
int ftGlyphOffset = 0; | int ftGlyphOffset = 0; | ||||
int x, y; | |||||
unsigned int x, y; | |||||
FONS_NOTUSED(outWidth); | FONS_NOTUSED(outWidth); | ||||
FONS_NOTUSED(outHeight); | FONS_NOTUSED(outHeight); | ||||
FONS_NOTUSED(scaleX); | FONS_NOTUSED(scaleX); | ||||
@@ -233,7 +264,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
{ | { | ||||
FT_Vector ftKerning; | FT_Vector ftKerning; | ||||
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | ||||
return ftKerning.x; | |||||
return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer | |||||
} | } | ||||
#else | #else | ||||
@@ -256,13 +287,24 @@ int fons__tt_init(FONScontext *context) | |||||
return 1; | return 1; | ||||
} | } | ||||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||||
int fons__tt_done(FONScontext *context) | |||||
{ | |||||
FONS_NOTUSED(context); | |||||
return 1; | |||||
} | |||||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||||
{ | { | ||||
int stbError; | |||||
int offset, stbError; | |||||
FONS_NOTUSED(dataSize); | FONS_NOTUSED(dataSize); | ||||
font->font.userdata = context; | font->font.userdata = context; | ||||
stbError = stbtt_InitFont(&font->font, data, 0); | |||||
offset = stbtt_GetFontOffsetForIndex(data, fontIndex); | |||||
if (offset == -1) { | |||||
stbError = 0; | |||||
} else { | |||||
stbError = stbtt_InitFont(&font->font, data, offset); | |||||
} | |||||
return stbError; | return stbError; | ||||
} | } | ||||
@@ -273,7 +315,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i | |||||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | ||||
{ | { | ||||
#if 1 | |||||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||||
return stbtt_ScaleForPixelHeight(&font->font, size); | return stbtt_ScaleForPixelHeight(&font->font, size); | ||||
#else | |||||
return stbtt_ScaleForMappingEmToPixels(&font->font, size); | |||||
#endif | |||||
} | } | ||||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | ||||
@@ -304,7 +351,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
#endif | #endif | ||||
#ifndef FONS_SCRATCH_BUF_SIZE | #ifndef FONS_SCRATCH_BUF_SIZE | ||||
# define FONS_SCRATCH_BUF_SIZE 16000 | |||||
# define FONS_SCRATCH_BUF_SIZE 96000 | |||||
#endif | #endif | ||||
#ifndef FONS_HASH_LUT_SIZE | #ifndef FONS_HASH_LUT_SIZE | ||||
# define FONS_HASH_LUT_SIZE 256 | # define FONS_HASH_LUT_SIZE 256 | ||||
@@ -324,6 +371,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||||
#ifndef FONS_MAX_STATES | #ifndef FONS_MAX_STATES | ||||
# define FONS_MAX_STATES 20 | # define FONS_MAX_STATES 20 | ||||
#endif | #endif | ||||
#ifndef FONS_MAX_FALLBACKS | |||||
# define FONS_MAX_FALLBACKS 20 | |||||
#endif | |||||
static unsigned int fons__hashint(unsigned int a) | static unsigned int fons__hashint(unsigned int a) | ||||
{ | { | ||||
@@ -361,7 +411,7 @@ struct FONSfont | |||||
{ | { | ||||
FONSttFontImpl font; | FONSttFontImpl font; | ||||
char name[64]; | char name[64]; | ||||
const unsigned char* data; | |||||
unsigned char* data; | |||||
int dataSize; | int dataSize; | ||||
unsigned char freeData; | unsigned char freeData; | ||||
float ascender; | float ascender; | ||||
@@ -371,6 +421,8 @@ struct FONSfont | |||||
int cglyphs; | int cglyphs; | ||||
int nglyphs; | int nglyphs; | ||||
int lut[FONS_HASH_LUT_SIZE]; | int lut[FONS_HASH_LUT_SIZE]; | ||||
int fallbacks[FONS_MAX_FALLBACKS]; | |||||
int nfallbacks; | |||||
}; | }; | ||||
typedef struct FONSfont FONSfont; | typedef struct FONSfont FONSfont; | ||||
@@ -421,6 +473,8 @@ struct FONScontext | |||||
void* errorUptr; | void* errorUptr; | ||||
}; | }; | ||||
#ifdef STB_TRUETYPE_IMPLEMENTATION | |||||
static void* fons__tmpalloc(size_t size, void* up) | static void* fons__tmpalloc(size_t size, void* up) | ||||
{ | { | ||||
unsigned char* ptr; | unsigned char* ptr; | ||||
@@ -446,6 +500,8 @@ static void fons__tmpfree(void* ptr, void* up) | |||||
// empty | // empty | ||||
} | } | ||||
#endif // STB_TRUETYPE_IMPLEMENTATION | |||||
// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> | // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> | ||||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. | ||||
@@ -751,6 +807,27 @@ static FONSstate* fons__getState(FONScontext* stash) | |||||
return &stash->states[stash->nstates-1]; | return &stash->states[stash->nstates-1]; | ||||
} | } | ||||
int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) | |||||
{ | |||||
FONSfont* baseFont = stash->fonts[base]; | |||||
if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { | |||||
baseFont->fallbacks[baseFont->nfallbacks++] = fallback; | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} | |||||
void fonsResetFallbackFont(FONScontext* stash, int base) | |||||
{ | |||||
int i; | |||||
FONSfont* baseFont = stash->fonts[base]; | |||||
baseFont->nfallbacks = 0; | |||||
baseFont->nglyphs = 0; | |||||
for (i = 0; i < FONS_HASH_LUT_SIZE; i++) | |||||
baseFont->lut[i] = -1; | |||||
} | |||||
void fonsSetSize(FONScontext* stash, float size) | void fonsSetSize(FONScontext* stash, float size) | ||||
{ | { | ||||
fons__getState(stash)->size = size; | fons__getState(stash)->size = size; | ||||
@@ -818,7 +895,7 @@ static void fons__freeFont(FONSfont* font) | |||||
{ | { | ||||
if (font == NULL) return; | if (font == NULL) return; | ||||
if (font->glyphs) free(font->glyphs); | if (font->glyphs) free(font->glyphs); | ||||
if (font->freeData && font->data) free((void*)font->data); | |||||
if (font->freeData && font->data) free(font->data); | |||||
free(font); | free(font); | ||||
} | } | ||||
@@ -849,12 +926,12 @@ error: | |||||
return FONS_INVALID; | return FONS_INVALID; | ||||
} | } | ||||
int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||||
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) | |||||
{ | { | ||||
FILE* fp = 0; | FILE* fp = 0; | ||||
int dataSize = 0; | int dataSize = 0; | ||||
size_t readed; | |||||
unsigned char* data = NULL; | unsigned char* data = NULL; | ||||
size_t ignore; | |||||
// Read in the font data. | // Read in the font data. | ||||
fp = fopen(path, "rb"); | fp = fopen(path, "rb"); | ||||
@@ -864,21 +941,20 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||||
fseek(fp,0,SEEK_SET); | fseek(fp,0,SEEK_SET); | ||||
data = (unsigned char*)malloc(dataSize); | data = (unsigned char*)malloc(dataSize); | ||||
if (data == NULL) goto error; | if (data == NULL) goto error; | ||||
ignore = fread(data, 1, dataSize, fp); | |||||
readed = fread(data, 1, dataSize, fp); | |||||
fclose(fp); | fclose(fp); | ||||
fp = 0; | fp = 0; | ||||
if (readed != (size_t)dataSize) goto error; | |||||
return fonsAddFontMem(stash, name, data, dataSize, 1); | |||||
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); | |||||
error: | error: | ||||
if (data) free(data); | if (data) free(data); | ||||
if (fp) fclose(fp); | if (fp) fclose(fp); | ||||
return FONS_INVALID; | return FONS_INVALID; | ||||
FONS_NOTUSED(ignore); | |||||
} | } | ||||
int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData) | |||||
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) | |||||
{ | { | ||||
int i, ascent, descent, fh, lineGap; | int i, ascent, descent, fh, lineGap; | ||||
FONSfont* font; | FONSfont* font; | ||||
@@ -903,15 +979,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da | |||||
// Init font | // Init font | ||||
stash->nscratch = 0; | stash->nscratch = 0; | ||||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; | |||||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; | |||||
// Store normalized line height. The real line height is got | // Store normalized line height. The real line height is got | ||||
// by multiplying the lineh by font size. | // by multiplying the lineh by font size. | ||||
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | ||||
ascent += lineGap; | |||||
fh = ascent - descent; | fh = ascent - descent; | ||||
font->ascender = (float)ascent / (float)fh; | font->ascender = (float)ascent / (float)fh; | ||||
font->descender = (float)descent / (float)fh; | font->descender = (float)descent / (float)fh; | ||||
font->lineh = (float)(fh + lineGap) / (float)fh; | |||||
font->lineh = font->ascender - font->descender; | |||||
return idx; | return idx; | ||||
@@ -1010,7 +1087,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int | |||||
} | } | ||||
static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, | static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, | ||||
short isize, short iblur) | |||||
short isize, short iblur, int bitmapOption) | |||||
{ | { | ||||
int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; | int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; | ||||
float scale; | float scale; | ||||
@@ -1020,6 +1097,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
int pad, added; | int pad, added; | ||||
unsigned char* bdst; | unsigned char* bdst; | ||||
unsigned char* dst; | unsigned char* dst; | ||||
FONSfont* renderFont = font; | |||||
if (isize < 2) return NULL; | if (isize < 2) return NULL; | ||||
if (iblur > 20) iblur = 20; | if (iblur > 20) iblur = 20; | ||||
@@ -1032,32 +1110,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); | h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); | ||||
i = font->lut[h]; | i = font->lut[h]; | ||||
while (i != -1) { | while (i != -1) { | ||||
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) | |||||
return &font->glyphs[i]; | |||||
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) { | |||||
glyph = &font->glyphs[i]; | |||||
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) { | |||||
return glyph; | |||||
} | |||||
// At this point, glyph exists but the bitmap data is not yet created. | |||||
break; | |||||
} | |||||
i = font->glyphs[i].next; | i = font->glyphs[i].next; | ||||
} | } | ||||
// Could not find glyph, create it. | |||||
scale = fons__tt_getPixelHeightScale(&font->font, size); | |||||
// Create a new glyph or rasterize bitmap data for a cached glyph. | |||||
g = fons__tt_getGlyphIndex(&font->font, codepoint); | g = fons__tt_getGlyphIndex(&font->font, codepoint); | ||||
fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||||
// Try to find the glyph in fallback fonts. | |||||
if (g == 0) { | |||||
for (i = 0; i < font->nfallbacks; ++i) { | |||||
FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; | |||||
int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); | |||||
if (fallbackIndex != 0) { | |||||
g = fallbackIndex; | |||||
renderFont = fallbackFont; | |||||
break; | |||||
} | |||||
} | |||||
// It is possible that we did not find a fallback glyph. | |||||
// In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. | |||||
} | |||||
scale = fons__tt_getPixelHeightScale(&renderFont->font, size); | |||||
fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||||
gw = x1-x0 + pad*2; | gw = x1-x0 + pad*2; | ||||
gh = y1-y0 + pad*2; | gh = y1-y0 + pad*2; | ||||
// Find free spot for the rect in the atlas | |||||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||||
if (added == 0 && stash->handleError != NULL) { | |||||
// Atlas is full, let the user to resize the atlas (or not), and try again. | |||||
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||||
// Determines the spot to draw glyph in the atlas. | |||||
if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) { | |||||
// Find free spot for the rect in the atlas | |||||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | ||||
if (added == 0 && stash->handleError != NULL) { | |||||
// Atlas is full, let the user to resize the atlas (or not), and try again. | |||||
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||||
} | |||||
if (added == 0) return NULL; | |||||
} else { | |||||
// Negative coordinate indicates there is no bitmap data created. | |||||
gx = -1; | |||||
gy = -1; | |||||
} | } | ||||
if (added == 0) return NULL; | |||||
// Init glyph. | // Init glyph. | ||||
glyph = fons__allocGlyph(font); | |||||
glyph->codepoint = codepoint; | |||||
glyph->size = isize; | |||||
glyph->blur = iblur; | |||||
if (glyph == NULL) { | |||||
glyph = fons__allocGlyph(font); | |||||
glyph->codepoint = codepoint; | |||||
glyph->size = isize; | |||||
glyph->blur = iblur; | |||||
glyph->next = 0; | |||||
// Insert char to hash lookup. | |||||
glyph->next = font->lut[h]; | |||||
font->lut[h] = font->nglyphs-1; | |||||
} | |||||
glyph->index = g; | glyph->index = g; | ||||
glyph->x0 = (short)gx; | glyph->x0 = (short)gx; | ||||
glyph->y0 = (short)gy; | glyph->y0 = (short)gy; | ||||
@@ -1066,15 +1178,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
glyph->xadv = (short)(scale * advance * 10.0f); | glyph->xadv = (short)(scale * advance * 10.0f); | ||||
glyph->xoff = (short)(x0 - pad); | glyph->xoff = (short)(x0 - pad); | ||||
glyph->yoff = (short)(y0 - pad); | glyph->yoff = (short)(y0 - pad); | ||||
glyph->next = 0; | |||||
// Insert char to hash lookup. | |||||
glyph->next = font->lut[h]; | |||||
font->lut[h] = font->nglyphs-1; | |||||
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) { | |||||
return glyph; | |||||
} | |||||
// Rasterize | // Rasterize | ||||
dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; | dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; | ||||
fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); | |||||
fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g); | |||||
// Make sure there is one pixel empty border. | // Make sure there is one pixel empty border. | ||||
dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | ||||
@@ -1101,7 +1212,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||||
if (iblur > 0) { | if (iblur > 0) { | ||||
stash->nscratch = 0; | stash->nscratch = 0; | ||||
bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | ||||
fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); | |||||
fons__blur(stash, bdst, gw, gh, stash->params.width, iblur); | |||||
} | } | ||||
stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); | ||||
@@ -1134,8 +1245,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||||
y1 = (float)(glyph->y1-1); | y1 = (float)(glyph->y1-1); | ||||
if (stash->params.flags & FONS_ZERO_TOPLEFT) { | if (stash->params.flags & FONS_ZERO_TOPLEFT) { | ||||
rx = (float)(int)(*x + xoff); | |||||
ry = (float)(int)(*y + yoff); | |||||
rx = floorf(*x + xoff); | |||||
ry = floorf(*y + yoff); | |||||
q->x0 = rx; | q->x0 = rx; | ||||
q->y0 = ry; | q->y0 = ry; | ||||
@@ -1147,8 +1258,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||||
q->s1 = x1 * stash->itw; | q->s1 = x1 * stash->itw; | ||||
q->t1 = y1 * stash->ith; | q->t1 = y1 * stash->ith; | ||||
} else { | } else { | ||||
rx = (float)(int)(*x + xoff); | |||||
ry = (float)(int)(*y - yoff); | |||||
rx = floorf(*x + xoff); | |||||
ry = floorf(*y - yoff); | |||||
q->x0 = rx; | q->x0 = rx; | ||||
q->y0 = ry; | q->y0 = ry; | ||||
@@ -1226,7 +1337,7 @@ float fonsDrawText(FONScontext* stash, | |||||
const char* str, const char* end) | const char* str, const char* end) | ||||
{ | { | ||||
FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
unsigned int codepoint = 0; | |||||
unsigned int codepoint; | |||||
unsigned int utf8state = 0; | unsigned int utf8state = 0; | ||||
FONSglyph* glyph = NULL; | FONSglyph* glyph = NULL; | ||||
FONSquad q; | FONSquad q; | ||||
@@ -1263,7 +1374,7 @@ float fonsDrawText(FONScontext* stash, | |||||
for (; str != end; ++str) { | for (; str != end; ++str) { | ||||
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | ||||
continue; | continue; | ||||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); | |||||
if (glyph != NULL) { | if (glyph != NULL) { | ||||
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | ||||
@@ -1286,7 +1397,7 @@ float fonsDrawText(FONScontext* stash, | |||||
} | } | ||||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | ||||
float x, float y, const char* str, const char* end) | |||||
float x, float y, const char* str, const char* end, int bitmapOption) | |||||
{ | { | ||||
FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
float width; | float width; | ||||
@@ -1326,6 +1437,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | |||||
iter->end = end; | iter->end = end; | ||||
iter->codepoint = 0; | iter->codepoint = 0; | ||||
iter->prevGlyphIndex = -1; | iter->prevGlyphIndex = -1; | ||||
iter->bitmapOption = bitmapOption; | |||||
return 1; | return 1; | ||||
} | } | ||||
@@ -1346,7 +1458,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) | |||||
// Get glyph and quad | // Get glyph and quad | ||||
iter->x = iter->nextx; | iter->x = iter->nextx; | ||||
iter->y = iter->nexty; | iter->y = iter->nexty; | ||||
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); | |||||
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption); | |||||
// If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid. | |||||
if (glyph != NULL) | if (glyph != NULL) | ||||
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | ||||
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; | iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; | ||||
@@ -1406,12 +1519,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y) | |||||
} | } | ||||
float fonsTextBounds(FONScontext* stash, | float fonsTextBounds(FONScontext* stash, | ||||
float x, float y, | |||||
float x, float y, | |||||
const char* str, const char* end, | const char* str, const char* end, | ||||
float* bounds) | float* bounds) | ||||
{ | { | ||||
FONSstate* state = fons__getState(stash); | FONSstate* state = fons__getState(stash); | ||||
unsigned int codepoint = 0; | |||||
unsigned int codepoint; | |||||
unsigned int utf8state = 0; | unsigned int utf8state = 0; | ||||
FONSquad q; | FONSquad q; | ||||
FONSglyph* glyph = NULL; | FONSglyph* glyph = NULL; | ||||
@@ -1443,7 +1556,7 @@ float fonsTextBounds(FONScontext* stash, | |||||
for (; str != end; ++str) { | for (; str != end; ++str) { | ||||
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | ||||
continue; | continue; | ||||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
if (glyph != NULL) { | if (glyph != NULL) { | ||||
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | ||||
if (q.x0 < minx) minx = q.x0; | if (q.x0 < minx) minx = q.x0; | ||||
@@ -1568,6 +1681,7 @@ void fonsDeleteInternal(FONScontext* stash) | |||||
if (stash->texData) free(stash->texData); | if (stash->texData) free(stash->texData); | ||||
if (stash->scratch) free(stash->scratch); | if (stash->scratch) free(stash->scratch); | ||||
free(stash); | free(stash); | ||||
fons__tt_done(stash); | |||||
} | } | ||||
void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) | void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) | ||||
@@ -1594,7 +1708,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height) | |||||
height = fons__maxi(height, stash->params.height); | height = fons__maxi(height, stash->params.height); | ||||
if (width == stash->params.width && height == stash->params.height) | if (width == stash->params.width && height == stash->params.height) | ||||
return 1; | |||||
return 1; | |||||
// Flush pending glyphs. | // Flush pending glyphs. | ||||
fons__flush(stash); | fons__flush(stash); | ||||
@@ -16,8 +16,11 @@ | |||||
// 3. This notice may not be removed or altered from any source distribution. | // 3. This notice may not be removed or altered from any source distribution. | ||||
// | // | ||||
#include <stdlib.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <math.h> | #include <math.h> | ||||
#include <memory.h> | |||||
#include "nanovg.h" | #include "nanovg.h" | ||||
#define FONTSTASH_IMPLEMENTATION | #define FONTSTASH_IMPLEMENTATION | ||||
#include "fontstash.h" | #include "fontstash.h" | ||||
@@ -63,6 +66,8 @@ enum NVGpointFlags | |||||
}; | }; | ||||
struct NVGstate { | struct NVGstate { | ||||
NVGcompositeOperationState compositeOperation; | |||||
int shapeAntiAlias; | |||||
NVGpaint fill; | NVGpaint fill; | ||||
NVGpaint stroke; | NVGpaint stroke; | ||||
float strokeWidth; | float strokeWidth; | ||||
@@ -200,6 +205,84 @@ static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio) | |||||
ctx->devicePxRatio = ratio; | ctx->devicePxRatio = ratio; | ||||
} | } | ||||
static NVGcompositeOperationState nvg__compositeOperationState(int op) | |||||
{ | |||||
int sfactor, dfactor; | |||||
if (op == NVG_SOURCE_OVER) | |||||
{ | |||||
sfactor = NVG_ONE; | |||||
dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
} | |||||
else if (op == NVG_SOURCE_IN) | |||||
{ | |||||
sfactor = NVG_DST_ALPHA; | |||||
dfactor = NVG_ZERO; | |||||
} | |||||
else if (op == NVG_SOURCE_OUT) | |||||
{ | |||||
sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
dfactor = NVG_ZERO; | |||||
} | |||||
else if (op == NVG_ATOP) | |||||
{ | |||||
sfactor = NVG_DST_ALPHA; | |||||
dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
} | |||||
else if (op == NVG_DESTINATION_OVER) | |||||
{ | |||||
sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
dfactor = NVG_ONE; | |||||
} | |||||
else if (op == NVG_DESTINATION_IN) | |||||
{ | |||||
sfactor = NVG_ZERO; | |||||
dfactor = NVG_SRC_ALPHA; | |||||
} | |||||
else if (op == NVG_DESTINATION_OUT) | |||||
{ | |||||
sfactor = NVG_ZERO; | |||||
dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
} | |||||
else if (op == NVG_DESTINATION_ATOP) | |||||
{ | |||||
sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
dfactor = NVG_SRC_ALPHA; | |||||
} | |||||
else if (op == NVG_LIGHTER) | |||||
{ | |||||
sfactor = NVG_ONE; | |||||
dfactor = NVG_ONE; | |||||
} | |||||
else if (op == NVG_COPY) | |||||
{ | |||||
sfactor = NVG_ONE; | |||||
dfactor = NVG_ZERO; | |||||
} | |||||
else if (op == NVG_XOR) | |||||
{ | |||||
sfactor = NVG_ONE_MINUS_DST_ALPHA; | |||||
dfactor = NVG_ONE_MINUS_SRC_ALPHA; | |||||
} | |||||
else | |||||
{ | |||||
sfactor = NVG_ONE; | |||||
dfactor = NVG_ZERO; | |||||
} | |||||
NVGcompositeOperationState state; | |||||
state.srcRGB = sfactor; | |||||
state.dstRGB = dfactor; | |||||
state.srcAlpha = sfactor; | |||||
state.dstAlpha = dfactor; | |||||
return state; | |||||
} | |||||
static NVGstate* nvg__getState(NVGcontext* ctx) | |||||
{ | |||||
return &ctx->states[ctx->nstates-1]; | |||||
} | |||||
NVGcontext* nvgCreateInternal(NVGparams* params) | NVGcontext* nvgCreateInternal(NVGparams* params) | ||||
{ | { | ||||
FONSparams fontParams; | FONSparams fontParams; | ||||
@@ -280,7 +363,7 @@ void nvgDeleteInternal(NVGcontext* ctx) | |||||
free(ctx); | free(ctx); | ||||
} | } | ||||
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio) | |||||
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio) | |||||
{ | { | ||||
/* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", | /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", | ||||
ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, | ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, | ||||
@@ -291,8 +374,8 @@ void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float dev | |||||
nvgReset(ctx); | nvgReset(ctx); | ||||
nvg__setDevicePixelRatio(ctx, devicePixelRatio); | nvg__setDevicePixelRatio(ctx, devicePixelRatio); | ||||
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight); | |||||
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio); | |||||
ctx->drawCallCount = 0; | ctx->drawCallCount = 0; | ||||
ctx->fillTriCount = 0; | ctx->fillTriCount = 0; | ||||
@@ -383,7 +466,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) | |||||
{ | { | ||||
int i; | int i; | ||||
float oneminu; | float oneminu; | ||||
NVGcolor cint; | |||||
NVGcolor cint = {{{0}}}; | |||||
u = nvg__clampf(u, 0.0f, 1.0f); | u = nvg__clampf(u, 0.0f, 1.0f); | ||||
oneminu = 1.0f - u; | oneminu = 1.0f - u; | ||||
@@ -391,7 +474,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) | |||||
{ | { | ||||
cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; | cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; | ||||
} | } | ||||
return cint; | return cint; | ||||
} | } | ||||
@@ -430,12 +513,6 @@ NVGcolor nvgHSLA(float h, float s, float l, unsigned char a) | |||||
return col; | return col; | ||||
} | } | ||||
static NVGstate* nvg__getState(NVGcontext* ctx) | |||||
{ | |||||
return &ctx->states[ctx->nstates-1]; | |||||
} | |||||
void nvgTransformIdentity(float* t) | void nvgTransformIdentity(float* t) | ||||
{ | { | ||||
t[0] = 1.0f; t[1] = 0.0f; | t[0] = 1.0f; t[1] = 0.0f; | ||||
@@ -568,6 +645,8 @@ void nvgReset(NVGcontext* ctx) | |||||
nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); | nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); | ||||
nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); | nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); | ||||
state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER); | |||||
state->shapeAntiAlias = 1; | |||||
state->strokeWidth = 1.0f; | state->strokeWidth = 1.0f; | ||||
state->miterLimit = 10.0f; | state->miterLimit = 10.0f; | ||||
state->lineCap = NVG_BUTT; | state->lineCap = NVG_BUTT; | ||||
@@ -587,6 +666,12 @@ void nvgReset(NVGcontext* ctx) | |||||
} | } | ||||
// State setting | // State setting | ||||
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled) | |||||
{ | |||||
NVGstate* state = nvg__getState(ctx); | |||||
state->shapeAntiAlias = enabled; | |||||
} | |||||
void nvgStrokeWidth(NVGcontext* ctx, float width) | void nvgStrokeWidth(NVGcontext* ctx, float width) | ||||
{ | { | ||||
NVGstate* state = nvg__getState(ctx); | NVGstate* state = nvg__getState(ctx); | ||||
@@ -719,7 +804,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) | |||||
return image; | return image; | ||||
} | } | ||||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata) | |||||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) | |||||
{ | { | ||||
int w, h, n, image; | int w, h, n, image; | ||||
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); | unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); | ||||
@@ -913,7 +998,7 @@ void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h) | |||||
} | } | ||||
// Transform the current scissor rect into current transform space. | // Transform the current scissor rect into current transform space. | ||||
// If there is difference in rotation, this will be approximation. | |||||
// If there is difference in rotation, this will be approximation. | |||||
memcpy(pxform, state->scissor.xform, sizeof(float)*6); | memcpy(pxform, state->scissor.xform, sizeof(float)*6); | ||||
ex = state->scissor.extent[0]; | ex = state->scissor.extent[0]; | ||||
ey = state->scissor.extent[1]; | ey = state->scissor.extent[1]; | ||||
@@ -936,6 +1021,30 @@ void nvgResetScissor(NVGcontext* ctx) | |||||
state->scissor.extent[1] = -1.0f; | state->scissor.extent[1] = -1.0f; | ||||
} | } | ||||
// Global composite operation. | |||||
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op) | |||||
{ | |||||
NVGstate* state = nvg__getState(ctx); | |||||
state->compositeOperation = nvg__compositeOperationState(op); | |||||
} | |||||
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor) | |||||
{ | |||||
nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor); | |||||
} | |||||
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) | |||||
{ | |||||
NVGcompositeOperationState op; | |||||
op.srcRGB = srcRGB; | |||||
op.dstRGB = dstRGB; | |||||
op.srcAlpha = srcAlpha; | |||||
op.dstAlpha = dstAlpha; | |||||
NVGstate* state = nvg__getState(ctx); | |||||
state->compositeOperation = op; | |||||
} | |||||
static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) | static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) | ||||
{ | { | ||||
float dx = x2 - x1; | float dx = x2 - x1; | ||||
@@ -1173,7 +1282,7 @@ static void nvg__tesselateBezier(NVGcontext* ctx, | |||||
{ | { | ||||
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; | float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; | ||||
float dx,dy,d2,d3; | float dx,dy,d2,d3; | ||||
if (level > 10) return; | if (level > 10) return; | ||||
x12 = (x1+x2)*0.5f; | x12 = (x1+x2)*0.5f; | ||||
@@ -1205,8 +1314,8 @@ static void nvg__tesselateBezier(NVGcontext* ctx, | |||||
x1234 = (x123+x234)*0.5f; | x1234 = (x123+x234)*0.5f; | ||||
y1234 = (y123+y234)*0.5f; | y1234 = (y123+y234)*0.5f; | ||||
nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); | |||||
nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); | |||||
nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); | |||||
nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); | |||||
} | } | ||||
static void nvg__flattenPaths(NVGcontext* ctx) | static void nvg__flattenPaths(NVGcontext* ctx) | ||||
@@ -1331,7 +1440,8 @@ static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w, | |||||
} | } | ||||
static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | ||||
float lw, float rw, float lu, float ru, int ncap, float fringe) | |||||
float lw, float rw, float lu, float ru, int ncap, | |||||
float fringe) | |||||
{ | { | ||||
int i, n; | int i, n; | ||||
float dlx0 = p0->dy; | float dlx0 = p0->dy; | ||||
@@ -1464,36 +1574,39 @@ static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, | |||||
} | } | ||||
static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, | ||||
float dx, float dy, float w, float d, float aa) | |||||
float dx, float dy, float w, float d, | |||||
float aa, float u0, float u1) | |||||
{ | { | ||||
float px = p->x - dx*d; | float px = p->x - dx*d; | ||||
float py = p->y - dy*d; | float py = p->y - dy*d; | ||||
float dlx = dy; | float dlx = dy; | ||||
float dly = -dx; | float dly = -dx; | ||||
nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, 0,0); dst++; | |||||
nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, 1,0); dst++; | |||||
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++; | |||||
nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++; | |||||
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
return dst; | return dst; | ||||
} | } | ||||
static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, | ||||
float dx, float dy, float w, float d, float aa) | |||||
float dx, float dy, float w, float d, | |||||
float aa, float u0, float u1) | |||||
{ | { | ||||
float px = p->x + dx*d; | float px = p->x + dx*d; | ||||
float py = p->y + dy*d; | float py = p->y + dy*d; | ||||
float dlx = dy; | float dlx = dy; | ||||
float dly = -dx; | float dly = -dx; | ||||
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, 0,0); dst++; | |||||
nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, 1,0); dst++; | |||||
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++; | |||||
nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++; | |||||
return dst; | return dst; | ||||
} | } | ||||
static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | ||||
float dx, float dy, float w, int ncap, float aa) | |||||
float dx, float dy, float w, int ncap, | |||||
float aa, float u0, float u1) | |||||
{ | { | ||||
int i; | int i; | ||||
float px = p->x; | float px = p->x; | ||||
@@ -1504,16 +1617,17 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, | |||||
for (i = 0; i < ncap; i++) { | for (i = 0; i < ncap; i++) { | ||||
float a = i/(float)(ncap-1)*NVG_PI; | float a = i/(float)(ncap-1)*NVG_PI; | ||||
float ax = cosf(a) * w, ay = sinf(a) * w; | float ax = cosf(a) * w, ay = sinf(a) * w; | ||||
nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++; | |||||
nvg__vset(dst, px, py, 0.5f,1); dst++; | nvg__vset(dst, px, py, 0.5f,1); dst++; | ||||
} | } | ||||
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
return dst; | return dst; | ||||
} | } | ||||
static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | ||||
float dx, float dy, float w, int ncap, float aa) | |||||
float dx, float dy, float w, int ncap, | |||||
float aa, float u0, float u1) | |||||
{ | { | ||||
int i; | int i; | ||||
float px = p->x; | float px = p->x; | ||||
@@ -1521,13 +1635,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, | |||||
float dlx = dy; | float dlx = dy; | ||||
float dly = -dx; | float dly = -dx; | ||||
NVG_NOTUSED(aa); | NVG_NOTUSED(aa); | ||||
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++; | |||||
nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; | |||||
nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; | |||||
for (i = 0; i < ncap; i++) { | for (i = 0; i < ncap; i++) { | ||||
float a = i/(float)(ncap-1)*NVG_PI; | float a = i/(float)(ncap-1)*NVG_PI; | ||||
float ax = cosf(a) * w, ay = sinf(a) * w; | float ax = cosf(a) * w, ay = sinf(a) * w; | ||||
nvg__vset(dst, px, py, 0.5f,1); dst++; | nvg__vset(dst, px, py, 0.5f,1); dst++; | ||||
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, 0,1); dst++; | |||||
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++; | |||||
} | } | ||||
return dst; | return dst; | ||||
} | } | ||||
@@ -1603,15 +1717,24 @@ static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float mi | |||||
} | } | ||||
static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin, float miterLimit) | |||||
{ | |||||
static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit) | |||||
{ | |||||
NVGpathCache* cache = ctx->cache; | NVGpathCache* cache = ctx->cache; | ||||
NVGvertex* verts; | NVGvertex* verts; | ||||
NVGvertex* dst; | NVGvertex* dst; | ||||
int cverts, i, j; | int cverts, i, j; | ||||
float aa = ctx->fringeWidth; | |||||
float aa = fringe;//ctx->fringeWidth; | |||||
float u0 = 0.0f, u1 = 1.0f; | |||||
int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. | int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. | ||||
w += aa * 0.5f; | |||||
// Disable the gradient used for antialiasing when antialiasing is not used. | |||||
if (aa == 0.0f) { | |||||
u0 = 0.5f; | |||||
u1 = 0.5f; | |||||
} | |||||
nvg__calculateJoins(ctx, w, lineJoin, miterLimit); | nvg__calculateJoins(ctx, w, lineJoin, miterLimit); | ||||
// Calculate max vertex usage. | // Calculate max vertex usage. | ||||
@@ -1672,42 +1795,42 @@ static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin | |||||
dy = p1->y - p0->y; | dy = p1->y - p0->y; | ||||
nvg__normalize(&dx, &dy); | nvg__normalize(&dx, &dy); | ||||
if (lineCap == NVG_BUTT) | if (lineCap == NVG_BUTT) | ||||
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa); | |||||
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1); | |||||
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | ||||
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa); | |||||
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1); | |||||
else if (lineCap == NVG_ROUND) | else if (lineCap == NVG_ROUND) | ||||
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa); | |||||
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1); | |||||
} | } | ||||
for (j = s; j < e; ++j) { | for (j = s; j < e; ++j) { | ||||
if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { | if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { | ||||
if (lineJoin == NVG_ROUND) { | if (lineJoin == NVG_ROUND) { | ||||
dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa); | |||||
dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa); | |||||
} else { | } else { | ||||
dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa); | |||||
dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa); | |||||
} | } | ||||
} else { | } else { | ||||
nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), 0,1); dst++; | |||||
nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), 1,1); dst++; | |||||
nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++; | |||||
nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++; | |||||
} | } | ||||
p0 = p1++; | p0 = p1++; | ||||
} | } | ||||
if (loop) { | if (loop) { | ||||
// Loop it | // Loop it | ||||
nvg__vset(dst, verts[0].x, verts[0].y, 0,1); dst++; | |||||
nvg__vset(dst, verts[1].x, verts[1].y, 1,1); dst++; | |||||
nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++; | |||||
nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++; | |||||
} else { | } else { | ||||
// Add cap | // Add cap | ||||
dx = p1->x - p0->x; | dx = p1->x - p0->x; | ||||
dy = p1->y - p0->y; | dy = p1->y - p0->y; | ||||
nvg__normalize(&dx, &dy); | nvg__normalize(&dx, &dy); | ||||
if (lineCap == NVG_BUTT) | if (lineCap == NVG_BUTT) | ||||
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa); | |||||
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1); | |||||
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) | ||||
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa); | |||||
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1); | |||||
else if (lineCap == NVG_ROUND) | else if (lineCap == NVG_ROUND) | ||||
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa); | |||||
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1); | |||||
} | } | ||||
path->nstroke = (int)(dst - verts); | path->nstroke = (int)(dst - verts); | ||||
@@ -1868,7 +1991,7 @@ void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y) | |||||
{ | { | ||||
float x0 = ctx->commandx; | float x0 = ctx->commandx; | ||||
float y0 = ctx->commandy; | float y0 = ctx->commandy; | ||||
float vals[] = { NVG_BEZIERTO, | |||||
float vals[] = { NVG_BEZIERTO, | |||||
x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), | x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), | ||||
x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), | x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), | ||||
x, y }; | x, y }; | ||||
@@ -1950,7 +2073,7 @@ void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, in | |||||
float px = 0, py = 0, ptanx = 0, ptany = 0; | float px = 0, py = 0, ptanx = 0, ptany = 0; | ||||
float vals[3 + 5*7 + 100]; | float vals[3 + 5*7 + 100]; | ||||
int i, ndivs, nvals; | int i, ndivs, nvals; | ||||
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; | |||||
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; | |||||
// Clamp angles | // Clamp angles | ||||
da = a1 - a0; | da = a1 - a0; | ||||
@@ -2022,22 +2145,31 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h) | |||||
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) | ||||
{ | { | ||||
if (r < 0.1f) { | |||||
nvgRect(ctx, x,y,w,h); | |||||
nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r); | |||||
} | |||||
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) | |||||
{ | |||||
if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) { | |||||
nvgRect(ctx, x, y, w, h); | |||||
return; | return; | ||||
} | |||||
else { | |||||
float rx = nvg__minf(r, nvg__absf(w)*0.5f) * nvg__signf(w), ry = nvg__minf(r, nvg__absf(h)*0.5f) * nvg__signf(h); | |||||
} else { | |||||
float halfw = nvg__absf(w)*0.5f; | |||||
float halfh = nvg__absf(h)*0.5f; | |||||
float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h); | |||||
float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h); | |||||
float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h); | |||||
float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h); | |||||
float vals[] = { | float vals[] = { | ||||
NVG_MOVETO, x, y+ry, | |||||
NVG_LINETO, x, y+h-ry, | |||||
NVG_BEZIERTO, x, y+h-ry*(1-NVG_KAPPA90), x+rx*(1-NVG_KAPPA90), y+h, x+rx, y+h, | |||||
NVG_LINETO, x+w-rx, y+h, | |||||
NVG_BEZIERTO, x+w-rx*(1-NVG_KAPPA90), y+h, x+w, y+h-ry*(1-NVG_KAPPA90), x+w, y+h-ry, | |||||
NVG_LINETO, x+w, y+ry, | |||||
NVG_BEZIERTO, x+w, y+ry*(1-NVG_KAPPA90), x+w-rx*(1-NVG_KAPPA90), y, x+w-rx, y, | |||||
NVG_LINETO, x+rx, y, | |||||
NVG_BEZIERTO, x+rx*(1-NVG_KAPPA90), y, x, y+ry*(1-NVG_KAPPA90), x, y+ry, | |||||
NVG_MOVETO, x, y + ryTL, | |||||
NVG_LINETO, x, y + h - ryBL, | |||||
NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h, | |||||
NVG_LINETO, x + w - rxBR, y + h, | |||||
NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR, | |||||
NVG_LINETO, x + w, y + ryTR, | |||||
NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y, | |||||
NVG_LINETO, x + rxTL, y, | |||||
NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL, | |||||
NVG_CLOSE | NVG_CLOSE | ||||
}; | }; | ||||
nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); | nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); | ||||
@@ -2092,7 +2224,7 @@ void nvgFill(NVGcontext* ctx) | |||||
int i; | int i; | ||||
nvg__flattenPaths(ctx); | nvg__flattenPaths(ctx); | ||||
if (ctx->params.edgeAntiAlias) | |||||
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) | |||||
nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); | nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); | ||||
else | else | ||||
nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); | nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); | ||||
@@ -2101,7 +2233,7 @@ void nvgFill(NVGcontext* ctx) | |||||
fillPaint.innerColor.a *= state->alpha; | fillPaint.innerColor.a *= state->alpha; | ||||
fillPaint.outerColor.a *= state->alpha; | fillPaint.outerColor.a *= state->alpha; | ||||
ctx->params.renderFill(ctx->params.userPtr, &fillPaint, &state->scissor, ctx->fringeWidth, | |||||
ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, | |||||
ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); | ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); | ||||
// Count triangles | // Count triangles | ||||
@@ -2122,6 +2254,7 @@ void nvgStroke(NVGcontext* ctx) | |||||
const NVGpath* path; | const NVGpath* path; | ||||
int i; | int i; | ||||
if (strokeWidth < ctx->fringeWidth) { | if (strokeWidth < ctx->fringeWidth) { | ||||
// If the stroke width is less than pixel size, use alpha to emulate coverage. | // If the stroke width is less than pixel size, use alpha to emulate coverage. | ||||
// Since coverage is area, scale by alpha*alpha. | // Since coverage is area, scale by alpha*alpha. | ||||
@@ -2137,12 +2270,12 @@ void nvgStroke(NVGcontext* ctx) | |||||
nvg__flattenPaths(ctx); | nvg__flattenPaths(ctx); | ||||
if (ctx->params.edgeAntiAlias) | |||||
nvg__expandStroke(ctx, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) | |||||
nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit); | |||||
else | else | ||||
nvg__expandStroke(ctx, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit); | |||||
ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, &state->scissor, ctx->fringeWidth, | |||||
ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, | |||||
strokeWidth, ctx->cache->paths, ctx->cache->npaths); | strokeWidth, ctx->cache->paths, ctx->cache->npaths); | ||||
// Count triangles | // Count triangles | ||||
@@ -2154,14 +2287,24 @@ void nvgStroke(NVGcontext* ctx) | |||||
} | } | ||||
// Add fonts | // Add fonts | ||||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path) | |||||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) | |||||
{ | { | ||||
return fonsAddFont(ctx->fs, name, path); | |||||
return fonsAddFont(ctx->fs, name, filename, 0); | |||||
} | } | ||||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData) | |||||
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) | |||||
{ | { | ||||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData); | |||||
return fonsAddFont(ctx->fs, name, filename, fontIndex); | |||||
} | |||||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) | |||||
{ | |||||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); | |||||
} | |||||
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) | |||||
{ | |||||
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); | |||||
} | } | ||||
int nvgFindFont(NVGcontext* ctx, const char* name) | int nvgFindFont(NVGcontext* ctx, const char* name) | ||||
@@ -2170,6 +2313,28 @@ int nvgFindFont(NVGcontext* ctx, const char* name) | |||||
return fonsGetFontByName(ctx->fs, name); | return fonsGetFontByName(ctx->fs, name); | ||||
} | } | ||||
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont) | |||||
{ | |||||
if(baseFont == -1 || fallbackFont == -1) return 0; | |||||
return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont); | |||||
} | |||||
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont) | |||||
{ | |||||
return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); | |||||
} | |||||
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) | |||||
{ | |||||
fonsResetFallbackFont(ctx->fs, baseFont); | |||||
} | |||||
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) | |||||
{ | |||||
nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); | |||||
} | |||||
// State setting | // State setting | ||||
void nvgFontSize(NVGcontext* ctx, float size) | void nvgFontSize(NVGcontext* ctx, float size) | ||||
{ | { | ||||
@@ -2278,7 +2443,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) | |||||
paint.innerColor.a *= state->alpha; | paint.innerColor.a *= state->alpha; | ||||
paint.outerColor.a *= state->alpha; | paint.outerColor.a *= state->alpha; | ||||
ctx->params.renderTriangles(ctx->params.userPtr, &paint, &state->scissor, verts, nverts); | |||||
ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); | |||||
ctx->drawCallCount++; | ctx->drawCallCount++; | ||||
ctx->textTriCount += nverts/3; | ctx->textTriCount += nverts/3; | ||||
@@ -2310,17 +2475,17 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||||
verts = nvg__allocTempVerts(ctx, cverts); | verts = nvg__allocTempVerts(ctx, cverts); | ||||
if (verts == NULL) return x; | if (verts == NULL) return x; | ||||
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end); | |||||
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); | |||||
prevIter = iter; | prevIter = iter; | ||||
while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
float c[4*2]; | float c[4*2]; | ||||
if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? | if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? | ||||
if (!nvg__allocTextAtlas(ctx)) | |||||
break; // no memory :( | |||||
if (nverts != 0) { | if (nverts != 0) { | ||||
nvg__renderText(ctx, verts, nverts); | nvg__renderText(ctx, verts, nverts); | ||||
nverts = 0; | nverts = 0; | ||||
} | } | ||||
if (!nvg__allocTextAtlas(ctx)) | |||||
break; // no memory :( | |||||
iter = prevIter; | iter = prevIter; | ||||
fonsTextIterNext(ctx->fs, &iter, &q); // try again | fonsTextIterNext(ctx->fs, &iter, &q); // try again | ||||
if (iter.prevGlyphIndex == -1) // still can not find glyph? | if (iter.prevGlyphIndex == -1) // still can not find glyph? | ||||
@@ -2343,12 +2508,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* | |||||
} | } | ||||
} | } | ||||
// TODO: add back-end bit to do this just once per frame. | |||||
// TODO: add back-end bit to do this just once per frame. | |||||
nvg__flushTextTexture(ctx); | nvg__flushTextTexture(ctx); | ||||
nvg__renderText(ctx, verts, nverts); | nvg__renderText(ctx, verts, nverts); | ||||
return iter.x; | |||||
return iter.nextx / scale; | |||||
} | } | ||||
void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) | ||||
@@ -2407,7 +2572,7 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, | |||||
fonsSetAlign(ctx->fs, state->textAlign); | fonsSetAlign(ctx->fs, state->textAlign); | ||||
fonsSetFont(ctx->fs, state->fontId); | fonsSetFont(ctx->fs, state->fontId); | ||||
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end); | |||||
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
prevIter = iter; | prevIter = iter; | ||||
while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | ||||
@@ -2431,6 +2596,7 @@ enum NVGcodepointType { | |||||
NVG_SPACE, | NVG_SPACE, | ||||
NVG_NEWLINE, | NVG_NEWLINE, | ||||
NVG_CHAR, | NVG_CHAR, | ||||
NVG_CJK_CHAR, | |||||
}; | }; | ||||
int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) | ||||
@@ -2472,7 +2638,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
breakRowWidth *= scale; | breakRowWidth *= scale; | ||||
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end); | |||||
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); | |||||
prevIter = iter; | prevIter = iter; | ||||
while (fonsTextIterNext(ctx->fs, &iter, &q)) { | while (fonsTextIterNext(ctx->fs, &iter, &q)) { | ||||
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? | ||||
@@ -2498,7 +2664,15 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
type = NVG_NEWLINE; | type = NVG_NEWLINE; | ||||
break; | break; | ||||
default: | default: | ||||
type = NVG_CHAR; | |||||
if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || | |||||
(iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || | |||||
(iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || | |||||
(iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || | |||||
(iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || | |||||
(iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF)) | |||||
type = NVG_CJK_CHAR; | |||||
else | |||||
type = NVG_CHAR; | |||||
break; | break; | ||||
} | } | ||||
@@ -2525,12 +2699,12 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
} else { | } else { | ||||
if (rowStart == NULL) { | if (rowStart == NULL) { | ||||
// Skip white space until the beginning of the line | // Skip white space until the beginning of the line | ||||
if (type == NVG_CHAR) { | |||||
if (type == NVG_CHAR || type == NVG_CJK_CHAR) { | |||||
// The current char is the row so far | // The current char is the row so far | ||||
rowStartX = iter.x; | rowStartX = iter.x; | ||||
rowStart = iter.str; | rowStart = iter.str; | ||||
rowEnd = iter.next; | rowEnd = iter.next; | ||||
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||||
rowWidth = iter.nextx - rowStartX; | |||||
rowMinX = q.x0 - rowStartX; | rowMinX = q.x0 - rowStartX; | ||||
rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
wordStart = iter.str; | wordStart = iter.str; | ||||
@@ -2545,26 +2719,26 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
float nextWidth = iter.nextx - rowStartX; | float nextWidth = iter.nextx - rowStartX; | ||||
// track last non-white space character | // track last non-white space character | ||||
if (type == NVG_CHAR) { | |||||
if (type == NVG_CHAR || type == NVG_CJK_CHAR) { | |||||
rowEnd = iter.next; | rowEnd = iter.next; | ||||
rowWidth = iter.nextx - rowStartX; | rowWidth = iter.nextx - rowStartX; | ||||
rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
} | } | ||||
// track last end of a word | // track last end of a word | ||||
if (ptype == NVG_CHAR && type == NVG_SPACE) { | |||||
if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) { | |||||
breakEnd = iter.str; | breakEnd = iter.str; | ||||
breakWidth = rowWidth; | breakWidth = rowWidth; | ||||
breakMaxX = rowMaxX; | breakMaxX = rowMaxX; | ||||
} | } | ||||
// track last beginning of a word | // track last beginning of a word | ||||
if (ptype == NVG_SPACE && type == NVG_CHAR) { | |||||
if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { | |||||
wordStart = iter.str; | wordStart = iter.str; | ||||
wordStartX = iter.x; | wordStartX = iter.x; | ||||
wordMinX = q.x0 - rowStartX; | |||||
wordMinX = q.x0; | |||||
} | } | ||||
// Break to new line when a character is beyond break width. | // Break to new line when a character is beyond break width. | ||||
if (type == NVG_CHAR && nextWidth > breakRowWidth) { | |||||
if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) { | |||||
// The run length is too long, need to break to new line. | // The run length is too long, need to break to new line. | ||||
if (breakEnd == rowStart) { | if (breakEnd == rowStart) { | ||||
// The current word is longer than the row length, just break it from here. | // The current word is longer than the row length, just break it from here. | ||||
@@ -2597,13 +2771,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||||
nrows++; | nrows++; | ||||
if (nrows >= maxRows) | if (nrows >= maxRows) | ||||
return nrows; | return nrows; | ||||
// Update row | |||||
rowStartX = wordStartX; | rowStartX = wordStartX; | ||||
rowStart = wordStart; | rowStart = wordStart; | ||||
rowEnd = iter.next; | rowEnd = iter.next; | ||||
rowWidth = iter.nextx - rowStartX; | rowWidth = iter.nextx - rowStartX; | ||||
rowMinX = wordMinX; | |||||
rowMinX = wordMinX - rowStartX; | |||||
rowMaxX = q.x1 - rowStartX; | rowMaxX = q.x1 - rowStartX; | ||||
// No change to the word start | |||||
} | } | ||||
// Set null break point | // Set null break point | ||||
breakEnd = rowStart; | breakEnd = rowStart; | ||||
@@ -79,10 +79,46 @@ enum NVGalign { | |||||
// Vertical align | // Vertical align | ||||
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | ||||
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | ||||
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||||
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||||
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||||
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||||
}; | }; | ||||
enum NVGblendFactor { | |||||
NVG_ZERO = 1<<0, | |||||
NVG_ONE = 1<<1, | |||||
NVG_SRC_COLOR = 1<<2, | |||||
NVG_ONE_MINUS_SRC_COLOR = 1<<3, | |||||
NVG_DST_COLOR = 1<<4, | |||||
NVG_ONE_MINUS_DST_COLOR = 1<<5, | |||||
NVG_SRC_ALPHA = 1<<6, | |||||
NVG_ONE_MINUS_SRC_ALPHA = 1<<7, | |||||
NVG_DST_ALPHA = 1<<8, | |||||
NVG_ONE_MINUS_DST_ALPHA = 1<<9, | |||||
NVG_SRC_ALPHA_SATURATE = 1<<10, | |||||
}; | |||||
enum NVGcompositeOperation { | |||||
NVG_SOURCE_OVER, | |||||
NVG_SOURCE_IN, | |||||
NVG_SOURCE_OUT, | |||||
NVG_ATOP, | |||||
NVG_DESTINATION_OVER, | |||||
NVG_DESTINATION_IN, | |||||
NVG_DESTINATION_OUT, | |||||
NVG_DESTINATION_ATOP, | |||||
NVG_LIGHTER, | |||||
NVG_COPY, | |||||
NVG_XOR, | |||||
}; | |||||
struct NVGcompositeOperationState { | |||||
int srcRGB; | |||||
int dstRGB; | |||||
int srcAlpha; | |||||
int dstAlpha; | |||||
}; | |||||
typedef struct NVGcompositeOperationState NVGcompositeOperationState; | |||||
struct NVGglyphPosition { | struct NVGglyphPosition { | ||||
const char* str; // Position of the glyph in the input string. | const char* str; // Position of the glyph in the input string. | ||||
float x; // The x-coordinate of the logical glyph position. | float x; // The x-coordinate of the logical glyph position. | ||||
@@ -100,11 +136,12 @@ struct NVGtextRow { | |||||
typedef struct NVGtextRow NVGtextRow; | typedef struct NVGtextRow NVGtextRow; | ||||
enum NVGimageFlags { | enum NVGimageFlags { | ||||
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||||
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||||
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | ||||
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | ||||
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | ||||
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||||
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||||
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear | |||||
}; | }; | ||||
// Begin drawing a new frame | // Begin drawing a new frame | ||||
@@ -115,7 +152,7 @@ enum NVGimageFlags { | |||||
// For example, GLFW returns two dimension for an opened window: window size and | // For example, GLFW returns two dimension for an opened window: window size and | ||||
// frame buffer size. In that case you would set windowWidth/Height to the window size | // frame buffer size. In that case you would set windowWidth/Height to the window size | ||||
// devicePixelRatio to: frameBufferWidth / windowWidth. | // devicePixelRatio to: frameBufferWidth / windowWidth. | ||||
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); | |||||
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); | |||||
// Cancels drawing the current frame. | // Cancels drawing the current frame. | ||||
void nvgCancelFrame(NVGcontext* ctx); | void nvgCancelFrame(NVGcontext* ctx); | ||||
@@ -123,6 +160,22 @@ void nvgCancelFrame(NVGcontext* ctx); | |||||
// Ends drawing flushing remaining render state. | // Ends drawing flushing remaining render state. | ||||
void nvgEndFrame(NVGcontext* ctx); | void nvgEndFrame(NVGcontext* ctx); | ||||
// | |||||
// Composite operation | |||||
// | |||||
// The composite operations in NanoVG are modeled after HTML Canvas API, and | |||||
// the blend func is based on OpenGL (see corresponding manuals for more info). | |||||
// The colors in the blending state have premultiplied alpha. | |||||
// Sets the composite operation. The op parameter should be one of NVGcompositeOperation. | |||||
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); | |||||
// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. | |||||
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); | |||||
// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. | |||||
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); | |||||
// | // | ||||
// Color utils | // Color utils | ||||
// | // | ||||
@@ -183,7 +236,10 @@ void nvgReset(NVGcontext* ctx); | |||||
// Solid color is simply defined as a color value, different kinds of paints can be created | // Solid color is simply defined as a color value, different kinds of paints can be created | ||||
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | ||||
// | // | ||||
// Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||||
// Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||||
// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. | |||||
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); | |||||
// Sets current stroke style to a solid color. | // Sets current stroke style to a solid color. | ||||
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | ||||
@@ -231,7 +287,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||||
// Apart from nvgResetTransform(), each transformation function first creates | // Apart from nvgResetTransform(), each transformation function first creates | ||||
// specific transformation matrix and pre-multiplies the current transformation by it. | // specific transformation matrix and pre-multiplies the current transformation by it. | ||||
// | // | ||||
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||||
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||||
// Resets current transform to a identity matrix. | // Resets current transform to a identity matrix. | ||||
void nvgResetTransform(NVGcontext* ctx); | void nvgResetTransform(NVGcontext* ctx); | ||||
@@ -317,7 +373,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); | |||||
// Creates image by loading it from the specified chunk of memory. | // Creates image by loading it from the specified chunk of memory. | ||||
// Returns handle to the image. | // Returns handle to the image. | ||||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); | |||||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); | |||||
// Creates image from specified image data. | // Creates image from specified image data. | ||||
// Returns handle to the image. | // Returns handle to the image. | ||||
@@ -368,7 +424,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey | |||||
// Scissoring | // Scissoring | ||||
// | // | ||||
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various | // Scissoring allows you to clip the rendering into a rectangle. This is useful for various | ||||
// user interface cases like rendering a text edit or a timeline. | |||||
// user interface cases like rendering a text edit or a timeline. | |||||
// Sets the current scissor rectangle. | // Sets the current scissor rectangle. | ||||
// The scissor rectangle is transformed by the current transform. | // The scissor rectangle is transformed by the current transform. | ||||
@@ -423,7 +479,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad | |||||
// Closes current sub-path with a line segment. | // Closes current sub-path with a line segment. | ||||
void nvgClosePath(NVGcontext* ctx); | void nvgClosePath(NVGcontext* ctx); | ||||
// Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||||
// Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||||
void nvgPathWinding(NVGcontext* ctx, int dir); | void nvgPathWinding(NVGcontext* ctx, int dir); | ||||
// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | ||||
@@ -437,10 +493,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); | |||||
// Creates new rounded rectangle shaped sub-path. | // Creates new rounded rectangle shaped sub-path. | ||||
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | ||||
// Creates new rounded rectangle shaped sub-path with varying radii for each corner. | |||||
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); | |||||
// Creates new ellipse shaped sub-path. | // Creates new ellipse shaped sub-path. | ||||
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | ||||
// Creates new circle shaped sub-path. | |||||
// Creates new circle shaped sub-path. | |||||
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | ||||
// Fills the current path with current fill style. | // Fills the current path with current fill style. | ||||
@@ -487,13 +546,31 @@ void nvgStroke(NVGcontext* ctx); | |||||
// Returns handle to the font. | // Returns handle to the font. | ||||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | ||||
// Creates image by loading it from the specified memory chunk. | |||||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||||
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); | |||||
// Creates font by loading it from the specified memory chunk. | |||||
// Returns handle to the font. | // Returns handle to the font. | ||||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData); | |||||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); | |||||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||||
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); | |||||
// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | ||||
int nvgFindFont(NVGcontext* ctx, const char* name); | int nvgFindFont(NVGcontext* ctx, const char* name); | ||||
// Adds a fallback font by handle. | |||||
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); | |||||
// Adds a fallback font by name. | |||||
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); | |||||
// Resets fallback fonts by handle. | |||||
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); | |||||
// Resets fallback fonts by name. | |||||
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); | |||||
// Sets the font size of current text style. | // Sets the font size of current text style. | ||||
void nvgFontSize(NVGcontext* ctx, float size); | void nvgFontSize(NVGcontext* ctx, float size); | ||||
@@ -503,7 +580,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur); | |||||
// Sets the letter spacing of current text style. | // Sets the letter spacing of current text style. | ||||
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | ||||
// Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
// Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | ||||
// Sets the text align of current text style, see NVGalign for options. | // Sets the text align of current text style, see NVGalign for options. | ||||
@@ -588,12 +665,12 @@ struct NVGparams { | |||||
int (*renderDeleteTexture)(void* uptr, int image); | int (*renderDeleteTexture)(void* uptr, int image); | ||||
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | ||||
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | ||||
void (*renderViewport)(void* uptr, int width, int height); | |||||
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); | |||||
void (*renderCancel)(void* uptr); | void (*renderCancel)(void* uptr); | ||||
void (*renderFlush)(void* uptr); | void (*renderFlush)(void* uptr); | ||||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||||
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); | |||||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||||
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); | |||||
void (*renderDelete)(void* uptr); | void (*renderDelete)(void* uptr); | ||||
}; | }; | ||||
typedef struct NVGparams NVGparams; | typedef struct NVGparams NVGparams; | ||||
@@ -59,6 +59,9 @@ enum NVGcreateFlags { | |||||
NVGcontext* nvgCreateGL2(int flags); | NVGcontext* nvgCreateGL2(int flags); | ||||
void nvgDeleteGL2(NVGcontext* ctx); | void nvgDeleteGL2(NVGcontext* ctx); | ||||
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); | |||||
#endif | #endif | ||||
#if defined NANOVG_GL3 | #if defined NANOVG_GL3 | ||||
@@ -66,6 +69,9 @@ void nvgDeleteGL2(NVGcontext* ctx); | |||||
NVGcontext* nvgCreateGL3(int flags); | NVGcontext* nvgCreateGL3(int flags); | ||||
void nvgDeleteGL3(NVGcontext* ctx); | void nvgDeleteGL3(NVGcontext* ctx); | ||||
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); | |||||
#endif | #endif | ||||
#if defined NANOVG_GLES2 | #if defined NANOVG_GLES2 | ||||
@@ -73,6 +79,9 @@ void nvgDeleteGL3(NVGcontext* ctx); | |||||
NVGcontext* nvgCreateGLES2(int flags); | NVGcontext* nvgCreateGLES2(int flags); | ||||
void nvgDeleteGLES2(NVGcontext* ctx); | void nvgDeleteGLES2(NVGcontext* ctx); | ||||
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); | |||||
#endif | #endif | ||||
#if defined NANOVG_GLES3 | #if defined NANOVG_GLES3 | ||||
@@ -80,6 +89,9 @@ void nvgDeleteGLES2(NVGcontext* ctx); | |||||
NVGcontext* nvgCreateGLES3(int flags); | NVGcontext* nvgCreateGLES3(int flags); | ||||
void nvgDeleteGLES3(NVGcontext* ctx); | void nvgDeleteGLES3(NVGcontext* ctx); | ||||
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); | |||||
#endif | #endif | ||||
// These are additional flags on top of NVGimageFlags. | // These are additional flags on top of NVGimageFlags. | ||||
@@ -87,10 +99,6 @@ enum NVGimageFlagsGL { | |||||
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. | NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. | ||||
}; | }; | ||||
int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||||
GLuint nvglImageHandle(NVGcontext* ctx, int image); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
@@ -142,6 +150,15 @@ struct GLNVGtexture { | |||||
}; | }; | ||||
typedef struct GLNVGtexture GLNVGtexture; | typedef struct GLNVGtexture GLNVGtexture; | ||||
struct GLNVGblend | |||||
{ | |||||
GLenum srcRGB; | |||||
GLenum dstRGB; | |||||
GLenum srcAlpha; | |||||
GLenum dstAlpha; | |||||
}; | |||||
typedef struct GLNVGblend GLNVGblend; | |||||
enum GLNVGcallType { | enum GLNVGcallType { | ||||
GLNVG_NONE = 0, | GLNVG_NONE = 0, | ||||
GLNVG_FILL, | GLNVG_FILL, | ||||
@@ -158,6 +175,7 @@ struct GLNVGcall { | |||||
int triangleOffset; | int triangleOffset; | ||||
int triangleCount; | int triangleCount; | ||||
int uniformOffset; | int uniformOffset; | ||||
GLNVGblend blendFunc; | |||||
}; | }; | ||||
typedef struct GLNVGcall GLNVGcall; | typedef struct GLNVGcall GLNVGcall; | ||||
@@ -248,7 +266,10 @@ struct GLNVGcontext { | |||||
GLenum stencilFunc; | GLenum stencilFunc; | ||||
GLint stencilFuncRef; | GLint stencilFuncRef; | ||||
GLuint stencilFuncMask; | GLuint stencilFuncMask; | ||||
GLNVGblend blendFunc; | |||||
#endif | #endif | ||||
int dummyTex; | |||||
}; | }; | ||||
typedef struct GLNVGcontext GLNVGcontext; | typedef struct GLNVGcontext GLNVGcontext; | ||||
@@ -298,7 +319,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||||
if ((gl->stencilFunc != func) || | if ((gl->stencilFunc != func) || | ||||
(gl->stencilFuncRef != ref) || | (gl->stencilFuncRef != ref) || | ||||
(gl->stencilFuncMask != mask)) { | (gl->stencilFuncMask != mask)) { | ||||
gl->stencilFunc = func; | gl->stencilFunc = func; | ||||
gl->stencilFuncRef = ref; | gl->stencilFuncRef = ref; | ||||
gl->stencilFuncMask = mask; | gl->stencilFuncMask = mask; | ||||
@@ -308,6 +329,21 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||||
glStencilFunc(func, ref, mask); | glStencilFunc(func, ref, mask); | ||||
#endif | #endif | ||||
} | } | ||||
static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) | |||||
{ | |||||
#if NANOVG_GL_USE_STATE_FILTER | |||||
if ((gl->blendFunc.srcRGB != blend->srcRGB) || | |||||
(gl->blendFunc.dstRGB != blend->dstRGB) || | |||||
(gl->blendFunc.srcAlpha != blend->srcAlpha) || | |||||
(gl->blendFunc.dstAlpha != blend->dstAlpha)) { | |||||
gl->blendFunc = *blend; | |||||
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||||
} | |||||
#else | |||||
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||||
#endif | |||||
} | |||||
static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | ||||
{ | { | ||||
@@ -331,10 +367,10 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | |||||
} | } | ||||
tex = &gl->textures[gl->ntextures++]; | tex = &gl->textures[gl->ntextures++]; | ||||
} | } | ||||
memset(tex, 0, sizeof(*tex)); | memset(tex, 0, sizeof(*tex)); | ||||
tex->id = ++gl->textureId; | tex->id = ++gl->textureId; | ||||
return tex; | return tex; | ||||
} | } | ||||
@@ -363,8 +399,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||||
static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) | ||||
{ | { | ||||
char str[512+1]; | |||||
int len = 0; | |||||
GLchar str[512+1]; | |||||
GLsizei len = 0; | |||||
glGetShaderInfoLog(shader, 512, &len, str); | glGetShaderInfoLog(shader, 512, &len, str); | ||||
if (len > 512) len = 512; | if (len > 512) len = 512; | ||||
str[len] = '\0'; | str[len] = '\0'; | ||||
@@ -373,8 +409,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* | |||||
static void glnvg__dumpProgramError(GLuint prog, const char* name) | static void glnvg__dumpProgramError(GLuint prog, const char* name) | ||||
{ | { | ||||
char str[512+1]; | |||||
int len = 0; | |||||
GLchar str[512+1]; | |||||
GLsizei len = 0; | |||||
glGetProgramInfoLog(prog, 512, &len, str); | glGetProgramInfoLog(prog, 512, &len, str); | ||||
if (len > 512) len = 512; | if (len > 512) len = 512; | ||||
str[len] = '\0'; | str[len] = '\0'; | ||||
@@ -466,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader) | |||||
#endif | #endif | ||||
} | } | ||||
static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||||
static int glnvg__renderCreate(void* uptr) | static int glnvg__renderCreate(void* uptr) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
@@ -514,7 +552,7 @@ static int glnvg__renderCreate(void* uptr) | |||||
" gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" | ||||
"}\n"; | "}\n"; | ||||
static const char* fillFragShader = | |||||
static const char* fillFragShader = | |||||
"#ifdef GL_ES\n" | "#ifdef GL_ES\n" | ||||
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" | "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" | ||||
" precision highp float;\n" | " precision highp float;\n" | ||||
@@ -592,6 +630,7 @@ static int glnvg__renderCreate(void* uptr) | |||||
" float scissor = scissorMask(fpos);\n" | " float scissor = scissorMask(fpos);\n" | ||||
"#ifdef EDGE_AA\n" | "#ifdef EDGE_AA\n" | ||||
" float strokeAlpha = strokeMask();\n" | " float strokeAlpha = strokeMask();\n" | ||||
" if (strokeAlpha < strokeThr) discard;\n" | |||||
"#else\n" | "#else\n" | ||||
" float strokeAlpha = 1.0;\n" | " float strokeAlpha = 1.0;\n" | ||||
"#endif\n" | "#endif\n" | ||||
@@ -631,9 +670,6 @@ static int glnvg__renderCreate(void* uptr) | |||||
" color *= scissor;\n" | " color *= scissor;\n" | ||||
" result = color * innerCol;\n" | " result = color * innerCol;\n" | ||||
" }\n" | " }\n" | ||||
"#ifdef EDGE_AA\n" | |||||
" if (strokeAlpha < strokeThr) discard;\n" | |||||
"#endif\n" | |||||
"#ifdef NANOVG_GL3\n" | "#ifdef NANOVG_GL3\n" | ||||
" outColor = result;\n" | " outColor = result;\n" | ||||
"#else\n" | "#else\n" | ||||
@@ -663,11 +699,15 @@ static int glnvg__renderCreate(void* uptr) | |||||
#if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
// Create UBOs | // Create UBOs | ||||
glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); | ||||
glGenBuffers(1, &gl->fragBuf); | |||||
glGenBuffers(1, &gl->fragBuf); | |||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); | ||||
#endif | #endif | ||||
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | ||||
// Some platforms does not allow to have samples to unset textures. | |||||
// Create empty one which is bound when there's no texture specified. | |||||
gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); | |||||
glnvg__checkError(gl, "create done"); | glnvg__checkError(gl, "create done"); | ||||
glFinish(); | glFinish(); | ||||
@@ -690,7 +730,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); | printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); | ||||
imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | ||||
} | } | ||||
// No mips. | |||||
// No mips. | |||||
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | ||||
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | ||||
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | ||||
@@ -722,7 +762,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
if (type == NVG_TEXTURE_RGBA) | if (type == NVG_TEXTURE_RGBA) | ||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
else | else | ||||
#if defined(NANOVG_GLES2) | |||||
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2) | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | ||||
#elif defined(NANOVG_GLES3) | #elif defined(NANOVG_GLES3) | ||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
@@ -731,11 +771,24 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
#endif | #endif | ||||
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); | |||||
} else { | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||||
} | |||||
} else { | |||||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||||
} else { | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
} | |||||
} | |||||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||||
} else { | } else { | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
} | } | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
if (imageFlags & NVG_IMAGE_REPEATX) | if (imageFlags & NVG_IMAGE_REPEATX) | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | ||||
@@ -801,7 +854,7 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||||
if (tex->type == NVG_TEXTURE_RGBA) | if (tex->type == NVG_TEXTURE_RGBA) | ||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
else | else | ||||
#ifdef NANOVG_GLES2 | |||||
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2) | |||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | ||||
#else | #else | ||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
@@ -887,19 +940,30 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai | |||||
tex = glnvg__findTexture(gl, paint->image); | tex = glnvg__findTexture(gl, paint->image); | ||||
if (tex == NULL) return 0; | if (tex == NULL) return 0; | ||||
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { | if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { | ||||
float flipped[6]; | |||||
nvgTransformScale(flipped, 1.0f, -1.0f); | |||||
nvgTransformMultiply(flipped, paint->xform); | |||||
nvgTransformInverse(invxform, flipped); | |||||
float m1[6], m2[6]; | |||||
nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); | |||||
nvgTransformMultiply(m1, paint->xform); | |||||
nvgTransformScale(m2, 1.0f, -1.0f); | |||||
nvgTransformMultiply(m2, m1); | |||||
nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); | |||||
nvgTransformMultiply(m1, m2); | |||||
nvgTransformInverse(invxform, m1); | |||||
} else { | } else { | ||||
nvgTransformInverse(invxform, paint->xform); | nvgTransformInverse(invxform, paint->xform); | ||||
} | } | ||||
frag->type = NSVG_SHADER_FILLIMG; | frag->type = NSVG_SHADER_FILLIMG; | ||||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||||
if (tex->type == NVG_TEXTURE_RGBA) | if (tex->type == NVG_TEXTURE_RGBA) | ||||
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | ||||
else | else | ||||
frag->texType = 2; | frag->texType = 2; | ||||
#else | |||||
if (tex->type == NVG_TEXTURE_RGBA) | |||||
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; | |||||
else | |||||
frag->texType = 2.0f; | |||||
#endif | |||||
// printf("frag->texType = %d\n", frag->texType); | // printf("frag->texType = %d\n", frag->texType); | ||||
} else { | } else { | ||||
frag->type = NSVG_SHADER_FILLGRAD; | frag->type = NSVG_SHADER_FILLGRAD; | ||||
@@ -917,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); | |||||
static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | ||||
{ | { | ||||
GLNVGtexture* tex = NULL; | |||||
#if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | ||||
#else | #else | ||||
@@ -925,19 +990,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||||
#endif | #endif | ||||
if (image != 0) { | if (image != 0) { | ||||
GLNVGtexture* tex = glnvg__findTexture(gl, image); | |||||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||||
glnvg__checkError(gl, "tex paint tex"); | |||||
} else { | |||||
glnvg__bindTexture(gl, 0); | |||||
tex = glnvg__findTexture(gl, image); | |||||
} | } | ||||
// If no image is set, use empty texture | |||||
if (tex == NULL) { | |||||
tex = glnvg__findTexture(gl, gl->dummyTex); | |||||
} | |||||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||||
glnvg__checkError(gl, "tex paint tex"); | |||||
} | } | ||||
static void glnvg__renderViewport(void* uptr, int width, int height) | |||||
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) | |||||
{ | { | ||||
NVG_NOTUSED(devicePixelRatio); | |||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
gl->view[0] = (float)width; | |||||
gl->view[1] = (float)height; | |||||
gl->view[0] = width; | |||||
gl->view[1] = height; | |||||
} | } | ||||
static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | ||||
@@ -979,7 +1047,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | |||||
// Draw fill | // Draw fill | ||||
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); | glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); | ||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | ||||
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); | |||||
glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); | |||||
glDisable(GL_STENCIL_TEST); | glDisable(GL_STENCIL_TEST); | ||||
} | } | ||||
@@ -992,12 +1060,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) | |||||
glnvg__setUniforms(gl, call->uniformOffset, call->image); | glnvg__setUniforms(gl, call->uniformOffset, call->image); | ||||
glnvg__checkError(gl, "convex fill"); | glnvg__checkError(gl, "convex fill"); | ||||
for (i = 0; i < npaths; i++) | |||||
for (i = 0; i < npaths; i++) { | |||||
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); | ||||
if (gl->flags & NVG_ANTIALIAS) { | |||||
// Draw fringes | // Draw fringes | ||||
for (i = 0; i < npaths; i++) | |||||
if (paths[i].strokeCount > 0) { | |||||
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | ||||
} | |||||
} | } | ||||
} | } | ||||
@@ -1026,7 +1094,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) | |||||
for (i = 0; i < npaths; i++) | for (i = 0; i < npaths; i++) | ||||
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | ||||
// Clear stencil buffer. | |||||
// Clear stencil buffer. | |||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | ||||
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); | glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); | ||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | ||||
@@ -1064,6 +1132,50 @@ static void glnvg__renderCancel(void* uptr) { | |||||
gl->nuniforms = 0; | gl->nuniforms = 0; | ||||
} | } | ||||
static GLenum glnvg_convertBlendFuncFactor(int factor) | |||||
{ | |||||
if (factor == NVG_ZERO) | |||||
return GL_ZERO; | |||||
if (factor == NVG_ONE) | |||||
return GL_ONE; | |||||
if (factor == NVG_SRC_COLOR) | |||||
return GL_SRC_COLOR; | |||||
if (factor == NVG_ONE_MINUS_SRC_COLOR) | |||||
return GL_ONE_MINUS_SRC_COLOR; | |||||
if (factor == NVG_DST_COLOR) | |||||
return GL_DST_COLOR; | |||||
if (factor == NVG_ONE_MINUS_DST_COLOR) | |||||
return GL_ONE_MINUS_DST_COLOR; | |||||
if (factor == NVG_SRC_ALPHA) | |||||
return GL_SRC_ALPHA; | |||||
if (factor == NVG_ONE_MINUS_SRC_ALPHA) | |||||
return GL_ONE_MINUS_SRC_ALPHA; | |||||
if (factor == NVG_DST_ALPHA) | |||||
return GL_DST_ALPHA; | |||||
if (factor == NVG_ONE_MINUS_DST_ALPHA) | |||||
return GL_ONE_MINUS_DST_ALPHA; | |||||
if (factor == NVG_SRC_ALPHA_SATURATE) | |||||
return GL_SRC_ALPHA_SATURATE; | |||||
return GL_INVALID_ENUM; | |||||
} | |||||
static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) | |||||
{ | |||||
GLNVGblend blend; | |||||
blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); | |||||
blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); | |||||
blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); | |||||
blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); | |||||
if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) | |||||
{ | |||||
blend.srcRGB = GL_ONE; | |||||
blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; | |||||
blend.srcAlpha = GL_ONE; | |||||
blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; | |||||
} | |||||
return blend; | |||||
} | |||||
static void glnvg__renderFlush(void* uptr) | static void glnvg__renderFlush(void* uptr) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
@@ -1074,7 +1186,6 @@ static void glnvg__renderFlush(void* uptr) | |||||
// Setup require GL state. | // Setup require GL state. | ||||
glUseProgram(gl->shader.prog); | glUseProgram(gl->shader.prog); | ||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||||
glEnable(GL_CULL_FACE); | glEnable(GL_CULL_FACE); | ||||
glCullFace(GL_BACK); | glCullFace(GL_BACK); | ||||
glFrontFace(GL_CCW); | glFrontFace(GL_CCW); | ||||
@@ -1093,6 +1204,10 @@ static void glnvg__renderFlush(void* uptr) | |||||
gl->stencilFunc = GL_ALWAYS; | gl->stencilFunc = GL_ALWAYS; | ||||
gl->stencilFuncRef = 0; | gl->stencilFuncRef = 0; | ||||
gl->stencilFuncMask = 0xffffffff; | gl->stencilFuncMask = 0xffffffff; | ||||
gl->blendFunc.srcRGB = GL_INVALID_ENUM; | |||||
gl->blendFunc.srcAlpha = GL_INVALID_ENUM; | |||||
gl->blendFunc.dstRGB = GL_INVALID_ENUM; | |||||
gl->blendFunc.dstAlpha = GL_INVALID_ENUM; | |||||
#endif | #endif | ||||
#if NANOVG_GL_USE_UNIFORMBUFFER | #if NANOVG_GL_USE_UNIFORMBUFFER | ||||
@@ -1122,6 +1237,7 @@ static void glnvg__renderFlush(void* uptr) | |||||
for (i = 0; i < gl->ncalls; i++) { | for (i = 0; i < gl->ncalls; i++) { | ||||
GLNVGcall* call = &gl->calls[i]; | GLNVGcall* call = &gl->calls[i]; | ||||
glnvg__blendFuncSeparate(gl,&call->blendFunc); | |||||
if (call->type == GLNVG_FILL) | if (call->type == GLNVG_FILL) | ||||
glnvg__fill(gl, call); | glnvg__fill(gl, call); | ||||
else if (call->type == GLNVG_CONVEXFILL) | else if (call->type == GLNVG_CONVEXFILL) | ||||
@@ -1136,9 +1252,9 @@ static void glnvg__renderFlush(void* uptr) | |||||
glDisableVertexAttribArray(1); | glDisableVertexAttribArray(1); | ||||
#if defined NANOVG_GL3 | #if defined NANOVG_GL3 | ||||
glBindVertexArray(0); | glBindVertexArray(0); | ||||
#endif | |||||
#endif | |||||
glDisable(GL_CULL_FACE); | glDisable(GL_CULL_FACE); | ||||
glBindBuffer(GL_ARRAY_BUFFER, 0); | |||||
glBindBuffer(GL_ARRAY_BUFFER, 0); | |||||
glUseProgram(0); | glUseProgram(0); | ||||
glnvg__bindTexture(gl, 0); | glnvg__bindTexture(gl, 0); | ||||
} | } | ||||
@@ -1237,7 +1353,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) | |||||
vtx->v = v; | vtx->v = v; | ||||
} | } | ||||
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||||
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||||
const float* bounds, const NVGpath* paths, int npaths) | const float* bounds, const NVGpath* paths, int npaths) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
@@ -1249,16 +1365,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
if (call == NULL) return; | if (call == NULL) return; | ||||
call->type = GLNVG_FILL; | call->type = GLNVG_FILL; | ||||
call->triangleCount = 4; | |||||
call->pathOffset = glnvg__allocPaths(gl, npaths); | call->pathOffset = glnvg__allocPaths(gl, npaths); | ||||
if (call->pathOffset == -1) goto error; | if (call->pathOffset == -1) goto error; | ||||
call->pathCount = npaths; | call->pathCount = npaths; | ||||
call->image = paint->image; | call->image = paint->image; | ||||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
if (npaths == 1 && paths[0].convex) | if (npaths == 1 && paths[0].convex) | ||||
{ | |||||
call->type = GLNVG_CONVEXFILL; | call->type = GLNVG_CONVEXFILL; | ||||
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill | |||||
} | |||||
// Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
maxverts = glnvg__maxVertCount(paths, npaths) + 6; | |||||
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; | |||||
offset = glnvg__allocVerts(gl, maxverts); | offset = glnvg__allocVerts(gl, maxverts); | ||||
if (offset == -1) goto error; | if (offset == -1) goto error; | ||||
@@ -1280,20 +1401,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
} | } | ||||
} | } | ||||
// Quad | |||||
call->triangleOffset = offset; | |||||
call->triangleCount = 6; | |||||
quad = &gl->verts[call->triangleOffset]; | |||||
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f); | |||||
// Setup uniforms for draw calls | // Setup uniforms for draw calls | ||||
if (call->type == GLNVG_FILL) { | if (call->type == GLNVG_FILL) { | ||||
// Quad | |||||
call->triangleOffset = offset; | |||||
quad = &gl->verts[call->triangleOffset]; | |||||
glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); | |||||
glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); | |||||
call->uniformOffset = glnvg__allocFragUniforms(gl, 2); | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); | ||||
if (call->uniformOffset == -1) goto error; | if (call->uniformOffset == -1) goto error; | ||||
// Simple shader for stencil | // Simple shader for stencil | ||||
@@ -1318,7 +1435,7 @@ error: | |||||
if (gl->ncalls > 0) gl->ncalls--; | if (gl->ncalls > 0) gl->ncalls--; | ||||
} | } | ||||
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||||
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||||
float strokeWidth, const NVGpath* paths, int npaths) | float strokeWidth, const NVGpath* paths, int npaths) | ||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
@@ -1332,6 +1449,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor | |||||
if (call->pathOffset == -1) goto error; | if (call->pathOffset == -1) goto error; | ||||
call->pathCount = npaths; | call->pathCount = npaths; | ||||
call->image = paint->image; | call->image = paint->image; | ||||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
// Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
maxverts = glnvg__maxVertCount(paths, npaths); | maxverts = glnvg__maxVertCount(paths, npaths); | ||||
@@ -1373,8 +1491,8 @@ error: | |||||
if (gl->ncalls > 0) gl->ncalls--; | if (gl->ncalls > 0) gl->ncalls--; | ||||
} | } | ||||
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||||
const NVGvertex* verts, int nverts) | |||||
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, | |||||
const NVGvertex* verts, int nverts, float fringe) | |||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | GLNVGcontext* gl = (GLNVGcontext*)uptr; | ||||
GLNVGcall* call = glnvg__allocCall(gl); | GLNVGcall* call = glnvg__allocCall(gl); | ||||
@@ -1384,6 +1502,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||||
call->type = GLNVG_TRIANGLES; | call->type = GLNVG_TRIANGLES; | ||||
call->image = paint->image; | call->image = paint->image; | ||||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||||
// Allocate vertices for all the paths. | // Allocate vertices for all the paths. | ||||
call->triangleOffset = glnvg__allocVerts(gl, nverts); | call->triangleOffset = glnvg__allocVerts(gl, nverts); | ||||
@@ -1396,7 +1515,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||||
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | ||||
if (call->uniformOffset == -1) goto error; | if (call->uniformOffset == -1) goto error; | ||||
frag = nvg__fragUniformPtr(gl, call->uniformOffset); | frag = nvg__fragUniformPtr(gl, call->uniformOffset); | ||||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); | |||||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); | |||||
frag->type = NSVG_SHADER_IMG; | frag->type = NSVG_SHADER_IMG; | ||||
return; | return; | ||||
@@ -1499,7 +1618,15 @@ void nvgDeleteGLES3(NVGcontext* ctx) | |||||
nvgDeleteInternal(ctx); | nvgDeleteInternal(ctx); | ||||
} | } | ||||
int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
#if defined NANOVG_GL2 | |||||
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
#elif defined NANOVG_GL3 | |||||
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
#elif defined NANOVG_GLES2 | |||||
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
#elif defined NANOVG_GLES3 | |||||
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||||
#endif | |||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | ||||
GLNVGtexture* tex = glnvg__allocTexture(gl); | GLNVGtexture* tex = glnvg__allocTexture(gl); | ||||
@@ -1515,7 +1642,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i | |||||
return tex->id; | return tex->id; | ||||
} | } | ||||
GLuint nvglImageHandle(NVGcontext* ctx, int image) | |||||
#if defined NANOVG_GL2 | |||||
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image) | |||||
#elif defined NANOVG_GL3 | |||||
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image) | |||||
#elif defined NANOVG_GLES2 | |||||
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image) | |||||
#elif defined NANOVG_GLES3 | |||||
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image) | |||||
#endif | |||||
{ | { | ||||
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | ||||
GLNVGtexture* tex = glnvg__findTexture(gl, image); | GLNVGtexture* tex = glnvg__findTexture(gl, image); | ||||
@@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer; | |||||
// Helper function to create GL frame buffer to render to. | // Helper function to create GL frame buffer to render to. | ||||
void nvgluBindFramebuffer(NVGLUframebuffer* fb); | void nvgluBindFramebuffer(NVGLUframebuffer* fb); | ||||
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | ||||
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb); | |||||
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); | |||||
#endif // NANOVG_GL_UTILS_H | #endif // NANOVG_GL_UTILS_H | ||||
@@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
memset(fb, 0, sizeof(NVGLUframebuffer)); | memset(fb, 0, sizeof(NVGLUframebuffer)); | ||||
fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | ||||
fb->texture = nvglImageHandle(ctx, fb->image); | |||||
#if defined NANOVG_GL2 | |||||
fb->texture = nvglImageHandleGL2(ctx, fb->image); | |||||
#elif defined NANOVG_GL3 | |||||
fb->texture = nvglImageHandleGL3(ctx, fb->image); | |||||
#elif defined NANOVG_GLES2 | |||||
fb->texture = nvglImageHandleGLES2(ctx, fb->image); | |||||
#elif defined NANOVG_GLES3 | |||||
fb->texture = nvglImageHandleGLES3(ctx, fb->image); | |||||
#endif | |||||
fb->ctx = ctx; | |||||
// frame buffer object | // frame buffer object | ||||
glGenFramebuffers(1, &fb->fbo); | glGenFramebuffers(1, &fb->fbo); | ||||
@@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | ||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | ||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; | |||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | |||||
#ifdef GL_DEPTH24_STENCIL8 | |||||
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback. | |||||
// Some graphics cards require a depth buffer along with a stencil. | |||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); | |||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | |||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | |||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |||||
#endif // GL_DEPTH24_STENCIL8 | |||||
goto error; | |||||
} | |||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | ||||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | ||||
@@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||||
error: | error: | ||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | ||||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | ||||
nvgluDeleteFramebuffer(ctx, fb); | |||||
nvgluDeleteFramebuffer(fb); | |||||
return NULL; | return NULL; | ||||
#else | #else | ||||
NVG_NOTUSED(ctx); | NVG_NOTUSED(ctx); | ||||
@@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb) | |||||
#endif | #endif | ||||
} | } | ||||
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||||
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) | |||||
{ | { | ||||
#ifdef NANOVG_FBO_VALID | #ifdef NANOVG_FBO_VALID | ||||
if (fb == NULL) return; | if (fb == NULL) return; | ||||
@@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||||
if (fb->rbo != 0) | if (fb->rbo != 0) | ||||
glDeleteRenderbuffers(1, &fb->rbo); | glDeleteRenderbuffers(1, &fb->rbo); | ||||
if (fb->image >= 0) | if (fb->image >= 0) | ||||
nvgDeleteImage(ctx, fb->image); | |||||
nvgDeleteImage(fb->ctx, fb->image); | |||||
fb->ctx = NULL; | |||||
fb->fbo = 0; | fb->fbo = 0; | ||||
fb->rbo = 0; | fb->rbo = 0; | ||||
fb->texture = 0; | fb->texture = 0; | ||||
fb->image = -1; | fb->image = -1; | ||||
free(fb); | free(fb); | ||||
#else | #else | ||||
NVG_NOTUSED(ctx); | |||||
NVG_NOTUSED(fb); | NVG_NOTUSED(fb); | ||||
#endif | #endif | ||||
} | } | ||||
@@ -83,6 +83,7 @@ static int attrListSgl[] = { | |||||
GLX_GREEN_SIZE, 4, | GLX_GREEN_SIZE, 4, | ||||
GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
GLX_STENCIL_SIZE, 8, | |||||
GLX_ARB_multisample, 1, | GLX_ARB_multisample, 1, | ||||
None | None | ||||
}; | }; | ||||
@@ -98,6 +99,7 @@ static int attrListDbl[] = { | |||||
GLX_GREEN_SIZE, 4, | GLX_GREEN_SIZE, 4, | ||||
GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
GLX_STENCIL_SIZE, 8, | |||||
GLX_ARB_multisample, 1, | GLX_ARB_multisample, 1, | ||||
None | None | ||||
}; | }; | ||||
@@ -114,6 +116,7 @@ static int attrListDblMS[] = { | |||||
GLX_BLUE_SIZE, 4, | GLX_BLUE_SIZE, 4, | ||||
GLX_ALPHA_SIZE, 4, | GLX_ALPHA_SIZE, 4, | ||||
GLX_DEPTH_SIZE, 16, | GLX_DEPTH_SIZE, 16, | ||||
GLX_STENCIL_SIZE, 8, | |||||
GLX_SAMPLE_BUFFERS, 1, | GLX_SAMPLE_BUFFERS, 1, | ||||
GLX_SAMPLES, 4, | GLX_SAMPLES, 4, | ||||
None | None |
@@ -0,0 +1,29 @@ | |||||
/* | |||||
Copyright (C) 2012-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file extras.c pugl extra implementations for DPF. | |||||
*/ | |||||
#include "extras.h" | |||||
#include "../pugl-upstream/src/implementation.h" | |||||
const char* | |||||
puglGetWindowTitle(const PuglView* view) | |||||
{ | |||||
return view->title; | |||||
} |
@@ -0,0 +1,50 @@ | |||||
/* | |||||
Copyright (C) 2012-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file pugl.h pugl extra API for DPF. | |||||
*/ | |||||
#ifndef PUGL_EXTRAS_PUGL_H | |||||
#define PUGL_EXTRAS_PUGL_H | |||||
#include "../pugl-upstream/include/pugl/pugl.h" | |||||
PUGL_BEGIN_DECLS | |||||
PUGL_API const char* | |||||
puglGetWindowTitle(const PuglView* view); | |||||
PUGL_API int | |||||
puglGetViewHint(const PuglView* view, PuglViewHint hint); | |||||
PUGL_API void | |||||
puglRaiseWindow(PuglView* view); | |||||
PUGL_API void | |||||
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); | |||||
PUGL_API void | |||||
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
PUGL_API void | |||||
puglWin32SetWindowResizable(PuglView* view, bool resizable); | |||||
#endif | |||||
PUGL_END_DECLS | |||||
#endif // PUGL_EXTRAS_PUGL_H |
@@ -0,0 +1,81 @@ | |||||
/* | |||||
Copyright 2012-2019 David Robillard <http://drobilla.net> | |||||
Copyright 2019-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file mac.cpp HaikuOS implementation. | |||||
*/ | |||||
#include "haiku.h" | |||||
#include "pugl/detail/implementation.h" | |||||
PuglStatus | |||||
puglGrabFocus(PuglView* view) | |||||
{ | |||||
view->impl->bView->MakeFocus(true); | |||||
return PUGL_SUCCESS; | |||||
} | |||||
// extras follow | |||||
void | |||||
puglRaiseWindow(PuglView* view) | |||||
{ | |||||
} | |||||
void | |||||
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) | |||||
{ | |||||
bView->ResizeTo(width, height); | |||||
if (bWindow != nullptr && bWindow->LockLooper()) | |||||
{ | |||||
bWindow->MoveTo(50, 50); | |||||
bWindow->ResizeTo(width, height); | |||||
if (! forced) | |||||
bWindow->Flush(); | |||||
bWindow->UnlockLooper(); | |||||
} | |||||
// TODO resizable | |||||
} | |||||
void setVisible(const bool yesNo) | |||||
{ | |||||
if (bWindow != nullptr) | |||||
{ | |||||
if (bWindow->LockLooper()) | |||||
{ | |||||
if (yesNo) | |||||
bWindow->Show(); | |||||
else | |||||
bWindow->Hide(); | |||||
// TODO use flush? | |||||
bWindow->Sync(); | |||||
bWindow->UnlockLooper(); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (yesNo) | |||||
bView->Show(); | |||||
else | |||||
bView->Hide(); | |||||
} | |||||
} |
@@ -0,0 +1,35 @@ | |||||
/* | |||||
Copyright 2012-2019 David Robillard <http://drobilla.net> | |||||
Copyright 2019-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file haiku.h Shared definitions for HaikuOS implementation. | |||||
*/ | |||||
#include "pugl/pugl.h" | |||||
#include <Application.h> | |||||
#include <Window.h> | |||||
// using? interface/ | |||||
struct PuglWorldInternalsImpl { | |||||
BApplication* app; | |||||
}; | |||||
struct PuglInternalsImpl { | |||||
BViewType* view; | |||||
BWindow* window; | |||||
}; |
@@ -0,0 +1,48 @@ | |||||
/* | |||||
Copyright (C) 2012-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file mac.m MacOS extra implementation for DPF. | |||||
*/ | |||||
#include "pugl/detail/mac.h" | |||||
void | |||||
puglRaiseWindow(PuglView* view) | |||||
{ | |||||
} | |||||
void | |||||
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) | |||||
{ | |||||
// NOTE: pugl mac code does nothing with x and y | |||||
const PuglRect frame = { 0.0, 0.0, (double)width, (double)height }; | |||||
puglSetFrame(view, frame); | |||||
} | |||||
void | |||||
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) | |||||
{ | |||||
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio | |||||
view->minWidth = width; | |||||
view->minHeight = height; | |||||
[view->impl->window setContentMinSize:sizePoints(view, width, height)]; | |||||
if (aspect) { | |||||
[view->impl->window setContentAspectRatio:sizePoints(view, width, height)]; | |||||
} | |||||
} |
@@ -0,0 +1,118 @@ | |||||
/* | |||||
Copyright (C) 2012-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file win.c Windows extra implementation for DPF. | |||||
*/ | |||||
#include "pugl/detail/win.h" | |||||
#include "pugl/detail/implementation.h" | |||||
void | |||||
puglRaiseWindow(PuglView* view) | |||||
{ | |||||
SetForegroundWindow(view->impl->hwnd); | |||||
SetActiveWindow(view->impl->hwnd); | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) | |||||
{ | |||||
view->frame.width = width; | |||||
view->frame.height = height; | |||||
// NOTE the following code matches upstream pugl, except we add SWP_NOMOVE flag | |||||
if (view->impl->hwnd) { | |||||
RECT rect = { (long)frame.x, | |||||
(long)frame.y, | |||||
(long)frame.x + (long)frame.width, | |||||
(long)frame.y + (long)frame.height }; | |||||
AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), | |||||
FALSE, | |||||
puglWinGetWindowExFlags(view)); | |||||
SetWindowPos(view->impl->hwnd, | |||||
HWND_TOP, | |||||
rect.left, | |||||
rect.top, | |||||
rect.right - rect.left, | |||||
rect.bottom - rect.top, | |||||
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); | |||||
} | |||||
} | |||||
void | |||||
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) | |||||
{ | |||||
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio, but stilL TODO on pugl | |||||
Display* display = view->world->impl->display; | |||||
view->minWidth = width; | |||||
view->minHeight = height; | |||||
if (aspect) { | |||||
view->minAspectX = width; | |||||
view->minAspectY = height; | |||||
view->maxAspectX = width; | |||||
view->maxAspectY = height; | |||||
} | |||||
} | |||||
void | |||||
puglWin32RestoreWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
ShowWindow(impl->hwnd, SW_RESTORE); | |||||
SetFocus(impl->hwnd); | |||||
} | |||||
void | |||||
puglWin32ShowWindowCentered(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
RECT rectChild, rectParent; | |||||
if (impl->transientParent != 0 && | |||||
GetWindowRect(impl->hwnd, &rectChild) && | |||||
GetWindowRect(impl->transientParent, &rectParent)) | |||||
{ | |||||
SetWindowPos(impl->hwnd, (HWND)impl->transientParent, | |||||
rectParent.left + (rectChild.right-rectChild.left)/2, | |||||
rectParent.top + (rectChild.bottom-rectChild.top)/2, | |||||
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); | |||||
} | |||||
else | |||||
{ | |||||
ShowWindow(hwnd, SW_SHOWNORMAL); | |||||
} | |||||
SetFocus(impl->hwnd); | |||||
} | |||||
void | |||||
puglWin32SetWindowResizable(PuglView* view, bool resizable) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
const int winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX | |||||
: GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; | |||||
SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); | |||||
} |
@@ -0,0 +1,111 @@ | |||||
/* | |||||
Copyright (C) 2012-2020 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. | |||||
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. | |||||
*/ | |||||
/** | |||||
@file x11.c X11 extra implementation for DPF. | |||||
*/ | |||||
// NOTE can't import this file twice! | |||||
#ifndef PUGL_DETAIL_X11_H_INCLUDED | |||||
#include "../pugl-upstream/src/x11.h" | |||||
#endif | |||||
#include "../pugl-upstream/src/implementation.h" | |||||
#include <sys/types.h> | |||||
#include <unistd.h> | |||||
void | |||||
puglRaiseWindow(PuglView* view) | |||||
{ | |||||
XRaiseWindow(view->impl->display, view->impl->win); | |||||
} | |||||
void | |||||
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) | |||||
{ | |||||
view->frame.width = width; | |||||
view->frame.height = height; | |||||
if (view->impl->win) { | |||||
#if 0 | |||||
if (! fResizable) | |||||
{ | |||||
XSizeHints sizeHints; | |||||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
sizeHints.flags = PSize|PMinSize|PMaxSize; | |||||
sizeHints.width = static_cast<int>(width); | |||||
sizeHints.height = static_cast<int>(height); | |||||
sizeHints.min_width = static_cast<int>(width); | |||||
sizeHints.min_height = static_cast<int>(height); | |||||
sizeHints.max_width = static_cast<int>(width); | |||||
sizeHints.max_height = static_cast<int>(height); | |||||
XSetWMNormalHints(xDisplay, xWindow, &sizeHints); | |||||
} | |||||
#endif | |||||
XResizeWindow(view->world->impl->display, view->impl->win, width, height); | |||||
} | |||||
} | |||||
void | |||||
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) | |||||
{ | |||||
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio | |||||
Display* display = view->world->impl->display; | |||||
view->minWidth = width; | |||||
view->minHeight = height; | |||||
if (aspect) { | |||||
view->minAspectX = width; | |||||
view->minAspectY = height; | |||||
view->maxAspectX = width; | |||||
view->maxAspectY = height; | |||||
} | |||||
#if 0 | |||||
if (view->impl->win) { | |||||
XSizeHints sizeHints = getSizeHints(view); | |||||
XSetNormalHints(display, view->impl->win, &sizeHints); | |||||
// NOTE old code used this instead | |||||
// XSetWMNormalHints(display, view->impl->win, &sizeHints); | |||||
} | |||||
#endif | |||||
} | |||||
void | |||||
puglExtraSetWindowTypeAndPID(PuglView* view) | |||||
{ | |||||
PuglInternals* const impl = view->impl; | |||||
const pid_t pid = getpid(); | |||||
const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False); | |||||
XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); | |||||
const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False); | |||||
// Setting the window to both dialog and normal will produce a decorated floating dialog | |||||
// Order is important: DIALOG needs to come before NORMAL | |||||
const Atom _wts[2] = { | |||||
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False), | |||||
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False) | |||||
}; | |||||
XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); | |||||
} |
@@ -0,0 +1,28 @@ | |||||
--- | |||||
AlignConsecutiveAssignments: true | |||||
AlignConsecutiveDeclarations: true | |||||
AlignEscapedNewlinesLeft: true | |||||
BasedOnStyle: Mozilla | |||||
BraceWrapping: | |||||
AfterNamespace: false | |||||
AfterClass: true | |||||
AfterEnum: false | |||||
AfterExternBlock: false | |||||
AfterFunction: true | |||||
AfterStruct: false | |||||
SplitEmptyFunction: false | |||||
SplitEmptyRecord: false | |||||
BreakBeforeBraces: Custom | |||||
Cpp11BracedListStyle: true | |||||
IndentCaseLabels: false | |||||
IndentPPDirectives: AfterHash | |||||
KeepEmptyLinesAtTheStartOfBlocks: false | |||||
SpacesInContainerLiterals: false | |||||
StatementMacros: | |||||
- PUGL_API | |||||
- PUGL_CONST_API | |||||
- PUGL_CONST_FUNC | |||||
- PUGL_DEPRECATED_BY | |||||
- PUGL_UNUSED | |||||
- _Pragma | |||||
... |
@@ -0,0 +1,4 @@ | |||||
Checks: > | |||||
*, | |||||
FormatStyle: file | |||||
WarningsAsErrors: '*' |
@@ -0,0 +1,13 @@ | |||||
{ | |||||
"version": "1.0.0", | |||||
"include_dirs": [ | |||||
"bindings/cpp/include", | |||||
"include" | |||||
], | |||||
"exclude_patterns": [ | |||||
"glad\\.c" | |||||
], | |||||
"mapping_files": [ | |||||
".includes.imp" | |||||
] | |||||
} |
@@ -0,0 +1,19 @@ | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
end_of_line = lf | |||||
trim_trailing_whitespace = true | |||||
insert_final_newline = true | |||||
[*.{c,h,m,cpp,hpp,mm,glsl,frag,vert}] | |||||
indent_style = space | |||||
indent_size = 2 | |||||
[*.py] | |||||
indent_style = space | |||||
indent_size = 4 | |||||
[doc/**.{html,xml,css}] | |||||
indent_style = space | |||||
indent_size = 2 |
@@ -0,0 +1,3 @@ | |||||
build/ | |||||
__pycache__ | |||||
*.pyc |
@@ -0,0 +1,153 @@ | |||||
stages: | |||||
- build | |||||
- deploy | |||||
.build_template: &build_definition | |||||
stage: build | |||||
arm32_dbg: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-arm32 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
arm32_rel: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-arm32 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
arm64_dbg: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-arm64 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
arm64_rel: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-arm64 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
x64_dbg: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-x64 | |||||
script: | |||||
- meson setup build -Dbuildtype=debug -Ddocs=enabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
artifacts: | |||||
paths: | |||||
- build/doc | |||||
x64_rel: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-x64 | |||||
script: | |||||
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
x64_static: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-x64 | |||||
script: | |||||
- meson setup build -Ddefault_library=static -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
x64_sanitize: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-x64-clang | |||||
script: | |||||
- meson setup build -Db_lundef=false -Dbuildtype=plain -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
variables: | |||||
CC: "clang" | |||||
CXX: "clang++" | |||||
CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" | |||||
CXXFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" | |||||
LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" | |||||
mingw32_dbg: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-mingw32 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
mingw32_rel: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-mingw32 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
mingw64_dbg: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-mingw64 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
mingw64_rel: | |||||
<<: *build_definition | |||||
image: lv2plugin/debian-mingw64 | |||||
script: | |||||
- meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
mac_dbg: | |||||
<<: *build_definition | |||||
tags: [macos] | |||||
script: | |||||
- meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
mac_rel: | |||||
<<: *build_definition | |||||
tags: [macos] | |||||
script: | |||||
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
win_dbg: | |||||
<<: *build_definition | |||||
tags: [windows,meson] | |||||
script: | |||||
- meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
win_rel: | |||||
<<: *build_definition | |||||
tags: [windows,meson] | |||||
script: | |||||
- meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true | |||||
- ninja -C build | |||||
pages: | |||||
stage: deploy | |||||
script: | |||||
- mkdir public | |||||
- mkdir public/c | |||||
- mkdir public/cpp | |||||
- mv build/doc/c/singlehtml/ public/c/singlehtml/ | |||||
- mv build/doc/cpp/singlehtml/ public/cpp/singlehtml/ | |||||
- mv build/doc/c/html/ public/c/html/ | |||||
- mv build/doc/cpp/html/ public/cpp/html/ | |||||
dependencies: | |||||
- x64_dbg | |||||
artifacts: | |||||
paths: | |||||
- public | |||||
only: | |||||
- master |
@@ -0,0 +1,15 @@ | |||||
[ | |||||
{ "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": [ "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" ] } | |||||
] |
@@ -0,0 +1,13 @@ | |||||
Pugl is primarily written and maintained by David Robillard <d@drobilla.net> | |||||
with contributions from (in increasing chronological order): | |||||
Ben Loftis <ben@harrisonconsoles.com> | |||||
Robin Gareus <robin@gareus.org> | |||||
Erik Ă…ldstedt Sund <erikalds@gmail.com> | |||||
Hanspeter Portner <dev@open-music-kontrollers.ch> | |||||
Stefan Westerfeld <stefan@space.twc.de> | |||||
Jordan Halase <jordan@halase.me> | |||||
Oliver Schmidt <oliver@luced.de> | |||||
Zoë Sparks <zoe@milky.flowers> | |||||
Jean Pierre Cimalando <jp-dev@inbox.ru> | |||||
Thomas Brand <tom@trellis.ch> |
@@ -0,0 +1,13 @@ | |||||
Copyright 2011-2021 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. |
@@ -0,0 +1,98 @@ | |||||
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 is vaguely similar to libraries like GLUT and GLFW, but with some | |||||
distinguishing features: | |||||
* Minimal in scope, providing only a thin interface to isolate | |||||
platform-specific details from applications. | |||||
* Zero dependencies, aside from standard system libraries. | |||||
* 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. | |||||
* Suitable not only for continuously rendering applications like games, but | |||||
also event-driven applications that only draw when necessary. | |||||
* Explicit context and no static data whatsoever, so that several instances | |||||
can be used within a single program at once. | |||||
* 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. | |||||
Stability | |||||
--------- | |||||
Pugl is currently being developed towards a long-term stable API. For the time | |||||
being, however, the API may break occasionally. Please report any relevant | |||||
feedback, or file feature requests, so that we can ensure that the released API | |||||
is stable for as long as possible. | |||||
Documentation | |||||
------------- | |||||
Pugl is a C library that includes C++ bindings. | |||||
Each API is documented separately: | |||||
* [C Documentation (single page)](https://lv2.gitlab.io/pugl/c/singlehtml/) | |||||
* [C Documentation (paginated)](https://lv2.gitlab.io/pugl/c/html/) | |||||
* [C++ Documentation (single page)](https://lv2.gitlab.io/pugl/cpp/singlehtml/) | |||||
* [C++ Documentation (paginated)](https://lv2.gitlab.io/pugl/cpp/html/) | |||||
The documentation will also be built from the source if the `docs` | |||||
configuration option is enabled, and both Doxygen and Sphinx are available. | |||||
Testing | |||||
------- | |||||
Some unit tests are included, but unfortunately manual testing is still | |||||
required. The tests and example programs are built by default. You can run | |||||
all the tests at once via ninja: | |||||
meson setup build | |||||
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. | |||||
-- David Robillard <d@drobilla.net> |
@@ -0,0 +1,14 @@ | |||||
Checks: > | |||||
*, | |||||
-*-uppercase-literal-suffix, | |||||
-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/.*' |
@@ -0,0 +1,12 @@ | |||||
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') |
@@ -0,0 +1,45 @@ | |||||
/* | |||||
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 |
@@ -0,0 +1,70 @@ | |||||
/* | |||||
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 |
@@ -0,0 +1,734 @@ | |||||
/* | |||||
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 |
@@ -0,0 +1,45 @@ | |||||
/* | |||||
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 |
@@ -0,0 +1,168 @@ | |||||
/* | |||||
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 |
@@ -0,0 +1,7 @@ | |||||
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') |
@@ -0,0 +1,2 @@ | |||||
configure_file(copy: true, input: '../../resources/pugl.svg', output: 'pugl.svg') | |||||
@@ -0,0 +1,32 @@ | |||||
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@ |
@@ -0,0 +1,5 @@ | |||||
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') |
@@ -0,0 +1,101 @@ | |||||
.. 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. | |||||
@@ -0,0 +1,84 @@ | |||||
.. 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. | |||||
@@ -0,0 +1,11 @@ | |||||
#### | |||||
Pugl | |||||
#### | |||||
.. include:: summary.rst | |||||
.. toctree:: | |||||
deployment | |||||
overview | |||||
api/pugl |
@@ -0,0 +1,48 @@ | |||||
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') |
@@ -0,0 +1,26 @@ | |||||
.. 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/ |
@@ -0,0 +1,20 @@ | |||||
.. 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); |
@@ -0,0 +1,321 @@ | |||||
.. 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 |
@@ -0,0 +1,65 @@ | |||||
################ | |||||
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. | |||||
@@ -0,0 +1,19 @@ | |||||
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') |