| @@ -28,9 +28,11 @@ endif | |||
| dgl: | |||
| ifeq ($(HAVE_CAIRO_OR_OPENGL),true) | |||
| $(MAKE) USE_FILE_BROWSER=false -C dpf/dgl | |||
| ifeq ($(HAVE_OPENGL),true) | |||
| $(MAKE) USE_FILE_BROWSER=false -C dpf/dgl opengl | |||
| $(MAKE) USE_FILE_BROWSER=false -C dpf/dgl opengl3 | |||
| else | |||
| $(MAKE) USE_FILE_BROWSER=false -C dpf/dgl cairo | |||
| endif | |||
| endif | |||
| @@ -514,11 +514,21 @@ $(DGL_BUILD_DIR)/libdgl-vulkan.a: $(DGL_POSSIBLE_DEPS) | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| $(BUILD_DIR)/DistrhoPluginMain_%_single_obj.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES) | |||
| -@mkdir -p $(BUILD_DIR) | |||
| @echo "Compiling DistrhoPluginMain.cpp ($*)" | |||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -DDISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT=1 -c -o $@ | |||
| $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES) | |||
| -@mkdir -p $(BUILD_DIR) | |||
| @echo "Compiling DistrhoPluginMain.cpp ($*)" | |||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ | |||
| $(BUILD_DIR)/DistrhoUIMain_%_single_obj.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES) | |||
| -@mkdir -p $(BUILD_DIR) | |||
| @echo "Compiling DistrhoUIMain.cpp ($*)" | |||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -DDISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT=1 -c -o $@ | |||
| $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES) | |||
| -@mkdir -p $(BUILD_DIR) | |||
| @echo "Compiling DistrhoUIMain.cpp ($*)" | |||
| @@ -534,10 +544,10 @@ $(BUILD_DIR)/DistrhoUI_win32.cpp.o: $(DPF_PATH)/distrho/DistrhoUI_win32.cpp $(EX | |||
| @echo "Compiling DistrhoUI_win32.cpp ($*)" | |||
| $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -std=gnu++17 -c -o $@ | |||
| $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: BUILD_CXX_FLAGS += $(JACK_FLAGS) | |||
| $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.o: BUILD_CXX_FLAGS += -ObjC++ | |||
| $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: BUILD_CXX_FLAGS += $(JACK_FLAGS) | |||
| $(BUILD_DIR)/DistrhoUIMain_AU.cpp.o: BUILD_CXX_FLAGS += -ObjC++ | |||
| $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: BUILD_CXX_FLAGS += $(LIBLO_FLAGS) | |||
| @@ -591,7 +601,7 @@ lv2_dsp: $(lv2_dsp) | |||
| lv2_sep: $(lv2_dsp) $(lv2_ui) | |||
| ifeq ($(HAVE_DGL),true) | |||
| $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED) | |||
| $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2_single_obj.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2_single_obj.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED) | |||
| else | |||
| $(lv2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | |||
| endif | |||
| @@ -845,6 +855,7 @@ endif | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_LV2_single_obj.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d | |||
| @@ -856,6 +867,7 @@ endif | |||
| -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_LV2_single_obj.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d | |||
| -include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d | |||
| @@ -1,28 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * 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_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| #define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| #include "Base.hpp" | |||
| START_NAMESPACE_DGL | |||
| #include "../distrho/extra/FileBrowserDialogImpl.hpp" | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| @@ -1,71 +0,0 @@ | |||
| /* | |||
| * 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_LAYOUT_HPP_INCLUDED | |||
| #define DGL_LAYOUT_HPP_INCLUDED | |||
| #include "Geometry.hpp" | |||
| #include <list> | |||
| START_NAMESPACE_DGL | |||
| class SubWidget; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // NOTE: under development, API to be finalized and documented soon | |||
| enum SizeHint { | |||
| Expanding, | |||
| Fixed | |||
| }; | |||
| struct SubWidgetWithSizeHint { | |||
| SubWidget* widget; | |||
| SizeHint sizeHint; | |||
| }; | |||
| template<bool horizontal> | |||
| struct Layout | |||
| { | |||
| std::list<SubWidgetWithSizeHint> widgets; | |||
| uint setAbsolutePos(int x, int y, uint padding); | |||
| void setSize(uint size, uint padding); | |||
| }; | |||
| typedef Layout<true> HorizontalLayout; | |||
| typedef Layout<false> VerticalLayout; | |||
| struct HorizontallyStackedVerticalLayout | |||
| { | |||
| std::list<VerticalLayout*> items; | |||
| Size<uint> adjustSize(uint padding); // TODO | |||
| void setAbsolutePos(int x, int y, uint padding); | |||
| }; | |||
| struct VerticallyStackedHorizontalLayout | |||
| { | |||
| std::list<HorizontalLayout*> items; | |||
| Size<uint> adjustSize(uint padding); | |||
| void setAbsolutePos(int x, int y, uint padding); | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_LAYOUT_HPP_INCLUDED | |||
| @@ -43,7 +43,6 @@ OBJS_common = \ | |||
| $(BUILD_DIR)/dgl/Geometry.cpp.o \ | |||
| $(BUILD_DIR)/dgl/ImageBase.cpp.o \ | |||
| $(BUILD_DIR)/dgl/ImageBaseWidgets.cpp.o \ | |||
| $(BUILD_DIR)/dgl/Layout.cpp.o \ | |||
| $(BUILD_DIR)/dgl/Resources.cpp.o \ | |||
| $(BUILD_DIR)/dgl/SubWidget.cpp.o \ | |||
| $(BUILD_DIR)/dgl/SubWidgetPrivateData.cpp.o \ | |||
| @@ -1,103 +0,0 @@ | |||
| /* | |||
| * 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,28 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef DGL_WEB_VIEW_HPP_INCLUDED | |||
| #define DGL_WEB_VIEW_HPP_INCLUDED | |||
| #include "Base.hpp" | |||
| START_NAMESPACE_DGL | |||
| #include "../distrho/extra/WebViewImpl.hpp" | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_WEB_VIEW_HPP_INCLUDED | |||
| @@ -131,6 +131,10 @@ Application::Application(int argc, char* argv[]) | |||
| #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) | |||
| if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | |||
| std::exit(dpf_webview_start(argc, argv)); | |||
| #else | |||
| // unused | |||
| (void)argc; | |||
| (void)argv; | |||
| #endif | |||
| // build config sentinels | |||
| @@ -1,201 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 "../Layout.hpp" | |||
| #include "../SubWidget.hpp" | |||
| START_NAMESPACE_DGL | |||
| typedef std::list<SubWidgetWithSizeHint>::iterator SubWidgetWithSizeHintIterator; | |||
| typedef std::list<HorizontalLayout*>::iterator HorizontalLayoutIterator; | |||
| typedef std::list<VerticalLayout*>::iterator VerticalLayoutIterator; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| template<> // horizontal | |||
| uint Layout<true>::setAbsolutePos(int x, const int y, const uint padding) | |||
| { | |||
| uint maxHeight = 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| maxHeight = std::max(maxHeight, s.widget->getHeight()); | |||
| s.widget->setAbsolutePos(x, y); | |||
| x += static_cast<int>(s.widget->getWidth()); | |||
| x += static_cast<int>(padding); | |||
| } | |||
| return maxHeight; | |||
| } | |||
| template<> // vertical | |||
| uint Layout<false>::setAbsolutePos(const int x, int y, const uint padding) | |||
| { | |||
| uint maxWidth = 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| maxWidth = std::max(maxWidth, s.widget->getWidth()); | |||
| s.widget->setAbsolutePos(x, y); | |||
| y += static_cast<int>(s.widget->getHeight()); | |||
| y += static_cast<int>(padding); | |||
| } | |||
| return maxWidth; | |||
| } | |||
| template<> // horizontal | |||
| void Layout<true>::setSize(const uint width, const uint padding) | |||
| { | |||
| uint maxHeight = 0; | |||
| uint nonFixedWidth = width; | |||
| uint numDynamiclySizedWidgets = 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| maxHeight = std::max(maxHeight, s.widget->getHeight()); | |||
| if (s.sizeHint == Fixed) | |||
| nonFixedWidth -= s.widget->getWidth(); | |||
| else | |||
| ++numDynamiclySizedWidgets; | |||
| } | |||
| if (const size_t numWidgets = widgets.size()) | |||
| nonFixedWidth -= padding * static_cast<uint>(numWidgets - 1); | |||
| const uint widthPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedWidth / numDynamiclySizedWidgets : 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| if (s.sizeHint != Fixed) | |||
| s.widget->setSize(widthPerWidget, maxHeight); | |||
| else | |||
| s.widget->setHeight(maxHeight); | |||
| } | |||
| } | |||
| template<> // vertical | |||
| void Layout<false>::setSize(const uint height, const uint padding) | |||
| { | |||
| uint biggestWidth = 0; | |||
| uint nonFixedHeight = height; | |||
| uint numDynamiclySizedWidgets = 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| biggestWidth = std::max(biggestWidth, s.widget->getWidth()); | |||
| if (s.sizeHint == Fixed) | |||
| nonFixedHeight -= s.widget->getHeight(); | |||
| else | |||
| ++numDynamiclySizedWidgets; | |||
| } | |||
| if (const size_t numWidgets = widgets.size()) | |||
| nonFixedHeight -= padding * static_cast<uint>(numWidgets - 1); | |||
| const uint heightPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedHeight / numDynamiclySizedWidgets : 0; | |||
| for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it); | |||
| if (s.sizeHint != Fixed) | |||
| s.widget->setSize(biggestWidth, heightPerWidget); | |||
| else | |||
| s.widget->setWidth(biggestWidth); | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /* TODO | |||
| void HorizontallyStackedVerticalLayout::adjustSize(const uint padding) | |||
| { | |||
| } | |||
| */ | |||
| void HorizontallyStackedVerticalLayout::setAbsolutePos(int x, const int y, const uint padding) | |||
| { | |||
| for (VerticalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) | |||
| { | |||
| VerticalLayout* l(*it); | |||
| x += static_cast<int>(l->setAbsolutePos(x, y, padding)); | |||
| x += static_cast<int>(padding); | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| Size<uint> VerticallyStackedHorizontalLayout::adjustSize(const uint padding) | |||
| { | |||
| uint biggestWidth = 0; | |||
| uint totalHeight = 0; | |||
| // iterate all widgets to find which one is the biggest (horizontally) | |||
| for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) | |||
| { | |||
| HorizontalLayout* const l(*it); | |||
| uint width = 0; | |||
| uint height = 0; | |||
| for (SubWidgetWithSizeHintIterator it2=l->widgets.begin(), end2=l->widgets.end(); it2 != end2; ++it2) | |||
| { | |||
| SubWidgetWithSizeHint& s(*it2); | |||
| if (width != 0) | |||
| width += padding; | |||
| width += s.widget->getWidth(); | |||
| height = std::max(height, s.widget->getHeight()); | |||
| } | |||
| biggestWidth = std::max(biggestWidth, width); | |||
| if (totalHeight != 0) | |||
| totalHeight += padding; | |||
| totalHeight += height; | |||
| } | |||
| // now make all horizontal lines the same width | |||
| for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) | |||
| { | |||
| HorizontalLayout* const l(*it); | |||
| l->setSize(biggestWidth, padding); | |||
| } | |||
| return Size<uint>(biggestWidth, totalHeight); | |||
| } | |||
| void VerticallyStackedHorizontalLayout::setAbsolutePos(const int x, int y, const uint padding) | |||
| { | |||
| for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) | |||
| { | |||
| HorizontalLayout* l(*it); | |||
| y += static_cast<int>(l->setAbsolutePos(x, y, padding)); | |||
| y += static_cast<int>(padding); | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -1,193 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 "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("stub 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& context, 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>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void SubWidget::PrivateData::display(uint, uint, double) | |||
| { | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void TopLevelWidget::PrivateData::display() | |||
| { | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) | |||
| { | |||
| notImplemented("Window::PrivateData::renderToPicture"); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| GraphicsContext& context((GraphicsContext&)graphicsContext); | |||
| return context; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -1,250 +0,0 @@ | |||
| /* | |||
| * 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); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) | |||
| { | |||
| notImplemented("Window::PrivateData::renderToPicture"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| return (const GraphicsContext&)graphicsContext; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1,23 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| // Include CHOC separately because it requires C++17 | |||
| #define DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION | |||
| #define WEB_VIEW_NAMESPACE DGL_NAMESPACE | |||
| #define WEB_VIEW_DGL_NAMESPACE | |||
| #include "../WebView.hpp" | |||
| #include "../../distrho/extra/WebViewWin32.hpp" | |||
| @@ -1,14 +0,0 @@ | |||
| # Copyright 2022 David Robillard <d@drobilla.net> | |||
| # SPDX-License-Identifier: 0BSD OR ISC | |||
| [wrap-file] | |||
| directory = sphinxygen-1.0.0 | |||
| source_url = https://download.drobilla.net/sphinxygen-1.0.0.tar.gz | |||
| source_filename = sphinxygen-1.0.0.tar.gz | |||
| source_hash = 96b19e3b37d4886dcf3e89d4ccf0b66c0deb9f2e34ac151a7a6659a421f0282d | |||
| # [wrap-git] | |||
| # url = https://gitlab.com/drobilla/sphinxygen.git | |||
| # push-url = ssh://git@gitlab.com:drobilla/sphinxygen.git | |||
| # revision = main | |||
| # depth = 1 | |||
| @@ -23,7 +23,11 @@ | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| #if defined(DISTRHO_PLUGIN_TARGET_AU) | |||
| #if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT) | |||
| # if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| # warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0 | |||
| # endif | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_AU) | |||
| # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
| # import "src/DistrhoUIAU.mm" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -23,6 +23,13 @@ | |||
| # define __STDC_LIMIT_MACROS | |||
| #endif | |||
| #ifdef __WINE__ | |||
| # ifndef NOMINMAX | |||
| # define NOMINMAX | |||
| # endif | |||
| # include <winsock2.h> | |||
| #endif | |||
| #include <cstdarg> | |||
| #include <cstdio> | |||
| #include <cstdlib> | |||
| @@ -1,149 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 DISTRHO_BASE64_HPP_INCLUDED | |||
| #define DISTRHO_BASE64_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| #include <vector> | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
| /* | |||
| Copyright (C) 2004-2008 René Nyffenegger | |||
| This source code is provided 'as-is', without any express or implied | |||
| warranty. In no event will the author be held liable for any damages | |||
| arising from the use of this software. | |||
| Permission is granted to anyone to use this software for any purpose, | |||
| including commercial applications, and to alter it and redistribute it | |||
| freely, subject to the following restrictions: | |||
| 1. The origin of this source code must not be misrepresented; you must not | |||
| claim that you wrote the original source code. If you use this source code | |||
| in a product, an acknowledgment in the product documentation would be | |||
| appreciated but is not required. | |||
| 2. Altered source versions must be plainly marked as such, and must not be | |||
| misrepresented as being the original source code. | |||
| 3. This notice may not be removed or altered from any source distribution. | |||
| René Nyffenegger rene.nyffenegger@adp-gmbh.ch | |||
| */ | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Helpers | |||
| #ifndef DOXYGEN | |||
| namespace DistrhoBase64Helpers { | |||
| static constexpr const char* const kBase64Chars = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| "abcdefghijklmnopqrstuvwxyz" | |||
| "0123456789+/"; | |||
| static inline | |||
| uint8_t findBase64CharIndex(const char c) | |||
| { | |||
| static const uint8_t kBase64CharsLen = static_cast<uint8_t>(std::strlen(kBase64Chars)); | |||
| for (uint8_t i=0; i<kBase64CharsLen; ++i) | |||
| { | |||
| if (kBase64Chars[i] == c) | |||
| return i; | |||
| } | |||
| d_stderr2("findBase64CharIndex('%c') - failed", c); | |||
| return 0; | |||
| } | |||
| static constexpr inline | |||
| bool isBase64Char(const char c) | |||
| { | |||
| return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '+' || c == '/'; | |||
| } | |||
| } // namespace DistrhoBase64Helpers | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static inline | |||
| std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(base64string != nullptr, std::vector<uint8_t>()); | |||
| uint i=0, j=0; | |||
| uint charArray3[3], charArray4[4]; | |||
| std::vector<uint8_t> ret; | |||
| ret.reserve(std::strlen(base64string)*3/4 + 4); | |||
| for (std::size_t l=0, len=std::strlen(base64string); l<len; ++l) | |||
| { | |||
| const char c = base64string[l]; | |||
| if (c == '\0' || c == '=') | |||
| break; | |||
| if (c == ' ' || c == '\n') | |||
| continue; | |||
| DISTRHO_SAFE_ASSERT_CONTINUE(DistrhoBase64Helpers::isBase64Char(c)); | |||
| charArray4[i++] = static_cast<uint>(c); | |||
| if (i == 4) | |||
| { | |||
| for (i=0; i<4; ++i) | |||
| charArray4[i] = DistrhoBase64Helpers::findBase64CharIndex(static_cast<char>(charArray4[i])); | |||
| charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
| charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
| charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
| for (i=0; i<3; ++i) | |||
| ret.push_back(static_cast<uint8_t>(charArray3[i])); | |||
| i = 0; | |||
| } | |||
| } | |||
| if (i != 0) | |||
| { | |||
| for (j=0; j<i && j<4; ++j) | |||
| charArray4[j] = DistrhoBase64Helpers::findBase64CharIndex(static_cast<char>(charArray4[j])); | |||
| for (j=i; j<4; ++j) | |||
| charArray4[j] = 0; | |||
| charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
| charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
| charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
| for (j=0; i>0 && j<i-1; j++) | |||
| ret.push_back(static_cast<uint8_t>(charArray3[j])); | |||
| } | |||
| return ret; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #endif // DISTRHO_BASE64_HPP_INCLUDED | |||
| @@ -1,294 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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. | |||
| */ | |||
| #pragma once | |||
| #include "Sleep.hpp" | |||
| #include "Time.hpp" | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| # include <string> | |||
| # include <winsock2.h> | |||
| # include <windows.h> | |||
| #else | |||
| # include <cerrno> | |||
| # include <ctime> | |||
| # include <signal.h> | |||
| # include <sys/wait.h> | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| class ChildProcess | |||
| { | |||
| #ifdef _WIN32 | |||
| PROCESS_INFORMATION pinfo; | |||
| #else | |||
| pid_t pid; | |||
| #endif | |||
| public: | |||
| ChildProcess() | |||
| #ifdef _WIN32 | |||
| : pinfo(CPP_AGGREGATE_INIT(PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }) | |||
| #else | |||
| : pid(-1) | |||
| #endif | |||
| { | |||
| } | |||
| ~ChildProcess() | |||
| { | |||
| stop(); | |||
| } | |||
| #ifdef _WIN32 | |||
| bool start(const char* const args[], const WCHAR* const envp) | |||
| #else | |||
| bool start(const char* const args[], char* const* const envp = nullptr) | |||
| #endif | |||
| { | |||
| #ifdef _WIN32 | |||
| std::string cmd; | |||
| for (uint i = 0; args[i] != nullptr; ++i) | |||
| { | |||
| if (i != 0) | |||
| cmd += " "; | |||
| if (args[i][0] != '"' && std::strchr(args[i], ' ') != nullptr) | |||
| { | |||
| cmd += "\""; | |||
| cmd += args[i]; | |||
| cmd += "\""; | |||
| } | |||
| else | |||
| { | |||
| cmd += args[i]; | |||
| } | |||
| } | |||
| wchar_t wcmd[PATH_MAX]; | |||
| if (MultiByteToWideChar(CP_UTF8, 0, cmd.data(), -1, wcmd, PATH_MAX) <= 0) | |||
| return false; | |||
| STARTUPINFOW si = {}; | |||
| si.cb = sizeof(si); | |||
| d_stdout("will start process with args '%s'", cmd.data()); | |||
| return CreateProcessW(nullptr, // lpApplicationName | |||
| wcmd, // lpCommandLine | |||
| nullptr, // lpProcessAttributes | |||
| nullptr, // lpThreadAttributes | |||
| TRUE, // bInheritHandles | |||
| CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags | |||
| const_cast<LPWSTR>(envp), // lpEnvironment | |||
| nullptr, // lpCurrentDirectory | |||
| &si, // lpStartupInfo | |||
| &pinfo) != FALSE; | |||
| #else | |||
| const pid_t ret = pid = vfork(); | |||
| switch (ret) | |||
| { | |||
| // child process | |||
| case 0: | |||
| if (envp != nullptr) | |||
| execve(args[0], const_cast<char* const*>(args), envp); | |||
| else | |||
| execvp(args[0], const_cast<char* const*>(args)); | |||
| d_stderr2("exec failed: %d:%s", errno, std::strerror(errno)); | |||
| _exit(1); | |||
| break; | |||
| // error | |||
| case -1: | |||
| d_stderr2("vfork() failed: %d:%s", errno, std::strerror(errno)); | |||
| break; | |||
| } | |||
| return ret > 0; | |||
| #endif | |||
| } | |||
| void stop(const uint32_t timeoutInMilliseconds = 2000) | |||
| { | |||
| const uint32_t timeout = d_gettime_ms() + timeoutInMilliseconds; | |||
| bool sendTerminate = true; | |||
| #ifdef _WIN32 | |||
| if (pinfo.hProcess == INVALID_HANDLE_VALUE) | |||
| return; | |||
| const PROCESS_INFORMATION opinfo = pinfo; | |||
| pinfo = (PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; | |||
| for (DWORD exitCode;;) | |||
| { | |||
| if (GetExitCodeProcess(opinfo.hProcess, &exitCode) == FALSE || | |||
| exitCode != STILL_ACTIVE || | |||
| WaitForSingleObject(opinfo.hProcess, 0) != WAIT_TIMEOUT) | |||
| { | |||
| CloseHandle(opinfo.hThread); | |||
| CloseHandle(opinfo.hProcess); | |||
| return; | |||
| } | |||
| if (sendTerminate) | |||
| { | |||
| sendTerminate = false; | |||
| TerminateProcess(opinfo.hProcess, ERROR_BROKEN_PIPE); | |||
| } | |||
| if (d_gettime_ms() < timeout) | |||
| { | |||
| d_msleep(5); | |||
| continue; | |||
| } | |||
| d_stderr("ChildProcess::stop() - timed out"); | |||
| TerminateProcess(opinfo.hProcess, 9); | |||
| d_msleep(5); | |||
| CloseHandle(opinfo.hThread); | |||
| CloseHandle(opinfo.hProcess); | |||
| break; | |||
| } | |||
| #else | |||
| if (pid <= 0) | |||
| return; | |||
| const pid_t opid = pid; | |||
| pid = -1; | |||
| for (pid_t ret;;) | |||
| { | |||
| try { | |||
| ret = ::waitpid(opid, nullptr, WNOHANG); | |||
| } DISTRHO_SAFE_EXCEPTION_BREAK("waitpid"); | |||
| switch (ret) | |||
| { | |||
| case -1: | |||
| if (errno == ECHILD) | |||
| { | |||
| // success, child doesn't exist | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| d_stderr("ChildProcess::stop() - waitpid failed: %d:%s", errno, std::strerror(errno)); | |||
| return; | |||
| } | |||
| break; | |||
| case 0: | |||
| if (sendTerminate) | |||
| { | |||
| sendTerminate = false; | |||
| kill(opid, SIGTERM); | |||
| } | |||
| if (d_gettime_ms() < timeout) | |||
| { | |||
| d_msleep(5); | |||
| continue; | |||
| } | |||
| d_stderr("ChildProcess::stop() - timed out"); | |||
| kill(opid, SIGKILL); | |||
| waitpid(opid, nullptr, WNOHANG); | |||
| break; | |||
| default: | |||
| if (ret == opid) | |||
| { | |||
| // success | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| d_stderr("ChildProcess::stop() - got wrong pid %i (requested was %i)", int(ret), int(opid)); | |||
| return; | |||
| } | |||
| } | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| bool isRunning() | |||
| { | |||
| #ifdef _WIN32 | |||
| if (pinfo.hProcess == INVALID_HANDLE_VALUE) | |||
| return false; | |||
| DWORD exitCode; | |||
| if (GetExitCodeProcess(pinfo.hProcess, &exitCode) == FALSE || | |||
| exitCode != STILL_ACTIVE || | |||
| WaitForSingleObject(pinfo.hProcess, 0) != WAIT_TIMEOUT) | |||
| { | |||
| const PROCESS_INFORMATION opinfo = pinfo; | |||
| pinfo = (PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; | |||
| CloseHandle(opinfo.hThread); | |||
| CloseHandle(opinfo.hProcess); | |||
| return false; | |||
| } | |||
| return true; | |||
| #else | |||
| if (pid <= 0) | |||
| return false; | |||
| const pid_t ret = ::waitpid(pid, nullptr, WNOHANG); | |||
| if (ret == pid || (ret == -1 && errno == ECHILD)) | |||
| { | |||
| pid = 0; | |||
| return false; | |||
| } | |||
| return true; | |||
| #endif | |||
| } | |||
| #ifndef _WIN32 | |||
| void signal(const int sig) | |||
| { | |||
| if (pid > 0) | |||
| kill(pid, sig); | |||
| } | |||
| #endif | |||
| void terminate() | |||
| { | |||
| #ifdef _WIN32 | |||
| if (pinfo.hProcess != INVALID_HANDLE_VALUE) | |||
| TerminateProcess(pinfo.hProcess, 15); | |||
| #else | |||
| if (pid > 0) | |||
| kill(pid, SIGTERM); | |||
| #endif | |||
| } | |||
| DISTRHO_DECLARE_NON_COPYABLE(ChildProcess) | |||
| }; | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -1,578 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED | |||
| #define DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED | |||
| #include "String.hpp" | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| # include <cerrno> | |||
| # include <signal.h> | |||
| # include <sys/wait.h> | |||
| # include <unistd.h> | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // ExternalWindow class | |||
| /** | |||
| External Window class. | |||
| This is a standalone TopLevelWidget/Window-compatible class, but without any real event handling. | |||
| Being compatible with TopLevelWidget/Window, it allows to be used as DPF UI target. | |||
| It can be used to embed non-DPF things or to run a tool in a new process as the "UI". | |||
| The uiIdle() function will be called at regular intervals to keep UI running. | |||
| There are helper methods in place to launch external tools and keep track of its running state. | |||
| External windows can be setup to run in 3 different modes: | |||
| * Embed: | |||
| Embed into the host UI, even-loop driven by the host. | |||
| This is basically working as a regular plugin UI, as you typically expect them to. | |||
| The plugin side does not get control over showing, hiding or closing the window (as usual for plugins). | |||
| No restrictions on supported plugin format, everything should work. | |||
| Requires DISTRHO_PLUGIN_HAS_EMBED_UI to be set to 1. | |||
| * Semi-external: | |||
| The UI is not embed into the host, but the even-loop is still driven by it. | |||
| In this mode the host does not have control over the UI except for showing, hiding and setting transient parent. | |||
| It is possible to close the window from the plugin, the host will be notified of such case. | |||
| Host regularly calls isQuitting() to check if the UI got closed by the user or plugin side. | |||
| This mode is only possible in LV2 plugin formats, using lv2ui:showInterface extension. | |||
| * Standalone: | |||
| The UI is not embed into the host or uses its event-loop, basically running as standalone. | |||
| The host only has control over showing and hiding the window, nothing else. | |||
| The UI is still free to close itself at any point. | |||
| DPF will keep calling isRunning() to check if it should keep the event-loop running. | |||
| Only possible in JACK and DSSI targets, as the UIs are literally standalone applications there. | |||
| Please note that for non-embed windows, you cannot show the window yourself. | |||
| The plugin window is only allowed to hide or close itself, a "show" action needs to come from the host. | |||
| A few callbacks are provided so that implementations do not need to care about checking for state changes. | |||
| They are not called on construction, but will be everytime something changes either by the host or the window itself. | |||
| */ | |||
| class ExternalWindow | |||
| { | |||
| struct PrivateData; | |||
| public: | |||
| /** | |||
| Constructor. | |||
| */ | |||
| explicit ExternalWindow() | |||
| : pData() {} | |||
| /** | |||
| Constructor for DPF internal use. | |||
| */ | |||
| explicit ExternalWindow(const PrivateData& data) | |||
| : pData(data) {} | |||
| /** | |||
| Destructor. | |||
| */ | |||
| virtual ~ExternalWindow() | |||
| { | |||
| DISTRHO_SAFE_ASSERT(!pData.visible); | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * ExternalWindow specific calls - Host side calls that you can reimplement for fine-grained funtionality */ | |||
| /** | |||
| Check if main-loop is running. | |||
| This is used under standalone mode to check whether to keep things running. | |||
| Returning false from this function will stop the event-loop and close the window. | |||
| */ | |||
| virtual bool isRunning() const | |||
| { | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| if (ext.inUse) | |||
| return ext.isRunning(); | |||
| #endif | |||
| return isVisible(); | |||
| } | |||
| /** | |||
| Check if we are about to close. | |||
| This is used when the event-loop is provided by the host to check if it should close the window. | |||
| It is also used in standalone mode right after isRunning() returns false to verify if window needs to be closed. | |||
| */ | |||
| virtual bool isQuitting() const | |||
| { | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| return ext.inUse ? ext.isQuitting : pData.isQuitting; | |||
| #else | |||
| return pData.isQuitting; | |||
| #endif | |||
| } | |||
| /** | |||
| Get the "native" window handle. | |||
| This can be reimplemented in order to pass the native window to hosts that can use such informaton. | |||
| Returned value type 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`. | |||
| @note Only available to override if DISTRHO_PLUGIN_HAS_EMBED_UI is set to 1. | |||
| */ | |||
| virtual uintptr_t getNativeWindowHandle() const noexcept | |||
| { | |||
| return 0; | |||
| } | |||
| /** | |||
| Grab the keyboard input focus. | |||
| Typically you would setup OS-native methods to bring the window to front and give it focus. | |||
| Default implementation does nothing. | |||
| */ | |||
| virtual void focus() {} | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * TopLevelWidget-like calls - Information, can be called by either host or plugin */ | |||
| #if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| /** | |||
| Whether this Window is embed into another (usually not DGL-controlled) Window. | |||
| */ | |||
| bool isEmbed() const noexcept | |||
| { | |||
| return pData.parentWindowHandle != 0; | |||
| } | |||
| #endif | |||
| /** | |||
| Check if this window is visible. | |||
| @see setVisible(bool) | |||
| */ | |||
| bool isVisible() const noexcept | |||
| { | |||
| return pData.visible; | |||
| } | |||
| /** | |||
| Whether this Window is running as standalone, that is, without being coupled to a host event-loop. | |||
| When in standalone mode, isRunning() is called to check if the event-loop should keep running. | |||
| */ | |||
| bool isStandalone() const noexcept | |||
| { | |||
| return pData.isStandalone; | |||
| } | |||
| /** | |||
| Get width of this window. | |||
| Only relevant to hosts when the UI is embedded. | |||
| */ | |||
| uint getWidth() const noexcept | |||
| { | |||
| return pData.width; | |||
| } | |||
| /** | |||
| Get height of this window. | |||
| Only relevant to hosts when the UI is embedded. | |||
| */ | |||
| uint getHeight() const noexcept | |||
| { | |||
| return pData.height; | |||
| } | |||
| /** | |||
| Get the scale factor requested for this window. | |||
| This is purely informational, and up to developers to choose what to do with it. | |||
| */ | |||
| double getScaleFactor() const noexcept | |||
| { | |||
| return pData.scaleFactor; | |||
| } | |||
| /** | |||
| Get the title of the window previously set with setTitle(). | |||
| This is typically displayed in the title bar or in window switchers. | |||
| */ | |||
| const char* getTitle() const noexcept | |||
| { | |||
| return pData.title; | |||
| } | |||
| #if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
| /** | |||
| Get the "native" window handle that this window should embed itself into. | |||
| Returned value type 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 getParentWindowHandle() const noexcept | |||
| { | |||
| return pData.parentWindowHandle; | |||
| } | |||
| #endif | |||
| /** | |||
| Get the transient window that we should attach ourselves to. | |||
| TODO what id? also NSView* on macOS, or NSWindow? | |||
| */ | |||
| uintptr_t getTransientWindowId() const noexcept | |||
| { | |||
| return pData.transientWinId; | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * TopLevelWidget-like calls - actions called by either host or plugin */ | |||
| /** | |||
| Hide window. | |||
| This is the same as calling setVisible(false). | |||
| Embed windows should never call this! | |||
| @see isVisible(), setVisible(bool) | |||
| */ | |||
| void hide() | |||
| { | |||
| setVisible(false); | |||
| } | |||
| /** | |||
| Hide the UI and gracefully terminate. | |||
| Embed windows should never call this! | |||
| */ | |||
| virtual void close() | |||
| { | |||
| pData.isQuitting = true; | |||
| hide(); | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| if (ext.inUse) | |||
| terminateAndWaitForExternalProcess(); | |||
| #endif | |||
| } | |||
| /** | |||
| Set width of this window. | |||
| Can trigger a sizeChanged callback. | |||
| Only relevant to hosts when the UI is embedded. | |||
| */ | |||
| void setWidth(uint width) | |||
| { | |||
| setSize(width, getHeight()); | |||
| } | |||
| /** | |||
| Set height of this window. | |||
| Can trigger a sizeChanged callback. | |||
| Only relevant to hosts when the UI is embedded. | |||
| */ | |||
| void setHeight(uint height) | |||
| { | |||
| setSize(getWidth(), height); | |||
| } | |||
| /** | |||
| Set size of this window using @a width and @a height values. | |||
| Can trigger a sizeChanged callback. | |||
| Only relevant to hosts when the UI is embedded. | |||
| */ | |||
| void setSize(uint width, uint height) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_UINT_RETURN(width > 1, width,); | |||
| DISTRHO_SAFE_ASSERT_UINT_RETURN(height > 1, height,); | |||
| if (pData.width == width && pData.height == height) | |||
| return; | |||
| pData.width = width; | |||
| pData.height = height; | |||
| sizeChanged(width, height); | |||
| } | |||
| /** | |||
| Set the title of the window, typically displayed in the title bar or in window switchers. | |||
| Can trigger a titleChanged callback. | |||
| Only relevant to hosts when the UI is not embedded. | |||
| */ | |||
| void setTitle(const char* title) | |||
| { | |||
| if (pData.title == title) | |||
| return; | |||
| pData.title = title; | |||
| titleChanged(title); | |||
| } | |||
| /** | |||
| Set geometry constraints for the Window when resized by the user. | |||
| */ | |||
| void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumWidth > 0, minimumWidth,); | |||
| DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumHeight > 0, minimumHeight,); | |||
| pData.minWidth = minimumWidth; | |||
| pData.minHeight = minimumHeight; | |||
| pData.keepAspectRatio = keepAspectRatio; | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * TopLevelWidget-like calls - actions called by the host */ | |||
| /** | |||
| Show window. | |||
| This is the same as calling setVisible(true). | |||
| @see isVisible(), setVisible(bool) | |||
| */ | |||
| void show() | |||
| { | |||
| setVisible(true); | |||
| } | |||
| /** | |||
| Set window visible (or not) according to @a visible. | |||
| @see isVisible(), hide(), show() | |||
| */ | |||
| void setVisible(bool visible) | |||
| { | |||
| if (pData.visible == visible) | |||
| return; | |||
| pData.visible = visible; | |||
| visibilityChanged(visible); | |||
| } | |||
| /** | |||
| Called by the host to set the transient parent window that we should attach ourselves to. | |||
| TODO what id? also NSView* on macOS, or NSWindow? | |||
| */ | |||
| void setTransientWindowId(uintptr_t winId) | |||
| { | |||
| if (pData.transientWinId == winId) | |||
| return; | |||
| pData.transientWinId = winId; | |||
| transientParentWindowChanged(winId); | |||
| } | |||
| protected: | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * ExternalWindow special calls for running externals tools */ | |||
| bool startExternalProcess(const char* args[]) | |||
| { | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| ext.inUse = true; | |||
| return ext.start(args); | |||
| #else | |||
| (void)args; | |||
| return false; // TODO | |||
| #endif | |||
| } | |||
| void terminateAndWaitForExternalProcess() | |||
| { | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| ext.isQuitting = true; | |||
| ext.terminateAndWait(); | |||
| #else | |||
| // TODO | |||
| #endif | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * ExternalWindow specific callbacks */ | |||
| /** | |||
| A callback for when the window size changes. | |||
| @note WIP this might need to get fed back into the host somehow. | |||
| */ | |||
| virtual void sizeChanged(uint /* width */, uint /* height */) | |||
| { | |||
| // unused, meant for custom implementations | |||
| } | |||
| /** | |||
| A callback for when the window title changes. | |||
| @note WIP this might need to get fed back into the host somehow. | |||
| */ | |||
| virtual void titleChanged(const char* /* title */) | |||
| { | |||
| // unused, meant for custom implementations | |||
| } | |||
| /** | |||
| A callback for when the window visibility changes. | |||
| @note WIP this might need to get fed back into the host somehow. | |||
| */ | |||
| virtual void visibilityChanged(bool /* visible */) | |||
| { | |||
| // unused, meant for custom implementations | |||
| } | |||
| /** | |||
| A callback for when the transient parent window changes. | |||
| */ | |||
| virtual void transientParentWindowChanged(uintptr_t /* winId */) | |||
| { | |||
| // unused, meant for custom implementations | |||
| } | |||
| private: | |||
| friend class PluginWindow; | |||
| friend class UI; | |||
| #ifndef DISTRHO_OS_WINDOWS | |||
| struct ExternalProcess { | |||
| bool inUse; | |||
| bool isQuitting; | |||
| mutable pid_t pid; | |||
| ExternalProcess() | |||
| : inUse(false), | |||
| isQuitting(false), | |||
| pid(0) {} | |||
| bool isRunning() const noexcept | |||
| { | |||
| if (pid <= 0) | |||
| return false; | |||
| const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||
| if (p == pid || (p == -1 && errno == ECHILD)) | |||
| { | |||
| d_stdout("NOTICE: Child process exited while idle"); | |||
| pid = 0; | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| bool start(const char* args[]) | |||
| { | |||
| terminateAndWait(); | |||
| pid = vfork(); | |||
| switch (pid) | |||
| { | |||
| case 0: | |||
| execvp(args[0], (char**)args); | |||
| _exit(1); | |||
| return false; | |||
| case -1: | |||
| d_stderr("Could not start external ui"); | |||
| return false; | |||
| default: | |||
| return true; | |||
| } | |||
| } | |||
| void terminateAndWait() | |||
| { | |||
| if (pid <= 0) | |||
| return; | |||
| d_stdout("Waiting for external process to stop,,,"); | |||
| bool sendTerm = true; | |||
| for (pid_t p;;) | |||
| { | |||
| p = ::waitpid(pid, nullptr, WNOHANG); | |||
| switch (p) | |||
| { | |||
| case 0: | |||
| if (sendTerm) | |||
| { | |||
| sendTerm = false; | |||
| ::kill(pid, SIGTERM); | |||
| } | |||
| break; | |||
| case -1: | |||
| if (errno == ECHILD) | |||
| { | |||
| d_stdout("Done! (no such process)"); | |||
| pid = 0; | |||
| return; | |||
| } | |||
| break; | |||
| default: | |||
| if (p == pid) | |||
| { | |||
| d_stdout("Done! (clean wait)"); | |||
| pid = 0; | |||
| return; | |||
| } | |||
| break; | |||
| } | |||
| // 5 msec | |||
| usleep(5*1000); | |||
| } | |||
| } | |||
| } ext; | |||
| #endif | |||
| struct PrivateData { | |||
| uintptr_t parentWindowHandle; | |||
| uintptr_t transientWinId; | |||
| uint width; | |||
| uint height; | |||
| double scaleFactor; | |||
| String title; | |||
| uint minWidth; | |||
| uint minHeight; | |||
| bool keepAspectRatio; | |||
| bool isQuitting; | |||
| bool isStandalone; | |||
| bool visible; | |||
| PrivateData() | |||
| : parentWindowHandle(0), | |||
| transientWinId(0), | |||
| width(1), | |||
| height(1), | |||
| scaleFactor(1.0), | |||
| title(), | |||
| minWidth(0), | |||
| minHeight(0), | |||
| keepAspectRatio(false), | |||
| isQuitting(false), | |||
| isStandalone(false), | |||
| visible(false) {} | |||
| } pData; | |||
| DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED | |||
| @@ -1,28 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * 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 DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| #define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| #include "FileBrowserDialogImpl.hpp" | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
| @@ -1,894 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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. | |||
| */ | |||
| #if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) | |||
| # error bad include | |||
| #endif | |||
| #if !defined(FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE) && !defined(FILE_BROWSER_DIALOG_DGL_NAMESPACE) | |||
| # error bad usage | |||
| #endif | |||
| #include "ScopedPointer.hpp" | |||
| #include "String.hpp" | |||
| #ifdef DISTRHO_OS_MAC | |||
| # import <Cocoa/Cocoa.h> | |||
| #endif | |||
| #ifdef DISTRHO_OS_WASM | |||
| # include <emscripten/emscripten.h> | |||
| # include <sys/stat.h> | |||
| #endif | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| # include <direct.h> | |||
| # include <process.h> | |||
| # include <winsock2.h> | |||
| # include <windows.h> | |||
| # include <commdlg.h> | |||
| # include <vector> | |||
| #else | |||
| # include <unistd.h> | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| # include <dbus/dbus.h> | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| # define DBLCLKTME 400 | |||
| # include "sofd/libsofd.h" | |||
| # if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||
| # pragma GCC diagnostic push | |||
| # pragma GCC diagnostic ignored "-Wcast-qual" | |||
| # pragma GCC diagnostic ignored "-Wconversion" | |||
| # pragma GCC diagnostic ignored "-Wfloat-conversion" | |||
| # pragma GCC diagnostic ignored "-Wshadow" | |||
| # pragma GCC diagnostic ignored "-Wsign-conversion" | |||
| # pragma GCC diagnostic ignored "-Wstrict-overflow" | |||
| # endif | |||
| # include "sofd/libsofd.c" | |||
| # if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||
| # pragma GCC diagnostic pop | |||
| # endif | |||
| # undef HAVE_MNTENT | |||
| # undef MAX | |||
| # undef MIN | |||
| #endif | |||
| #ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE | |||
| START_NAMESPACE_DGL | |||
| using DISTRHO_NAMESPACE::ScopedPointer; | |||
| using DISTRHO_NAMESPACE::String; | |||
| #else | |||
| START_NAMESPACE_DISTRHO | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // static pointer used for signal null/none action taken | |||
| static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; | |||
| #ifdef HAVE_DBUS | |||
| static constexpr bool isHexChar(const char c) noexcept | |||
| { | |||
| return c >= '0' && c <= 'f' && (c <= '9' || (c >= 'A' && c <= 'F') || c >= 'a'); | |||
| } | |||
| static constexpr int toHexChar(const char c) noexcept | |||
| { | |||
| return c >= '0' && c <= '9' ? c - '0' : (c >= 'A' && c <= 'F' ? c - 'A' : c - 'a') + 10; | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef DISTRHO_OS_WASM | |||
| # define DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION | |||
| # define DISTRHO_WASM_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) | |||
| # define DISTRHO_WASM_NAMESPACE_HELPER(NS) #NS | |||
| # define DISTRHO_WASM_NAMESPACE(NS) DISTRHO_WASM_NAMESPACE_HELPER(NS) | |||
| # define fileBrowserSetPathNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, fileBrowserSetPath) | |||
| # define fileBrowserSetPathFuncName DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE) "_fileBrowserSetPath" | |||
| // FIXME use world class name as prefix | |||
| static bool openWebBrowserFileDialog(const char* const funcname, void* const handle) | |||
| { | |||
| const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); | |||
| return EM_ASM_INT({ | |||
| var canvasFileObjName = UTF8ToString($0) + "_file_open"; | |||
| var canvasFileOpenElem = document.getElementById(canvasFileObjName); | |||
| var jsfuncname = UTF8ToString($1); | |||
| var jsfunc = Module.cwrap(jsfuncname, 'null', ['number', 'string']); | |||
| if (canvasFileOpenElem) { | |||
| document.body.removeChild(canvasFileOpenElem); | |||
| } | |||
| canvasFileOpenElem = document.createElement('input'); | |||
| canvasFileOpenElem.type = 'file'; | |||
| canvasFileOpenElem.id = canvasFileObjName; | |||
| canvasFileOpenElem.style.display = 'none'; | |||
| document.body.appendChild(canvasFileOpenElem); | |||
| canvasFileOpenElem.onchange = function(e) { | |||
| if (!canvasFileOpenElem.files) { | |||
| jsfunc($2, ""); | |||
| return; | |||
| } | |||
| // store uploaded files inside a specific dir | |||
| try { | |||
| Module.FS.mkdir('/userfiles'); | |||
| } catch (e) {} | |||
| var file = canvasFileOpenElem.files[0]; | |||
| var filename = '/userfiles/' + file.name; | |||
| var reader = new FileReader(); | |||
| reader.onloadend = function(e) { | |||
| var content = new Uint8Array(reader.result); | |||
| Module.FS.writeFile(filename, content); | |||
| jsfunc($2, filename); | |||
| }; | |||
| reader.readAsArrayBuffer(file); | |||
| }; | |||
| canvasFileOpenElem.click(); | |||
| return 1; | |||
| }, nameprefix, funcname, handle) != 0; | |||
| } | |||
| static bool downloadWebBrowserFile(const char* const filename) | |||
| { | |||
| const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); | |||
| return EM_ASM_INT({ | |||
| var canvasFileObjName = UTF8ToString($0) + "_file_save"; | |||
| var jsfilename = UTF8ToString($1); | |||
| var canvasFileSaveElem = document.getElementById(canvasFileObjName); | |||
| if (canvasFileSaveElem) { | |||
| // only 1 file save allowed at once | |||
| console.warn("One file save operation already in progress, refusing to open another"); | |||
| return 0; | |||
| } | |||
| canvasFileSaveElem = document.createElement('a'); | |||
| canvasFileSaveElem.download = jsfilename; | |||
| canvasFileSaveElem.id = canvasFileObjName; | |||
| canvasFileSaveElem.style.display = 'none'; | |||
| document.body.appendChild(canvasFileSaveElem); | |||
| var content = Module.FS.readFile('/userfiles/' + jsfilename); | |||
| canvasFileSaveElem.href = URL.createObjectURL(new Blob([content])); | |||
| canvasFileSaveElem.click(); | |||
| setTimeout(function() { | |||
| URL.revokeObjectURL(canvasFileSaveElem.href); | |||
| document.body.removeChild(canvasFileSaveElem); | |||
| }, 2000); | |||
| return 1; | |||
| }, nameprefix, filename) != 0; | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| struct FileBrowserData { | |||
| const char* selectedFile; | |||
| #ifdef DISTRHO_OS_MAC | |||
| NSSavePanel* nsBasePanel; | |||
| NSOpenPanel* nsOpenPanel; | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| DBusConnection* dbuscon; | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| Display* x11display; | |||
| #endif | |||
| #ifdef DISTRHO_OS_WASM | |||
| char* defaultName; | |||
| bool saving; | |||
| #endif | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| OPENFILENAMEW ofn; | |||
| volatile bool threadCancelled; | |||
| uintptr_t threadHandle; | |||
| std::vector<WCHAR> fileNameW; | |||
| std::vector<WCHAR> startDirW; | |||
| std::vector<WCHAR> titleW; | |||
| const bool saving; | |||
| bool isEmbed; | |||
| FileBrowserData(const bool save) | |||
| : selectedFile(nullptr), | |||
| threadCancelled(false), | |||
| threadHandle(0), | |||
| fileNameW(32768), | |||
| saving(save), | |||
| isEmbed(false) | |||
| { | |||
| std::memset(&ofn, 0, sizeof(ofn)); | |||
| ofn.lStructSize = sizeof(ofn); | |||
| ofn.lpstrFile = fileNameW.data(); | |||
| ofn.nMaxFile = (DWORD)fileNameW.size(); | |||
| } | |||
| ~FileBrowserData() | |||
| { | |||
| if (cancelAndStop()) | |||
| free(); | |||
| } | |||
| void setupAndStart(const bool embed, | |||
| const char* const startDir, | |||
| const char* const windowTitle, | |||
| const uintptr_t winId, | |||
| const FileBrowserOptions options) | |||
| { | |||
| isEmbed = embed; | |||
| ofn.hwndOwner = (HWND)winId; | |||
| ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; | |||
| if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) | |||
| ofn.Flags |= OFN_FORCESHOWHIDDEN; | |||
| ofn.FlagsEx = 0x0; | |||
| if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) | |||
| ofn.FlagsEx |= OFN_EX_NOPLACESBAR; | |||
| startDirW.resize(std::strlen(startDir) + 1); | |||
| if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast<int>(startDirW.size()))) | |||
| ofn.lpstrInitialDir = startDirW.data(); | |||
| titleW.resize(std::strlen(windowTitle) + 1); | |||
| if (MultiByteToWideChar(CP_UTF8, 0, windowTitle, -1, titleW.data(), static_cast<int>(titleW.size()))) | |||
| ofn.lpstrTitle = titleW.data(); | |||
| uint threadId; | |||
| threadCancelled = false; | |||
| threadHandle = _beginthreadex(nullptr, 0, _run, this, 0, &threadId); | |||
| } | |||
| bool cancelAndStop() | |||
| { | |||
| threadCancelled = true; | |||
| if (threadHandle == 0) | |||
| return true; | |||
| // if previous dialog running, carefully close its window | |||
| const HWND owner = isEmbed ? GetParent(ofn.hwndOwner) : ofn.hwndOwner; | |||
| if (owner != nullptr && owner != INVALID_HANDLE_VALUE) | |||
| { | |||
| const HWND window = GetWindow(owner, GW_HWNDFIRST); | |||
| if (window != nullptr && window != INVALID_HANDLE_VALUE) | |||
| { | |||
| SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); | |||
| SendMessage(window, WM_CLOSE, 0, 0); | |||
| WaitForSingleObject((HANDLE)threadHandle, 5000); | |||
| } | |||
| } | |||
| if (threadHandle == 0) | |||
| return true; | |||
| // not good if thread still running, but let's close the handle anyway | |||
| CloseHandle((HANDLE)threadHandle); | |||
| threadHandle = 0; | |||
| return false; | |||
| } | |||
| void run() | |||
| { | |||
| const char* nextFile = nullptr; | |||
| if (saving ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) | |||
| { | |||
| if (threadCancelled) | |||
| { | |||
| threadHandle = 0; | |||
| return; | |||
| } | |||
| // back to UTF-8 | |||
| std::vector<char> fileNameA(4 * 32768); | |||
| if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, | |||
| fileNameA.data(), (int)fileNameA.size(), | |||
| nullptr, nullptr)) | |||
| { | |||
| nextFile = strdup(fileNameA.data()); | |||
| } | |||
| } | |||
| if (threadCancelled) | |||
| { | |||
| threadHandle = 0; | |||
| return; | |||
| } | |||
| if (nextFile == nullptr) | |||
| nextFile = kSelectedFileCancelled; | |||
| selectedFile = nextFile; | |||
| threadHandle = 0; | |||
| } | |||
| static unsigned __stdcall _run(void* const arg) | |||
| { | |||
| // CoInitializeEx(nullptr, COINIT_MULTITHREADED); | |||
| static_cast<FileBrowserData*>(arg)->run(); | |||
| // CoUninitialize(); | |||
| _endthreadex(0); | |||
| return 0; | |||
| } | |||
| #else // DISTRHO_OS_WINDOWS | |||
| FileBrowserData(const bool save) | |||
| : selectedFile(nullptr) | |||
| #ifdef DISTRHO_OS_MAC | |||
| , nsBasePanel(nullptr) | |||
| , nsOpenPanel(nullptr) | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| , dbuscon(nullptr) | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| , x11display(nullptr) | |||
| #endif | |||
| { | |||
| #ifdef DISTRHO_OS_MAC | |||
| if (save) | |||
| { | |||
| nsOpenPanel = nullptr; | |||
| nsBasePanel = [[NSSavePanel savePanel]retain]; | |||
| } | |||
| else | |||
| { | |||
| nsOpenPanel = [[NSOpenPanel openPanel]retain]; | |||
| nsBasePanel = nsOpenPanel; | |||
| } | |||
| #endif | |||
| #ifdef DISTRHO_OS_WASM | |||
| defaultName = nullptr; | |||
| saving = save; | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| if ((dbuscon = dbus_bus_get(DBUS_BUS_SESSION, nullptr)) != nullptr) | |||
| dbus_connection_set_exit_on_disconnect(dbuscon, false); | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| x11display = XOpenDisplay(nullptr); | |||
| #endif | |||
| // maybe unused | |||
| return; (void)save; | |||
| } | |||
| ~FileBrowserData() | |||
| { | |||
| #ifdef DISTRHO_OS_MAC | |||
| [nsBasePanel release]; | |||
| #endif | |||
| #ifdef DISTRHO_OS_WASM | |||
| std::free(defaultName); | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| if (dbuscon != nullptr) | |||
| dbus_connection_unref(dbuscon); | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| if (x11display != nullptr) | |||
| XCloseDisplay(x11display); | |||
| #endif | |||
| free(); | |||
| } | |||
| #endif | |||
| void free() | |||
| { | |||
| if (selectedFile == nullptr) | |||
| return; | |||
| if (selectedFile == kSelectedFileCancelled || std::strcmp(selectedFile, kSelectedFileCancelled) == 0) | |||
| { | |||
| selectedFile = nullptr; | |||
| return; | |||
| } | |||
| std::free(const_cast<char*>(selectedFile)); | |||
| selectedFile = nullptr; | |||
| } | |||
| DISTRHO_DECLARE_NON_COPYABLE(FileBrowserData) | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef DISTRHO_OS_WASM | |||
| extern "C" { | |||
| EMSCRIPTEN_KEEPALIVE | |||
| void fileBrowserSetPathNamespaced(FileBrowserHandle handle, const char* filename) | |||
| { | |||
| handle->free(); | |||
| if (filename != nullptr && filename[0] != '\0') | |||
| handle->selectedFile = strdup(filename); | |||
| else | |||
| handle->selectedFile = kSelectedFileCancelled; | |||
| } | |||
| } | |||
| #endif | |||
| FileBrowserHandle fileBrowserCreate(const bool isEmbed, | |||
| const uintptr_t windowId, | |||
| const double scaleFactor, | |||
| const FileBrowserOptions& options) | |||
| { | |||
| String startDir(options.startDir); | |||
| if (startDir.isEmpty()) | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| if (char* const cwd = _getcwd(nullptr, 0)) | |||
| #else | |||
| if (char* const cwd = getcwd(nullptr, 0)) | |||
| #endif | |||
| { | |||
| startDir = cwd; | |||
| std::free(cwd); | |||
| } | |||
| } | |||
| DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), nullptr); | |||
| if (! startDir.endsWith(DISTRHO_OS_SEP)) | |||
| startDir += DISTRHO_OS_SEP_STR; | |||
| String windowTitle(options.title); | |||
| if (windowTitle.isEmpty()) | |||
| windowTitle = "FileBrowser"; | |||
| ScopedPointer<FileBrowserData> handle(new FileBrowserData(options.saving)); | |||
| #ifdef DISTRHO_OS_MAC | |||
| # if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 | |||
| // unsupported | |||
| d_stderr2("fileBrowserCreate is unsupported on macos < 10.8"); | |||
| return nullptr; | |||
| # else | |||
| NSSavePanel* const nsBasePanel = handle->nsBasePanel; | |||
| DISTRHO_SAFE_ASSERT_RETURN(nsBasePanel != nullptr, nullptr); | |||
| if (! options.saving) | |||
| { | |||
| NSOpenPanel* const nsOpenPanel = handle->nsOpenPanel; | |||
| DISTRHO_SAFE_ASSERT_RETURN(nsOpenPanel != nullptr, nullptr); | |||
| [nsOpenPanel setAllowsMultipleSelection:NO]; | |||
| [nsOpenPanel setCanChooseDirectories:NO]; | |||
| [nsOpenPanel setCanChooseFiles:YES]; | |||
| } | |||
| NSString* const startDirString = [[NSString alloc] | |||
| initWithBytes:startDir | |||
| length:strlen(startDir) | |||
| encoding:NSUTF8StringEncoding]; | |||
| [nsBasePanel setDirectoryURL:[NSURL fileURLWithPath:startDirString]]; | |||
| // TODO file filter using allowedContentTypes: [UTType] | |||
| if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) | |||
| [nsBasePanel setAllowsOtherFileTypes:YES]; | |||
| if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) | |||
| [nsBasePanel setShowsHiddenFiles:YES]; | |||
| NSString* const titleString = [[NSString alloc] | |||
| initWithBytes:windowTitle | |||
| length:strlen(windowTitle) | |||
| encoding:NSUTF8StringEncoding]; | |||
| [nsBasePanel setTitle:titleString]; | |||
| FileBrowserData* const handleptr = handle.get(); | |||
| dispatch_async(dispatch_get_main_queue(), ^ | |||
| { | |||
| [nsBasePanel beginSheetModalForWindow:[(NSView*)windowId window] | |||
| completionHandler:^(NSModalResponse result) | |||
| { | |||
| if (result == NSModalResponseOK && [[nsBasePanel URL] isFileURL]) | |||
| { | |||
| NSString* const path = [[nsBasePanel URL] path]; | |||
| handleptr->selectedFile = strdup([path UTF8String]); | |||
| } | |||
| else | |||
| { | |||
| handleptr->selectedFile = kSelectedFileCancelled; | |||
| } | |||
| }]; | |||
| }); | |||
| [startDirString release]; | |||
| [titleString release]; | |||
| # endif | |||
| #endif | |||
| #ifdef DISTRHO_OS_WASM | |||
| if (options.saving) | |||
| { | |||
| const size_t len = options.defaultName != nullptr ? strlen(options.defaultName) : 0; | |||
| DISTRHO_SAFE_ASSERT_RETURN(len != 0, nullptr); | |||
| // store uploaded files inside a specific dir | |||
| mkdir("/userfiles", 0777); | |||
| char* const filename = static_cast<char*>(malloc(len + 12)); | |||
| std::strncpy(filename, "/userfiles/", 12); | |||
| std::memcpy(filename + 11, options.defaultName, len + 1); | |||
| handle->defaultName = strdup(options.defaultName); | |||
| handle->selectedFile = filename; | |||
| return handle.release(); | |||
| } | |||
| const char* const funcname = fileBrowserSetPathFuncName; | |||
| if (openWebBrowserFileDialog(funcname, handle.get())) | |||
| return handle.release(); | |||
| return nullptr; | |||
| #endif | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options); | |||
| #endif | |||
| #ifdef HAVE_DBUS | |||
| // optional, can be null | |||
| DBusConnection* const dbuscon = handle->dbuscon; | |||
| // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser | |||
| if (dbuscon != nullptr) | |||
| { | |||
| // if this is the first time we are calling into DBus, check if things are working | |||
| static bool checkAvailable = !dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr); | |||
| if (checkAvailable) | |||
| { | |||
| checkAvailable = false; | |||
| if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", | |||
| "/org/freedesktop/portal/desktop", | |||
| "org.freedesktop.portal.FileChooser", | |||
| "version")) | |||
| { | |||
| if (DBusMessage* const reply = dbus_connection_send_with_reply_and_block(dbuscon, msg, 250, nullptr)) | |||
| dbus_message_unref(reply); | |||
| dbus_message_unref(msg); | |||
| } | |||
| } | |||
| // Any subsquent calls should have this DBus service active | |||
| if (dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) | |||
| { | |||
| if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", | |||
| "/org/freedesktop/portal/desktop", | |||
| "org.freedesktop.portal.FileChooser", | |||
| options.saving ? "SaveFile" : "OpenFile")) | |||
| { | |||
| #ifdef HAVE_X11 | |||
| char windowIdStr[32]; | |||
| memset(windowIdStr, 0, sizeof(windowIdStr)); | |||
| snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); | |||
| const char* windowIdStrPtr = windowIdStr; | |||
| #endif | |||
| dbus_message_append_args(msg, | |||
| #ifdef HAVE_X11 | |||
| DBUS_TYPE_STRING, &windowIdStrPtr, | |||
| #endif | |||
| DBUS_TYPE_STRING, &windowTitle, | |||
| DBUS_TYPE_INVALID); | |||
| DBusMessageIter iter, array; | |||
| dbus_message_iter_init_append(msg, &iter); | |||
| dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); | |||
| { | |||
| DBusMessageIter dict, variant, variantArray; | |||
| const char* const current_folder_key = "current_folder"; | |||
| const char* const current_folder_val = startDir.buffer(); | |||
| dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); | |||
| dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, ¤t_folder_key); | |||
| dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); | |||
| dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); | |||
| dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, | |||
| ¤t_folder_val, | |||
| static_cast<int>(startDir.length() + 1)); | |||
| dbus_message_iter_close_container(&variant, &variantArray); | |||
| dbus_message_iter_close_container(&dict, &variant); | |||
| dbus_message_iter_close_container(&array, &dict); | |||
| } | |||
| dbus_message_iter_close_container(&iter, &array); | |||
| dbus_connection_send(dbuscon, msg, nullptr); | |||
| dbus_message_unref(msg); | |||
| return handle.release(); | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| Display* const x11display = handle->x11display; | |||
| DISTRHO_SAFE_ASSERT_RETURN(x11display != nullptr, nullptr); | |||
| // unsupported at the moment | |||
| if (options.saving) | |||
| return nullptr; | |||
| DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, windowTitle) == 0, nullptr); | |||
| const int button1 = options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked ? 1 | |||
| : options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; | |||
| const int button2 = options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked ? 1 | |||
| : options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; | |||
| const int button3 = options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked ? 1 | |||
| : options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; | |||
| x_fib_cfg_buttons(1, button1); | |||
| x_fib_cfg_buttons(2, button2); | |||
| x_fib_cfg_buttons(3, button3); | |||
| if (x_fib_show(x11display, windowId, 0, 0, scaleFactor + 0.5) != 0) | |||
| return nullptr; | |||
| #endif | |||
| return handle.release(); | |||
| // might be unused | |||
| (void)isEmbed; | |||
| (void)windowId; | |||
| (void)scaleFactor; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // returns true if dialog was closed (with or without a file selection) | |||
| bool fileBrowserIdle(const FileBrowserHandle handle) | |||
| { | |||
| #ifdef HAVE_DBUS | |||
| if (DBusConnection* dbuscon = handle->dbuscon) | |||
| { | |||
| while (dbus_connection_dispatch(dbuscon) == DBUS_DISPATCH_DATA_REMAINS) {} | |||
| dbus_connection_read_write_dispatch(dbuscon, 0); | |||
| if (DBusMessage* const msg = dbus_connection_pop_message(dbuscon)) | |||
| { | |||
| const char* const interface = dbus_message_get_interface(msg); | |||
| const char* const member = dbus_message_get_member(msg); | |||
| if (interface != nullptr && std::strcmp(interface, "org.freedesktop.portal.Request") == 0 | |||
| && member != nullptr && std::strcmp(member, "Response") == 0) | |||
| { | |||
| do { | |||
| DBusMessageIter iter; | |||
| dbus_message_iter_init(msg, &iter); | |||
| // starts with uint32 for return/exit code | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); | |||
| uint32_t ret = 1; | |||
| dbus_message_iter_get_basic(&iter, &ret); | |||
| if (ret != 0) | |||
| break; | |||
| // next must be array | |||
| dbus_message_iter_next(&iter); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); | |||
| // open dict array | |||
| DBusMessageIter dictArray; | |||
| dbus_message_iter_recurse(&iter, &dictArray); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); | |||
| // open containing dict | |||
| DBusMessageIter dict; | |||
| dbus_message_iter_recurse(&dictArray, &dict); | |||
| // look for dict with string "uris" | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); | |||
| const char* key = nullptr; | |||
| dbus_message_iter_get_basic(&dict, &key); | |||
| DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); | |||
| // keep going until we find it | |||
| while (std::strcmp(key, "uris") != 0) | |||
| { | |||
| key = nullptr; | |||
| dbus_message_iter_next(&dictArray); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); | |||
| dbus_message_iter_recurse(&dictArray, &dict); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); | |||
| dbus_message_iter_get_basic(&dict, &key); | |||
| DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); | |||
| } | |||
| if (key == nullptr) | |||
| break; | |||
| // then comes variant | |||
| dbus_message_iter_next(&dict); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_VARIANT); | |||
| DBusMessageIter variant; | |||
| dbus_message_iter_recurse(&dict, &variant); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY); | |||
| // open variant array (variant type is string) | |||
| DBusMessageIter variantArray; | |||
| dbus_message_iter_recurse(&variant, &variantArray); | |||
| DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variantArray) == DBUS_TYPE_STRING); | |||
| const char* value = nullptr; | |||
| dbus_message_iter_get_basic(&variantArray, &value); | |||
| // and finally we have our dear value, just make sure it is local | |||
| DISTRHO_SAFE_ASSERT_BREAK(value != nullptr); | |||
| if (const char* const localvalue = std::strstr(value, "file:///")) | |||
| { | |||
| if (char* const decodedvalue = strdup(localvalue + 7)) | |||
| { | |||
| for (char* s = decodedvalue; (s = std::strchr(s, '%')) != nullptr; ++s) | |||
| { | |||
| if (! isHexChar(s[1]) || ! isHexChar(s[2])) | |||
| continue; | |||
| const int decodedNum = toHexChar(s[1]) * 0x10 + toHexChar(s[2]); | |||
| char replacementChar; | |||
| switch (decodedNum) | |||
| { | |||
| case 0x20: replacementChar = ' '; break; | |||
| case 0x22: replacementChar = '\"'; break; | |||
| case 0x23: replacementChar = '#'; break; | |||
| case 0x25: replacementChar = '%'; break; | |||
| case 0x3c: replacementChar = '<'; break; | |||
| case 0x3e: replacementChar = '>'; break; | |||
| case 0x5b: replacementChar = '['; break; | |||
| case 0x5c: replacementChar = '\\'; break; | |||
| case 0x5d: replacementChar = ']'; break; | |||
| case 0x5e: replacementChar = '^'; break; | |||
| case 0x60: replacementChar = '`'; break; | |||
| case 0x7b: replacementChar = '{'; break; | |||
| case 0x7c: replacementChar = '|'; break; | |||
| case 0x7d: replacementChar = '}'; break; | |||
| case 0x7e: replacementChar = '~'; break; | |||
| default: continue; | |||
| } | |||
| s[0] = replacementChar; | |||
| std::memmove(s + 1, s + 3, std::strlen(s) - 2); | |||
| } | |||
| handle->selectedFile = decodedvalue; | |||
| } | |||
| } | |||
| } while(false); | |||
| if (handle->selectedFile == nullptr) | |||
| handle->selectedFile = kSelectedFileCancelled; | |||
| } | |||
| dbus_message_unref(msg); | |||
| } | |||
| } | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| Display* const x11display = handle->x11display; | |||
| if (x11display == nullptr) | |||
| return false; | |||
| XEvent event; | |||
| while (XPending(x11display) > 0) | |||
| { | |||
| XNextEvent(x11display, &event); | |||
| if (x_fib_handle_events(x11display, &event) == 0) | |||
| continue; | |||
| if (x_fib_status() > 0) | |||
| handle->selectedFile = x_fib_filename(); | |||
| else | |||
| handle->selectedFile = kSelectedFileCancelled; | |||
| x_fib_close(x11display); | |||
| XCloseDisplay(x11display); | |||
| handle->x11display = nullptr; | |||
| break; | |||
| } | |||
| #endif | |||
| return handle->selectedFile != nullptr; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // close sofd file dialog | |||
| void fileBrowserClose(const FileBrowserHandle handle) | |||
| { | |||
| #ifdef DISTRHO_OS_WASM | |||
| if (handle->saving && fileBrowserGetPath(handle) != nullptr) | |||
| downloadWebBrowserFile(handle->defaultName); | |||
| #endif | |||
| #ifdef HAVE_X11 | |||
| if (Display* const x11display = handle->x11display) | |||
| x_fib_close(x11display); | |||
| #endif | |||
| delete handle; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // get path chosen via sofd file dialog | |||
| const char* fileBrowserGetPath(const FileBrowserHandle handle) | |||
| { | |||
| if (const char* const selectedFile = handle->selectedFile) | |||
| if (selectedFile != kSelectedFileCancelled && std::strcmp(selectedFile, kSelectedFileCancelled) != 0) | |||
| return selectedFile; | |||
| return nullptr; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE | |||
| END_NAMESPACE_DGL | |||
| #else | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| #undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE | |||
| #undef FILE_BROWSER_DIALOG_DGL_NAMESPACE | |||
| #undef FILE_BROWSER_DIALOG_NAMESPACE | |||
| #undef fileBrowserSetPathNamespaced | |||
| #undef fileBrowserSetPathFuncName | |||
| @@ -1,129 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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. | |||
| */ | |||
| #if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) | |||
| # error bad include | |||
| #endif | |||
| #if !defined(DGL_USE_FILE_BROWSER) && defined(DISTRHO_UI_FILE_BROWSER) && DISTRHO_UI_FILE_BROWSER == 0 | |||
| # error To use File Browser in DPF plugins please set DISTRHO_UI_FILE_BROWSER to 1 | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // File Browser Dialog stuff | |||
| struct FileBrowserData; | |||
| typedef FileBrowserData* FileBrowserHandle; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| File browser options, for customizing the file browser dialog.@n | |||
| By default the file browser dialog will be work as "open file" in the current working directory. | |||
| */ | |||
| struct FileBrowserOptions { | |||
| /** Whether we are saving, opening files otherwise (default) */ | |||
| bool saving; | |||
| /** Default filename when saving, required in some platforms (basename without path separators) */ | |||
| const char* defaultName; | |||
| /** Start directory, uses current working directory if null */ | |||
| const char* startDir; | |||
| /** File browser dialog window title, uses "FileBrowser" if null */ | |||
| const char* title; | |||
| /** Class name of the matching Application instance that controls this dialog */ | |||
| const char* className; | |||
| // TODO file filter | |||
| /** | |||
| File browser button state. | |||
| This allows to customize the behaviour of the file browse dialog buttons. | |||
| Note these are merely hints, not all systems support them. | |||
| */ | |||
| enum ButtonState { | |||
| kButtonInvisible, | |||
| kButtonVisibleUnchecked, | |||
| kButtonVisibleChecked, | |||
| }; | |||
| /** | |||
| File browser buttons. | |||
| */ | |||
| struct Buttons { | |||
| /** 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; | |||
| /** Constructor for default values */ | |||
| Buttons() | |||
| : listAllFiles(kButtonVisibleChecked), | |||
| showHidden(kButtonVisibleUnchecked), | |||
| showPlaces(kButtonVisibleChecked) {} | |||
| } buttons; | |||
| /** Constructor for default values */ | |||
| FileBrowserOptions() | |||
| : saving(false), | |||
| defaultName(nullptr), | |||
| startDir(nullptr), | |||
| title(nullptr), | |||
| className(nullptr), | |||
| buttons() {} | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Create a new file browser dialog. | |||
| @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) | |||
| @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) | |||
| @p scaleFactor: Scale factor to use (only used on X11) | |||
| @p options: Extra options, optional | |||
| By default the file browser dialog will work as "open file" in the current working directory. | |||
| */ | |||
| FileBrowserHandle fileBrowserCreate(bool isEmbed, | |||
| uintptr_t windowId, | |||
| double scaleFactor, | |||
| const FileBrowserOptions& options = FileBrowserOptions()); | |||
| /** | |||
| Idle the file browser dialog handle.@n | |||
| Returns true if dialog was closed (with or without a file selection), | |||
| in which case this idle function must not be called anymore for this handle. | |||
| You can then call fileBrowserGetPath to know the selected file (or null if cancelled). | |||
| */ | |||
| bool fileBrowserIdle(FileBrowserHandle handle); | |||
| /** | |||
| Close and free the file browser dialog, handle must not be used afterwards. | |||
| */ | |||
| void fileBrowserClose(FileBrowserHandle handle); | |||
| /** | |||
| Get the path chosen by the user or null.@n | |||
| Should only be called after fileBrowserIdle returns true. | |||
| */ | |||
| const char* fileBrowserGetPath(FileBrowserHandle handle); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,106 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef DISTRHO_FILESYSTEM_UTILS_HPP_INCLUDED | |||
| #define DISTRHO_FILESYSTEM_UTILS_HPP_INCLUDED | |||
| #include "String.hpp" | |||
| #include <cstdio> | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| # include <stringapiset.h> | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // filesystem related calls | |||
| /* | |||
| * Wrapper around `fopen` call, needed on Windows because its C standard functions use ASCII instead of UTF-8. | |||
| */ | |||
| static inline | |||
| FILE* d_fopen(const char* const pathname, const char* const mode) | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| WCHAR lpathname[MAX_PATH]; | |||
| WCHAR lmode[4]; | |||
| if (MultiByteToWideChar(CP_UTF8, 0, pathname, -1, lpathname, ARRAY_SIZE(lpathname)) != 0 && | |||
| MultiByteToWideChar(CP_UTF8, 0, mode, -1, lmode, ARRAY_SIZE(lmode)) != 0) | |||
| return _wfopen(lpathname, lmode); | |||
| #endif | |||
| return fopen(pathname, mode); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // filesystem related classes | |||
| /** | |||
| Handy class to help write files in a safe way, which does: | |||
| - open pathname + ".tmp" instead of opening a file directly (so partial writes are safe) | |||
| - on close, flush data to disk and rename file to remove ".tmp" | |||
| To use it, create a local variable (on the stack) and call ok() or manually check @a fd variable. | |||
| @code | |||
| if (const SafeFileWriter file("/path/to/file.txt"); file.ok()) | |||
| file.write("Success!"); | |||
| @endcode | |||
| */ | |||
| struct SafeFileWriter | |||
| { | |||
| String filename; | |||
| FILE* const fd; | |||
| /** | |||
| Constructor, opening @a pathname + ".tmp" for writing. | |||
| */ | |||
| SafeFileWriter(const char* const pathname, const char* const mode = "w") | |||
| : filename(pathname), | |||
| fd(d_fopen(filename + ".tmp", mode)) {} | |||
| /** | |||
| Destructor, will flush file data contents, close and rename file. | |||
| */ | |||
| ~SafeFileWriter() | |||
| { | |||
| if (fd == nullptr) | |||
| return; | |||
| std::fflush(fd); | |||
| std::fclose(fd); | |||
| std::rename(filename + ".tmp", filename); | |||
| } | |||
| /** Check if the file was opened successfully. */ | |||
| inline bool ok() const noexcept | |||
| { | |||
| return fd != nullptr; | |||
| } | |||
| /** Wrapper around `fwrite`, purely for convenience. */ | |||
| inline size_t write(const void* const ptr, const size_t size, const size_t nmemb = 1) const | |||
| { | |||
| return std::fwrite(ptr, size, nmemb, fd); | |||
| } | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_FILESYSTEM_UTILS_HPP_INCLUDED | |||
| @@ -1,251 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * 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 DISTRHO_RUNNER_HPP_INCLUDED | |||
| #define DISTRHO_RUNNER_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| #ifndef DISTRHO_OS_WASM | |||
| # include "Thread.hpp" | |||
| #else | |||
| # include "String.hpp" | |||
| # include <emscripten/html5.h> | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| #ifdef DISTRHO_RUNNER_INDIRECT_WASM_CALLS | |||
| long d_emscripten_set_interval(void (*)(void*), double, void*); | |||
| void d_emscripten_clear_interval(long); | |||
| #else | |||
| # define d_emscripten_set_interval emscripten_set_interval | |||
| # define d_emscripten_clear_interval emscripten_clear_interval | |||
| #endif | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| // Runner class | |||
| /** | |||
| Runner class for DPF. | |||
| This is a handy class that handles "idle" time in either background or main thread, | |||
| whichever is more suitable to the target platform. | |||
| Typically background threads on desktop platforms, main thread on web. | |||
| A single function is expected to be implemented by subclasses, | |||
| which directly allows it to stop the runner by returning false. | |||
| You can use it for quick operations that do not need to be handled in the main thread if possible. | |||
| The target is to spread out execution over many runs, instead of spending a lot of time on a single task. | |||
| */ | |||
| class Runner | |||
| { | |||
| protected: | |||
| /* | |||
| * Constructor. | |||
| */ | |||
| Runner(const char* const runnerName = nullptr) noexcept | |||
| #ifndef DISTRHO_OS_WASM | |||
| : fRunnerThread(this, runnerName), | |||
| fTimeInterval(0) | |||
| #else | |||
| : fRunnerName(runnerName), | |||
| fIntervalId(0) | |||
| #endif | |||
| { | |||
| } | |||
| /* | |||
| * Destructor. | |||
| */ | |||
| virtual ~Runner() /*noexcept*/ | |||
| { | |||
| DISTRHO_SAFE_ASSERT(! isRunnerActive()); | |||
| stopRunner(); | |||
| } | |||
| /* | |||
| * Virtual function to be implemented by the subclass. | |||
| * Return true to keep running, false to stop execution. | |||
| */ | |||
| virtual bool run() = 0; | |||
| /* | |||
| * Check if the runner should stop. | |||
| * To be called from inside the runner to know if a stop request has been made. | |||
| */ | |||
| bool shouldRunnerStop() const noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| return fRunnerThread.shouldThreadExit(); | |||
| #else | |||
| return fIntervalId == 0; | |||
| #endif | |||
| } | |||
| // --------------------------------------------------------------------------------------------------------------- | |||
| public: | |||
| /* | |||
| * Check if the runner is active. | |||
| */ | |||
| bool isRunnerActive() noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| return fRunnerThread.isThreadRunning(); | |||
| #else | |||
| return fIntervalId != 0; | |||
| #endif | |||
| } | |||
| /* | |||
| * Start the runner. | |||
| */ | |||
| bool startRunner(const uint timeIntervalMilliseconds = 0) noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| DISTRHO_SAFE_ASSERT_RETURN(!fRunnerThread.isThreadRunning(), false); | |||
| fTimeInterval = timeIntervalMilliseconds; | |||
| return fRunnerThread.startThread(); | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fIntervalId == 0, false); | |||
| fIntervalId = d_emscripten_set_interval(_entryPoint, timeIntervalMilliseconds, this); | |||
| return true; | |||
| #endif | |||
| } | |||
| /* | |||
| * Stop the runner. | |||
| * This will signal the runner to stop if active, and wait until it finishes. | |||
| */ | |||
| bool stopRunner() noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| return fRunnerThread.stopThread(-1); | |||
| #else | |||
| signalRunnerShouldStop(); | |||
| return true; | |||
| #endif | |||
| } | |||
| /* | |||
| * Tell the runner to stop as soon as possible. | |||
| */ | |||
| void signalRunnerShouldStop() noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| fRunnerThread.signalThreadShouldExit(); | |||
| #else | |||
| if (fIntervalId != 0) | |||
| { | |||
| d_emscripten_clear_interval(fIntervalId); | |||
| fIntervalId = 0; | |||
| } | |||
| #endif | |||
| } | |||
| // --------------------------------------------------------------------------------------------------------------- | |||
| /* | |||
| * Returns the name of the runner. | |||
| * This is the name that gets set in the constructor. | |||
| */ | |||
| const String& getRunnerName() const noexcept | |||
| { | |||
| #ifndef DISTRHO_OS_WASM | |||
| return fRunnerThread.getThreadName(); | |||
| #else | |||
| return fRunnerName; | |||
| #endif | |||
| } | |||
| // --------------------------------------------------------------------------------------------------------------- | |||
| private: | |||
| #ifndef DISTRHO_OS_WASM | |||
| class RunnerThread : public Thread | |||
| { | |||
| Runner* const runner; | |||
| public: | |||
| RunnerThread(Runner* const r, const char* const rn) | |||
| : Thread(rn), | |||
| runner(r) {} | |||
| protected: | |||
| void run() override | |||
| { | |||
| const uint timeInterval = runner->fTimeInterval; | |||
| while (!shouldThreadExit()) | |||
| { | |||
| bool stillRunning = false; | |||
| try { | |||
| stillRunning = runner->run(); | |||
| } catch(...) {} | |||
| if (stillRunning && !shouldThreadExit()) | |||
| { | |||
| if (timeInterval != 0) | |||
| d_msleep(timeInterval); | |||
| // FIXME | |||
| // pthread_yield(); | |||
| continue; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } fRunnerThread; | |||
| uint fTimeInterval; | |||
| #else | |||
| const String fRunnerName; | |||
| long fIntervalId; | |||
| void _runEntryPoint() noexcept | |||
| { | |||
| bool stillRunning = false; | |||
| try { | |||
| stillRunning = run(); | |||
| } catch(...) {} | |||
| if (fIntervalId != 0 && !stillRunning) | |||
| { | |||
| d_emscripten_clear_interval(fIntervalId); | |||
| fIntervalId = 0; | |||
| } | |||
| } | |||
| static void _entryPoint(void* const userData) noexcept | |||
| { | |||
| static_cast<Runner*>(userData)->_runEntryPoint(); | |||
| } | |||
| #endif | |||
| DISTRHO_DECLARE_NON_COPYABLE(Runner) | |||
| }; | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_RUNNER_HPP_INCLUDED | |||
| @@ -1,127 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 DISTRHO_TIME_HPP_INCLUDED | |||
| #define DISTRHO_TIME_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| # include <winsock2.h> | |||
| # include <windows.h> | |||
| # include <mmsystem.h> | |||
| #else | |||
| # include <ctime> | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| // d_gettime_* | |||
| /* | |||
| * Get a monotonically-increasing time in milliseconds. | |||
| */ | |||
| static inline | |||
| uint32_t d_gettime_ms() noexcept | |||
| { | |||
| #if defined(DISTRHO_OS_MAC) | |||
| static const time_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000; | |||
| return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000) - s; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| return static_cast<uint32_t>(timeGetTime()); | |||
| #else | |||
| static struct { | |||
| timespec ts; | |||
| int r; | |||
| uint32_t ms; | |||
| } s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint32_t>(s.ts.tv_sec * 1000 + | |||
| s.ts.tv_nsec / 1000000) }; | |||
| timespec ts; | |||
| clock_gettime(CLOCK_MONOTONIC, &ts); | |||
| return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000) - s.ms; | |||
| #endif | |||
| } | |||
| /* | |||
| * Get a monotonically-increasing time in microseconds. | |||
| */ | |||
| static inline | |||
| uint64_t d_gettime_us() noexcept | |||
| { | |||
| #if defined(DISTRHO_OS_MAC) | |||
| static const uint64_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000; | |||
| return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000) - s; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| static struct { | |||
| LARGE_INTEGER freq; | |||
| LARGE_INTEGER counter; | |||
| BOOL r1, r2; | |||
| } s = { {}, {}, QueryPerformanceFrequency(&s.freq), QueryPerformanceCounter(&s.counter) }; | |||
| LARGE_INTEGER counter; | |||
| QueryPerformanceCounter(&counter); | |||
| return (counter.QuadPart - s.counter.QuadPart) * 1000000 / s.freq.QuadPart; | |||
| #else | |||
| static struct { | |||
| timespec ts; | |||
| int r; | |||
| uint64_t us; | |||
| } s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint64_t>(s.ts.tv_sec * 1000000 + | |||
| s.ts.tv_nsec / 1000) }; | |||
| timespec ts; | |||
| clock_gettime(CLOCK_MONOTONIC, &ts); | |||
| return (ts.tv_sec * 1000000 + ts.tv_nsec / 1000) - s.us; | |||
| #endif | |||
| } | |||
| /* | |||
| * Get a monotonically-increasing time in nanoseconds. | |||
| */ | |||
| static inline | |||
| uint64_t d_gettime_ns() noexcept | |||
| { | |||
| #if defined(DISTRHO_OS_MAC) | |||
| static const uint64_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); | |||
| return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) - s; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| static struct { | |||
| LARGE_INTEGER freq; | |||
| LARGE_INTEGER counter; | |||
| BOOL r1, r2; | |||
| } s = { {}, {}, QueryPerformanceFrequency(&s.freq), QueryPerformanceCounter(&s.counter) }; | |||
| LARGE_INTEGER counter; | |||
| QueryPerformanceCounter(&counter); | |||
| return (counter.QuadPart - s.counter.QuadPart) * 1000000000ULL / s.freq.QuadPart; | |||
| #else | |||
| static struct { | |||
| timespec ts; | |||
| int r; | |||
| uint64_t ns; | |||
| } s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint64_t>(s.ts.tv_sec * 1000000000ULL + | |||
| s.ts.tv_nsec) }; | |||
| timespec ts; | |||
| clock_gettime(CLOCK_MONOTONIC, &ts); | |||
| return (ts.tv_sec * 1000000000ULL + ts.tv_nsec) - s.ns; | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_TIME_HPP_INCLUDED | |||
| @@ -1,28 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 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 DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| #define DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| #include "WebViewImpl.hpp" | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| @@ -1,136 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #if !defined(DISTRHO_WEB_VIEW_HPP_INCLUDED) && !defined(DGL_WEB_VIEW_HPP_INCLUDED) | |||
| # error bad include | |||
| #endif | |||
| #if !defined(DGL_USE_WEB_VIEW) && defined(DISTRHO_UI_WEB_VIEW) && DISTRHO_UI_WEB_VIEW == 0 | |||
| # error To use WebViews in DPF plugins please set DISTRHO_UI_WEB_VIEW to 1 | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Web View stuff | |||
| struct WebViewData; | |||
| typedef WebViewData* WebViewHandle; | |||
| typedef void (*WebViewMessageCallback)(void* arg, char* msg); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Web view options, for customizing web view details. | |||
| */ | |||
| struct WebViewOptions { | |||
| /** | |||
| Position offset, for cases of mixing regular widgets with web views. | |||
| */ | |||
| struct PositionOffset { | |||
| /** Horizontal offset, with scale factor pre-applied */ | |||
| int x; | |||
| /** Vertical offset, with scale factor pre-applied */ | |||
| int y; | |||
| /** Constructor for default values */ | |||
| PositionOffset() : x(0), y(0) {} | |||
| } offset; | |||
| /** | |||
| Set some JavaScript to evalute on every new page load. | |||
| */ | |||
| const char* initialJS; | |||
| /** | |||
| Message callback triggered from JavaScript code inside the WebView. | |||
| */ | |||
| WebViewMessageCallback callback; | |||
| void* callbackPtr; | |||
| /** Constructor for default values */ | |||
| WebViewOptions() | |||
| : offset(), | |||
| initialJS(nullptr), | |||
| callback(nullptr), | |||
| callbackPtr(nullptr) {} | |||
| /** Constructor providing a callback */ | |||
| WebViewOptions(const WebViewMessageCallback cb, void* const ptr) | |||
| : offset(), | |||
| initialJS(nullptr), | |||
| callback(cb), | |||
| callbackPtr(ptr) {} | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Create a new web view. | |||
| The web view will be added on top of an existing platform-specific view/window. | |||
| This means it will draw on top of whatever is below it, | |||
| something to take into consideration if mixing regular widgets with web views. | |||
| Provided metrics must have scale factor pre-applied. | |||
| @p url: The URL to open, assumed to be in encoded form (e.g spaces converted to %20) | |||
| @p windowId: The native window id to attach this view to (X11 Window, HWND or NSView*) | |||
| @p scaleFactor: Scale factor in use | |||
| @p options: Extra options, optional | |||
| */ | |||
| WebViewHandle webViewCreate(const char* url, | |||
| uintptr_t windowId, | |||
| uint initialWidth, | |||
| uint initialHeight, | |||
| double scaleFactor, | |||
| const WebViewOptions& options = WebViewOptions()); | |||
| /** | |||
| Destroy the web view, handle must not be used afterwards. | |||
| */ | |||
| void webViewDestroy(WebViewHandle webview); | |||
| /** | |||
| Idle the web view, to be called on regular intervals. | |||
| Can cause callbacks to trigger. | |||
| */ | |||
| void webViewIdle(WebViewHandle webview); | |||
| /** | |||
| Evaluate/run JavaScript on the web view. | |||
| */ | |||
| void webViewEvaluateJS(WebViewHandle webview, const char* js); | |||
| /** | |||
| Reload the web view current page. | |||
| */ | |||
| void webViewReload(WebViewHandle webview); | |||
| /** | |||
| Resize the web view. | |||
| */ | |||
| void webViewResize(WebViewHandle webview, uint width, uint height, double scaleFactor); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Helper class for usage in std::shared_ptr and std::unique_ptr. | |||
| */ | |||
| struct WebViewDestroy { | |||
| void operator()(WebViewHandle handle) { webViewDestroy(handle); } | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,122 +0,0 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #if !defined(DISTRHO_WEB_VIEW_HPP_INCLUDED) && !defined(DGL_WEB_VIEW_HPP_INCLUDED) | |||
| # define DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION | |||
| # include "WebView.hpp" | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Web View stuff | |||
| START_NAMESPACE_DISTRHO | |||
| class WebView; | |||
| END_NAMESPACE_DISTRHO | |||
| #ifdef WEB_VIEW_DGL_NAMESPACE | |||
| START_NAMESPACE_DGL | |||
| using DISTRHO_NAMESPACE::WebView; | |||
| #else | |||
| START_NAMESPACE_DISTRHO | |||
| #endif | |||
| WebView* webview_choc_create(const WEB_VIEW_NAMESPACE::WebViewOptions& opts); | |||
| void webview_choc_destroy(WebView*); | |||
| void* webview_choc_handle(WebView*); | |||
| void webview_choc_eval(WebView*, const char* js); | |||
| void webview_choc_navigate(WebView*, const char* url); | |||
| #ifdef WEB_VIEW_DGL_NAMESPACE | |||
| END_NAMESPACE_DGL | |||
| #else | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION | |||
| # define WC_ERR_INVALID_CHARS 0 | |||
| # include "choc/choc_WebView.h" | |||
| #ifdef WEB_VIEW_DGL_NAMESPACE | |||
| START_NAMESPACE_DGL | |||
| #else | |||
| START_NAMESPACE_DISTRHO | |||
| #endif | |||
| WebView* webview_choc_create(const WEB_VIEW_NAMESPACE::WebViewOptions& opts) | |||
| { | |||
| WebView::Options wopts; | |||
| wopts.acceptsFirstMouseClick = true; | |||
| wopts.enableDebugMode = true; | |||
| std::unique_ptr<WebView> webview = std::make_unique<WebView>(wopts); | |||
| DISTRHO_SAFE_ASSERT_RETURN(webview->loadedOK(), nullptr); | |||
| if (const WEB_VIEW_NAMESPACE::WebViewMessageCallback callback = opts.callback) | |||
| { | |||
| webview->addInitScript("function postMessage(m){window.chrome.webview.postMessage(m);}"); | |||
| void* const callbackPtr = opts.callbackPtr; | |||
| webview->bind([callback, callbackPtr](const std::string& value) { | |||
| char* const data = strdup(value.data()); | |||
| callback(callbackPtr, data); | |||
| std::free(data); | |||
| }); | |||
| } | |||
| else | |||
| { | |||
| webview->addInitScript("function postMessage(m){}"); | |||
| } | |||
| if (opts.initialJS != nullptr) | |||
| webview->addInitScript(opts.initialJS); | |||
| return webview.release(); | |||
| } | |||
| void webview_choc_destroy(WebView* const webview) | |||
| { | |||
| delete webview; | |||
| } | |||
| void* webview_choc_handle(WebView* const webview) | |||
| { | |||
| return webview->getViewHandle(); | |||
| } | |||
| void webview_choc_eval(WebView* const webview, const char* const js) | |||
| { | |||
| webview->evaluateJavascript(js); | |||
| } | |||
| void webview_choc_navigate(WebView* const webview, const char* const url) | |||
| { | |||
| webview->navigate(url); | |||
| } | |||
| #ifdef WEB_VIEW_DGL_NAMESPACE | |||
| END_NAMESPACE_DGL | |||
| #else | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| #endif // DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,15 +0,0 @@ | |||
| ## CHOC: licensing | |||
| I'd like anyone to feel able to use this library code without worrying about the legal implications, so it's released under the permissive ISC license: | |||
| ---- | |||
| **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.** | |||
| https://en.wikipedia.org/wiki/ISC_license | |||
| ---- | |||
| Note that if you use the `choc::ui::WebView` class on Windows, it embeds some Microsoft redistributable code which has its own (permissive) license that you should be aware of. See inside the file `choc_WebView.h` for more details. | |||
| @@ -1,22 +0,0 @@ | |||
| Taken from https://github.com/Tracktion/choc | |||
| ``` | |||
| commit 2512542b2d65f3e92df7f2f1f7eeb712fa41a0de (HEAD -> main, origin/main, origin/HEAD) | |||
| Author: Cesare Ferrari <cesare.ferrari@gmail.com> | |||
| Date: Sun Apr 28 12:53:17 2024 +0100 | |||
| Disable additional gcc warnin | |||
| ``` | |||
| With the big [choc.patch](./choc.patch) patch applied to top for: | |||
| - remove everything not related to windows | |||
| - remove everything unused by DPF | |||
| - convert webview JS callbacks to pass raw strings instead of json | |||
| - remove even more stuff (json no longer needed) | |||
| - convert choc asserts into distrho ones | |||
| - put everything inside distrho namespace | |||
| And then backported: | |||
| - https://github.com/Tracktion/choc/commit/792e4bd9bedf38b9a28f12be0535c90649d5616b | |||
| @@ -1,154 +0,0 @@ | |||
| // | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes ** | |||
| // ██ ███████ ██ ██ ██ | |||
| // ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // | |||
| // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: | |||
| // | |||
| // 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 CHOC_DESKTOPWINDOW_HEADER_INCLUDED | |||
| #define CHOC_DESKTOPWINDOW_HEADER_INCLUDED | |||
| #include "choc_Platform.h" | |||
| //============================================================================== | |||
| // _ _ _ _ | |||
| // __| | ___ | |_ __ _ (_)| | ___ | |||
| // / _` | / _ \| __| / _` || || |/ __| | |||
| // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ | |||
| // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) | |||
| // | |||
| // Code beyond this point is implementation detail... | |||
| // | |||
| //============================================================================== | |||
| #undef WIN32_LEAN_AND_MEAN | |||
| #define WIN32_LEAN_AND_MEAN | |||
| #undef NOMINMAX | |||
| #define NOMINMAX | |||
| #define Rectangle Rectangle_renamed_to_avoid_name_collisions | |||
| #include <windows.h> | |||
| #undef Rectangle | |||
| START_NAMESPACE_DISTRHO | |||
| struct HWNDHolder | |||
| { | |||
| HWNDHolder() = default; | |||
| HWNDHolder (HWND h) : hwnd (h) {} | |||
| HWNDHolder (const HWNDHolder&) = delete; | |||
| HWNDHolder& operator= (const HWNDHolder&) = delete; | |||
| HWNDHolder (HWNDHolder&& other) : hwnd (other.hwnd) { other.hwnd = {}; } | |||
| HWNDHolder& operator= (HWNDHolder&& other) { reset(); hwnd = other.hwnd; other.hwnd = {}; return *this; } | |||
| ~HWNDHolder() { reset(); } | |||
| operator HWND() const { return hwnd; } | |||
| operator void*() const { return (void*) hwnd; } | |||
| void reset() { if (IsWindow (hwnd)) DestroyWindow (hwnd); hwnd = {}; } | |||
| HWND hwnd = {}; | |||
| }; | |||
| struct WindowClass | |||
| { | |||
| WindowClass (std::wstring name, WNDPROC wndProc) | |||
| { | |||
| name += std::to_wstring (static_cast<uint32_t> (GetTickCount())); | |||
| moduleHandle = GetModuleHandle (nullptr); | |||
| auto icon = (HICON) LoadImage (moduleHandle, IDI_APPLICATION, IMAGE_ICON, | |||
| GetSystemMetrics (SM_CXSMICON), | |||
| GetSystemMetrics (SM_CYSMICON), | |||
| LR_DEFAULTCOLOR); | |||
| WNDCLASSEXW wc; | |||
| ZeroMemory (&wc, sizeof(wc)); | |||
| wc.cbSize = sizeof(wc); | |||
| wc.style = CS_OWNDC; | |||
| wc.hInstance = moduleHandle; | |||
| wc.lpszClassName = name.c_str(); | |||
| wc.hIcon = icon; | |||
| wc.hIconSm = icon; | |||
| wc.lpfnWndProc = wndProc; | |||
| classAtom = (LPCWSTR) (uintptr_t) RegisterClassExW (&wc); | |||
| DISTRHO_SAFE_ASSERT (classAtom != 0); | |||
| } | |||
| ~WindowClass() | |||
| { | |||
| UnregisterClassW (classAtom, moduleHandle); | |||
| } | |||
| HWNDHolder createWindow (DWORD style, int w, int h, void* userData) | |||
| { | |||
| if (auto hwnd = CreateWindowW (classAtom, L"", style, CW_USEDEFAULT, CW_USEDEFAULT, | |||
| w, h, nullptr, nullptr, moduleHandle, nullptr)) | |||
| { | |||
| SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData); | |||
| return hwnd; | |||
| } | |||
| return {}; | |||
| } | |||
| auto getClassName() const { return classAtom; } | |||
| HINSTANCE moduleHandle = {}; | |||
| LPCWSTR classAtom = {}; | |||
| }; | |||
| static std::string createUTF8FromUTF16 (const std::wstring& utf16) | |||
| { | |||
| if (! utf16.empty()) | |||
| { | |||
| auto numWideChars = static_cast<int> (utf16.size()); | |||
| auto resultSize = WideCharToMultiByte (CP_UTF8, WC_ERR_INVALID_CHARS, utf16.data(), numWideChars, nullptr, 0, nullptr, nullptr); | |||
| if (resultSize > 0) | |||
| { | |||
| std::string result; | |||
| result.resize (static_cast<size_t> (resultSize)); | |||
| if (WideCharToMultiByte (CP_UTF8, WC_ERR_INVALID_CHARS, utf16.data(), numWideChars, result.data(), resultSize, nullptr, nullptr) > 0) | |||
| return result; | |||
| } | |||
| } | |||
| return {}; | |||
| } | |||
| static std::wstring createUTF16StringFromUTF8 (std::string_view utf8) | |||
| { | |||
| if (! utf8.empty()) | |||
| { | |||
| auto numUTF8Bytes = static_cast<int> (utf8.size()); | |||
| auto resultSize = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, utf8.data(), numUTF8Bytes, nullptr, 0); | |||
| if (resultSize > 0) | |||
| { | |||
| std::wstring result; | |||
| result.resize (static_cast<size_t> (resultSize)); | |||
| if (MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, utf8.data(), numUTF8Bytes, result.data(), resultSize) > 0) | |||
| return result; | |||
| } | |||
| } | |||
| return {}; | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // CHOC_DESKTOPWINDOW_HEADER_INCLUDED | |||
| @@ -1,141 +0,0 @@ | |||
| // | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes ** | |||
| // ██ ███████ ██ ██ ██ | |||
| // ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // | |||
| // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: | |||
| // | |||
| // 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 CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED | |||
| #define CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED | |||
| #include <string> | |||
| START_NAMESPACE_DISTRHO | |||
| //============================================================================== | |||
| /** | |||
| A minimal cross-platform loader for .dll/.so files. | |||
| */ | |||
| struct DynamicLibrary | |||
| { | |||
| DynamicLibrary() = default; | |||
| /// Attempts to load a library with the given name or path. | |||
| DynamicLibrary (std::string_view library); | |||
| DynamicLibrary (const DynamicLibrary&) = delete; | |||
| DynamicLibrary& operator= (const DynamicLibrary&) = delete; | |||
| DynamicLibrary (DynamicLibrary&&); | |||
| DynamicLibrary& operator= (DynamicLibrary&&); | |||
| /// On destruction, this object releases the library that was loaded | |||
| ~DynamicLibrary(); | |||
| /// Returns a pointer to the function with this name, or nullptr if not found. | |||
| void* findFunction (std::string_view functionName); | |||
| /// Returns true if the library was successfully loaded | |||
| operator bool() const noexcept { return handle != nullptr; } | |||
| /// Releases any handle that this object is holding | |||
| void close(); | |||
| /// platform-specific handle. Will be nullptr if not loaded | |||
| void* handle = nullptr; | |||
| }; | |||
| //============================================================================== | |||
| // _ _ _ _ | |||
| // __| | ___ | |_ __ _ (_)| | ___ | |||
| // / _` | / _ \| __| / _` || || |/ __| | |||
| // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ | |||
| // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) | |||
| // | |||
| // Code beyond this point is implementation detail... | |||
| // | |||
| //============================================================================== | |||
| inline DynamicLibrary::~DynamicLibrary() | |||
| { | |||
| close(); | |||
| } | |||
| inline DynamicLibrary::DynamicLibrary (DynamicLibrary&& other) : handle (other.handle) | |||
| { | |||
| other.handle = nullptr; | |||
| } | |||
| inline DynamicLibrary& DynamicLibrary::operator= (DynamicLibrary&& other) | |||
| { | |||
| close(); | |||
| handle = other.handle; | |||
| other.handle = nullptr; | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| namespace win32_defs | |||
| { | |||
| #if ! (defined (_WINDOWS_) || defined (_APISETLIBLOADER_)) // only use these local definitions if windows.h isn't already included | |||
| using CHOC_HMODULE = void*; | |||
| #ifdef _MSC_VER | |||
| extern "C" CHOC_HMODULE __stdcall choc_LoadLibraryA (const char*); | |||
| extern "C" int __stdcall choc_FreeLibrary (CHOC_HMODULE); | |||
| extern "C" void* __stdcall choc_GetProcAddress (CHOC_HMODULE, const char*); | |||
| #pragma comment(linker,"/alternatename:choc_LoadLibraryA=LoadLibraryA") | |||
| #pragma comment(linker,"/alternatename:choc_FreeLibrary=FreeLibrary") | |||
| #pragma comment(linker,"/alternatename:choc_GetProcAddress=GetProcAddress") | |||
| static inline CHOC_HMODULE LoadLibraryA (const char* l) { return choc_LoadLibraryA (l); } | |||
| static inline int FreeLibrary (CHOC_HMODULE m) { return choc_FreeLibrary (m); } | |||
| static inline void* GetProcAddress (CHOC_HMODULE m, const char* f) { return choc_GetProcAddress (m, f); } | |||
| #else | |||
| extern "C" __declspec(dllimport) CHOC_HMODULE __stdcall LoadLibraryA (const char*); | |||
| extern "C" __declspec(dllimport) int __stdcall FreeLibrary (CHOC_HMODULE); | |||
| extern "C" __declspec(dllimport) void* __stdcall GetProcAddress (CHOC_HMODULE, const char*); | |||
| #endif | |||
| #else | |||
| using CHOC_HMODULE = HMODULE; | |||
| static inline CHOC_HMODULE LoadLibraryA (const char* l) { return ::LoadLibraryA (l); } | |||
| static inline int FreeLibrary (CHOC_HMODULE m) { return ::FreeLibrary (m); } | |||
| static inline void* GetProcAddress (CHOC_HMODULE m, const char* f) { return (void*) ::GetProcAddress (m, f); } | |||
| #endif | |||
| } | |||
| inline DynamicLibrary::DynamicLibrary (std::string_view library) | |||
| { | |||
| handle = (void*) win32_defs::LoadLibraryA (std::string (library).c_str()); | |||
| } | |||
| inline void DynamicLibrary::close() | |||
| { | |||
| if (handle != nullptr) | |||
| { | |||
| win32_defs::FreeLibrary ((win32_defs::CHOC_HMODULE) handle); | |||
| handle = nullptr; | |||
| } | |||
| } | |||
| inline void* DynamicLibrary::findFunction (std::string_view name) | |||
| { | |||
| if (handle != nullptr) | |||
| return (void*) win32_defs::GetProcAddress ((win32_defs::CHOC_HMODULE) handle, std::string (name).c_str()); | |||
| return {}; | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED | |||
| @@ -1,511 +0,0 @@ | |||
| // | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes ** | |||
| // ██ ███████ ██ ██ ██ | |||
| // ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // | |||
| // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: | |||
| // | |||
| // 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 CHOC_MEMORYDLL_HEADER_INCLUDED | |||
| #define CHOC_MEMORYDLL_HEADER_INCLUDED | |||
| #include <stddef.h> | |||
| #include <memory> | |||
| #include <string> | |||
| START_NAMESPACE_DISTRHO | |||
| /** | |||
| MemoryDLL is an egregious hack that allows you to load DLL files from a chunk | |||
| of memory in the same way you might load them from a file on disk. | |||
| This opens up the ability to do horrible things such as embedding a random DLL | |||
| in a chunk of C++ code, so that it gets baked directly into your executable.. | |||
| That means that if your app requires a 3rd-party DLL but you don't want the hassle | |||
| of having to install the DLL file in the right place on your users' machines, you | |||
| could use this trick to embed it invisibly inside your executable... | |||
| Currently this is only implemented for Windows DLLs, but a linux/OSX loader for | |||
| .dylibs is also totally feasible if anyone fancies having a go :) | |||
| @see DynamicLibrary | |||
| */ | |||
| struct MemoryDLL | |||
| { | |||
| MemoryDLL() = default; | |||
| MemoryDLL (MemoryDLL&&) = default; | |||
| MemoryDLL& operator= (MemoryDLL&&) = default; | |||
| ~MemoryDLL(); | |||
| /// Attempts to load a chunk of memory that contains a DLL file image. | |||
| MemoryDLL (const void* data, size_t size); | |||
| /// Returns a pointer to the function with this name, or nullptr if not found. | |||
| void* findFunction (std::string_view functionName); | |||
| /// Returns true if the library was successfully loaded | |||
| operator bool() const { return pimpl != nullptr; } | |||
| private: | |||
| struct Pimpl; | |||
| std::unique_ptr<Pimpl> pimpl; | |||
| }; | |||
| END_NAMESPACE_DISTRHO | |||
| //============================================================================== | |||
| // _ _ _ _ | |||
| // __| | ___ | |_ __ _ (_)| | ___ | |||
| // / _` | / _ \| __| / _` || || |/ __| | |||
| // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ | |||
| // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) | |||
| // | |||
| // Code beyond this point is implementation detail... | |||
| // | |||
| //============================================================================== | |||
| #include <vector> | |||
| #include <unordered_map> | |||
| #undef WIN32_LEAN_AND_MEAN | |||
| #define WIN32_LEAN_AND_MEAN | |||
| #undef NOMINMAX | |||
| #define NOMINMAX | |||
| #define Rectangle Rectangle_renamed_to_avoid_name_collisions | |||
| #include <windows.h> | |||
| #undef Rectangle | |||
| START_NAMESPACE_DISTRHO | |||
| struct MemoryDLL::Pimpl | |||
| { | |||
| Pimpl() = default; | |||
| ~Pimpl() | |||
| { | |||
| if (entryFunction != nullptr) | |||
| (*reinterpret_cast<DLLEntryFn> (entryFunction)) ((HINSTANCE) imageData, DLL_PROCESS_DETACH, nullptr); | |||
| for (auto& m : loadedModules) | |||
| FreeLibrary (m); | |||
| if (imageData != nullptr) | |||
| VirtualFree (imageData, 0, MEM_RELEASE); | |||
| for (auto m : virtualBlocks) | |||
| VirtualFree (m, 0, MEM_RELEASE); | |||
| } | |||
| bool initialise (const void* data, size_t size) | |||
| { | |||
| if (size < sizeof (IMAGE_DOS_HEADER)) | |||
| return false; | |||
| auto dosHeader = static_cast<const IMAGE_DOS_HEADER*> (data); | |||
| if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE | |||
| || size < static_cast<size_t> (dosHeader->e_lfanew) + sizeof (IMAGE_NT_HEADERS)) | |||
| return false; | |||
| const auto& headers = *getOffsetAs<IMAGE_NT_HEADERS> (data, dosHeader->e_lfanew); | |||
| if (headers.Signature != IMAGE_NT_SIGNATURE | |||
| || (headers.OptionalHeader.SectionAlignment & 1) != 0) | |||
| return false; | |||
| #ifdef _M_ARM64 | |||
| if (headers.FileHeader.Machine != IMAGE_FILE_MACHINE_ARM64) return false; | |||
| #elif defined (_WIN64) | |||
| if (headers.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) return false; | |||
| #else | |||
| if (headers.FileHeader.Machine != IMAGE_FILE_MACHINE_I386) return false; | |||
| #endif | |||
| SYSTEM_INFO systemInfo; | |||
| GetNativeSystemInfo (std::addressof (systemInfo)); | |||
| auto alignedImageSize = roundUp (headers.OptionalHeader.SizeOfImage, systemInfo.dwPageSize); | |||
| if (alignedImageSize != roundUp (getLastSectionEnd (headers), systemInfo.dwPageSize)) | |||
| return false; | |||
| imageData = VirtualAlloc (reinterpret_cast<void*> (headers.OptionalHeader.ImageBase), | |||
| alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |||
| if (imageData == nullptr) | |||
| { | |||
| imageData = VirtualAlloc (nullptr, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |||
| if (imageData == nullptr) | |||
| return false; | |||
| } | |||
| while ((((uint64_t) imageData) >> 32) < (((uint64_t) imageData + alignedImageSize) >> 32)) | |||
| { | |||
| virtualBlocks.push_back (imageData); | |||
| imageData = VirtualAlloc (nullptr, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |||
| if (imageData == nullptr) | |||
| return false; | |||
| } | |||
| isDLL = (headers.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0; | |||
| pageSize = systemInfo.dwPageSize; | |||
| if (size < headers.OptionalHeader.SizeOfHeaders) | |||
| return false; | |||
| auto newHeaders = VirtualAlloc (imageData, headers.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE); | |||
| memcpy (newHeaders, dosHeader, headers.OptionalHeader.SizeOfHeaders); | |||
| imageHeaders = getOffsetAs<IMAGE_NT_HEADERS> (newHeaders, dosHeader->e_lfanew); | |||
| imageHeaders->OptionalHeader.ImageBase = reinterpret_cast<uintptr_t> (imageData); | |||
| if (copySections (data, size, headers.OptionalHeader.SectionAlignment)) | |||
| { | |||
| if (auto locationDelta = (ptrdiff_t) (imageHeaders->OptionalHeader.ImageBase - headers.OptionalHeader.ImageBase)) | |||
| performRelocation (locationDelta); | |||
| if (loadImports() && prepareSections()) | |||
| { | |||
| executeTLS(); | |||
| loadNameTable(); | |||
| if (imageHeaders->OptionalHeader.AddressOfEntryPoint != 0) | |||
| { | |||
| entryFunction = getOffsetAs<char> (imageData, imageHeaders->OptionalHeader.AddressOfEntryPoint); | |||
| if (isDLL) | |||
| return (*reinterpret_cast<DLLEntryFn> (entryFunction)) ((HINSTANCE) imageData, DLL_PROCESS_ATTACH, nullptr); | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| void* findFunction (std::string_view name) | |||
| { | |||
| if (auto found = exportedFunctionOffsets.find (std::string (name)); found != exportedFunctionOffsets.end()) | |||
| return getOffsetAs<char> (imageData, found->second); | |||
| return {}; | |||
| } | |||
| private: | |||
| PIMAGE_NT_HEADERS imageHeaders = {}; | |||
| void* imageData = nullptr; | |||
| std::vector<HMODULE> loadedModules; | |||
| uint32_t pageSize = 0; | |||
| bool isDLL = false; | |||
| std::vector<void*> virtualBlocks; | |||
| std::unordered_map<std::string, size_t> exportedFunctionOffsets; | |||
| using DLLEntryFn = BOOL(WINAPI*)(HINSTANCE, DWORD, void*); | |||
| void* entryFunction = {}; | |||
| template <typename Type, typename Diff> | |||
| static const Type* getOffsetAs (const void* address, Diff offset) | |||
| { | |||
| return reinterpret_cast<const Type*> (static_cast<const char*> (address) + offset); | |||
| } | |||
| template <typename Type, typename Diff> | |||
| static Type* getOffsetAs (void* address, Diff offset) | |||
| { | |||
| return reinterpret_cast<Type*> (static_cast<char*> (address) + offset); | |||
| } | |||
| template <typename Type> | |||
| Type* getDataDirectoryAddress (int type) const | |||
| { | |||
| auto& dd = imageHeaders->OptionalHeader.DataDirectory[type]; | |||
| if (dd.Size > 0) | |||
| return getOffsetAs<Type> (imageData, dd.VirtualAddress); | |||
| return {}; | |||
| } | |||
| static size_t getLastSectionEnd (const IMAGE_NT_HEADERS& headers) | |||
| { | |||
| auto section = IMAGE_FIRST_SECTION (&headers); | |||
| auto optionalSectionSize = headers.OptionalHeader.SectionAlignment; | |||
| size_t lastSectionEnd = 0; | |||
| for (uint32_t i = 0; i < headers.FileHeader.NumberOfSections; ++i, ++section) | |||
| { | |||
| auto end = section->VirtualAddress + (section->SizeOfRawData == 0 ? optionalSectionSize | |||
| : section->SizeOfRawData); | |||
| if (end > lastSectionEnd) | |||
| lastSectionEnd = end; | |||
| } | |||
| return lastSectionEnd; | |||
| } | |||
| void loadNameTable() | |||
| { | |||
| if (auto exports = getDataDirectoryAddress<IMAGE_EXPORT_DIRECTORY> (IMAGE_DIRECTORY_ENTRY_EXPORT)) | |||
| { | |||
| if (exports->NumberOfNames > 0 && exports->NumberOfFunctions > 0) | |||
| { | |||
| auto name = getOffsetAs<const DWORD> (imageData, exports->AddressOfNames); | |||
| auto ordinal = getOffsetAs<const WORD> (imageData, exports->AddressOfNameOrdinals); | |||
| exportedFunctionOffsets.reserve (exports->NumberOfNames); | |||
| for (size_t i = 0; i < exports->NumberOfNames; ++i, ++name, ++ordinal) | |||
| if (*ordinal <= exports->NumberOfFunctions) | |||
| exportedFunctionOffsets[std::string (getOffsetAs<const char> (imageData, *name))] | |||
| = getOffsetAs<DWORD> (imageData, exports->AddressOfFunctions)[*ordinal]; | |||
| } | |||
| } | |||
| } | |||
| void executeTLS() | |||
| { | |||
| if (auto tls = getDataDirectoryAddress<IMAGE_TLS_DIRECTORY> (IMAGE_DIRECTORY_ENTRY_TLS)) | |||
| if (auto callback = reinterpret_cast<PIMAGE_TLS_CALLBACK*> (tls->AddressOfCallBacks)) | |||
| while (*callback != nullptr) | |||
| (*callback++) ((void*) imageData, DLL_PROCESS_ATTACH, nullptr); | |||
| } | |||
| bool prepareSections() | |||
| { | |||
| auto section = IMAGE_FIRST_SECTION (imageHeaders); | |||
| auto size = getSectionSize (*section); | |||
| auto address = getSectionAddress (*section); | |||
| auto addressPage = getPageBase (address); | |||
| auto characteristics = section->Characteristics; | |||
| for (WORD i = 1; i < imageHeaders->FileHeader.NumberOfSections; ++i) | |||
| { | |||
| ++section; | |||
| auto nextSize = getSectionSize (*section); | |||
| auto nextAddress = getSectionAddress (*section); | |||
| auto nextAddressPage = getPageBase (nextAddress); | |||
| if (addressPage == nextAddressPage || address + size > nextAddressPage) | |||
| { | |||
| if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) | |||
| characteristics = (characteristics | section->Characteristics) & ~static_cast<DWORD> (IMAGE_SCN_MEM_DISCARDABLE); | |||
| else | |||
| characteristics |= section->Characteristics; | |||
| size = static_cast<size_t> ((nextAddress + nextSize) - address); | |||
| continue; | |||
| } | |||
| if (! setProtectionFlags (size, characteristics, address, addressPage, false)) | |||
| return false; | |||
| size = nextSize; | |||
| address = nextAddress; | |||
| addressPage = nextAddressPage; | |||
| characteristics = section->Characteristics; | |||
| } | |||
| return setProtectionFlags (size, characteristics, address, addressPage, true); | |||
| } | |||
| bool setProtectionFlags (size_t sectionSize, DWORD sectionCharacteristics, void* sectionAddress, void* addressPage, bool isLast) | |||
| { | |||
| if (sectionSize == 0) | |||
| return true; | |||
| if ((sectionCharacteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) | |||
| { | |||
| if (sectionAddress == addressPage | |||
| && (isLast || imageHeaders->OptionalHeader.SectionAlignment == pageSize || (sectionSize % pageSize) == 0)) | |||
| VirtualFree (sectionAddress, sectionSize, MEM_DECOMMIT); | |||
| return true; | |||
| } | |||
| auto getProtectionFlags = [] (DWORD type) | |||
| { | |||
| return ((type & IMAGE_SCN_MEM_NOT_CACHED) ? PAGE_NOCACHE : 0) | |||
| | ((type & IMAGE_SCN_MEM_EXECUTE) | |||
| ? ((type & IMAGE_SCN_MEM_READ) | |||
| ? ((type & IMAGE_SCN_MEM_WRITE) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) | |||
| : ((type & IMAGE_SCN_MEM_WRITE) ? PAGE_EXECUTE_WRITECOPY : PAGE_EXECUTE)) | |||
| : ((type & IMAGE_SCN_MEM_READ) | |||
| ? ((type & IMAGE_SCN_MEM_WRITE) ? PAGE_READWRITE : PAGE_READONLY) | |||
| : ((type & IMAGE_SCN_MEM_WRITE) ? PAGE_WRITECOPY : PAGE_NOACCESS))); | |||
| }; | |||
| DWORD oldProtectValue; | |||
| return VirtualProtect (sectionAddress, sectionSize, | |||
| static_cast<DWORD> (getProtectionFlags (sectionCharacteristics)), | |||
| std::addressof (oldProtectValue)) != 0; | |||
| } | |||
| bool loadImports() | |||
| { | |||
| auto imp = getDataDirectoryAddress<IMAGE_IMPORT_DESCRIPTOR> (IMAGE_DIRECTORY_ENTRY_IMPORT); | |||
| if (imp == nullptr) | |||
| return false; | |||
| while (! IsBadReadPtr (imp, sizeof (IMAGE_IMPORT_DESCRIPTOR)) && imp->Name != 0) | |||
| { | |||
| auto handle = LoadLibraryA (getOffsetAs<const char> (imageData, imp->Name)); | |||
| if (handle == nullptr) | |||
| return false; | |||
| loadedModules.push_back (handle); | |||
| auto thunkRef = getOffsetAs<uintptr_t> (imageData, imp->OriginalFirstThunk ? imp->OriginalFirstThunk | |||
| : imp->FirstThunk); | |||
| auto funcRef = getOffsetAs<FARPROC> (imageData, imp->FirstThunk); | |||
| for (; *thunkRef != 0; ++thunkRef, ++funcRef) | |||
| { | |||
| auto name = IMAGE_SNAP_BY_ORDINAL(*thunkRef) | |||
| ? (LPCSTR) IMAGE_ORDINAL(*thunkRef) | |||
| : (LPCSTR) std::addressof (getOffsetAs<IMAGE_IMPORT_BY_NAME> (imageData, *thunkRef)->Name); | |||
| *funcRef = GetProcAddress (handle, name); | |||
| if (*funcRef == 0) | |||
| return false; | |||
| } | |||
| ++imp; | |||
| } | |||
| return true; | |||
| } | |||
| size_t getSectionSize (const IMAGE_SECTION_HEADER& section) const | |||
| { | |||
| if (auto size = section.SizeOfRawData) | |||
| return size; | |||
| if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) | |||
| return imageHeaders->OptionalHeader.SizeOfInitializedData; | |||
| if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) | |||
| return imageHeaders->OptionalHeader.SizeOfUninitializedData; | |||
| return 0; | |||
| } | |||
| static size_t roundUp (size_t value, size_t alignment) { return (value + alignment - 1) & ~(alignment - 1); } | |||
| char* getPageBase (void* address) const { return (char*) (reinterpret_cast<uintptr_t> (address) & ~static_cast<uintptr_t> (pageSize - 1)); } | |||
| char* getSectionAddress (const IMAGE_SECTION_HEADER& section) const | |||
| { | |||
| #ifdef _WIN64 | |||
| return reinterpret_cast<char*> (static_cast<uintptr_t> (section.Misc.PhysicalAddress) | |||
| | (static_cast<uintptr_t> (imageHeaders->OptionalHeader.ImageBase & 0xffffffff00000000))); | |||
| #else | |||
| return reinterpret_cast<char*> (section.Misc.PhysicalAddress); | |||
| #endif | |||
| } | |||
| bool copySections (const void* data, size_t size, size_t sectionAlignment) const | |||
| { | |||
| auto section = IMAGE_FIRST_SECTION (imageHeaders); | |||
| for (int i = 0; i < imageHeaders->FileHeader.NumberOfSections; ++i, ++section) | |||
| { | |||
| if (section->SizeOfRawData == 0) | |||
| { | |||
| if (sectionAlignment == 0) | |||
| continue; | |||
| if (auto dest = VirtualAlloc (getOffsetAs<char> (imageData, section->VirtualAddress), | |||
| sectionAlignment, MEM_COMMIT, PAGE_READWRITE)) | |||
| { | |||
| dest = getOffsetAs<char> (imageData, section->VirtualAddress); | |||
| section->Misc.PhysicalAddress = static_cast<uint32_t> (reinterpret_cast<uintptr_t> (dest)); | |||
| memset (dest, 0, sectionAlignment); | |||
| continue; | |||
| } | |||
| return false; | |||
| } | |||
| if (size < section->PointerToRawData + section->SizeOfRawData) | |||
| return false; | |||
| if (auto dest = VirtualAlloc (getOffsetAs<char> (imageData, section->VirtualAddress), | |||
| section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE)) | |||
| { | |||
| dest = getOffsetAs<char> (imageData, section->VirtualAddress); | |||
| memcpy (dest, static_cast<const char*> (data) + section->PointerToRawData, section->SizeOfRawData); | |||
| section->Misc.PhysicalAddress = static_cast<uint32_t> (reinterpret_cast<uintptr_t> (dest)); | |||
| continue; | |||
| } | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| void performRelocation (ptrdiff_t delta) | |||
| { | |||
| auto directory = imageHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |||
| if (directory.Size != 0) | |||
| { | |||
| auto relocation = getOffsetAs<IMAGE_BASE_RELOCATION> (imageData, directory.VirtualAddress); | |||
| while (relocation->VirtualAddress > 0) | |||
| { | |||
| auto dest = getOffsetAs<char> (imageData, relocation->VirtualAddress); | |||
| auto offset = getOffsetAs<uint16_t> (relocation, sizeof (IMAGE_BASE_RELOCATION)); | |||
| for (uint32_t i = 0; i < (relocation->SizeOfBlock - sizeof (IMAGE_BASE_RELOCATION)) / 2; ++i, ++offset) | |||
| { | |||
| switch (*offset >> 12) | |||
| { | |||
| case IMAGE_REL_BASED_HIGHLOW: addDelta<uint32_t> (dest + (*offset & 0xfff), delta); break; | |||
| case IMAGE_REL_BASED_DIR64: addDelta<uint64_t> (dest + (*offset & 0xfff), delta); break; | |||
| case IMAGE_REL_BASED_ABSOLUTE: | |||
| default: break; | |||
| } | |||
| } | |||
| relocation = getOffsetAs<IMAGE_BASE_RELOCATION> (relocation, relocation->SizeOfBlock); | |||
| } | |||
| } | |||
| } | |||
| template <typename Type> | |||
| static void addDelta (char* addr, ptrdiff_t delta) | |||
| { | |||
| *reinterpret_cast<Type*> (addr) = static_cast<Type> (static_cast<ptrdiff_t> (*reinterpret_cast<Type*> (addr)) + delta); | |||
| } | |||
| }; | |||
| inline MemoryDLL::~MemoryDLL() = default; | |||
| inline MemoryDLL::MemoryDLL (const void* data, size_t size) : pimpl (std::make_unique<Pimpl>()) | |||
| { | |||
| if (! pimpl->initialise (data, size)) | |||
| pimpl.reset(); | |||
| } | |||
| inline void* MemoryDLL::findFunction (std::string_view name) | |||
| { | |||
| return pimpl != nullptr ? pimpl->findFunction (name) : nullptr; | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // CHOC_MEMORYDLL_HEADER_INCLUDED | |||
| @@ -1,67 +0,0 @@ | |||
| // | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes ** | |||
| // ██ ███████ ██ ██ ██ | |||
| // ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // | |||
| // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: | |||
| // | |||
| // 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 CHOC_PLATFORM_DETECT_HEADER_INCLUDED | |||
| #define CHOC_PLATFORM_DETECT_HEADER_INCLUDED | |||
| /* | |||
| These conditionals declare the macros | |||
| - CHOC_WINDOWS | |||
| - CHOC_ANDROID | |||
| - CHOC_LINUX | |||
| - CHOC_OSX | |||
| - CHOC_IOS | |||
| ...based on the current operating system. | |||
| It also declares a string literal macro CHOC_OPERATING_SYSTEM_NAME | |||
| which can be used if you need a text description of the OS. | |||
| */ | |||
| #if defined (_WIN32) || defined (_WIN64) | |||
| #define CHOC_WINDOWS 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "Windows" | |||
| #elif __ANDROID__ | |||
| #define CHOC_ANDROID 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "Android" | |||
| #elif defined (LINUX) || defined (__linux__) | |||
| #define CHOC_LINUX 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "Linux" | |||
| #elif __APPLE__ | |||
| #define CHOC_APPLE 1 | |||
| #include <TargetConditionals.h> | |||
| #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR | |||
| #define CHOC_IOS 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "iOS" | |||
| #else | |||
| #define CHOC_OSX 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "OSX" | |||
| #endif | |||
| #elif defined (__FreeBSD__) || (__OpenBSD__) | |||
| #define CHOC_BSD 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "BSD" | |||
| #elif defined (_POSIX_VERSION) | |||
| #define CHOC_POSIX 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "Posix" | |||
| #elif defined (__EMSCRIPTEN__) | |||
| #define CHOC_EMSCRIPTEN 1 | |||
| #define CHOC_OPERATING_SYSTEM_NAME "Emscripten" | |||
| #else | |||
| #error "Unknown platform!" | |||
| #endif | |||
| #endif // CHOC_PLATFORM_DETECT_HEADER_INCLUDED | |||
| @@ -1,74 +0,0 @@ | |||
| // | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes ** | |||
| // ██ ███████ ██ ██ ██ | |||
| // ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc | |||
| // ██████ ██ ██ ██████ ██████ | |||
| // | |||
| // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: | |||
| // | |||
| // 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 CHOC_STRING_UTILS_HEADER_INCLUDED | |||
| #define CHOC_STRING_UTILS_HEADER_INCLUDED | |||
| #include <cctype> | |||
| #include <string> | |||
| #include <vector> | |||
| #include <cmath> | |||
| #include <chrono> | |||
| #include <memory> | |||
| #include <algorithm> | |||
| #include <cwctype> | |||
| START_NAMESPACE_DISTRHO | |||
| //============================================================================== | |||
| /// Returns a hex string for the given value. | |||
| /// If the minimum number of digits is non-zero, it will be zero-padded to fill this length; | |||
| template <typename IntegerType> | |||
| std::string createHexString (IntegerType value); | |||
| //============================================================================== | |||
| // _ _ _ _ | |||
| // __| | ___ | |_ __ _ (_)| | ___ | |||
| // / _` | / _ \| __| / _` || || |/ __| | |||
| // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ | |||
| // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) | |||
| // | |||
| // Code beyond this point is implementation detail... | |||
| // | |||
| //============================================================================== | |||
| template <typename IntegerType> | |||
| std::string createHexString (IntegerType v) | |||
| { | |||
| static_assert (std::is_integral<IntegerType>::value, "Need to pass integers into this method"); | |||
| auto value = static_cast<typename std::make_unsigned<IntegerType>::type> (v); | |||
| char hex[40]; | |||
| const auto end = hex + sizeof (hex) - 1; | |||
| auto d = end; | |||
| *d = 0; | |||
| for (;;) | |||
| { | |||
| *--d = "0123456789abcdef"[static_cast<uint32_t> (value) & 15u]; | |||
| value = static_cast<decltype (value)> (value >> 4); | |||
| if (value == 0) | |||
| return std::string (d, end); | |||
| } | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| @@ -1,194 +0,0 @@ | |||
| /* libSOFD - Simple Open File Dialog [for X11 without toolkit] | |||
| * | |||
| * Copyright (C) 2014 Robin Gareus <robin@gareus.org> | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice and this permission notice shall be included in | |||
| * all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #ifndef LIBSOFD_H | |||
| #define LIBSOFD_H 1 | |||
| #ifdef HAVE_X11 | |||
| #include <X11/Xlib.h> | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| /* public API */ | |||
| /** open a file select dialog | |||
| * @param dpy X Display connection | |||
| * @param parent (optional) if not NULL, become transient for given window | |||
| * @param x if >0 set explict initial width of the window | |||
| * @param y if >0 set explict initial height of the window | |||
| * @return 0 on success | |||
| */ | |||
| int x_fib_show (Display *dpy, Window parent, int x, int y, double scalefactor); | |||
| /** force close the dialog. | |||
| * This is normally not needed, the dialog closes itself | |||
| * when a file is selected or the user cancels selection. | |||
| * @param dpy X Display connection | |||
| */ | |||
| void x_fib_close (Display *dpy); | |||
| /** non-blocking X11 event handler. | |||
| * It is safe to run this function even if the dialog is | |||
| * closed or was not initialized. | |||
| * | |||
| * @param dpy X Display connection | |||
| * @param event the XEvent to process | |||
| * @return status | |||
| * 0: the event was not for this window, or file-dialog still | |||
| * active, or the dialog window is not displayed. | |||
| * >0: file was selected, dialog closed | |||
| * <0: file selection was cancelled. | |||
| */ | |||
| int x_fib_handle_events (Display *dpy, XEvent *event); | |||
| /** last status of the dialog | |||
| * @return >0: file was selected, <0: canceled or inactive. 0: active | |||
| */ | |||
| int x_fib_status (); | |||
| /** query the selected filename | |||
| * @return NULL if none set, or allocated string to be free()ed by the called | |||
| */ | |||
| char *x_fib_filename (); | |||
| /** customize/configure the dialog before calling \ref x_fib_show | |||
| * changes only have any effect if the dialog is not visible. | |||
| * @param k key to change | |||
| * 0: set current dir to display (must end with slash) | |||
| * 1: set title of dialog window | |||
| * 2: specify a custom X11 font to use | |||
| * 3: specify a custom 'places' file to include | |||
| * (following gtk-bookmark convention) | |||
| * @param v value | |||
| * @return 0 on success. | |||
| */ | |||
| int x_fib_configure (int k, const char *v); | |||
| /** customize/configure the dialog before calling \ref x_fib_show | |||
| * changes only have any effect if the dialog is not visible. | |||
| * | |||
| * @param k button to change: | |||
| * 1: show hidden files | |||
| * 2: show places | |||
| * 3: show filter/list all (automatically hidden if there is no | |||
| * filter function) | |||
| * @param v <0 to hide the button >=0 show button, | |||
| * 0: set button-state to not-checked | |||
| * 1: set button-state to checked | |||
| * >1: retain current state | |||
| * @return 0 on success. | |||
| */ | |||
| int x_fib_cfg_buttons (int k, int v); | |||
| /** set custom callback to filter file-names. | |||
| * NULL will disable the filter and hide the 'show all' button. | |||
| * changes only have any effect if the dialog is not visible. | |||
| * | |||
| * @param cb callback function to check file | |||
| * the callback function is called with the file name (basename only) | |||
| * and is expected to return 1 if the file passes the filter | |||
| * and 0 if the file should not be listed by default. | |||
| * @return 0 on success. | |||
| */ | |||
| int x_fib_cfg_filter_callback (int (*cb)(const char*)); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* END X11 specific functions */ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /* 'recently used' API. x-platform | |||
| * NOTE: all functions use a static cache and are not reentrant. | |||
| * It is expected that none of these functions are called in | |||
| * parallel from different threads. | |||
| */ | |||
| /** release static resources of 'recently used files' | |||
| */ | |||
| void x_fib_free_recent (); | |||
| /** add an entry to the recently used list | |||
| * | |||
| * The dialog does not add files automatically on open, | |||
| * if the application succeeds to open a selected file, | |||
| * this function should be called. | |||
| * | |||
| * @param path complete path to file | |||
| * @param atime time of last use, 0: NOW | |||
| * @return -1 on error, number of current entries otherwise | |||
| */ | |||
| int x_fib_add_recent (const char *path, time_t atime); | |||
| /** get a platform specific path to a good location for | |||
| * saving the recently used file list. | |||
| * (follows XDG_DATA_HOME on Unix, and CSIDL_LOCAL_APPDATA spec) | |||
| * | |||
| * @param application-name to use to include in file | |||
| * @return pointer to static path or NULL | |||
| */ | |||
| const char *x_fib_recent_file(const char *appname); | |||
| /** save the current list of recently used files to the given filename | |||
| * (the format is one file per line, filename URL encoded and space separated | |||
| * with last-used timestamp) | |||
| * | |||
| * This function tries to creates the containing directory if it does | |||
| * not exist. | |||
| * | |||
| * @param fn file to save the list to | |||
| * @return 0: on success | |||
| */ | |||
| int x_fib_save_recent (const char *fn); | |||
| /** load a recently used file list. | |||
| * | |||
| * @param fn file to load the list from | |||
| * @return 0: on success | |||
| */ | |||
| int x_fib_load_recent (const char *fn); | |||
| /** get number of entries in the current list | |||
| * @return number of entries in the recently used list | |||
| */ | |||
| unsigned int x_fib_recent_count (); | |||
| /** get recently used entry at given position | |||
| * | |||
| * @param i entry to query | |||
| * @return pointer to static string | |||
| */ | |||
| const char *x_fib_recent_at (unsigned int i); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif // header guard | |||
| @@ -66,6 +66,10 @@ | |||
| # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_USES_MODGUI | |||
| # define DISTRHO_PLUGIN_USES_MODGUI 0 | |||
| #endif | |||
| @@ -260,14 +264,14 @@ void lv2_generate_ttl(const char* const basename) | |||
| const String pluginDLL(basename); | |||
| const String pluginTTL(pluginDLL + ".ttl"); | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| String pluginUI(pluginDLL); | |||
| # if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| #if ! DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| pluginUI.truncate(pluginDLL.rfind("_dsp")); | |||
| pluginUI += "_ui"; | |||
| const String uiTTL(pluginUI + ".ttl"); | |||
| # endif | |||
| #endif | |||
| #endif | |||
| #endif | |||
| // --------------------------------------------- | |||
| @@ -278,7 +282,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| String manifestString; | |||
| manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
| manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
| #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| manifestString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| @@ -304,14 +308,14 @@ void lv2_generate_ttl(const char* const basename) | |||
| manifestString += "<" DISTRHO_UI_URI ">\n"; | |||
| manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | |||
| manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||
| # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| # if DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| addAttribute(manifestString, "lv2:extensionData", lv2ManifestUiExtensionData, 4); | |||
| addAttribute(manifestString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4); | |||
| addAttribute(manifestString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | |||
| addAttribute(manifestString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | |||
| # else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| # else // DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | |||
| # endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| # endif // DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| manifestString += "\n"; | |||
| #endif | |||
| @@ -1564,7 +1568,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| // --------------------------------------------- | |||
| #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
| { | |||
| std::cout << "Writing " << uiTTL << "..."; std::cout.flush(); | |||
| std::fstream uiFile(uiTTL, std::ios::out); | |||
| @@ -35,16 +35,40 @@ mkdir plugins | |||
| mv repos/DPF dpf | |||
| rm -rf dpf/.git* | |||
| rm -rf dpf/.travis* | |||
| rm -rf dpf/distrho/extra/choc | |||
| rm -rf dpf/distrho/extra/sofd | |||
| rm -rf dpf/dgl/pugl-extra | |||
| rm -rf dpf/dgl/src/pugl-upstream/bindings | |||
| rm -rf dpf/dgl/src/pugl-upstream/doc | |||
| rm -rf dpf/dgl/src/pugl-upstream/examples | |||
| rm -rf dpf/dgl/src/pugl-upstream/meson* | |||
| rm -rf dpf/dgl/src/pugl-upstream/resources | |||
| rm -rf dpf/dgl/src/pugl-upstream/scripts | |||
| rm -rf dpf/dgl/src/pugl-upstream/subprojects | |||
| rm -rf dpf/dgl/src/pugl-upstream/test | |||
| rm -rf dpf/cmake | |||
| rm -rf dpf/examples | |||
| rm -rf dpf/lac | |||
| rm -rf dpf/tests | |||
| rm -f dpf/Makefile | |||
| rm -f dpf/dgl/FileBrowserDialog.hpp | |||
| rm -f dpf/dgl/Layout.hpp | |||
| rm -f dpf/dgl/Vulkan.hpp | |||
| rm -f dpf/dgl/WebView.hpp | |||
| rm -f dpf/dgl/src/Layout.cpp | |||
| rm -f dpf/dgl/src/Stub.cpp | |||
| rm -f dpf/dgl/src/Vulkan.cpp | |||
| rm -f dpf/dgl/src/WebViewWin32.cpp | |||
| rm -f dpf/distrho/extra/Base64.hpp | |||
| rm -f dpf/distrho/extra/ChildProcess.hpp | |||
| rm -f dpf/distrho/extra/ExternalWindow.hpp | |||
| rm -f dpf/distrho/extra/FileBrowserDialog* | |||
| rm -f dpf/distrho/extra/Filesystem.hpp | |||
| rm -f dpf/distrho/extra/Runner.hpp | |||
| rm -f dpf/distrho/extra/Time.hpp | |||
| rm -f dpf/distrho/extra/WebView* | |||
| sed -i '/ $(BUILD_DIR)\/dgl\/Layout.cpp.o \\/d' dpf/dgl/Makefile | |||
| for PLUGIN in ${PLUGINS[@]}; do | |||
| for f in $(ls repos/${PLUGIN}/plugins/); do | |||