| @@ -0,0 +1,13 @@ | |||||
| DISTRHO Plugin Framework (DPF) | |||||
| Copyright (C) 2012-2014 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. | |||||
| @@ -0,0 +1,32 @@ | |||||
| # DPF - DISTRHO Plugin Framework | |||||
| DPF is designed to make development of new plugins an easy and enjoyable task.<br/> | |||||
| It allows developers to create plugins with custom UIs using a simple C++ API.<br/> | |||||
| The framework facilitates exporting various different plugin formats from the same code-base.<br/> | |||||
| DPF can build for LADSPA, DSSI, LV2 and VST formats.<br/> | |||||
| LADSPA, DSSI and VST implementations are complete, LV2 at ~95% completion.<br/> | |||||
| A JACK/Standalone mode is also available, allowing you to quickly test plugins.<br/> | |||||
| Plugin DSP and UI communication is done via key-value string pairs.<br/> | |||||
| You send messages from the UI to the DSP side, which is automatically saved in the host when required.<br/> | |||||
| Getting time information from the host is possible.<br/> | |||||
| It uses the same format as the JACK Transport API, making porting some code easier.<br/> | |||||
| List of plugins made with DPF:<br/> | |||||
| - [DISTRHO Mini-Series](https://github.com/DISTRHO/Mini-Series) | |||||
| - [DISTRHO MVerb](https://github.com/DISTRHO/MVerb) | |||||
| - [DISTRHO Nekobi](https://github.com/DISTRHO/Nekobi) | |||||
| - [DISTRHO ProM](https://github.com/DISTRHO/ProM) | |||||
| - [DISTRHO ndc Plugs](https://github.com/DISTRHO/ndc-Plugs) | |||||
| - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) (work in progress) | |||||
| - [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | |||||
| Plugin examples are available [here](https://github.com/DISTRHO/plugin-examples).<br/> | |||||
| OpenGL UI examples are available [here](https://github.com/DISTRHO/gl-examples). | |||||
| Online documentation is available at [https://distrho.github.io/DPF/](https://distrho.github.io/DPF/). | |||||
| @@ -0,0 +1,91 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_APP_HPP_INCLUDED | |||||
| #define DGL_APP_HPP_INCLUDED | |||||
| #include "Base.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // Forward class names | |||||
| class Window; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| Base DGL Application class. | |||||
| One application instance is required for creating a window. | |||||
| There's no single/global application instance in DGL, and multiple | |||||
| windows can share the same app instance. | |||||
| In standalone mode an application will automatically quit its | |||||
| event-loop when all its windows are closed. | |||||
| */ | |||||
| class Application | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor. | |||||
| */ | |||||
| Application(); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| ~Application(); | |||||
| /** | |||||
| Idle function. | |||||
| This runs the application event-loop once. | |||||
| */ | |||||
| void idle(); | |||||
| /** | |||||
| Run the application event-loop until all Windows are closed. | |||||
| idle() is called at regular intervals. | |||||
| @note This function is meant for standalones only, *never* call this from plugins. | |||||
| */ | |||||
| void exec(); | |||||
| /** | |||||
| Quit the application. | |||||
| This stops the event-loop and closes all Windows. | |||||
| */ | |||||
| void quit(); | |||||
| /** | |||||
| Check if the application is about to quit. | |||||
| Returning true means there's no event-loop running at the moment (or it's just about to stop). | |||||
| */ | |||||
| bool isQuiting() const noexcept; | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| friend class Window; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_APP_HPP_INCLUDED | |||||
| @@ -0,0 +1,195 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_BASE_HPP_INCLUDED | |||||
| #define DGL_BASE_HPP_INCLUDED | |||||
| #include "../distrho/extra/LeakDetector.hpp" | |||||
| #include "../distrho/extra/ScopedPointer.hpp" | |||||
| // ----------------------------------------------------------------------- | |||||
| // Define namespace | |||||
| #ifndef DGL_NAMESPACE | |||||
| # define DGL_NAMESPACE DGL | |||||
| #endif | |||||
| #define START_NAMESPACE_DGL namespace DGL_NAMESPACE { | |||||
| #define END_NAMESPACE_DGL } | |||||
| #define USE_NAMESPACE_DGL using namespace DGL_NAMESPACE; | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| // ----------------------------------------------------------------------- | |||||
| // Fix OpenGL includes for Windows, based on glfw code | |||||
| #ifndef APIENTRY | |||||
| # define APIENTRY __stdcall | |||||
| #endif // APIENTRY | |||||
| /* We need WINGDIAPI defined */ | |||||
| #ifndef WINGDIAPI | |||||
| # if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) | |||||
| # define WINGDIAPI __declspec(dllimport) | |||||
| # elif defined(__LCC__) | |||||
| # define WINGDIAPI __stdcall | |||||
| # else | |||||
| # define WINGDIAPI extern | |||||
| # endif | |||||
| # define DGL_WINGDIAPI_DEFINED | |||||
| #endif // WINGDIAPI | |||||
| /* Some <GL/glu.h> files also need CALLBACK defined */ | |||||
| #ifndef CALLBACK | |||||
| # if defined(_MSC_VER) | |||||
| # if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) | |||||
| # define CALLBACK __stdcall | |||||
| # else | |||||
| # define CALLBACK | |||||
| # endif | |||||
| # else | |||||
| # define CALLBACK __stdcall | |||||
| # endif | |||||
| # define DGL_CALLBACK_DEFINED | |||||
| #endif // CALLBACK | |||||
| /* Most GL/glu.h variants on Windows need wchar_t */ | |||||
| #include <cstddef> | |||||
| #endif // DISTRHO_OS_WINDOWS | |||||
| // ----------------------------------------------------------------------- | |||||
| // OpenGL includes | |||||
| #ifdef DISTRHO_OS_MAC | |||||
| # include "OpenGL/gl.h" | |||||
| #else | |||||
| # define GL_GLEXT_PROTOTYPES | |||||
| # include "GL/gl.h" | |||||
| # include "GL/glext.h" | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Missing OpenGL defines | |||||
| #if defined(GL_BGR_EXT) && ! defined(GL_BGR) | |||||
| # define GL_BGR GL_BGR_EXT | |||||
| #endif | |||||
| #if defined(GL_BGRA_EXT) && ! defined(GL_BGRA) | |||||
| # define GL_BGRA GL_BGRA_EXT | |||||
| #endif | |||||
| #ifndef GL_CLAMP_TO_BORDER | |||||
| # define GL_CLAMP_TO_BORDER 0x812D | |||||
| #endif | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| // ----------------------------------------------------------------------- | |||||
| // Fix OpenGL includes for Windows, based on glfw code | |||||
| #ifdef DGL_WINGDIAPI_DEFINED | |||||
| # undef WINGDIAPI | |||||
| # undef DGL_WINGDIAPI_DEFINED | |||||
| #endif | |||||
| #ifdef DGL_CALLBACK_DEFINED | |||||
| # undef CALLBACK | |||||
| # undef DGL_CALLBACK_DEFINED | |||||
| #endif | |||||
| #endif // DISTRHO_OS_WINDOWS | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // Base DGL enums | |||||
| /** | |||||
| Convenience symbols for ASCII control characters. | |||||
| */ | |||||
| enum Char { | |||||
| kCharBackspace = 0x08, | |||||
| kCharEscape = 0x1B, | |||||
| kCharDelete = 0x7F | |||||
| }; | |||||
| /** | |||||
| Keyboard modifier flags. | |||||
| */ | |||||
| enum Modifier { | |||||
| kModifierShift = 1 << 0, /**< Shift key */ | |||||
| kModifierControl = 1 << 1, /**< Control key */ | |||||
| kModifierAlt = 1 << 2, /**< Alt/Option key */ | |||||
| kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */ | |||||
| }; | |||||
| /** | |||||
| Special (non-Unicode) keyboard keys. | |||||
| */ | |||||
| enum Key { | |||||
| kKeyF1 = 1, | |||||
| kKeyF2, | |||||
| kKeyF3, | |||||
| kKeyF4, | |||||
| kKeyF5, | |||||
| kKeyF6, | |||||
| kKeyF7, | |||||
| kKeyF8, | |||||
| kKeyF9, | |||||
| kKeyF10, | |||||
| kKeyF11, | |||||
| kKeyF12, | |||||
| kKeyLeft, | |||||
| kKeyUp, | |||||
| kKeyRight, | |||||
| kKeyDown, | |||||
| kKeyPageUp, | |||||
| kKeyPageDown, | |||||
| kKeyHome, | |||||
| kKeyEnd, | |||||
| kKeyInsert, | |||||
| kKeyShift, | |||||
| kKeyControl, | |||||
| kKeyAlt, | |||||
| kKeySuper | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Base DGL classes | |||||
| /** | |||||
| Idle callback. | |||||
| */ | |||||
| class IdleCallback | |||||
| { | |||||
| public: | |||||
| virtual ~IdleCallback() {} | |||||
| virtual void idleCallback() = 0; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #ifndef DONT_SET_USING_DGL_NAMESPACE | |||||
| // If your code uses a lot of DGL classes, then this will obviously save you | |||||
| // a lot of typing, but can be disabled by setting DONT_SET_USING_DGL_NAMESPACE. | |||||
| using namespace DGL_NAMESPACE; | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| #endif // DGL_BASE_HPP_INCLUDED | |||||
| @@ -0,0 +1,110 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_COLOR_HPP_INCLUDED | |||||
| #define DGL_COLOR_HPP_INCLUDED | |||||
| #include "Base.hpp" | |||||
| struct NVGcolor; | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| A color made from red, green, blue and alpha floating-point values in [0..1] range. | |||||
| */ | |||||
| struct Color { | |||||
| /** | |||||
| Direct access to the color values. | |||||
| */ | |||||
| union { | |||||
| float rgba[4]; | |||||
| struct { float red, green, blue, alpha; }; | |||||
| }; | |||||
| /** | |||||
| Create solid black color. | |||||
| */ | |||||
| Color() noexcept; | |||||
| /** | |||||
| Create a color from red, green, blue and alpha numeric values. | |||||
| Values must be in [0..255] range. | |||||
| */ | |||||
| Color(int red, int green, int blue, int alpha = 255) noexcept; | |||||
| /** | |||||
| Create a color from red, green, blue and alpha floating-point values. | |||||
| Values must in [0..1] range. | |||||
| */ | |||||
| Color(float red, float green, float blue, float alpha = 1.0f) noexcept; | |||||
| /** | |||||
| Create a color by copying another color. | |||||
| */ | |||||
| Color(const Color& color) noexcept; | |||||
| Color& operator=(const Color& color) noexcept; | |||||
| /** | |||||
| Create a color by linearly interpolating two other colors. | |||||
| */ | |||||
| Color(const Color& color1, const Color& color2, float u) noexcept; | |||||
| /** | |||||
| Create a color specified by hue, saturation and lightness. | |||||
| Values must in [0..1] range. | |||||
| */ | |||||
| static Color fromHSL(float hue, float saturation, float lightness, float alpha = 1.0f); | |||||
| /** | |||||
| Create a color from a HTML string like "#333" or "#112233". | |||||
| */ | |||||
| static Color fromHTML(const char* rgb, float alpha = 1.0f); | |||||
| /** | |||||
| Linearly interpolate this color against another. | |||||
| */ | |||||
| void interpolate(const Color& other, float u) noexcept; | |||||
| /** | |||||
| Check if this color matches another. | |||||
| @note Comparison is forced within 8-bit color values. | |||||
| */ | |||||
| bool isEqual(const Color& color, bool withAlpha = true) noexcept; | |||||
| bool isNotEqual(const Color& color, bool withAlpha = true) noexcept; | |||||
| bool operator==(const Color& color) noexcept; | |||||
| bool operator!=(const Color& color) noexcept; | |||||
| /** | |||||
| Fix color bounds if needed. | |||||
| */ | |||||
| void fixBounds() noexcept; | |||||
| /** | |||||
| @internal | |||||
| Needed for NanoVG compatibility. | |||||
| */ | |||||
| Color(const NVGcolor&) noexcept; | |||||
| operator NVGcolor() const noexcept; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_COLOR_HPP_INCLUDED | |||||
| @@ -0,0 +1,750 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_GEOMETRY_HPP_INCLUDED | |||||
| #define DGL_GEOMETRY_HPP_INCLUDED | |||||
| #include "Base.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // Forward class names | |||||
| template<typename> class Line; | |||||
| template<typename> class Circle; | |||||
| template<typename> class Triangle; | |||||
| template<typename> class Rectangle; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Point class. | |||||
| This class describes a single point in space, defined by an X and Y value. | |||||
| */ | |||||
| template<typename T> | |||||
| class Point | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for (0, 0) point. | |||||
| */ | |||||
| Point() noexcept; | |||||
| /** | |||||
| Constructor using custom X and Y values. | |||||
| */ | |||||
| Point(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Constructor using another Point class values. | |||||
| */ | |||||
| Point(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Get X value. | |||||
| */ | |||||
| const T& getX() const noexcept; | |||||
| /** | |||||
| Get Y value. | |||||
| */ | |||||
| const T& getY() const noexcept; | |||||
| /** | |||||
| Set X value to @a x. | |||||
| */ | |||||
| void setX(const T& x) noexcept; | |||||
| /** | |||||
| Set Y value to @a y. | |||||
| */ | |||||
| void setY(const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values to @a x and @a y respectively. | |||||
| */ | |||||
| void setPos(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values according to @a pos. | |||||
| */ | |||||
| void setPos(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Move this point by @a x and @a y values. | |||||
| */ | |||||
| void moveBy(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Move this point by @a pos. | |||||
| */ | |||||
| void moveBy(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Return true if point is (0, 0). | |||||
| */ | |||||
| bool isZero() const noexcept; | |||||
| /** | |||||
| Return true if point is not (0, 0). | |||||
| */ | |||||
| bool isNotZero() const noexcept; | |||||
| Point<T> operator+(const Point<T>& pos) noexcept; | |||||
| Point<T> operator-(const Point<T>& pos) noexcept; | |||||
| Point<T>& operator=(const Point<T>& pos) noexcept; | |||||
| Point<T>& operator+=(const Point<T>& pos) noexcept; | |||||
| Point<T>& operator-=(const Point<T>& pos) noexcept; | |||||
| bool operator==(const Point<T>& pos) const noexcept; | |||||
| bool operator!=(const Point<T>& pos) const noexcept; | |||||
| private: | |||||
| T fX, fY; | |||||
| template<typename> friend class Line; | |||||
| template<typename> friend class Circle; | |||||
| template<typename> friend class Triangle; | |||||
| template<typename> friend class Rectangle; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Size class. | |||||
| This class describes a size, defined by a width and height value. | |||||
| */ | |||||
| template<typename T> | |||||
| class Size | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for null size (0x0). | |||||
| */ | |||||
| Size() noexcept; | |||||
| /** | |||||
| Constructor using custom width and height values. | |||||
| */ | |||||
| Size(const T& width, const T& height) noexcept; | |||||
| /** | |||||
| Constructor using another Size class values. | |||||
| */ | |||||
| Size(const Size<T>& size) noexcept; | |||||
| /** | |||||
| Get width. | |||||
| */ | |||||
| const T& getWidth() const noexcept; | |||||
| /** | |||||
| Get height. | |||||
| */ | |||||
| const T& getHeight() const noexcept; | |||||
| /** | |||||
| Set width. | |||||
| */ | |||||
| void setWidth(const T& width) noexcept; | |||||
| /** | |||||
| Set height. | |||||
| */ | |||||
| void setHeight(const T& height) noexcept; | |||||
| /** | |||||
| Set size to @a width and @a height. | |||||
| */ | |||||
| void setSize(const T& width, const T& height) noexcept; | |||||
| /** | |||||
| Set size. | |||||
| */ | |||||
| void setSize(const Size<T>& size) noexcept; | |||||
| /** | |||||
| Grow size by @a multiplier. | |||||
| */ | |||||
| void growBy(double multiplier) noexcept; | |||||
| /** | |||||
| Shrink size by @a divider. | |||||
| */ | |||||
| void shrinkBy(double divider) noexcept; | |||||
| /** | |||||
| Return true if size is null (0x0). | |||||
| An null size is also invalid. | |||||
| */ | |||||
| bool isNull() const noexcept; | |||||
| /** | |||||
| Return true if size is not null (0x0). | |||||
| A non-null size is still invalid if its width or height is negative. | |||||
| */ | |||||
| bool isNotNull() const noexcept; | |||||
| /** | |||||
| Return true if size is valid (width and height are higher than zero). | |||||
| */ | |||||
| bool isValid() const noexcept; | |||||
| /** | |||||
| Return true if size is invalid (width or height are lower or equal to zero). | |||||
| An invalid size might not be null under some circumstances. | |||||
| */ | |||||
| bool isInvalid() const noexcept; | |||||
| Size<T> operator+(const Size<T>& size) noexcept; | |||||
| Size<T> operator-(const Size<T>& size) noexcept; | |||||
| Size<T>& operator=(const Size<T>& size) noexcept; | |||||
| Size<T>& operator+=(const Size<T>& size) noexcept; | |||||
| Size<T>& operator-=(const Size<T>& size) noexcept; | |||||
| Size<T>& operator*=(double m) noexcept; | |||||
| Size<T>& operator/=(double d) noexcept; | |||||
| bool operator==(const Size<T>& size) const noexcept; | |||||
| bool operator!=(const Size<T>& size) const noexcept; | |||||
| private: | |||||
| T fWidth, fHeight; | |||||
| template<typename> friend class Rectangle; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Line class. | |||||
| This class describes a line, defined by two points. | |||||
| */ | |||||
| template<typename T> | |||||
| class Line | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for a null line ([0,0] to [0,0]). | |||||
| */ | |||||
| Line() noexcept; | |||||
| /** | |||||
| Constructor using custom start X, start Y, end X and end Y values. | |||||
| */ | |||||
| Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept; | |||||
| /** | |||||
| Constructor using custom start X, start Y and end pos values. | |||||
| */ | |||||
| Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept; | |||||
| /** | |||||
| Constructor using custom start pos, end X and end Y values. | |||||
| */ | |||||
| Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept; | |||||
| /** | |||||
| Constructor using custom start and end pos values. | |||||
| */ | |||||
| Line(const Point<T>& startPos, const Point<T>& endPos) noexcept; | |||||
| /** | |||||
| Constructor using another Line class values. | |||||
| */ | |||||
| Line(const Line<T>& line) noexcept; | |||||
| /** | |||||
| Get start X value. | |||||
| */ | |||||
| const T& getStartX() const noexcept; | |||||
| /** | |||||
| Get start Y value. | |||||
| */ | |||||
| const T& getStartY() const noexcept; | |||||
| /** | |||||
| Get end X value. | |||||
| */ | |||||
| const T& getEndX() const noexcept; | |||||
| /** | |||||
| Get end Y value. | |||||
| */ | |||||
| const T& getEndY() const noexcept; | |||||
| /** | |||||
| Get start position. | |||||
| */ | |||||
| const Point<T>& getStartPos() const noexcept; | |||||
| /** | |||||
| Get end position. | |||||
| */ | |||||
| const Point<T>& getEndPos() const noexcept; | |||||
| /** | |||||
| Set start X value to @a x. | |||||
| */ | |||||
| void setStartX(const T& x) noexcept; | |||||
| /** | |||||
| Set start Y value to @a y. | |||||
| */ | |||||
| void setStartY(const T& y) noexcept; | |||||
| /** | |||||
| Set start X and Y values to @a x and @a y respectively. | |||||
| */ | |||||
| void setStartPos(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Set start X and Y values according to @a pos. | |||||
| */ | |||||
| void setStartPos(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Set end X value to @a x. | |||||
| */ | |||||
| void setEndX(const T& x) noexcept; | |||||
| /** | |||||
| Set end Y value to @a y. | |||||
| */ | |||||
| void setEndY(const T& y) noexcept; | |||||
| /** | |||||
| Set end X and Y values to @a x and @a y respectively. | |||||
| */ | |||||
| void setEndPos(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Set end X and Y values according to @a pos. | |||||
| */ | |||||
| void setEndPos(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Move this line by @a x and @a y values. | |||||
| */ | |||||
| void moveBy(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Move this line by @a pos. | |||||
| */ | |||||
| void moveBy(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Draw this line using the current OpenGL state. | |||||
| */ | |||||
| void draw(); | |||||
| /** | |||||
| Return true if line is null (start and end pos are equal). | |||||
| */ | |||||
| bool isNull() const noexcept; | |||||
| /** | |||||
| Return true if line is not null (start and end pos are different). | |||||
| */ | |||||
| bool isNotNull() const noexcept; | |||||
| Line<T>& operator=(const Line<T>& line) noexcept; | |||||
| bool operator==(const Line<T>& line) const noexcept; | |||||
| bool operator!=(const Line<T>& line) const noexcept; | |||||
| private: | |||||
| Point<T> fPosStart, fPosEnd; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Circle class. | |||||
| This class describes a circle, defined by position, size and a minimum of 3 segments. | |||||
| TODO: report if circle starts at top-left, bottom-right or center. | |||||
| and size grows from which point? | |||||
| */ | |||||
| template<typename T> | |||||
| class Circle | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for a null circle. | |||||
| */ | |||||
| Circle() noexcept; | |||||
| /** | |||||
| Constructor using custom X, Y and size values. | |||||
| */ | |||||
| Circle(const T& x, const T& y, const float size, const uint numSegments = 300); | |||||
| /** | |||||
| Constructor using custom position and size values. | |||||
| */ | |||||
| Circle(const Point<T>& pos, const float size, const uint numSegments = 300); | |||||
| /** | |||||
| Constructor using another Circle class values. | |||||
| */ | |||||
| Circle(const Circle<T>& cir) noexcept; | |||||
| /** | |||||
| Get X value. | |||||
| */ | |||||
| const T& getX() const noexcept; | |||||
| /** | |||||
| Get Y value. | |||||
| */ | |||||
| const T& getY() const noexcept; | |||||
| /** | |||||
| Get position. | |||||
| */ | |||||
| const Point<T>& getPos() const noexcept; | |||||
| /** | |||||
| Set X value to @a x. | |||||
| */ | |||||
| void setX(const T& x) noexcept; | |||||
| /** | |||||
| Set Y value to @a y. | |||||
| */ | |||||
| void setY(const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values to @a x and @a y respectively. | |||||
| */ | |||||
| void setPos(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values according to @a pos. | |||||
| */ | |||||
| void setPos(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Get size. | |||||
| */ | |||||
| float getSize() const noexcept; | |||||
| /** | |||||
| Set size. | |||||
| @note Must always be > 0 | |||||
| */ | |||||
| void setSize(const float size) noexcept; | |||||
| /** | |||||
| Get the current number of line segments that make this circle. | |||||
| */ | |||||
| uint getNumSegments() const noexcept; | |||||
| /** | |||||
| Set the number of line segments that will make this circle. | |||||
| @note Must always be >= 3 | |||||
| */ | |||||
| void setNumSegments(const uint num); | |||||
| /** | |||||
| Draw this circle using the current OpenGL state. | |||||
| */ | |||||
| void draw(); | |||||
| /** | |||||
| Draw lines (outline of this circle) using the current OpenGL state. | |||||
| */ | |||||
| void drawOutline(); | |||||
| Circle<T>& operator=(const Circle<T>& cir) noexcept; | |||||
| bool operator==(const Circle<T>& cir) const noexcept; | |||||
| bool operator!=(const Circle<T>& cir) const noexcept; | |||||
| private: | |||||
| Point<T> fPos; | |||||
| float fSize; | |||||
| uint fNumSegments; | |||||
| // cached values | |||||
| float fTheta, fCos, fSin; | |||||
| void _draw(const bool outline); | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Triangle class. | |||||
| This class describes a triangle, defined by 3 points. | |||||
| */ | |||||
| template<typename T> | |||||
| class Triangle | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for a null triangle. | |||||
| */ | |||||
| Triangle() noexcept; | |||||
| /** | |||||
| Constructor using custom X and Y values. | |||||
| */ | |||||
| Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept; | |||||
| /** | |||||
| Constructor using custom position values. | |||||
| */ | |||||
| Triangle(const Point<T>& pos1, const Point<T>& pos2, const Point<T>& pos3) noexcept; | |||||
| /** | |||||
| Constructor using another Triangle class values. | |||||
| */ | |||||
| Triangle(const Triangle<T>& tri) noexcept; | |||||
| /** | |||||
| Draw this triangle using the current OpenGL state. | |||||
| */ | |||||
| void draw(); | |||||
| /** | |||||
| Draw lines (outline of this triangle) using the current OpenGL state. | |||||
| */ | |||||
| void drawOutline(); | |||||
| /** | |||||
| Return true if triangle is null (all its points are equal). | |||||
| An null triangle is also invalid. | |||||
| */ | |||||
| bool isNull() const noexcept; | |||||
| /** | |||||
| Return true if triangle is not null (one its points is different from the others). | |||||
| A non-null triangle is still invalid if two of its points are equal. | |||||
| */ | |||||
| bool isNotNull() const noexcept; | |||||
| /** | |||||
| Return true if triangle is valid (all its points are different). | |||||
| */ | |||||
| bool isValid() const noexcept; | |||||
| /** | |||||
| Return true if triangle is invalid (one or two of its points are equal). | |||||
| An invalid triangle might not be null under some circumstances. | |||||
| */ | |||||
| bool isInvalid() const noexcept; | |||||
| Triangle<T>& operator=(const Triangle<T>& tri) noexcept; | |||||
| bool operator==(const Triangle<T>& tri) const noexcept; | |||||
| bool operator!=(const Triangle<T>& tri) const noexcept; | |||||
| private: | |||||
| Point<T> fPos1, fPos2, fPos3; | |||||
| void _draw(const bool outline); | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| DGL Rectangle class. | |||||
| This class describes a rectangle, defined by a starting point and a size. | |||||
| */ | |||||
| template<typename T> | |||||
| class Rectangle | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for a null rectangle. | |||||
| */ | |||||
| Rectangle() noexcept; | |||||
| /** | |||||
| Constructor using custom X, Y, width and height values. | |||||
| */ | |||||
| Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept; | |||||
| /** | |||||
| Constructor using custom X, Y and size values. | |||||
| */ | |||||
| Rectangle(const T& x, const T& y, const Size<T>& size) noexcept; | |||||
| /** | |||||
| Constructor using custom pos, width and height values. | |||||
| */ | |||||
| Rectangle(const Point<T>& pos, const T& width, const T& height) noexcept; | |||||
| /** | |||||
| Constructor using custom position and size. | |||||
| */ | |||||
| Rectangle(const Point<T>& pos, const Size<T>& size) noexcept; | |||||
| /** | |||||
| Constructor using another Rectangle class values. | |||||
| */ | |||||
| Rectangle(const Rectangle<T>& rect) noexcept; | |||||
| /** | |||||
| Get X value. | |||||
| */ | |||||
| const T& getX() const noexcept; | |||||
| /** | |||||
| Get Y value. | |||||
| */ | |||||
| const T& getY() const noexcept; | |||||
| /** | |||||
| Get width. | |||||
| */ | |||||
| const T& getWidth() const noexcept; | |||||
| /** | |||||
| Get height. | |||||
| */ | |||||
| const T& getHeight() const noexcept; | |||||
| /** | |||||
| Get position. | |||||
| */ | |||||
| const Point<T>& getPos() const noexcept; | |||||
| /** | |||||
| Get size. | |||||
| */ | |||||
| const Size<T>& getSize() const noexcept; | |||||
| /** | |||||
| Set X value as @a x. | |||||
| */ | |||||
| void setX(const T& x) noexcept; | |||||
| /** | |||||
| Set Y value as @a y. | |||||
| */ | |||||
| void setY(const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values as @a x and @a y respectively. | |||||
| */ | |||||
| void setPos(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Set X and Y values according to @a pos. | |||||
| */ | |||||
| void setPos(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Move this rectangle by @a x and @a y values. | |||||
| */ | |||||
| void moveBy(const T& x, const T& y) noexcept; | |||||
| /** | |||||
| Move this rectangle by @a pos. | |||||
| */ | |||||
| void moveBy(const Point<T>& pos) noexcept; | |||||
| /** | |||||
| Set width. | |||||
| */ | |||||
| void setWidth(const T& width) noexcept; | |||||
| /** | |||||
| Set height. | |||||
| */ | |||||
| void setHeight(const T& height) noexcept; | |||||
| /** | |||||
| Set size using @a width and @a height. | |||||
| */ | |||||
| void setSize(const T& width, const T& height) noexcept; | |||||
| /** | |||||
| Set size. | |||||
| */ | |||||
| void setSize(const Size<T>& size) noexcept; | |||||
| /** | |||||
| Grow size by @a multiplier. | |||||
| */ | |||||
| void growBy(double multiplier) noexcept; | |||||
| /** | |||||
| Shrink size by @a divider. | |||||
| */ | |||||
| void shrinkBy(double divider) noexcept; | |||||
| /** | |||||
| Set rectangle using @a pos and @a size. | |||||
| */ | |||||
| void setRectangle(const Point<T>& pos, const Size<T>& size) noexcept; | |||||
| /** | |||||
| Set rectangle. | |||||
| */ | |||||
| void setRectangle(const Rectangle<T>& rect) noexcept; | |||||
| /** | |||||
| Check if this rectangle contains the point defined by @a X and @a Y. | |||||
| */ | |||||
| bool contains(const T& x, const T& y) const noexcept; | |||||
| /** | |||||
| Check if this rectangle contains the point @a pos. | |||||
| */ | |||||
| bool contains(const Point<T>& pos) const noexcept; | |||||
| /** | |||||
| Check if this rectangle contains X. | |||||
| */ | |||||
| bool containsX(const T& x) const noexcept; | |||||
| /** | |||||
| Check if this rectangle contains Y. | |||||
| */ | |||||
| bool containsY(const T& y) const noexcept; | |||||
| /** | |||||
| Draw this rectangle using the current OpenGL state. | |||||
| */ | |||||
| void draw(); | |||||
| /** | |||||
| Draw lines (outline of this rectangle) using the current OpenGL state. | |||||
| */ | |||||
| void drawOutline(); | |||||
| Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; | |||||
| Rectangle<T>& operator*=(double m) noexcept; | |||||
| Rectangle<T>& operator/=(double d) noexcept; | |||||
| bool operator==(const Rectangle<T>& size) const noexcept; | |||||
| bool operator!=(const Rectangle<T>& size) const noexcept; | |||||
| private: | |||||
| Point<T> fPos; | |||||
| Size<T> fSize; | |||||
| void _draw(const bool outline); | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_GEOMETRY_HPP_INCLUDED | |||||
| @@ -0,0 +1,147 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_HPP_INCLUDED | |||||
| #define DGL_IMAGE_HPP_INCLUDED | |||||
| #include "Geometry.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| Base DGL Image class. | |||||
| This is an Image class that handles raw image data in pixels. | |||||
| You can init the image data on the contructor or later on by calling loadFromMemory(). | |||||
| To generate raw data useful for this class see the utils/png2rgba.py script. | |||||
| Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR' | |||||
| instead of the default 'GL_BGRA'. | |||||
| Images are drawn on screen via 2D textures. | |||||
| */ | |||||
| class Image | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor for a null Image. | |||||
| */ | |||||
| Image(); | |||||
| /** | |||||
| Constructor using raw image data. | |||||
| @note @a rawData must remain valid for the lifetime of this Image. | |||||
| */ | |||||
| Image(const char* const rawData, const uint width, const uint height, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE); | |||||
| /** | |||||
| Constructor using raw image data. | |||||
| @note @a rawData must remain valid for the lifetime of this Image. | |||||
| */ | |||||
| Image(const char* const rawData, const Size<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE); | |||||
| /** | |||||
| Constructor using another image data. | |||||
| */ | |||||
| Image(const Image& image); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| ~Image(); | |||||
| /** | |||||
| Load image data from memory. | |||||
| @note @a rawData must remain valid for the lifetime of this Image. | |||||
| */ | |||||
| void loadFromMemory(const char* const rawData, const uint width, const uint height, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||||
| /** | |||||
| Load image data from memory. | |||||
| @note @a rawData must remain valid for the lifetime of this Image. | |||||
| */ | |||||
| void loadFromMemory(const char* const rawData, const Size<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||||
| /** | |||||
| Check if this image is valid. | |||||
| */ | |||||
| bool isValid() const noexcept; | |||||
| /** | |||||
| Get width. | |||||
| */ | |||||
| uint getWidth() const noexcept; | |||||
| /** | |||||
| Get height. | |||||
| */ | |||||
| uint getHeight() const noexcept; | |||||
| /** | |||||
| Get size. | |||||
| */ | |||||
| const Size<uint>& getSize() const noexcept; | |||||
| /** | |||||
| Get the raw image data. | |||||
| */ | |||||
| const char* getRawData() const noexcept; | |||||
| /** | |||||
| Get the image format. | |||||
| */ | |||||
| GLenum getFormat() const noexcept; | |||||
| /** | |||||
| Get the image type. | |||||
| */ | |||||
| GLenum getType() const noexcept; | |||||
| /** | |||||
| Draw this image at (0, 0) point. | |||||
| */ | |||||
| void draw(); | |||||
| /** | |||||
| Draw this image at (x, y) point. | |||||
| */ | |||||
| void drawAt(const int x, const int y); | |||||
| /** | |||||
| Draw this image at position @a pos. | |||||
| */ | |||||
| void drawAt(const Point<int>& pos); | |||||
| Image& operator=(const Image& image) noexcept; | |||||
| bool operator==(const Image& image) const noexcept; | |||||
| bool operator!=(const Image& image) const noexcept; | |||||
| private: | |||||
| const char* fRawData; | |||||
| Size<uint> fSize; | |||||
| GLenum fFormat; | |||||
| GLenum fType; | |||||
| GLuint fTextureId; | |||||
| bool fIsReady; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_IMAGE_HPP_INCLUDED | |||||
| @@ -0,0 +1,23 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED | |||||
| #define DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED | |||||
| #warning This is a deprecated file, please include ImageWidgets.hpp instead. | |||||
| #include "ImageWidgets.hpp" | |||||
| #endif // DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED | |||||
| @@ -0,0 +1,23 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_BUTTON_HPP_INCLUDED | |||||
| #define DGL_IMAGE_BUTTON_HPP_INCLUDED | |||||
| #warning This is a deprecated file, please include ImageWidgets.hpp instead. | |||||
| #include "ImageWidgets.hpp" | |||||
| #endif // DGL_IMAGE_BUTTON_HPP_INCLUDED | |||||
| @@ -0,0 +1,23 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_KNOB_HPP_INCLUDED | |||||
| #define DGL_IMAGE_KNOB_HPP_INCLUDED | |||||
| #warning This is a deprecated file, please include ImageWidgets.hpp instead. | |||||
| #include "ImageWidgets.hpp" | |||||
| #endif // DGL_IMAGE_KNOB_HPP_INCLUDED | |||||
| @@ -0,0 +1,23 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_SLIDER_HPP_INCLUDED | |||||
| #define DGL_IMAGE_SLIDER_HPP_INCLUDED | |||||
| #warning This is a deprecated file, please include ImageWidgets.hpp instead. | |||||
| #include "ImageWidgets.hpp" | |||||
| #endif // DGL_IMAGE_SLIDER_HPP_INCLUDED | |||||
| @@ -0,0 +1,23 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_SWITCH_HPP_INCLUDED | |||||
| #define DGL_IMAGE_SWITCH_HPP_INCLUDED | |||||
| #warning This is a deprecated file, please include ImageWidgets.hpp instead. | |||||
| #include "ImageWidgets.hpp" | |||||
| #endif // DGL_IMAGE_SWITCH_HPP_INCLUDED | |||||
| @@ -0,0 +1,271 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||||
| #define DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||||
| #include "Image.hpp" | |||||
| #include "Widget.hpp" | |||||
| #include "Window.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| class ImageAboutWindow : public Window, | |||||
| public Widget | |||||
| { | |||||
| public: | |||||
| explicit ImageAboutWindow(Window& parent, const Image& image = Image()); | |||||
| explicit ImageAboutWindow(Widget* widget, const Image& image = Image()); | |||||
| void setImage(const Image& image); | |||||
| protected: | |||||
| void onDisplay() override; | |||||
| bool onKeyboard(const KeyboardEvent&) override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| void onReshape(uint width, uint height) override; | |||||
| private: | |||||
| Image fImgBackground; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| class ImageButton : public Widget | |||||
| { | |||||
| public: | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; | |||||
| }; | |||||
| explicit ImageButton(Window& parent, const Image& image); | |||||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown); | |||||
| explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||||
| explicit ImageButton(Widget* widget, const Image& image); | |||||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown); | |||||
| explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||||
| ~ImageButton() override; | |||||
| void setCallback(Callback* callback) noexcept; | |||||
| protected: | |||||
| void onDisplay() override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| bool onMotion(const MotionEvent&) override; | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| DISTRHO_LEAK_DETECTOR(ImageButton) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| class ImageKnob : public Widget | |||||
| { | |||||
| public: | |||||
| enum Orientation { | |||||
| Horizontal, | |||||
| Vertical | |||||
| }; | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0; | |||||
| virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0; | |||||
| virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; | |||||
| }; | |||||
| explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept; | |||||
| explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept; | |||||
| explicit ImageKnob(const ImageKnob& imageKnob); | |||||
| ImageKnob& operator=(const ImageKnob& imageKnob); | |||||
| ~ImageKnob() override; | |||||
| float getValue() const noexcept; | |||||
| void setDefault(float def) noexcept; | |||||
| void setRange(float min, float max) noexcept; | |||||
| void setStep(float step) noexcept; | |||||
| void setValue(float value, bool sendCallback = false) noexcept; | |||||
| void setUsingLogScale(bool yesNo) noexcept; | |||||
| void setCallback(Callback* callback) noexcept; | |||||
| void setOrientation(Orientation orientation) noexcept; | |||||
| void setRotationAngle(int angle); | |||||
| void setImageLayerCount(uint count) noexcept; | |||||
| protected: | |||||
| void onDisplay() override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| bool onMotion(const MotionEvent&) override; | |||||
| bool onScroll(const ScrollEvent&) override; | |||||
| private: | |||||
| Image fImage; | |||||
| float fMinimum; | |||||
| float fMaximum; | |||||
| float fStep; | |||||
| float fValue; | |||||
| float fValueDef; | |||||
| float fValueTmp; | |||||
| bool fUsingDefault; | |||||
| bool fUsingLog; | |||||
| Orientation fOrientation; | |||||
| int fRotationAngle; | |||||
| bool fDragging; | |||||
| int fLastX; | |||||
| int fLastY; | |||||
| Callback* fCallback; | |||||
| bool fIsImgVertical; | |||||
| uint fImgLayerWidth; | |||||
| uint fImgLayerHeight; | |||||
| uint fImgLayerCount; | |||||
| bool fIsReady; | |||||
| GLuint fTextureId; | |||||
| float _logscale(float value) const; | |||||
| float _invlogscale(float value) const; | |||||
| DISTRHO_LEAK_DETECTOR(ImageKnob) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // note set range and step before setting the value | |||||
| class ImageSlider : public Widget | |||||
| { | |||||
| public: | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0; | |||||
| virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0; | |||||
| virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; | |||||
| }; | |||||
| explicit ImageSlider(Window& parent, const Image& image) noexcept; | |||||
| explicit ImageSlider(Widget* widget, const Image& image) noexcept; | |||||
| float getValue() const noexcept; | |||||
| void setValue(float value, bool sendCallback = false) noexcept; | |||||
| void setStartPos(const Point<int>& startPos) noexcept; | |||||
| void setStartPos(int x, int y) noexcept; | |||||
| void setEndPos(const Point<int>& endPos) noexcept; | |||||
| void setEndPos(int x, int y) noexcept; | |||||
| void setInverted(bool inverted) noexcept; | |||||
| void setRange(float min, float max) noexcept; | |||||
| void setStep(float step) noexcept; | |||||
| void setCallback(Callback* callback) noexcept; | |||||
| protected: | |||||
| void onDisplay() override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| bool onMotion(const MotionEvent&) override; | |||||
| private: | |||||
| Image fImage; | |||||
| float fMinimum; | |||||
| float fMaximum; | |||||
| float fStep; | |||||
| float fValue; | |||||
| float fValueTmp; | |||||
| bool fDragging; | |||||
| bool fInverted; | |||||
| bool fValueIsSet; | |||||
| int fStartedX; | |||||
| int fStartedY; | |||||
| Callback* fCallback; | |||||
| Point<int> fStartPos; | |||||
| Point<int> fEndPos; | |||||
| Rectangle<int> fSliderArea; | |||||
| void _recheckArea() noexcept; | |||||
| // these should not be used | |||||
| void setAbsoluteX(int) const noexcept {} | |||||
| void setAbsoluteY(int) const noexcept {} | |||||
| void setAbsolutePos(int, int) const noexcept {} | |||||
| void setAbsolutePos(const Point<int>&) const noexcept {} | |||||
| DISTRHO_LEAK_DETECTOR(ImageSlider) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| class ImageSwitch : public Widget | |||||
| { | |||||
| public: | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void imageSwitchClicked(ImageSwitch* imageButton, bool down) = 0; | |||||
| }; | |||||
| explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; | |||||
| explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept; | |||||
| explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept; | |||||
| ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept; | |||||
| bool isDown() const noexcept; | |||||
| void setDown(bool down) noexcept; | |||||
| void setCallback(Callback* callback) noexcept; | |||||
| protected: | |||||
| void onDisplay() override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| private: | |||||
| Image fImageNormal; | |||||
| Image fImageDown; | |||||
| bool fIsDown; | |||||
| Callback* fCallback; | |||||
| DISTRHO_LEAK_DETECTOR(ImageSwitch) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||||
| @@ -0,0 +1,81 @@ | |||||
| #!/usr/bin/make -f | |||||
| # Makefile for dgl # | |||||
| # ---------------- # | |||||
| # Created by falkTX | |||||
| # | |||||
| include Makefile.mk | |||||
| # -------------------------------------------------------------- | |||||
| BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc | |||||
| BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc | |||||
| LINK_FLAGS += $(DGL_LIBS) | |||||
| # -------------------------------------------------------------- | |||||
| OBJS = \ | |||||
| src/Application.cpp.o \ | |||||
| src/Color.cpp.o \ | |||||
| src/Geometry.cpp.o \ | |||||
| src/Image.cpp.o \ | |||||
| src/ImageWidgets.cpp.o \ | |||||
| src/NanoVG.cpp.o \ | |||||
| src/NanoWidgets.cpp.o \ | |||||
| src/Widget.cpp.o | |||||
| ifeq ($(MACOS),true) | |||||
| OBJS += src/Window.mm.o | |||||
| else | |||||
| OBJS += src/Window.cpp.o | |||||
| endif | |||||
| TARGET = ../libdgl.a | |||||
| # -------------------------------------------------------------- | |||||
| all: $(TARGET) | |||||
| # -------------------------------------------------------------- | |||||
| ../libdgl.a: $(OBJS) | |||||
| rm -f $@ | |||||
| $(AR) crs $@ $^ | |||||
| ../libdgl.dll: $(OBJS) | |||||
| # -Wl,--output-def,$@.def,--out-implib,$@.a | |||||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||||
| ../libdgl.dylib: $(OBJS) | |||||
| $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ | |||||
| ../libdgl.so: $(OBJS) | |||||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||||
| # -------------------------------------------------------------- | |||||
| %.c.o: %.c | |||||
| $(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||||
| %.cpp.o: %.cpp | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||||
| src/Window.cpp.o: src/Window.cpp src/pugl/* | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||||
| src/Window.mm.o: src/Window.cpp src/pugl/* | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | |||||
| # -------------------------------------------------------------- | |||||
| clean: | |||||
| rm -f src/*.d src/*.o ../libdgl.* | |||||
| debug: | |||||
| $(MAKE) DEBUG=true | |||||
| # -------------------------------------------------------------- | |||||
| -include $(OBJS:%.o=%.d) | |||||
| # -------------------------------------------------------------- | |||||
| @@ -0,0 +1,132 @@ | |||||
| #!/usr/bin/make -f | |||||
| # Makefile for dgl # | |||||
| # ---------------- # | |||||
| # Created by falkTX | |||||
| # | |||||
| AR ?= ar | |||||
| CC ?= gcc | |||||
| CXX ?= g++ | |||||
| # -------------------------------------------------------------- | |||||
| # Fallback to Linux if no other OS defined | |||||
| ifneq ($(HAIKU),true) | |||||
| ifneq ($(MACOS),true) | |||||
| ifneq ($(WIN32),true) | |||||
| LINUX=true | |||||
| endif | |||||
| endif | |||||
| endif | |||||
| # -------------------------------------------------------------- | |||||
| # Set build and link flags | |||||
| BASE_FLAGS = -Wall -Wextra -pipe -MD -MP | |||||
| BASE_OPTS = -O2 -ffast-math -mtune=generic -msse -msse2 -fdata-sections -ffunction-sections | |||||
| ifneq ($(MACOS),true) | |||||
| # MacOS doesn't support this | |||||
| BASE_OPTS += -mfpmath=sse | |||||
| endif | |||||
| ifeq ($(MACOS),true) | |||||
| # MacOS linker flags | |||||
| LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs | |||||
| else | |||||
| # Common linker flags | |||||
| LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed -Wl,--strip-all | |||||
| endif | |||||
| ifeq ($(RASPPI),true) | |||||
| # Raspberry-Pi optimization flags | |||||
| BASE_OPTS = -O2 -ffast-math -march=armv6 -mfpu=vfp -mfloat-abi=hard | |||||
| LINK_OPTS = -Wl,-O1 -Wl,--as-needed -Wl,--strip-all | |||||
| endif | |||||
| ifeq ($(PANDORA),true) | |||||
| # OpenPandora optimization flags | |||||
| BASE_OPTS = -O2 -ffast-math -march=armv7-a -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp | |||||
| LINK_OPTS = -Wl,-O1 -Wl,--as-needed -Wl,--strip-all | |||||
| endif | |||||
| ifneq ($(NOOPT),true) | |||||
| # No optimization flags | |||||
| BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | |||||
| endif | |||||
| ifneq ($(WIN32),true) | |||||
| # not needed for Windows | |||||
| BASE_FLAGS += -fPIC -DPIC | |||||
| endif | |||||
| ifeq ($(DEBUG),true) | |||||
| BASE_FLAGS += -DDEBUG -O0 -g | |||||
| LINK_OPTS = | |||||
| else | |||||
| BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | |||||
| CXXFLAGS += -fvisibility-inlines-hidden | |||||
| endif | |||||
| BUILD_C_FLAGS = $(BASE_FLAGS) -std=c99 -std=gnu99 $(CFLAGS) | |||||
| BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=c++0x -std=gnu++0x $(CXXFLAGS) $(CPPFLAGS) | |||||
| LINK_FLAGS = $(LINK_OPTS) -Wl,--no-undefined $(LDFLAGS) | |||||
| ifeq ($(MACOS),true) | |||||
| # No C++11 support | |||||
| BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) $(CPPFLAGS) | |||||
| LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) | |||||
| endif | |||||
| # -------------------------------------------------------------- | |||||
| # Strict test build | |||||
| ifeq ($(TESTBUILD),true) | |||||
| BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings | |||||
| BASE_FLAGS += -Wpointer-arith -Wabi -Winit-self -Wuninitialized -Wstrict-overflow=5 | |||||
| # BASE_FLAGS += -Wfloat-equal | |||||
| ifeq ($(CC),clang) | |||||
| BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command | |||||
| BASE_FLAGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-float-equal | |||||
| else | |||||
| BASE_FLAGS += -Wcast-align -Wunsafe-loop-optimizations | |||||
| endif | |||||
| ifneq ($(MACOS),true) | |||||
| BASE_FLAGS += -Wmissing-declarations -Wsign-conversion | |||||
| ifneq ($(CC),clang) | |||||
| BASE_FLAGS += -Wlogical-op | |||||
| endif | |||||
| endif | |||||
| CFLAGS += -Wold-style-definition -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes | |||||
| CXXFLAGS += -Weffc++ -Wnon-virtual-dtor -Woverloaded-virtual | |||||
| endif | |||||
| # -------------------------------------------------------------- | |||||
| # Check for required libs | |||||
| ifeq ($(LINUX),true) | |||||
| ifneq ($(shell pkg-config --exists gl && echo true),true) | |||||
| $(error OpenGL missing, cannot continue) | |||||
| endif | |||||
| ifneq ($(shell pkg-config --exists x11 && echo true),true) | |||||
| $(error X11 missing, cannot continue) | |||||
| endif | |||||
| endif | |||||
| # -------------------------------------------------------------- | |||||
| # Set libs stuff | |||||
| ifeq ($(LINUX),true) | |||||
| DGL_FLAGS = $(shell pkg-config --cflags gl x11) | |||||
| DGL_LIBS = $(shell pkg-config --libs gl x11) | |||||
| endif | |||||
| ifeq ($(MACOS),true) | |||||
| DGL_LIBS = -framework OpenGL -framework Cocoa | |||||
| endif | |||||
| ifeq ($(WIN32),true) | |||||
| DGL_LIBS = -lopengl32 -lgdi32 | |||||
| endif | |||||
| # -------------------------------------------------------------- | |||||
| @@ -0,0 +1,917 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_NANO_WIDGET_HPP_INCLUDED | |||||
| #define DGL_NANO_WIDGET_HPP_INCLUDED | |||||
| #include "Color.hpp" | |||||
| #include "Widget.hpp" | |||||
| struct NVGcontext; | |||||
| struct NVGpaint; | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // Forward class names | |||||
| class NanoVG; | |||||
| // ----------------------------------------------------------------------- | |||||
| // NanoImage | |||||
| /** | |||||
| NanoVG Image class. | |||||
| This implements NanoVG images as a C++ class where deletion is handled automatically. | |||||
| Images need to be created within a NanoVG or NanoWidget class. | |||||
| */ | |||||
| class NanoImage | |||||
| { | |||||
| private: | |||||
| struct Handle { | |||||
| NVGcontext* context; | |||||
| int imageId; | |||||
| Handle() noexcept | |||||
| : context(nullptr), | |||||
| imageId(0) {} | |||||
| Handle(NVGcontext* c, int id) noexcept | |||||
| : context(c), | |||||
| imageId(id) {} | |||||
| }; | |||||
| public: | |||||
| /** | |||||
| Constructor for an invalid/null image. | |||||
| */ | |||||
| NanoImage(); | |||||
| /** | |||||
| Constructor. | |||||
| */ | |||||
| NanoImage(const Handle& handle); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| ~NanoImage(); | |||||
| /** | |||||
| Create a new image without recreating the C++ class. | |||||
| */ | |||||
| NanoImage& operator=(const Handle& handle); | |||||
| /** | |||||
| Wherever this image is valid. | |||||
| */ | |||||
| bool isValid() const noexcept; | |||||
| /** | |||||
| Get size. | |||||
| */ | |||||
| Size<uint> getSize() const noexcept; | |||||
| /** | |||||
| Get the OpenGL texture handle. | |||||
| */ | |||||
| GLuint getTextureHandle() const; | |||||
| private: | |||||
| Handle fHandle; | |||||
| Size<uint> fSize; | |||||
| friend class NanoVG; | |||||
| /** @internal */ | |||||
| void _updateSize(); | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoImage) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // NanoVG | |||||
| /** | |||||
| NanoVG class. | |||||
| This class exposes the NanoVG drawing API. | |||||
| All calls should be wrapped in beginFrame() and endFrame(). | |||||
| @section State Handling | |||||
| NanoVG contains state which represents how paths will be rendered. | |||||
| The state contains transform, fill and stroke styles, text and font styles, and scissor clipping. | |||||
| @section Render styles | |||||
| Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. | |||||
| Solid color is simply defined as a color value, different kinds of paints can be created | |||||
| using linearGradient(), boxGradient(), radialGradient() and imagePattern(). | |||||
| Current render style can be saved and restored using save() and restore(). | |||||
| @section Transforms | |||||
| The paths, gradients, patterns and scissor region are transformed by an transformation | |||||
| matrix at the time when they are passed to the API. | |||||
| The current transformation matrix is a affine matrix: | |||||
| [sx kx tx] | |||||
| [ky sy ty] | |||||
| [ 0 0 1] | |||||
| Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. | |||||
| The last row is assumed to be 0,0,1 and is not stored. | |||||
| Apart from resetTransform(), each transformation function first creates | |||||
| specific transformation matrix and pre-multiplies the current transformation by it. | |||||
| Current coordinate system (transformation) can be saved and restored using save() and restore(). | |||||
| @section Images | |||||
| NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. | |||||
| In addition you can upload your own image. The image loading is provided by stb_image. | |||||
| @section Paints | |||||
| NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. | |||||
| These can be used as paints for strokes and fills. | |||||
| @section Scissoring | |||||
| Scissoring allows you to clip the rendering into a rectangle. This is useful for various | |||||
| user interface cases like rendering a text edit or a timeline. | |||||
| @section Paths | |||||
| Drawing a new shape starts with beginPath(), it clears all the currently defined paths. | |||||
| Then you define one or more paths and sub-paths which describe the shape. The are functions | |||||
| to draw common shapes like rectangles and circles, and lower level step-by-step functions, | |||||
| which allow to define a path curve by curve. | |||||
| NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise | |||||
| winding and holes should have counter clockwise order. To specify winding of a path you can | |||||
| call pathWinding(). This is useful especially for the common shapes, which are drawn CCW. | |||||
| Finally you can fill the path using current fill style by calling fill(), and stroke it | |||||
| with current stroke style by calling stroke(). | |||||
| The curve segments and sub-paths are transformed by the current transform. | |||||
| @section Text | |||||
| NanoVG allows you to load .ttf files and use the font to render text. | |||||
| The appearance of the text can be defined by setting the current text style | |||||
| and by specifying the fill color. Common text and font settings such as | |||||
| font size, letter spacing and text align are supported. Font blur allows you | |||||
| to create simple text effects such as drop shadows. | |||||
| At render time the font face can be set based on the font handles or name. | |||||
| Font measure functions return values in local space, the calculations are | |||||
| carried in the same resolution as the final rendering. This is done because | |||||
| the text glyph positions are snapped to the nearest pixels sharp rendering. | |||||
| The local space means that values are not rotated or scale as per the current | |||||
| transformation. For example if you set font size to 12, which would mean that | |||||
| line height is 16, then regardless of the current scaling and rotation, the | |||||
| returned line height is always 16. Some measures may vary because of the scaling | |||||
| since aforementioned pixel snapping. | |||||
| While this may sound a little odd, the setup allows you to always render the | |||||
| same way regardless of scaling. i.e. following works regardless of scaling: | |||||
| @code | |||||
| const char* txt = "Text me up."; | |||||
| textBounds(vg, x,y, txt, NULL, bounds); | |||||
| beginPath(vg); | |||||
| roundedRect(vg, bounds[0], bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); | |||||
| fill(vg); | |||||
| @endcode | |||||
| Note: currently only solid color fill is supported for text. | |||||
| */ | |||||
| class NanoVG | |||||
| { | |||||
| public: | |||||
| enum CreateFlags { | |||||
| /** | |||||
| Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). | |||||
| */ | |||||
| CREATE_ANTIALIAS = 1 << 0, | |||||
| /** | |||||
| Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little | |||||
| slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. | |||||
| */ | |||||
| CREATE_STENCIL_STROKES = 1 << 1, | |||||
| /** | |||||
| Flag indicating that additional debug checks are done. | |||||
| */ | |||||
| CREATE_DEBUG = 1 << 2, | |||||
| }; | |||||
| enum ImageFlags { | |||||
| IMAGE_GENERATE_MIPMAPS = 1 << 0, // Generate mipmaps during creation of the image. | |||||
| IMAGE_REPEAT_X = 1 << 1, // Repeat image in X direction. | |||||
| IMAGE_REPEAT_Y = 1 << 2, // Repeat image in Y direction. | |||||
| IMAGE_FLIP_Y = 1 << 3, // Flips (inverses) image in Y direction when rendered. | |||||
| IMAGE_PREMULTIPLIED = 1 << 4 // Image data has premultiplied alpha. | |||||
| }; | |||||
| enum Align { | |||||
| // Horizontal align | |||||
| ALIGN_LEFT = 1 << 0, // Align horizontally to left (default). | |||||
| ALIGN_CENTER = 1 << 1, // Align horizontally to center. | |||||
| ALIGN_RIGHT = 1 << 2, // Align horizontally to right. | |||||
| // Vertical align | |||||
| ALIGN_TOP = 1 << 3, // Align vertically to top. | |||||
| ALIGN_MIDDLE = 1 << 4, // Align vertically to middle. | |||||
| ALIGN_BOTTOM = 1 << 5, // Align vertically to bottom. | |||||
| ALIGN_BASELINE = 1 << 6 // Align vertically to baseline (default). | |||||
| }; | |||||
| enum LineCap { | |||||
| BUTT, | |||||
| ROUND, | |||||
| SQUARE, | |||||
| BEVEL, | |||||
| MITER | |||||
| }; | |||||
| enum Solidity { | |||||
| SOLID = 1, // CCW | |||||
| HOLE = 2 // CW | |||||
| }; | |||||
| enum Winding { | |||||
| CCW = 1, // Winding for solid shapes | |||||
| CW = 2 // Winding for holes | |||||
| }; | |||||
| struct Paint { | |||||
| float xform[6]; | |||||
| float extent[2]; | |||||
| float radius; | |||||
| float feather; | |||||
| Color innerColor; | |||||
| Color outerColor; | |||||
| int imageId; | |||||
| Paint() noexcept; | |||||
| /** | |||||
| @internal | |||||
| */ | |||||
| Paint(const NVGpaint&) noexcept; | |||||
| operator NVGpaint() const noexcept; | |||||
| }; | |||||
| struct GlyphPosition { | |||||
| const char* str; // Position of the glyph in the input string. | |||||
| float x; // The x-coordinate of the logical glyph position. | |||||
| float minx, maxx; // The bounds of the glyph shape. | |||||
| }; | |||||
| struct TextRow { | |||||
| const char* start; // Pointer to the input text where the row starts. | |||||
| const char* end; // Pointer to the input text where the row ends (one past the last character). | |||||
| const char* next; // Pointer to the beginning of the next row. | |||||
| float width; // Logical width of the row. | |||||
| float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. | |||||
| }; | |||||
| typedef int FontId; | |||||
| /** | |||||
| Constructor. | |||||
| @see CreateFlags | |||||
| */ | |||||
| NanoVG(int flags = CREATE_ANTIALIAS); | |||||
| /** | |||||
| Constructor reusing a NanoVG context, used for subwidgets. | |||||
| */ | |||||
| NanoVG(NanoWidget* groupWidget); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| virtual ~NanoVG(); | |||||
| /** | |||||
| Get the NanoVG context. | |||||
| You should not need this under normal circumstances. | |||||
| */ | |||||
| NVGcontext* getContext() const noexcept | |||||
| { | |||||
| return fContext; | |||||
| } | |||||
| /** | |||||
| Begin drawing a new frame. | |||||
| */ | |||||
| void beginFrame(const uint width, const uint height, const float scaleFactor = 1.0f); | |||||
| /** | |||||
| Begin drawing a new frame inside a widget. | |||||
| */ | |||||
| void beginFrame(Widget* const widget); | |||||
| /** | |||||
| Cancels drawing the current frame. | |||||
| */ | |||||
| void cancelFrame(); | |||||
| /** | |||||
| Ends drawing flushing remaining render state. | |||||
| */ | |||||
| void endFrame(); | |||||
| /* -------------------------------------------------------------------- | |||||
| * State Handling */ | |||||
| /** | |||||
| Pushes and saves the current render state into a state stack. | |||||
| A matching restore() must be used to restore the state. | |||||
| */ | |||||
| void save(); | |||||
| /** | |||||
| Pops and restores current render state. | |||||
| */ | |||||
| void restore(); | |||||
| /** | |||||
| Resets current render state to default values. Does not affect the render state stack. | |||||
| */ | |||||
| void reset(); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Render styles */ | |||||
| /** | |||||
| Sets current stroke style to a solid color. | |||||
| */ | |||||
| void strokeColor(const Color& color); | |||||
| /** | |||||
| Sets current stroke style to a solid color, made from red, green, blue and alpha numeric values. | |||||
| Values must be in [0..255] range. | |||||
| */ | |||||
| void strokeColor(const int red, const int green, const int blue, const int alpha = 255); | |||||
| /** | |||||
| Sets current stroke style to a solid color, made from red, green, blue and alpha numeric values. | |||||
| Values must in [0..1] range. | |||||
| */ | |||||
| void strokeColor(const float red, const float green, const float blue, const float alpha = 1.0f); | |||||
| /** | |||||
| Sets current stroke style to a paint, which can be a one of the gradients or a pattern. | |||||
| */ | |||||
| void strokePaint(const Paint& paint); | |||||
| /** | |||||
| Sets current fill style to a solid color. | |||||
| */ | |||||
| void fillColor(const Color& color); | |||||
| /** | |||||
| Sets current fill style to a solid color, made from red, green, blue and alpha numeric values. | |||||
| Values must be in [0..255] range. | |||||
| */ | |||||
| void fillColor(const int red, const int green, const int blue, const int alpha = 255); | |||||
| /** | |||||
| Sets current fill style to a solid color, made from red, green, blue and alpha numeric values. | |||||
| Values must in [0..1] range. | |||||
| */ | |||||
| void fillColor(const float red, const float green, const float blue, const float alpha = 1.0f); | |||||
| /** | |||||
| Sets current fill style to a paint, which can be a one of the gradients or a pattern. | |||||
| */ | |||||
| void fillPaint(const Paint& paint); | |||||
| /** | |||||
| Sets the miter limit of the stroke style. | |||||
| Miter limit controls when a sharp corner is beveled. | |||||
| */ | |||||
| void miterLimit(float limit); | |||||
| /** | |||||
| Sets the stroke width of the stroke style. | |||||
| */ | |||||
| void strokeWidth(float size); | |||||
| /** | |||||
| Sets how the end of the line (cap) is drawn, | |||||
| Can be one of: BUTT, ROUND, SQUARE. | |||||
| */ | |||||
| void lineCap(LineCap cap = BUTT); | |||||
| /** | |||||
| Sets how sharp path corners are drawn. | |||||
| Can be one of MITER, ROUND, BEVEL. | |||||
| */ | |||||
| void lineJoin(LineCap join = MITER); | |||||
| /** | |||||
| Sets the transparency applied to all rendered shapes. | |||||
| Already transparent paths will get proportionally more transparent as well. | |||||
| */ | |||||
| void globalAlpha(float alpha); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Transforms */ | |||||
| /** | |||||
| Resets current transform to a identity matrix. | |||||
| */ | |||||
| void resetTransform(); | |||||
| /** | |||||
| Pre-multiplies current coordinate system by specified matrix. | |||||
| The parameters are interpreted as matrix as follows: | |||||
| [a c e] | |||||
| [b d f] | |||||
| [0 0 1] | |||||
| */ | |||||
| void transform(float a, float b, float c, float d, float e, float f); | |||||
| /** | |||||
| Translates current coordinate system. | |||||
| */ | |||||
| void translate(float x, float y); | |||||
| /** | |||||
| Rotates current coordinate system. Angle is specified in radians. | |||||
| */ | |||||
| void rotate(float angle); | |||||
| /** | |||||
| Skews the current coordinate system along X axis. Angle is specified in radians. | |||||
| */ | |||||
| void skewX(float angle); | |||||
| /** | |||||
| Skews the current coordinate system along Y axis. Angle is specified in radians. | |||||
| */ | |||||
| void skewY(float angle); | |||||
| /** | |||||
| Scales the current coordinate system. | |||||
| */ | |||||
| void scale(float x, float y); | |||||
| /** | |||||
| Stores the top part (a-f) of the current transformation matrix in to the specified buffer. | |||||
| [a c e] | |||||
| [b d f] | |||||
| [0 0 1] | |||||
| */ | |||||
| void currentTransform(float xform[6]); | |||||
| /** | |||||
| The following functions can be used to make calculations on 2x3 transformation matrices. | |||||
| A 2x3 matrix is represented as float[6]. */ | |||||
| /** | |||||
| Sets the transform to identity matrix. | |||||
| */ | |||||
| static void transformIdentity(float dst[6]); | |||||
| /** | |||||
| Sets the transform to translation matrix | |||||
| */ | |||||
| static void transformTranslate(float dst[6], float tx, float ty); | |||||
| /** | |||||
| Sets the transform to scale matrix. | |||||
| */ | |||||
| static void transformScale(float dst[6], float sx, float sy); | |||||
| /** | |||||
| Sets the transform to rotate matrix. Angle is specified in radians. | |||||
| */ | |||||
| static void transformRotate(float dst[6], float a); | |||||
| /** | |||||
| Sets the transform to skew-x matrix. Angle is specified in radians. | |||||
| */ | |||||
| static void transformSkewX(float dst[6], float a); | |||||
| /** | |||||
| Sets the transform to skew-y matrix. Angle is specified in radians. | |||||
| */ | |||||
| static void transformSkewY(float dst[6], float a); | |||||
| /** | |||||
| Sets the transform to the result of multiplication of two transforms, of A = A*B. | |||||
| */ | |||||
| static void transformMultiply(float dst[6], const float src[6]); | |||||
| /** | |||||
| Sets the transform to the result of multiplication of two transforms, of A = B*A. | |||||
| */ | |||||
| static void transformPremultiply(float dst[6], const float src[6]); | |||||
| /** | |||||
| Sets the destination to inverse of specified transform. | |||||
| Returns 1 if the inverse could be calculated, else 0. | |||||
| */ | |||||
| static int transformInverse(float dst[6], const float src[6]); | |||||
| /** | |||||
| Transform a point by given transform. | |||||
| */ | |||||
| static void transformPoint(float& dstx, float& dsty, const float xform[6], float srcx, float srcy); | |||||
| /** | |||||
| Convert degrees to radians. | |||||
| */ | |||||
| static float degToRad(float deg); | |||||
| /** | |||||
| Convert radians to degrees. | |||||
| */ | |||||
| static float radToDeg(float rad); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Images */ | |||||
| /** | |||||
| Creates image by loading it from the disk from specified file name. | |||||
| */ | |||||
| NanoImage::Handle createImageFromFile(const char* filename, ImageFlags imageFlags); | |||||
| /** | |||||
| Creates image by loading it from the disk from specified file name. | |||||
| Overloaded function for convenience. | |||||
| @see ImageFlags | |||||
| */ | |||||
| NanoImage::Handle createImageFromFile(const char* filename, int imageFlags); | |||||
| /** | |||||
| Creates image by loading it from the specified chunk of memory. | |||||
| */ | |||||
| NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags); | |||||
| /** | |||||
| Creates image by loading it from the specified chunk of memory. | |||||
| Overloaded function for convenience. | |||||
| @see ImageFlags | |||||
| */ | |||||
| NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags); | |||||
| /** | |||||
| Creates image from specified image data. | |||||
| */ | |||||
| NanoImage::Handle createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags); | |||||
| /** | |||||
| Creates image from specified image data. | |||||
| Overloaded function for convenience. | |||||
| @see ImageFlags | |||||
| */ | |||||
| NanoImage::Handle createImageFromRGBA(uint w, uint h, const uchar* data, int imageFlags); | |||||
| /** | |||||
| Creates image from an OpenGL texture handle. | |||||
| */ | |||||
| NanoImage::Handle createImageFromTextureHandle(GLuint textureId, uint w, uint h, ImageFlags imageFlags, bool deleteTexture = false); | |||||
| /** | |||||
| Creates image from an OpenGL texture handle. | |||||
| Overloaded function for convenience. | |||||
| @see ImageFlags | |||||
| */ | |||||
| NanoImage::Handle createImageFromTextureHandle(GLuint textureId, uint w, uint h, int imageFlags, bool deleteTexture = false); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Paints */ | |||||
| /** | |||||
| Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates | |||||
| of the linear gradient, icol specifies the start color and ocol the end color. | |||||
| The gradient is transformed by the current transform when it is passed to fillPaint() or strokePaint(). | |||||
| */ | |||||
| Paint linearGradient(float sx, float sy, float ex, float ey, const Color& icol, const Color& ocol); | |||||
| /** | |||||
| Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering | |||||
| drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, | |||||
| (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry | |||||
| the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. | |||||
| The gradient is transformed by the current transform when it is passed to fillPaint() or strokePaint(). | |||||
| */ | |||||
| Paint boxGradient(float x, float y, float w, float h, float r, float f, const Color& icol, const Color& ocol); | |||||
| /** | |||||
| Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify | |||||
| the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. | |||||
| The gradient is transformed by the current transform when it is passed to fillPaint() or strokePaint(). | |||||
| */ | |||||
| Paint radialGradient(float cx, float cy, float inr, float outr, const Color& icol, const Color& ocol); | |||||
| /** | |||||
| Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, | |||||
| (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. | |||||
| The gradient is transformed by the current transform when it is passed to fillPaint() or strokePaint(). | |||||
| */ | |||||
| Paint imagePattern(float ox, float oy, float ex, float ey, float angle, const NanoImage& image, float alpha); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Scissoring */ | |||||
| /** | |||||
| Sets the current scissor rectangle. | |||||
| The scissor rectangle is transformed by the current transform. | |||||
| */ | |||||
| void scissor(float x, float y, float w, float h); | |||||
| /** | |||||
| Intersects current scissor rectangle with the specified rectangle. | |||||
| The scissor rectangle is transformed by the current transform. | |||||
| Note: in case the rotation of previous scissor rect differs from | |||||
| the current one, the intersection will be done between the specified | |||||
| rectangle and the previous scissor rectangle transformed in the current | |||||
| transform space. The resulting shape is always rectangle. | |||||
| */ | |||||
| void intersectScissor(float x, float y, float w, float h); | |||||
| /** | |||||
| Reset and disables scissoring. | |||||
| */ | |||||
| void resetScissor(); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Paths */ | |||||
| /** | |||||
| Clears the current path and sub-paths. | |||||
| */ | |||||
| void beginPath(); | |||||
| /** | |||||
| Starts new sub-path with specified point as first point. | |||||
| */ | |||||
| void moveTo(float x, float y); | |||||
| /** | |||||
| Adds line segment from the last point in the path to the specified point. | |||||
| */ | |||||
| void lineTo(float x, float y); | |||||
| /** | |||||
| Adds cubic bezier segment from last point in the path via two control points to the specified point. | |||||
| */ | |||||
| void bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y); | |||||
| /** | |||||
| Adds quadratic bezier segment from last point in the path via a control point to the specified point. | |||||
| */ | |||||
| void quadTo(float cx, float cy, float x, float y); | |||||
| /** | |||||
| Adds an arc segment at the corner defined by the last path point, and two specified points. | |||||
| */ | |||||
| void arcTo(float x1, float y1, float x2, float y2, float radius); | |||||
| /** | |||||
| Closes current sub-path with a line segment. | |||||
| */ | |||||
| void closePath(); | |||||
| /** | |||||
| Sets the current sub-path winding. | |||||
| */ | |||||
| void pathWinding(Winding dir); | |||||
| /** | |||||
| Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | |||||
| and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW or NVG_CW). | |||||
| Angles are specified in radians. | |||||
| */ | |||||
| void arc(float cx, float cy, float r, float a0, float a1, Winding dir); | |||||
| /** | |||||
| Creates new rectangle shaped sub-path. | |||||
| */ | |||||
| void rect(float x, float y, float w, float h); | |||||
| /** | |||||
| Creates new rounded rectangle shaped sub-path. | |||||
| */ | |||||
| void roundedRect(float x, float y, float w, float h, float r); | |||||
| /** | |||||
| Creates new ellipse shaped sub-path. | |||||
| */ | |||||
| void ellipse(float cx, float cy, float rx, float ry); | |||||
| /** | |||||
| Creates new circle shaped sub-path. | |||||
| */ | |||||
| void circle(float cx, float cy, float r); | |||||
| /** | |||||
| Fills the current path with current fill style. | |||||
| */ | |||||
| void fill(); | |||||
| /** | |||||
| Fills the current path with current stroke style. | |||||
| */ | |||||
| void stroke(); | |||||
| /* -------------------------------------------------------------------- | |||||
| * Text */ | |||||
| /** | |||||
| Creates font by loading it from the disk from specified file name. | |||||
| Returns handle to the font. | |||||
| */ | |||||
| FontId createFontFromFile(const char* name, const char* filename); | |||||
| /** | |||||
| Creates font by loading it from the specified memory chunk. | |||||
| Returns handle to the font. | |||||
| */ | |||||
| FontId createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData); | |||||
| /** | |||||
| Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | |||||
| */ | |||||
| FontId findFont(const char* name); | |||||
| /** | |||||
| Sets the font size of current text style. | |||||
| */ | |||||
| void fontSize(float size); | |||||
| /** | |||||
| Sets the blur of current text style. | |||||
| */ | |||||
| void fontBlur(float blur); | |||||
| /** | |||||
| Sets the letter spacing of current text style. | |||||
| */ | |||||
| void textLetterSpacing(float spacing); | |||||
| /** | |||||
| Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
| */ | |||||
| void textLineHeight(float lineHeight); | |||||
| /** | |||||
| Sets the text align of current text style. | |||||
| */ | |||||
| void textAlign(Align align); | |||||
| /** | |||||
| Sets the text align of current text style. | |||||
| Overloaded function for convenience. | |||||
| @see Align | |||||
| */ | |||||
| void textAlign(int align); | |||||
| /** | |||||
| Sets the font face based on specified id of current text style. | |||||
| */ | |||||
| void fontFaceId(FontId font); | |||||
| /** | |||||
| Sets the font face based on specified name of current text style. | |||||
| */ | |||||
| void fontFace(const char* font); | |||||
| /** | |||||
| Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. | |||||
| */ | |||||
| float text(float x, float y, const char* string, const char* end); | |||||
| /** | |||||
| Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. | |||||
| White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||||
| Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||||
| */ | |||||
| void textBox(float x, float y, float breakRowWidth, const char* string, const char* end); | |||||
| /** | |||||
| Measures the specified text string. The bounds value are [xmin,ymin, xmax,ymax]. | |||||
| Returns the horizontal advance of the measured text (i.e. where the next character should drawn). | |||||
| Measured values are returned in local coordinate space. | |||||
| */ | |||||
| float textBounds(float x, float y, const char* string, const char* end, Rectangle<float>& bounds); | |||||
| /** | |||||
| Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], | |||||
| if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] | |||||
| Measured values are returned in local coordinate space. | |||||
| */ | |||||
| void textBoxBounds(float x, float y, float breakRowWidth, const char* string, const char* end, float bounds[4]); | |||||
| /** | |||||
| Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. | |||||
| Measured values are returned in local coordinate space. | |||||
| */ | |||||
| int textGlyphPositions(float x, float y, const char* string, const char* end, GlyphPosition& positions, int maxPositions); | |||||
| /** | |||||
| Returns the vertical metrics based on the current text style. | |||||
| Measured values are returned in local coordinate space. | |||||
| */ | |||||
| void textMetrics(float* ascender, float* descender, float* lineh); | |||||
| /** | |||||
| Breaks the specified text into lines. If end is specified only the sub-string will be used. | |||||
| White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||||
| Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||||
| */ | |||||
| int textBreakLines(const char* string, const char* end, float breakRowWidth, TextRow& rows, int maxRows); | |||||
| private: | |||||
| NVGcontext* const fContext; | |||||
| bool fInFrame; | |||||
| bool fIsSubWidget; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // NanoWidget | |||||
| /** | |||||
| NanoVG Widget class. | |||||
| This class implements the NanoVG drawing API inside a DGL Widget. | |||||
| The drawing function onDisplay() is implemented internally but a | |||||
| new onNanoDisplay() needs to be overridden instead. | |||||
| */ | |||||
| class NanoWidget : public Widget, | |||||
| public NanoVG | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor. | |||||
| @see CreateFlags | |||||
| */ | |||||
| explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS); | |||||
| /** | |||||
| Constructor for a subwidget. | |||||
| */ | |||||
| explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS); | |||||
| /** | |||||
| Constructor for a subwidget, reusing a NanoVG context. | |||||
| */ | |||||
| explicit NanoWidget(NanoWidget* groupWidget); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| virtual ~NanoWidget(); | |||||
| protected: | |||||
| /** | |||||
| New virtual onDisplay function. | |||||
| @see onDisplay | |||||
| */ | |||||
| virtual void onNanoDisplay() = 0; | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const nData; | |||||
| /** | |||||
| Widget display function. | |||||
| Implemented internally to wrap begin/endFrame() automatically. | |||||
| */ | |||||
| void onDisplay() override; | |||||
| // these should not be used | |||||
| void beginFrame(uint,uint) {} | |||||
| void beginFrame(uint,uint,float) {} | |||||
| void beginFrame(Widget*) {} | |||||
| void cancelFrame() {} | |||||
| void endFrame() {} | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_NANO_WIDGET_HPP_INCLUDED | |||||
| @@ -0,0 +1,67 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_NANO_WIDGETS_HPP_INCLUDED | |||||
| #define DGL_NANO_WIDGETS_HPP_INCLUDED | |||||
| #include "NanoVG.hpp" | |||||
| #include "../distrho/extra/String.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| class BlendishButton : public NanoWidget | |||||
| { | |||||
| public: | |||||
| class Callback | |||||
| { | |||||
| public: | |||||
| virtual ~Callback() {} | |||||
| virtual void blendishButtonClicked(BlendishButton* blendishButton, int button) = 0; | |||||
| }; | |||||
| explicit BlendishButton(Window& parent, const char* text = "", int iconId = -1); | |||||
| explicit BlendishButton(NanoWidget* widget, const char* text = "", int iconId = -1); | |||||
| ~BlendishButton() override; | |||||
| int getIconId() const noexcept; | |||||
| void setIconId(int iconId) noexcept; | |||||
| const char* getText() const noexcept; | |||||
| void setText(const char* text) noexcept; | |||||
| void setCallback(Callback* callback) noexcept; | |||||
| protected: | |||||
| void onNanoDisplay() override; | |||||
| bool onMouse(const MouseEvent&) override; | |||||
| bool onMotion(const MotionEvent&) override; | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| void _updateBounds(); | |||||
| DISTRHO_LEAK_DETECTOR(BlendishButton) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_NANO_WIDGETS_HPP_INCLUDED | |||||
| @@ -0,0 +1,61 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_STANDALONE_WINDOW_HPP_INCLUDED | |||||
| #define DGL_STANDALONE_WINDOW_HPP_INCLUDED | |||||
| #include "Application.hpp" | |||||
| #include "Widget.hpp" | |||||
| #include "Window.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| class StandaloneWindow : public Application, | |||||
| public Window | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Constructor. | |||||
| */ | |||||
| StandaloneWindow(); | |||||
| /** | |||||
| Show window and execute application. | |||||
| */ | |||||
| void exec(); | |||||
| private: | |||||
| Widget* fWidget; | |||||
| /** @internal */ | |||||
| void onReshape(uint width, uint height) override; | |||||
| /** @internal */ | |||||
| void _addWidget(Widget* widget) override; | |||||
| /** @internal */ | |||||
| void _removeWidget(Widget* widget) override; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_STANDALONE_WINDOW_HPP_INCLUDED | |||||
| @@ -0,0 +1,385 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_WIDGET_HPP_INCLUDED | |||||
| #define DGL_WIDGET_HPP_INCLUDED | |||||
| #include "Geometry.hpp" | |||||
| #include <vector> | |||||
| // ----------------------------------------------------------------------- | |||||
| // Forward class names | |||||
| START_NAMESPACE_DISTRHO | |||||
| class UI; | |||||
| END_NAMESPACE_DISTRHO | |||||
| START_NAMESPACE_DGL | |||||
| class Application; | |||||
| class ImageSlider; | |||||
| class NanoWidget; | |||||
| class Window; | |||||
| class StandaloneWindow; | |||||
| // ----------------------------------------------------------------------- | |||||
| /** | |||||
| Base DGL Widget class. | |||||
| This is the base Widget class, from which all widgets are built. | |||||
| All widgets have a parent Window where they'll be drawn. | |||||
| This parent is never changed during the widget lifetime. | |||||
| Widgets receive events in relative coordinates. | |||||
| (0, 0) means its top-left position. | |||||
| Windows paint widgets in the order they are constructed. | |||||
| Early widgets are drawn first, at the bottom, then newer ones on top. | |||||
| Events are sent in the inverse order so that the top-most widget gets | |||||
| a chance to catch the event and stop its propagation. | |||||
| All widget event callbacks do nothing by default. | |||||
| */ | |||||
| class Widget | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Base event data. | |||||
| @a mod The currently active keyboard modifiers, @see Modifier. | |||||
| @a time The timestamp (if any). | |||||
| */ | |||||
| struct BaseEvent { | |||||
| uint mod; | |||||
| uint32_t time; | |||||
| /** Constuctor */ | |||||
| BaseEvent() noexcept : mod(0x0), time(0) {} | |||||
| /** Destuctor */ | |||||
| virtual ~BaseEvent() noexcept {} | |||||
| }; | |||||
| /** | |||||
| Keyboard event. | |||||
| @a press True if the key was pressed, false if released. | |||||
| @a key Unicode point of the key pressed. | |||||
| @see onKeyboard | |||||
| */ | |||||
| struct KeyboardEvent : BaseEvent { | |||||
| bool press; | |||||
| uint key; | |||||
| /** Constuctor */ | |||||
| KeyboardEvent() noexcept | |||||
| : BaseEvent(), | |||||
| press(false), | |||||
| key(0) {} | |||||
| }; | |||||
| /** | |||||
| Special keyboard event. | |||||
| @a press True if the key was pressed, false if released. | |||||
| @a key The key pressed. | |||||
| @see onSpecial | |||||
| */ | |||||
| struct SpecialEvent : BaseEvent { | |||||
| bool press; | |||||
| Key key; | |||||
| /** Constuctor */ | |||||
| SpecialEvent() noexcept | |||||
| : BaseEvent(), | |||||
| press(false), | |||||
| key(Key(0)) {} | |||||
| }; | |||||
| /** | |||||
| Mouse event. | |||||
| @a button The button number (1 = left, 2 = middle, 3 = right). | |||||
| @a press True if the key was pressed, false if released. | |||||
| @a pos The widget-relative coordinates of the pointer. | |||||
| @see onMouse | |||||
| */ | |||||
| struct MouseEvent : BaseEvent { | |||||
| int button; | |||||
| bool press; | |||||
| Point<int> pos; | |||||
| /** Constuctor */ | |||||
| MouseEvent() noexcept | |||||
| : BaseEvent(), | |||||
| button(0), | |||||
| press(false), | |||||
| pos(0, 0) {} | |||||
| }; | |||||
| /** | |||||
| Mouse motion event. | |||||
| @a pos The widget-relative coordinates of the pointer. | |||||
| @see onMotion | |||||
| */ | |||||
| struct MotionEvent : BaseEvent { | |||||
| Point<int> pos; | |||||
| /** Constuctor */ | |||||
| MotionEvent() noexcept | |||||
| : BaseEvent(), | |||||
| pos(0, 0) {} | |||||
| }; | |||||
| /** | |||||
| Mouse scroll event. | |||||
| @a pos The widget-relative coordinates of the pointer. | |||||
| @a delta The scroll distance. | |||||
| @see onScroll | |||||
| */ | |||||
| struct ScrollEvent : BaseEvent { | |||||
| Point<int> pos; | |||||
| Point<float> delta; | |||||
| /** Constuctor */ | |||||
| ScrollEvent() noexcept | |||||
| : BaseEvent(), | |||||
| pos(0, 0), | |||||
| delta(0.0f, 0.0f) {} | |||||
| }; | |||||
| /** | |||||
| Resize event. | |||||
| @a size The new widget size. | |||||
| @a oldSize The previous size, may be null. | |||||
| @see onResize | |||||
| */ | |||||
| struct ResizeEvent { | |||||
| Size<uint> size; | |||||
| Size<uint> oldSize; | |||||
| /** Constuctor */ | |||||
| ResizeEvent() noexcept | |||||
| : size(0, 0), | |||||
| oldSize(0, 0) {} | |||||
| }; | |||||
| /** | |||||
| Constructor. | |||||
| */ | |||||
| explicit Widget(Window& parent); | |||||
| /** | |||||
| Constructor for a subwidget. | |||||
| */ | |||||
| explicit Widget(Widget* groupWidget); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| virtual ~Widget(); | |||||
| /** | |||||
| Check if this widget is visible within its parent window. | |||||
| Invisible widgets do not receive events except resize. | |||||
| */ | |||||
| bool isVisible() const noexcept; | |||||
| /** | |||||
| Set widget visible (or not) according to @a yesNo. | |||||
| */ | |||||
| void setVisible(bool yesNo); | |||||
| /** | |||||
| Show widget. | |||||
| This is the same as calling setVisible(true). | |||||
| */ | |||||
| void show(); | |||||
| /** | |||||
| Hide widget. | |||||
| This is the same as calling setVisible(false). | |||||
| */ | |||||
| void hide(); | |||||
| /** | |||||
| Get width. | |||||
| */ | |||||
| uint getWidth() const noexcept; | |||||
| /** | |||||
| Get height. | |||||
| */ | |||||
| uint getHeight() const noexcept; | |||||
| /** | |||||
| Get size. | |||||
| */ | |||||
| const Size<uint>& getSize() const noexcept; | |||||
| /** | |||||
| Set width. | |||||
| */ | |||||
| void setWidth(uint width) noexcept; | |||||
| /** | |||||
| Set height. | |||||
| */ | |||||
| void setHeight(uint height) noexcept; | |||||
| /** | |||||
| Set size using @a width and @a height values. | |||||
| */ | |||||
| void setSize(uint width, uint height) noexcept; | |||||
| /** | |||||
| Set size. | |||||
| */ | |||||
| void setSize(const Size<uint>& size) noexcept; | |||||
| /** | |||||
| Get absolute X. | |||||
| */ | |||||
| int getAbsoluteX() const noexcept; | |||||
| /** | |||||
| Get absolute Y. | |||||
| */ | |||||
| int getAbsoluteY() const noexcept; | |||||
| /** | |||||
| Get absolute position. | |||||
| */ | |||||
| const Point<int>& getAbsolutePos() const noexcept; | |||||
| /** | |||||
| Set absolute X. | |||||
| */ | |||||
| void setAbsoluteX(int x) noexcept; | |||||
| /** | |||||
| Set absolute Y. | |||||
| */ | |||||
| void setAbsoluteY(int y) noexcept; | |||||
| /** | |||||
| Set absolute position using @a x and @a y values. | |||||
| */ | |||||
| void setAbsolutePos(int x, int y) noexcept; | |||||
| /** | |||||
| Set absolute position. | |||||
| */ | |||||
| void setAbsolutePos(const Point<int>& pos) noexcept; | |||||
| /** | |||||
| Get this widget's window application. | |||||
| Same as calling getParentWindow().getApp(). | |||||
| */ | |||||
| Application& getParentApp() const noexcept; | |||||
| /** | |||||
| Get parent window, as passed in the constructor. | |||||
| */ | |||||
| Window& getParentWindow() const noexcept; | |||||
| /** | |||||
| Check if this widget contains the point defined by @a x and @a y. | |||||
| */ | |||||
| bool contains(int x, int y) const noexcept; | |||||
| /** | |||||
| Check if this widget contains the point @a pos. | |||||
| */ | |||||
| bool contains(const Point<int>& pos) const noexcept; | |||||
| /** | |||||
| Tell this widget's window to repaint itself. | |||||
| */ | |||||
| void repaint() noexcept; | |||||
| /** | |||||
| Get the Id associated with this widget. | |||||
| @see setId | |||||
| */ | |||||
| uint getId() const noexcept; | |||||
| /** | |||||
| Set an Id to be associated with this widget. | |||||
| @see getId | |||||
| */ | |||||
| void setId(uint id) noexcept; | |||||
| protected: | |||||
| /** | |||||
| A function called to draw the view contents with OpenGL. | |||||
| */ | |||||
| virtual void onDisplay() = 0; | |||||
| /** | |||||
| A function called when a key is pressed or released. | |||||
| @return True to stop event propagation, false otherwise. | |||||
| */ | |||||
| virtual bool onKeyboard(const KeyboardEvent&); | |||||
| /** | |||||
| A function called when a special key is pressed or released. | |||||
| @return True to stop event propagation, false otherwise. | |||||
| */ | |||||
| virtual bool onSpecial(const SpecialEvent&); | |||||
| /** | |||||
| A function called when a mouse button is pressed or released. | |||||
| @return True to stop event propagation, false otherwise. | |||||
| */ | |||||
| virtual bool onMouse(const MouseEvent&); | |||||
| /** | |||||
| A function called when the pointer moves. | |||||
| @return True to stop event propagation, false otherwise. | |||||
| */ | |||||
| virtual bool onMotion(const MotionEvent&); | |||||
| /** | |||||
| A function called on scrolling (e.g. mouse wheel or track pad). | |||||
| @return True to stop event propagation, false otherwise. | |||||
| */ | |||||
| virtual bool onScroll(const ScrollEvent&); | |||||
| /** | |||||
| A function called when the widget is resized. | |||||
| */ | |||||
| virtual void onResize(const ResizeEvent&); | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| /** @internal */ | |||||
| explicit Widget(Widget* groupWidget, bool addToSubWidgets); | |||||
| friend class ImageSlider; | |||||
| friend class NanoWidget; | |||||
| friend class Window; | |||||
| friend class StandaloneWindow; | |||||
| friend class DISTRHO_NAMESPACE::UI; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_WIDGET_HPP_INCLUDED | |||||
| @@ -0,0 +1,134 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DGL_WINDOW_HPP_INCLUDED | |||||
| #define DGL_WINDOW_HPP_INCLUDED | |||||
| #include "Geometry.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| class Application; | |||||
| class Widget; | |||||
| class StandaloneWindow; | |||||
| class Window | |||||
| { | |||||
| public: | |||||
| /** | |||||
| File browser options. | |||||
| */ | |||||
| struct FileBrowserOptions { | |||||
| const char* startDir; | |||||
| const char* title; | |||||
| uint width; | |||||
| uint height; | |||||
| /** | |||||
| File browser buttons. | |||||
| 0 means hidden. | |||||
| 1 means visible and unchecked. | |||||
| 2 means visible and checked. | |||||
| */ | |||||
| struct Buttons { | |||||
| uint listAllFiles; | |||||
| uint showHidden; | |||||
| uint showPlaces; | |||||
| /** Constuctor for default values */ | |||||
| Buttons() | |||||
| : listAllFiles(2), | |||||
| showHidden(1), | |||||
| showPlaces(1) {} | |||||
| } buttons; | |||||
| /** Constuctor for default values */ | |||||
| FileBrowserOptions() | |||||
| : startDir(nullptr), | |||||
| title(nullptr), | |||||
| width(0), | |||||
| height(0), | |||||
| buttons() {} | |||||
| }; | |||||
| explicit Window(Application& app); | |||||
| explicit Window(Application& app, Window& parent); | |||||
| explicit Window(Application& app, intptr_t parentId); | |||||
| virtual ~Window(); | |||||
| void show(); | |||||
| void hide(); | |||||
| void close(); | |||||
| void exec(bool lockWait = false); | |||||
| void focus(); | |||||
| void repaint() noexcept; | |||||
| bool openFileBrowser(const FileBrowserOptions& options); | |||||
| bool isVisible() const noexcept; | |||||
| void setVisible(bool yesNo); | |||||
| bool isResizable() const noexcept; | |||||
| void setResizable(bool yesNo); | |||||
| uint getWidth() const noexcept; | |||||
| uint getHeight() const noexcept; | |||||
| Size<uint> getSize() const noexcept; | |||||
| void setSize(uint width, uint height); | |||||
| void setSize(Size<uint> size); | |||||
| const char* getTitle() const noexcept; | |||||
| void setTitle(const char* title); | |||||
| void setTransientWinId(uintptr_t winId); | |||||
| Application& getApp() const noexcept; | |||||
| intptr_t getWindowId() const noexcept; | |||||
| void addIdleCallback(IdleCallback* const callback); | |||||
| void removeIdleCallback(IdleCallback* const callback); | |||||
| protected: | |||||
| virtual void onDisplayBefore(); | |||||
| virtual void onDisplayAfter(); | |||||
| virtual void onReshape(uint width, uint height); | |||||
| virtual void onClose(); | |||||
| virtual void fileBrowserSelected(const char* filename); | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| friend class Application; | |||||
| friend class Widget; | |||||
| friend class StandaloneWindow; | |||||
| virtual void _addWidget(Widget* const widget); | |||||
| virtual void _removeWidget(Widget* const widget); | |||||
| void _idle(); | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_WINDOW_HPP_INCLUDED | |||||
| @@ -0,0 +1,75 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "ApplicationPrivateData.hpp" | |||||
| #include "../Window.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| Application::Application() | |||||
| : pData(new PrivateData()), | |||||
| leakDetector_Application() {} | |||||
| Application::~Application() | |||||
| { | |||||
| delete pData; | |||||
| } | |||||
| void Application::idle() | |||||
| { | |||||
| for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it) | |||||
| { | |||||
| Window* const window(*it); | |||||
| window->_idle(); | |||||
| } | |||||
| for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it) | |||||
| { | |||||
| IdleCallback* const idleCallback(*it); | |||||
| idleCallback->idleCallback(); | |||||
| } | |||||
| } | |||||
| void Application::exec() | |||||
| { | |||||
| for (; pData->doLoop;) | |||||
| { | |||||
| idle(); | |||||
| d_msleep(10); | |||||
| } | |||||
| } | |||||
| void Application::quit() | |||||
| { | |||||
| pData->doLoop = false; | |||||
| for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit) | |||||
| { | |||||
| Window* const window(*rit); | |||||
| window->close(); | |||||
| } | |||||
| } | |||||
| bool Application::isQuiting() const noexcept | |||||
| { | |||||
| return !pData->doLoop; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| @@ -0,0 +1,71 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_APP_PRIVATE_DATA_HPP_INCLUDED | |||||
| #define DGL_APP_PRIVATE_DATA_HPP_INCLUDED | |||||
| #include "../Application.hpp" | |||||
| #include "../../distrho/extra/Sleep.hpp" | |||||
| #include <list> | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| struct Application::PrivateData { | |||||
| bool doLoop; | |||||
| uint visibleWindows; | |||||
| std::list<Window*> windows; | |||||
| std::list<IdleCallback*> idleCallbacks; | |||||
| PrivateData() | |||||
| : doLoop(true), | |||||
| visibleWindows(0), | |||||
| windows(), | |||||
| idleCallbacks() {} | |||||
| ~PrivateData() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(! doLoop); | |||||
| DISTRHO_SAFE_ASSERT(visibleWindows == 0); | |||||
| windows.clear(); | |||||
| idleCallbacks.clear(); | |||||
| } | |||||
| void oneShown() noexcept | |||||
| { | |||||
| if (++visibleWindows == 1) | |||||
| doLoop = true; | |||||
| } | |||||
| void oneHidden() noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,); | |||||
| if (--visibleWindows == 0) | |||||
| doLoop = false; | |||||
| } | |||||
| DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED | |||||
| @@ -0,0 +1,245 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "nanovg/nanovg.h" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| static void fixRange(float& value) | |||||
| { | |||||
| /**/ if (value < 0.0f) | |||||
| value = 0.0f; | |||||
| else if (value > 1.0f) | |||||
| value = 1.0f; | |||||
| } | |||||
| static float getFixedRange(const float& value) | |||||
| { | |||||
| if (value <= 0.0f) | |||||
| return 0.0f; | |||||
| if (value >= 1.0f) | |||||
| return 1.0f; | |||||
| return value; | |||||
| } | |||||
| static uchar getFixedRange2(const float& value) | |||||
| { | |||||
| const float value2(getFixedRange(value)*255.0f); | |||||
| if (value2 <= 0.0f) | |||||
| return 0; | |||||
| if (value2 >= 255.0f) | |||||
| return 255; | |||||
| return static_cast<uchar>(value2); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| Color::Color() noexcept | |||||
| : red(1.0f), | |||||
| green(1.0f), | |||||
| blue(1.0f), | |||||
| alpha(1.0f) {} | |||||
| Color::Color(int r, int g, int b, int a) noexcept | |||||
| : red(static_cast<float>(r)/255.0f), | |||||
| green(static_cast<float>(g)/255.0f), | |||||
| blue(static_cast<float>(b)/255.0f), | |||||
| alpha(static_cast<float>(a)/255.0f) | |||||
| { | |||||
| fixBounds(); | |||||
| } | |||||
| Color::Color(float r, float g, float b, float a) noexcept | |||||
| : red(r), | |||||
| green(g), | |||||
| blue(b), | |||||
| alpha(a) | |||||
| { | |||||
| fixBounds(); | |||||
| } | |||||
| Color::Color(const Color& color) noexcept | |||||
| : red(color.red), | |||||
| green(color.green), | |||||
| blue(color.blue), | |||||
| alpha(color.alpha) | |||||
| { | |||||
| fixBounds(); | |||||
| } | |||||
| Color& Color::operator=(const Color& color) noexcept | |||||
| { | |||||
| red = color.red; | |||||
| green = color.green; | |||||
| blue = color.blue; | |||||
| alpha = color.alpha; | |||||
| fixBounds(); | |||||
| return *this; | |||||
| } | |||||
| Color::Color(const Color& color1, const Color& color2, float u) noexcept | |||||
| : red(color1.red), | |||||
| green(color1.green), | |||||
| blue(color1.blue), | |||||
| alpha(color1.alpha) | |||||
| { | |||||
| interpolate(color2, u); | |||||
| } | |||||
| Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | |||||
| { | |||||
| return nvgHSLA(hue, saturation, lightness, static_cast<uchar>(getFixedRange(alpha)*255.0f)); | |||||
| } | |||||
| Color Color::fromHTML(const char* rgb, float alpha) | |||||
| { | |||||
| Color fallback; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback); | |||||
| if (rgb[0] == '#') ++rgb; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback); | |||||
| std::size_t rgblen(std::strlen(rgb)); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback); | |||||
| char rgbtmp[3] = { '\0', '\0', '\0' }; | |||||
| int r, g, b; | |||||
| if (rgblen == 3) | |||||
| { | |||||
| rgbtmp[0] = rgb[0]; | |||||
| r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| rgbtmp[0] = rgb[1]; | |||||
| g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| rgbtmp[0] = rgb[2]; | |||||
| b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| } | |||||
| else | |||||
| { | |||||
| rgbtmp[0] = rgb[0]; | |||||
| rgbtmp[1] = rgb[1]; | |||||
| r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| rgbtmp[0] = rgb[2]; | |||||
| rgbtmp[1] = rgb[3]; | |||||
| g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| rgbtmp[0] = rgb[4]; | |||||
| rgbtmp[1] = rgb[5]; | |||||
| b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||||
| } | |||||
| return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f)); | |||||
| } | |||||
| void Color::interpolate(const Color& other, float u) noexcept | |||||
| { | |||||
| fixRange(u); | |||||
| const float oneMinusU(1.0f - u); | |||||
| red = red * oneMinusU + other.red * u; | |||||
| green = green * oneMinusU + other.green * u; | |||||
| blue = blue * oneMinusU + other.blue * u; | |||||
| alpha = alpha * oneMinusU + other.alpha * u; | |||||
| fixBounds(); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| bool Color::isEqual(const Color& color, bool withAlpha) noexcept | |||||
| { | |||||
| const uchar r1 = getFixedRange2(rgba[0]); | |||||
| const uchar g1 = getFixedRange2(rgba[1]); | |||||
| const uchar b1 = getFixedRange2(rgba[2]); | |||||
| const uchar a1 = getFixedRange2(rgba[3]); | |||||
| const uchar r2 = getFixedRange2(color.rgba[0]); | |||||
| const uchar g2 = getFixedRange2(color.rgba[1]); | |||||
| const uchar b2 = getFixedRange2(color.rgba[2]); | |||||
| const uchar a2 = getFixedRange2(color.rgba[3]); | |||||
| if (withAlpha) | |||||
| return (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2); | |||||
| else | |||||
| return (r1 == r2 && g1 == g2 && b1 == b2); | |||||
| } | |||||
| bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept | |||||
| { | |||||
| const uchar r1 = getFixedRange2(rgba[0]); | |||||
| const uchar g1 = getFixedRange2(rgba[1]); | |||||
| const uchar b1 = getFixedRange2(rgba[2]); | |||||
| const uchar a1 = getFixedRange2(rgba[3]); | |||||
| const uchar r2 = getFixedRange2(color.rgba[0]); | |||||
| const uchar g2 = getFixedRange2(color.rgba[1]); | |||||
| const uchar b2 = getFixedRange2(color.rgba[2]); | |||||
| const uchar a2 = getFixedRange2(color.rgba[3]); | |||||
| if (withAlpha) | |||||
| return (r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2); | |||||
| else | |||||
| return (r1 != r2 || g1 != g2 || b1 != b2); | |||||
| } | |||||
| bool Color::operator==(const Color& color) noexcept | |||||
| { | |||||
| return isEqual(color, true); | |||||
| } | |||||
| bool Color::operator!=(const Color& color) noexcept | |||||
| { | |||||
| return isNotEqual(color, true); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| void Color::fixBounds() noexcept | |||||
| { | |||||
| fixRange(red); | |||||
| fixRange(green); | |||||
| fixRange(blue); | |||||
| fixRange(alpha); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| Color::Color(const NVGcolor& c) noexcept | |||||
| : red(c.r), green(c.g), blue(c.b), alpha(c.a) | |||||
| { | |||||
| fixBounds(); | |||||
| } | |||||
| Color::operator NVGcolor() const noexcept | |||||
| { | |||||
| NVGcolor nc; | |||||
| nc.r = red; | |||||
| nc.g = green; | |||||
| nc.b = blue; | |||||
| nc.a = alpha; | |||||
| return nc; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| @@ -0,0 +1,129 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_COMMON_HPP_INCLUDED | |||||
| #define DGL_COMMON_HPP_INCLUDED | |||||
| #include "../ImageWidgets.hpp" | |||||
| #include "../NanoWidgets.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| struct ButtonImpl { | |||||
| enum State { | |||||
| kStateNormal = 0, | |||||
| kStateHover, | |||||
| kStateDown | |||||
| }; | |||||
| int button; | |||||
| int state; | |||||
| Widget* self; | |||||
| BlendishButton::Callback* callback_b; | |||||
| ImageButton::Callback* callback_i; | |||||
| ButtonImpl(Widget* const s) noexcept | |||||
| : button(-1), | |||||
| state(kStateNormal), | |||||
| self(s), | |||||
| callback_b(nullptr), | |||||
| callback_i(nullptr) {} | |||||
| bool onMouse(const Widget::MouseEvent& ev) | |||||
| { | |||||
| // button was released, handle it now | |||||
| if (button != -1 && ! ev.press) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(state == kStateDown); | |||||
| // release button | |||||
| const int button2 = button; | |||||
| button = -1; | |||||
| // cursor was moved outside the button bounds, ignore click | |||||
| if (! self->contains(ev.pos)) | |||||
| { | |||||
| state = kStateNormal; | |||||
| self->repaint(); | |||||
| return true; | |||||
| } | |||||
| // still on bounds, register click | |||||
| state = kStateHover; | |||||
| self->repaint(); | |||||
| if (callback_b != nullptr) | |||||
| callback_b->blendishButtonClicked((BlendishButton*)self, button2); | |||||
| if (callback_i != nullptr) | |||||
| callback_i->imageButtonClicked((ImageButton*)self, button2); | |||||
| return true; | |||||
| } | |||||
| // button was pressed, wait for release | |||||
| if (ev.press && self->contains(ev.pos)) | |||||
| { | |||||
| button = ev.button; | |||||
| state = kStateDown; | |||||
| self->repaint(); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| bool onMotion(const Widget::MotionEvent& ev) | |||||
| { | |||||
| // keep pressed | |||||
| if (button != -1) | |||||
| return true; | |||||
| if (self->contains(ev.pos)) | |||||
| { | |||||
| // check if entering hover | |||||
| if (state == kStateNormal) | |||||
| { | |||||
| state = kStateHover; | |||||
| self->repaint(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| // check if exiting hover | |||||
| if (state == kStateHover) | |||||
| { | |||||
| state = kStateNormal; | |||||
| self->repaint(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED | |||||
| @@ -0,0 +1,192 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "../Image.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| Image::Image() | |||||
| : fRawData(nullptr), | |||||
| fSize(0, 0), | |||||
| fFormat(0), | |||||
| fType(0), | |||||
| fTextureId(0), | |||||
| fIsReady(false) | |||||
| { | |||||
| glGenTextures(1, &fTextureId); | |||||
| } | |||||
| Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) | |||||
| : fRawData(rawData), | |||||
| fSize(width, height), | |||||
| fFormat(format), | |||||
| fType(type), | |||||
| fTextureId(0), | |||||
| fIsReady(false) | |||||
| { | |||||
| glGenTextures(1, &fTextureId); | |||||
| } | |||||
| Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) | |||||
| : fRawData(rawData), | |||||
| fSize(size), | |||||
| fFormat(format), | |||||
| fType(type), | |||||
| fTextureId(0), | |||||
| fIsReady(false) | |||||
| { | |||||
| glGenTextures(1, &fTextureId); | |||||
| } | |||||
| Image::Image(const Image& image) | |||||
| : fRawData(image.fRawData), | |||||
| fSize(image.fSize), | |||||
| fFormat(image.fFormat), | |||||
| fType(image.fType), | |||||
| fTextureId(0), | |||||
| fIsReady(false) | |||||
| { | |||||
| glGenTextures(1, &fTextureId); | |||||
| } | |||||
| Image::~Image() | |||||
| { | |||||
| if (fTextureId != 0) | |||||
| { | |||||
| glDeleteTextures(1, &fTextureId); | |||||
| fTextureId = 0; | |||||
| } | |||||
| } | |||||
| void Image::loadFromMemory(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) noexcept | |||||
| { | |||||
| loadFromMemory(rawData, Size<uint>(width, height), format, type); | |||||
| } | |||||
| void Image::loadFromMemory(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) noexcept | |||||
| { | |||||
| fRawData = rawData; | |||||
| fSize = size; | |||||
| fFormat = format; | |||||
| fType = type; | |||||
| fIsReady = false; | |||||
| } | |||||
| bool Image::isValid() const noexcept | |||||
| { | |||||
| return (fRawData != nullptr && fSize.getWidth() > 0 && fSize.getHeight() > 0); | |||||
| } | |||||
| uint Image::getWidth() const noexcept | |||||
| { | |||||
| return fSize.getWidth(); | |||||
| } | |||||
| uint Image::getHeight() const noexcept | |||||
| { | |||||
| return fSize.getHeight(); | |||||
| } | |||||
| const Size<uint>& Image::getSize() const noexcept | |||||
| { | |||||
| return fSize; | |||||
| } | |||||
| const char* Image::getRawData() const noexcept | |||||
| { | |||||
| return fRawData; | |||||
| } | |||||
| GLenum Image::getFormat() const noexcept | |||||
| { | |||||
| return fFormat; | |||||
| } | |||||
| GLenum Image::getType() const noexcept | |||||
| { | |||||
| return fType; | |||||
| } | |||||
| void Image::draw() | |||||
| { | |||||
| drawAt(0, 0); | |||||
| } | |||||
| void Image::drawAt(const int x, const int y) | |||||
| { | |||||
| drawAt(Point<int>(x, y)); | |||||
| } | |||||
| void Image::drawAt(const Point<int>& pos) | |||||
| { | |||||
| if (fTextureId == 0 || ! isValid()) | |||||
| return; | |||||
| glEnable(GL_TEXTURE_2D); | |||||
| glBindTexture(GL_TEXTURE_2D, fTextureId); | |||||
| if (! fIsReady) | |||||
| { | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||||
| static_cast<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0, | |||||
| fFormat, fType, fRawData); | |||||
| fIsReady = true; | |||||
| } | |||||
| Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw(); | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| glDisable(GL_TEXTURE_2D); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| Image& Image::operator=(const Image& image) noexcept | |||||
| { | |||||
| fRawData = image.fRawData; | |||||
| fSize = image.fSize; | |||||
| fFormat = image.fFormat; | |||||
| fType = image.fType; | |||||
| fIsReady = false; | |||||
| return *this; | |||||
| } | |||||
| bool Image::operator==(const Image& image) const noexcept | |||||
| { | |||||
| return (fRawData == image.fRawData && fSize == image.fSize); | |||||
| } | |||||
| bool Image::operator!=(const Image& image) const noexcept | |||||
| { | |||||
| return !operator==(image); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| @@ -0,0 +1,931 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "../NanoVG.hpp" | |||||
| #include "WidgetPrivateData.hpp" | |||||
| // ----------------------------------------------------------------------- | |||||
| // Ignore some warnings if debugging | |||||
| #if 0 //def DEBUG | |||||
| # define NANOVG_GL3 0 | |||||
| # define NANOVG_GLES2 0 | |||||
| # define NANOVG_GLES3 0 | |||||
| # define NANOVG_GL_USE_UNIFORMBUFFER 0 | |||||
| # if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Weverything" | |||||
| # elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wall" | |||||
| # pragma GCC diagnostic ignored "-Wextra" | |||||
| # pragma GCC diagnostic ignored "-Wconversion" | |||||
| # pragma GCC diagnostic ignored "-Weffc++" | |||||
| # pragma GCC diagnostic ignored "-Wsign-conversion" | |||||
| # pragma GCC diagnostic ignored "-Wundef" | |||||
| # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" | |||||
| # endif | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Include NanoVG OpenGL implementation | |||||
| //#define STB_IMAGE_STATIC | |||||
| #define NANOVG_GL2_IMPLEMENTATION | |||||
| #include "nanovg/nanovg_gl.h" | |||||
| #if defined(NANOVG_GL2) | |||||
| # define nvgCreateGL nvgCreateGL2 | |||||
| # define nvgDeleteGL nvgDeleteGL2 | |||||
| #elif defined(NANOVG_GL3) | |||||
| # define nvgCreateGL nvgCreateGL3 | |||||
| # define nvgDeleteGL nvgDeleteGL3 | |||||
| #elif defined(NANOVG_GLES2) | |||||
| # define nvgCreateGL nvgCreateGLES2 | |||||
| # define nvgDeleteGL nvgDeleteGLES2 | |||||
| #elif defined(NANOVG_GLES3) | |||||
| # define nvgCreateGL nvgCreateGLES3 | |||||
| # define nvgDeleteGL nvgDeleteGLES3 | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Restore normal state if debugging | |||||
| #if 0//def DEBUG | |||||
| # if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| # elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| # endif | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // NanoImage | |||||
| NanoImage::NanoImage() | |||||
| : fHandle(), | |||||
| fSize(), | |||||
| leakDetector_NanoImage() {} | |||||
| NanoImage::NanoImage(const Handle& handle) | |||||
| : fHandle(handle), | |||||
| fSize(), | |||||
| leakDetector_NanoImage() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fHandle.context != nullptr && fHandle.imageId != 0,); | |||||
| _updateSize(); | |||||
| } | |||||
| NanoImage::~NanoImage() | |||||
| { | |||||
| if (fHandle.context != nullptr && fHandle.imageId != 0) | |||||
| nvgDeleteImage(fHandle.context, fHandle.imageId); | |||||
| } | |||||
| NanoImage& NanoImage::operator=(const Handle& handle) | |||||
| { | |||||
| if (fHandle.context != nullptr && fHandle.imageId != 0) | |||||
| nvgDeleteImage(fHandle.context, fHandle.imageId); | |||||
| fHandle.context = handle.context; | |||||
| fHandle.imageId = handle.imageId; | |||||
| return *this; | |||||
| } | |||||
| bool NanoImage::isValid() const noexcept | |||||
| { | |||||
| return (fHandle.context != nullptr && fHandle.imageId != 0); | |||||
| } | |||||
| Size<uint> NanoImage::getSize() const noexcept | |||||
| { | |||||
| return fSize; | |||||
| } | |||||
| GLuint NanoImage::getTextureHandle() const | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fHandle.context != nullptr && fHandle.imageId != 0, 0); | |||||
| return nvglImageHandle(fHandle.context, fHandle.imageId); | |||||
| } | |||||
| void NanoImage::_updateSize() | |||||
| { | |||||
| int w=0, h=0; | |||||
| nvgImageSize(fHandle.context, fHandle.imageId, &w, &h); | |||||
| if (w < 0) w = 0; | |||||
| if (h < 0) h = 0; | |||||
| fSize.setSize(static_cast<uint>(w), static_cast<uint>(h)); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Paint | |||||
| NanoVG::Paint::Paint() noexcept | |||||
| : radius(0.0f), feather(0.0f), innerColor(), outerColor(), imageId(0) | |||||
| { | |||||
| std::memset(xform, 0, sizeof(float)*6); | |||||
| std::memset(extent, 0, sizeof(float)*2); | |||||
| } | |||||
| NanoVG::Paint::Paint(const NVGpaint& p) noexcept | |||||
| : radius(p.radius), feather(p.feather), innerColor(p.innerColor), outerColor(p.outerColor), imageId(p.image) | |||||
| { | |||||
| std::memcpy(xform, p.xform, sizeof(float)*6); | |||||
| std::memcpy(extent, p.extent, sizeof(float)*2); | |||||
| } | |||||
| NanoVG::Paint::operator NVGpaint() const noexcept | |||||
| { | |||||
| NVGpaint p; | |||||
| p.radius = radius; | |||||
| p.feather = feather; | |||||
| p.innerColor = innerColor; | |||||
| p.outerColor = outerColor; | |||||
| p.image = imageId; | |||||
| std::memcpy(p.xform, xform, sizeof(float)*6); | |||||
| std::memcpy(p.extent, extent, sizeof(float)*2); | |||||
| return p; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // NanoVG | |||||
| NanoVG::NanoVG(int flags) | |||||
| : fContext(nvgCreateGL(flags)), | |||||
| fInFrame(false), | |||||
| fIsSubWidget(false), | |||||
| leakDetector_NanoVG() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr,); | |||||
| } | |||||
| NanoVG::NanoVG(NanoWidget* groupWidget) | |||||
| : fContext(groupWidget->fContext), | |||||
| fInFrame(false), | |||||
| fIsSubWidget(true), | |||||
| leakDetector_NanoVG() | |||||
| { | |||||
| } | |||||
| NanoVG::~NanoVG() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(! fInFrame); | |||||
| if (fContext != nullptr && ! fIsSubWidget) | |||||
| nvgDeleteGL(fContext); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | |||||
| fInFrame = true; | |||||
| nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); | |||||
| } | |||||
| void NanoVG::beginFrame(Widget* const widget) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | |||||
| Window& window(widget->getParentWindow()); | |||||
| fInFrame = true; | |||||
| nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f); | |||||
| } | |||||
| void NanoVG::cancelFrame() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fInFrame,); | |||||
| if (fContext != nullptr) | |||||
| nvgCancelFrame(fContext); | |||||
| fInFrame = false; | |||||
| } | |||||
| void NanoVG::endFrame() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fInFrame,); | |||||
| // Save current blend state | |||||
| GLboolean blendEnabled; | |||||
| GLint blendSrc, blendDst; | |||||
| glGetBooleanv(GL_BLEND, &blendEnabled); | |||||
| glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrc); | |||||
| glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDst); | |||||
| if (fContext != nullptr) | |||||
| nvgEndFrame(fContext); | |||||
| // Restore blend state | |||||
| if (blendEnabled) | |||||
| glEnable(GL_BLEND); | |||||
| else | |||||
| glDisable(GL_BLEND); | |||||
| glBlendFunc(blendSrc, blendDst); | |||||
| fInFrame = false; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // State Handling | |||||
| void NanoVG::save() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgSave(fContext); | |||||
| } | |||||
| void NanoVG::restore() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgRestore(fContext); | |||||
| } | |||||
| void NanoVG::reset() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgReset(fContext); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Render styles | |||||
| void NanoVG::strokeColor(const Color& color) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgStrokeColor(fContext, color); | |||||
| } | |||||
| void NanoVG::strokeColor(const int red, const int green, const int blue, const int alpha) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,); | |||||
| nvgStrokeColor(fContext, nvgRGBA(static_cast<uchar>(red), | |||||
| static_cast<uchar>(green), | |||||
| static_cast<uchar>(blue), | |||||
| static_cast<uchar>(alpha))); | |||||
| } | |||||
| } | |||||
| void NanoVG::strokeColor(const float red, const float green, const float blue, const float alpha) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgStrokeColor(fContext, nvgRGBAf(red, green, blue, alpha)); | |||||
| } | |||||
| void NanoVG::strokePaint(const Paint& paint) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgStrokePaint(fContext, paint); | |||||
| } | |||||
| void NanoVG::fillColor(const Color& color) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgFillColor(fContext, color); | |||||
| } | |||||
| void NanoVG::fillColor(const int red, const int green, const int blue, const int alpha) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(red >= 0 && red <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(green >= 0 && green <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(blue >= 0 && blue <= 255,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(alpha >= 0 && alpha <= 255,); | |||||
| nvgFillColor(fContext, nvgRGBA(static_cast<uchar>(red), | |||||
| static_cast<uchar>(green), | |||||
| static_cast<uchar>(blue), | |||||
| static_cast<uchar>(alpha))); | |||||
| } | |||||
| } | |||||
| void NanoVG::fillColor(const float red, const float green, const float blue, const float alpha) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgFillColor(fContext, nvgRGBAf(red, green, blue, alpha)); | |||||
| } | |||||
| void NanoVG::fillPaint(const Paint& paint) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgFillPaint(fContext, paint); | |||||
| } | |||||
| void NanoVG::miterLimit(float limit) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(limit > 0.0f,); | |||||
| nvgMiterLimit(fContext, limit); | |||||
| } | |||||
| void NanoVG::strokeWidth(float size) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(size > 0.0f,); | |||||
| nvgStrokeWidth(fContext, size); | |||||
| } | |||||
| void NanoVG::lineCap(NanoVG::LineCap cap) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgLineCap(fContext, cap); | |||||
| } | |||||
| void NanoVG::lineJoin(NanoVG::LineCap join) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgLineJoin(fContext, join); | |||||
| } | |||||
| void NanoVG::globalAlpha(float alpha) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgGlobalAlpha(fContext, alpha); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Transforms | |||||
| void NanoVG::resetTransform() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgResetTransform(fContext); | |||||
| } | |||||
| void NanoVG::transform(float a, float b, float c, float d, float e, float f) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgTransform(fContext, a, b, c, d, e, f); | |||||
| } | |||||
| void NanoVG::translate(float x, float y) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgTranslate(fContext, x, y); | |||||
| } | |||||
| void NanoVG::rotate(float angle) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); | |||||
| nvgRotate(fContext, angle); | |||||
| } | |||||
| void NanoVG::skewX(float angle) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); | |||||
| nvgSkewX(fContext, angle); | |||||
| } | |||||
| void NanoVG::skewY(float angle) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); | |||||
| nvgSkewY(fContext, angle); | |||||
| } | |||||
| void NanoVG::scale(float x, float y) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(x > 0.0f,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(y > 0.0f,); | |||||
| nvgScale(fContext, x, y); | |||||
| } | |||||
| void NanoVG::currentTransform(float xform[6]) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgCurrentTransform(fContext, xform); | |||||
| } | |||||
| void NanoVG::transformIdentity(float dst[6]) | |||||
| { | |||||
| nvgTransformIdentity(dst); | |||||
| } | |||||
| void NanoVG::transformTranslate(float dst[6], float tx, float ty) | |||||
| { | |||||
| nvgTransformTranslate(dst, tx, ty); | |||||
| } | |||||
| void NanoVG::transformScale(float dst[6], float sx, float sy) | |||||
| { | |||||
| nvgTransformScale(dst, sx, sy); | |||||
| } | |||||
| void NanoVG::transformRotate(float dst[6], float a) | |||||
| { | |||||
| nvgTransformRotate(dst, a); | |||||
| } | |||||
| void NanoVG::transformSkewX(float dst[6], float a) | |||||
| { | |||||
| nvgTransformSkewX(dst, a); | |||||
| } | |||||
| void NanoVG::transformSkewY(float dst[6], float a) | |||||
| { | |||||
| nvgTransformSkewY(dst, a); | |||||
| } | |||||
| void NanoVG::transformMultiply(float dst[6], const float src[6]) | |||||
| { | |||||
| nvgTransformMultiply(dst, src); | |||||
| } | |||||
| void NanoVG::transformPremultiply(float dst[6], const float src[6]) | |||||
| { | |||||
| nvgTransformPremultiply(dst, src); | |||||
| } | |||||
| int NanoVG::transformInverse(float dst[6], const float src[6]) | |||||
| { | |||||
| return nvgTransformInverse(dst, src); | |||||
| } | |||||
| void NanoVG::transformPoint(float& dstx, float& dsty, const float xform[6], float srcx, float srcy) | |||||
| { | |||||
| nvgTransformPoint(&dstx, &dsty, xform, srcx, srcy); | |||||
| } | |||||
| float NanoVG::degToRad(float deg) | |||||
| { | |||||
| return nvgDegToRad(deg); | |||||
| } | |||||
| float NanoVG::radToDeg(float rad) | |||||
| { | |||||
| return nvgRadToDeg(rad); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Images | |||||
| NanoImage::Handle NanoVG::createImageFromFile(const char* filename, ImageFlags imageFlags) | |||||
| { | |||||
| return createImageFromFile(filename, static_cast<int>(imageFlags)); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromFile(const char* filename, int imageFlags) | |||||
| { | |||||
| if (fContext == nullptr) return NanoImage::Handle(); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', NanoImage::Handle()); | |||||
| return NanoImage::Handle(fContext, nvgCreateImage(fContext, filename, imageFlags)); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags) | |||||
| { | |||||
| return createImageFromMemory(data, dataSize, static_cast<int>(imageFlags)); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int imageFlags) | |||||
| { | |||||
| if (fContext == nullptr) return NanoImage::Handle(); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle()); | |||||
| return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<int>(dataSize))); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags) | |||||
| { | |||||
| return createImageFromRGBA(w, h, data, static_cast<int>(imageFlags)); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, int imageFlags) | |||||
| { | |||||
| if (fContext == nullptr) return NanoImage::Handle(); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); | |||||
| return NanoImage::Handle(fContext, nvgCreateImageRGBA(fContext, | |||||
| static_cast<int>(w), | |||||
| static_cast<int>(h), imageFlags, data)); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, ImageFlags imageFlags, bool deleteTexture) | |||||
| { | |||||
| return createImageFromTextureHandle(textureId, w, h, static_cast<int>(imageFlags), deleteTexture); | |||||
| } | |||||
| NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, int imageFlags, bool deleteTexture) | |||||
| { | |||||
| if (fContext == nullptr) return NanoImage::Handle(); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(textureId != 0, NanoImage::Handle()); | |||||
| if (! deleteTexture) | |||||
| imageFlags |= NVG_IMAGE_NODELETE; | |||||
| return NanoImage::Handle(fContext, nvglCreateImageFromHandle(fContext, | |||||
| textureId, | |||||
| static_cast<int>(w), | |||||
| static_cast<int>(h), imageFlags)); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Paints | |||||
| NanoVG::Paint NanoVG::linearGradient(float sx, float sy, float ex, float ey, const Color& icol, const Color& ocol) | |||||
| { | |||||
| if (fContext == nullptr) return Paint(); | |||||
| return nvgLinearGradient(fContext, sx, sy, ex, ey, icol, ocol); | |||||
| } | |||||
| NanoVG::Paint NanoVG::boxGradient(float x, float y, float w, float h, float r, float f, const Color& icol, const Color& ocol) | |||||
| { | |||||
| if (fContext == nullptr) return Paint(); | |||||
| return nvgBoxGradient(fContext, x, y, w, h, r, f, icol, ocol); | |||||
| } | |||||
| NanoVG::Paint NanoVG::radialGradient(float cx, float cy, float inr, float outr, const Color& icol, const Color& ocol) | |||||
| { | |||||
| if (fContext == nullptr) return Paint(); | |||||
| return nvgRadialGradient(fContext, cx, cy, inr, outr, icol, ocol); | |||||
| } | |||||
| NanoVG::Paint NanoVG::imagePattern(float ox, float oy, float ex, float ey, float angle, const NanoImage& image, float alpha) | |||||
| { | |||||
| if (fContext == nullptr) return Paint(); | |||||
| const int imageId(image.fHandle.imageId); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(imageId != 0, Paint()); | |||||
| return nvgImagePattern(fContext, ox, oy, ex, ey, angle, imageId, alpha); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Scissoring | |||||
| void NanoVG::scissor(float x, float y, float w, float h) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgScissor(fContext, x, y, w, h); | |||||
| } | |||||
| void NanoVG::intersectScissor(float x, float y, float w, float h) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgIntersectScissor(fContext, x, y, w, h); | |||||
| } | |||||
| void NanoVG::resetScissor() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgResetScissor(fContext); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Paths | |||||
| void NanoVG::beginPath() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgBeginPath(fContext); | |||||
| } | |||||
| void NanoVG::moveTo(float x, float y) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgMoveTo(fContext, x, y); | |||||
| } | |||||
| void NanoVG::lineTo(float x, float y) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgLineTo(fContext, x, y); | |||||
| } | |||||
| void NanoVG::bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgBezierTo(fContext, c1x, c1y, c2x, c2y, x, y); | |||||
| } | |||||
| void NanoVG::quadTo(float cx, float cy, float x, float y) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgQuadTo(fContext, cx, cy, x, y); | |||||
| } | |||||
| void NanoVG::arcTo(float x1, float y1, float x2, float y2, float radius) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgArcTo(fContext, x1, y1, x2, y2, radius); | |||||
| } | |||||
| void NanoVG::closePath() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgClosePath(fContext); | |||||
| } | |||||
| void NanoVG::pathWinding(NanoVG::Winding dir) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgPathWinding(fContext, dir); | |||||
| } | |||||
| void NanoVG::arc(float cx, float cy, float r, float a0, float a1, NanoVG::Winding dir) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgArc(fContext, cx, cy, r, a0, a1, dir); | |||||
| } | |||||
| void NanoVG::rect(float x, float y, float w, float h) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgRect(fContext, x, y, w, h); | |||||
| } | |||||
| void NanoVG::roundedRect(float x, float y, float w, float h, float r) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgRoundedRect(fContext, x, y, w, h, r); | |||||
| } | |||||
| void NanoVG::ellipse(float cx, float cy, float rx, float ry) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgEllipse(fContext, cx, cy, rx, ry); | |||||
| } | |||||
| void NanoVG::circle(float cx, float cy, float r) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgCircle(fContext, cx, cy, r); | |||||
| } | |||||
| void NanoVG::fill() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgFill(fContext); | |||||
| } | |||||
| void NanoVG::stroke() | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgStroke(fContext); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // Text | |||||
| NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) | |||||
| { | |||||
| if (fContext == nullptr) return -1; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); | |||||
| return nvgCreateFont(fContext, name, filename); | |||||
| } | |||||
| NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) | |||||
| { | |||||
| if (fContext == nullptr) return -1; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); | |||||
| return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData); | |||||
| } | |||||
| NanoVG::FontId NanoVG::findFont(const char* name) | |||||
| { | |||||
| if (fContext == nullptr) return -1; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); | |||||
| return nvgFindFont(fContext, name); | |||||
| } | |||||
| void NanoVG::fontSize(float size) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(size > 0.0f,); | |||||
| nvgFontSize(fContext, size); | |||||
| } | |||||
| void NanoVG::fontBlur(float blur) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(blur >= 0.0f,); | |||||
| nvgFontBlur(fContext, blur); | |||||
| } | |||||
| void NanoVG::textLetterSpacing(float spacing) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(spacing >= 0.0f,); | |||||
| nvgTextLetterSpacing(fContext, spacing); | |||||
| } | |||||
| void NanoVG::textLineHeight(float lineHeight) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineHeight > 0.0f,); | |||||
| nvgTextLineHeight(fContext, lineHeight); | |||||
| } | |||||
| void NanoVG::textAlign(NanoVG::Align align) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgTextAlign(fContext, align); | |||||
| } | |||||
| void NanoVG::textAlign(int align) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgTextAlign(fContext, align); | |||||
| } | |||||
| void NanoVG::fontFaceId(FontId font) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(font >= 0,); | |||||
| nvgFontFaceId(fContext, font); | |||||
| } | |||||
| void NanoVG::fontFace(const char* font) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(font != nullptr && font[0] != '\0',); | |||||
| nvgFontFace(fContext, font); | |||||
| } | |||||
| float NanoVG::text(float x, float y, const char* string, const char* end) | |||||
| { | |||||
| if (fContext == nullptr) return 0.0f; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0.0f); | |||||
| return nvgText(fContext, x, y, string, end); | |||||
| } | |||||
| void NanoVG::textBox(float x, float y, float breakRowWidth, const char* string, const char* end) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0',); | |||||
| nvgTextBox(fContext, x, y, breakRowWidth, string, end); | |||||
| } | |||||
| float NanoVG::textBounds(float x, float y, const char* string, const char* end, Rectangle<float>& bounds) | |||||
| { | |||||
| if (fContext == nullptr) return 0.0f; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0.0f); | |||||
| float b[4]; | |||||
| const float ret = nvgTextBounds(fContext, x, y, string, end, b); | |||||
| bounds = Rectangle<float>(b[0], b[1], b[2], b[3]); | |||||
| return ret; | |||||
| } | |||||
| void NanoVG::textBoxBounds(float x, float y, float breakRowWidth, const char* string, const char* end, float bounds[4]) | |||||
| { | |||||
| if (fContext == nullptr) return; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0',); | |||||
| nvgTextBoxBounds(fContext, x, y, breakRowWidth, string, end, bounds); | |||||
| } | |||||
| int NanoVG::textGlyphPositions(float x, float y, const char* string, const char* end, NanoVG::GlyphPosition& positions, int maxPositions) | |||||
| { | |||||
| if (fContext == nullptr) return 0; | |||||
| DISTRHO_SAFE_ASSERT_RETURN(string != nullptr && string[0] != '\0', 0); | |||||
| return nvgTextGlyphPositions(fContext, x, y, string, end, (NVGglyphPosition*)&positions, maxPositions); | |||||
| } | |||||
| void NanoVG::textMetrics(float* ascender, float* descender, float* lineh) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| nvgTextMetrics(fContext, ascender, descender, lineh); | |||||
| } | |||||
| int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWidth, NanoVG::TextRow& rows, int maxRows) | |||||
| { | |||||
| if (fContext != nullptr) | |||||
| return nvgTextBreakLines(fContext, string, end, breakRowWidth, (NVGtextRow*)&rows, maxRows); | |||||
| return 0; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| struct NanoWidget::PrivateData { | |||||
| NanoWidget* const self; | |||||
| std::vector<NanoWidget*> subWidgets; | |||||
| PrivateData(NanoWidget* const s) | |||||
| : self(s), | |||||
| subWidgets() {} | |||||
| ~PrivateData() | |||||
| { | |||||
| subWidgets.clear(); | |||||
| } | |||||
| }; | |||||
| NanoWidget::NanoWidget(Window& parent, int flags) | |||||
| : Widget(parent), | |||||
| NanoVG(flags), | |||||
| nData(new PrivateData(this)), | |||||
| leakDetector_NanoWidget() | |||||
| { | |||||
| pData->needsScaling = true; | |||||
| } | |||||
| NanoWidget::NanoWidget(Widget* groupWidget, int flags) | |||||
| : Widget(groupWidget, true), | |||||
| NanoVG(flags), | |||||
| nData(new PrivateData(this)), | |||||
| leakDetector_NanoWidget() | |||||
| { | |||||
| pData->needsScaling = true; | |||||
| } | |||||
| NanoWidget::NanoWidget(NanoWidget* groupWidget) | |||||
| : Widget(groupWidget, false), | |||||
| NanoVG(groupWidget), | |||||
| nData(new PrivateData(this)), | |||||
| leakDetector_NanoWidget() | |||||
| { | |||||
| pData->needsScaling = true; | |||||
| groupWidget->nData->subWidgets.push_back(this); | |||||
| } | |||||
| NanoWidget::~NanoWidget() | |||||
| { | |||||
| delete nData; | |||||
| } | |||||
| void NanoWidget::onDisplay() | |||||
| { | |||||
| NanoVG::beginFrame(getWidth(), getHeight()); | |||||
| onNanoDisplay(); | |||||
| for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it) | |||||
| { | |||||
| NanoWidget* const widget(*it); | |||||
| widget->onNanoDisplay(); | |||||
| } | |||||
| NanoVG::endFrame(); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| extern "C" { | |||||
| #include "nanovg/nanovg.c" | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,146 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "../NanoWidgets.hpp" | |||||
| #include "Common.hpp" | |||||
| #define BLENDISH_IMPLEMENTATION | |||||
| #include "nanovg/nanovg.h" | |||||
| #include "oui-blendish/blendish.h" | |||||
| #include "oui-blendish/blendish_resources.h" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| static void registerBlendishResourcesIfNeeded(NVGcontext* const context) | |||||
| { | |||||
| if (nvgFindFont(context, "__dpf_blendish__") >= 0) | |||||
| return; | |||||
| using namespace blendish_resources; | |||||
| bndSetFont(nvgCreateFontMem(context, "__dpf_blendish__", (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0)); | |||||
| bndSetIconImage(nvgCreateImageMem(context, 0, (const uchar*)blender_icons16_png, blender_icons16_png_size)); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| struct BlendishButton::PrivateData { | |||||
| ButtonImpl impl; | |||||
| int iconId; | |||||
| DISTRHO_NAMESPACE::String text; | |||||
| PrivateData(Widget* const s, const char* const t, const int i) noexcept | |||||
| : impl(s), | |||||
| iconId(i), | |||||
| text(t) {} | |||||
| DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| BlendishButton::BlendishButton(Window& parent, const char* text, int iconId) | |||||
| : NanoWidget(parent), | |||||
| pData(new PrivateData(this, text, iconId)), | |||||
| leakDetector_BlendishButton() | |||||
| { | |||||
| registerBlendishResourcesIfNeeded(getContext()); | |||||
| _updateBounds(); | |||||
| } | |||||
| BlendishButton::BlendishButton(NanoWidget* widget, const char* text, int iconId) | |||||
| : NanoWidget(widget), | |||||
| pData(new PrivateData(this, text, iconId)), | |||||
| leakDetector_BlendishButton() | |||||
| { | |||||
| registerBlendishResourcesIfNeeded(getContext()); | |||||
| _updateBounds(); | |||||
| } | |||||
| BlendishButton::~BlendishButton() | |||||
| { | |||||
| delete pData; | |||||
| } | |||||
| int BlendishButton::getIconId() const noexcept | |||||
| { | |||||
| return pData->iconId; | |||||
| } | |||||
| void BlendishButton::setIconId(int iconId) noexcept | |||||
| { | |||||
| if (pData->iconId == iconId) | |||||
| return; | |||||
| pData->iconId = iconId; | |||||
| _updateBounds(); | |||||
| repaint(); | |||||
| } | |||||
| const char* BlendishButton::getText() const noexcept | |||||
| { | |||||
| return pData->text; | |||||
| } | |||||
| void BlendishButton::setText(const char* text) noexcept | |||||
| { | |||||
| if (pData->text == text) | |||||
| return; | |||||
| pData->text = text; | |||||
| _updateBounds(); | |||||
| repaint(); | |||||
| } | |||||
| void BlendishButton::setCallback(Callback* callback) noexcept | |||||
| { | |||||
| pData->impl.callback_b = callback; | |||||
| } | |||||
| void BlendishButton::onNanoDisplay() | |||||
| { | |||||
| bndToolButton(getContext(), | |||||
| getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight(), | |||||
| 0, static_cast<BNDwidgetState>(pData->impl.state), pData->iconId, pData->text); | |||||
| } | |||||
| bool BlendishButton::onMouse(const MouseEvent& ev) | |||||
| { | |||||
| return pData->impl.onMouse(ev); | |||||
| } | |||||
| bool BlendishButton::onMotion(const MotionEvent& ev) | |||||
| { | |||||
| return pData->impl.onMotion(ev); | |||||
| } | |||||
| void BlendishButton::_updateBounds() | |||||
| { | |||||
| const float width = bndLabelWidth (getContext(), pData->iconId, pData->text); | |||||
| const float height = bndLabelHeight(getContext(), pData->iconId, pData->text, width); | |||||
| setSize(width, height); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #include "oui-blendish/blendish_resources.cpp" | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,253 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "WidgetPrivateData.hpp" | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| // Widget | |||||
| Widget::Widget(Window& parent) | |||||
| : pData(new PrivateData(this, parent, nullptr, false)), | |||||
| leakDetector_Widget() | |||||
| { | |||||
| parent._addWidget(this); | |||||
| } | |||||
| Widget::Widget(Widget* groupWidget) | |||||
| : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)), | |||||
| leakDetector_Widget() | |||||
| { | |||||
| pData->parent._addWidget(this); | |||||
| } | |||||
| Widget::Widget(Widget* groupWidget, bool addToSubWidgets) | |||||
| : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)), | |||||
| leakDetector_Widget() | |||||
| { | |||||
| pData->parent._addWidget(this); | |||||
| } | |||||
| Widget::~Widget() | |||||
| { | |||||
| pData->parent._removeWidget(this); | |||||
| delete pData; | |||||
| } | |||||
| bool Widget::isVisible() const noexcept | |||||
| { | |||||
| return pData->visible; | |||||
| } | |||||
| void Widget::setVisible(bool yesNo) | |||||
| { | |||||
| if (pData->visible == yesNo) | |||||
| return; | |||||
| pData->visible = yesNo; | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| void Widget::show() | |||||
| { | |||||
| setVisible(true); | |||||
| } | |||||
| void Widget::hide() | |||||
| { | |||||
| setVisible(false); | |||||
| } | |||||
| uint Widget::getWidth() const noexcept | |||||
| { | |||||
| return pData->size.getWidth(); | |||||
| } | |||||
| uint Widget::getHeight() const noexcept | |||||
| { | |||||
| return pData->size.getHeight(); | |||||
| } | |||||
| const Size<uint>& Widget::getSize() const noexcept | |||||
| { | |||||
| return pData->size; | |||||
| } | |||||
| void Widget::setWidth(uint width) noexcept | |||||
| { | |||||
| if (pData->size.getWidth() == width) | |||||
| return; | |||||
| ResizeEvent ev; | |||||
| ev.oldSize = pData->size; | |||||
| ev.size = Size<uint>(width, pData->size.getHeight()); | |||||
| pData->size.setWidth(width); | |||||
| onResize(ev); | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| void Widget::setHeight(uint height) noexcept | |||||
| { | |||||
| if (pData->size.getHeight() == height) | |||||
| return; | |||||
| ResizeEvent ev; | |||||
| ev.oldSize = pData->size; | |||||
| ev.size = Size<uint>(pData->size.getWidth(), height); | |||||
| pData->size.setHeight(height); | |||||
| onResize(ev); | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| void Widget::setSize(uint width, uint height) noexcept | |||||
| { | |||||
| setSize(Size<uint>(width, height)); | |||||
| } | |||||
| void Widget::setSize(const Size<uint>& size) noexcept | |||||
| { | |||||
| if (pData->size == size) | |||||
| return; | |||||
| ResizeEvent ev; | |||||
| ev.oldSize = pData->size; | |||||
| ev.size = size; | |||||
| pData->size = size; | |||||
| onResize(ev); | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| int Widget::getAbsoluteX() const noexcept | |||||
| { | |||||
| return pData->absolutePos.getX(); | |||||
| } | |||||
| int Widget::getAbsoluteY() const noexcept | |||||
| { | |||||
| return pData->absolutePos.getY(); | |||||
| } | |||||
| const Point<int>& Widget::getAbsolutePos() const noexcept | |||||
| { | |||||
| return pData->absolutePos; | |||||
| } | |||||
| void Widget::setAbsoluteX(int x) noexcept | |||||
| { | |||||
| if (pData->absolutePos.getX() == x) | |||||
| return; | |||||
| pData->absolutePos.setX(x); | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| void Widget::setAbsoluteY(int y) noexcept | |||||
| { | |||||
| if (pData->absolutePos.getY() == y) | |||||
| return; | |||||
| pData->absolutePos.setY(y); | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| void Widget::setAbsolutePos(int x, int y) noexcept | |||||
| { | |||||
| setAbsolutePos(Point<int>(x, y)); | |||||
| } | |||||
| void Widget::setAbsolutePos(const Point<int>& pos) noexcept | |||||
| { | |||||
| if (pData->absolutePos == pos) | |||||
| return; | |||||
| pData->absolutePos = pos; | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| Application& Widget::getParentApp() const noexcept | |||||
| { | |||||
| return pData->parent.getApp(); | |||||
| } | |||||
| Window& Widget::getParentWindow() const noexcept | |||||
| { | |||||
| return pData->parent; | |||||
| } | |||||
| bool Widget::contains(int x, int y) const noexcept | |||||
| { | |||||
| return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight()); | |||||
| } | |||||
| bool Widget::contains(const Point<int>& pos) const noexcept | |||||
| { | |||||
| return contains(pos.getX(), pos.getY()); | |||||
| } | |||||
| void Widget::repaint() noexcept | |||||
| { | |||||
| pData->parent.repaint(); | |||||
| } | |||||
| uint Widget::getId() const noexcept | |||||
| { | |||||
| return pData->id; | |||||
| } | |||||
| void Widget::setId(uint id) noexcept | |||||
| { | |||||
| pData->id = id; | |||||
| } | |||||
| bool Widget::onKeyboard(const KeyboardEvent&) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool Widget::onSpecial(const SpecialEvent&) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool Widget::onMouse(const MouseEvent&) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool Widget::onMotion(const MotionEvent&) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool Widget::onScroll(const ScrollEvent&) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| void Widget::onResize(const ResizeEvent&) | |||||
| { | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| @@ -0,0 +1,133 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
| #define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
| #include "../Widget.hpp" | |||||
| #include "../Window.hpp" | |||||
| #include <vector> | |||||
| START_NAMESPACE_DGL | |||||
| // ----------------------------------------------------------------------- | |||||
| struct Widget::PrivateData { | |||||
| Widget* const self; | |||||
| Window& parent; | |||||
| Point<int> absolutePos; | |||||
| Size<uint> size; | |||||
| std::vector<Widget*> subWidgets; | |||||
| uint id; | |||||
| bool needsFullViewport; | |||||
| bool needsScaling; | |||||
| bool skipDisplay; | |||||
| bool visible; | |||||
| PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets) | |||||
| : self(s), | |||||
| parent(p), | |||||
| absolutePos(0, 0), | |||||
| size(0, 0), | |||||
| id(0), | |||||
| needsFullViewport(false), | |||||
| needsScaling(false), | |||||
| skipDisplay(false), | |||||
| visible(true) | |||||
| { | |||||
| if (addToSubWidgets && groupWidget != nullptr) | |||||
| groupWidget->pData->subWidgets.push_back(self); | |||||
| } | |||||
| ~PrivateData() | |||||
| { | |||||
| subWidgets.clear(); | |||||
| } | |||||
| void display(const uint width, const uint height) | |||||
| { | |||||
| if (skipDisplay || ! visible) | |||||
| return; | |||||
| bool needsDisableScissor = false; | |||||
| // reset color | |||||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||||
| if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height))) | |||||
| { | |||||
| // full viewport size | |||||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||||
| } | |||||
| else if (needsScaling) | |||||
| { | |||||
| // limit viewport to widget bounds | |||||
| glViewport(absolutePos.getX(), | |||||
| height - static_cast<int>(self->getHeight()) - absolutePos.getY(), | |||||
| static_cast<GLsizei>(self->getWidth()), | |||||
| static_cast<GLsizei>(self->getHeight())); | |||||
| } | |||||
| else | |||||
| { | |||||
| // only set viewport pos | |||||
| glViewport(absolutePos.getX(), | |||||
| /*height - static_cast<int>(self->getHeight())*/ - absolutePos.getY(), | |||||
| static_cast<GLsizei>(width), | |||||
| static_cast<GLsizei>(height)); | |||||
| // then cut the outer bounds | |||||
| glScissor(absolutePos.getX(), | |||||
| height - static_cast<int>(self->getHeight()) - absolutePos.getY(), | |||||
| static_cast<GLsizei>(self->getWidth()), | |||||
| static_cast<GLsizei>(self->getHeight())); | |||||
| glEnable(GL_SCISSOR_TEST); | |||||
| needsDisableScissor = true; | |||||
| } | |||||
| // display widget | |||||
| self->onDisplay(); | |||||
| if (needsDisableScissor) | |||||
| { | |||||
| glDisable(GL_SCISSOR_TEST); | |||||
| needsDisableScissor = false; | |||||
| } | |||||
| displaySubWidgets(width, height); | |||||
| } | |||||
| void displaySubWidgets(const uint width, const uint height) | |||||
| { | |||||
| for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||||
| { | |||||
| Widget* const widget(*it); | |||||
| DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | |||||
| widget->pData->display(width, height); | |||||
| } | |||||
| } | |||||
| DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| #endif // DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||||
| @@ -0,0 +1,18 @@ | |||||
| Copyright (c) 2013 Mikko Mononen memon@inside.org | |||||
| This software is provided 'as-is', without any express or implied | |||||
| warranty. In no event will the authors 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 software must not be misrepresented; you must not | |||||
| claim that you wrote the original software. If you use this software | |||||
| 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 software. | |||||
| 3. This notice may not be removed or altered from any source distribution. | |||||
| @@ -0,0 +1,620 @@ | |||||
| // | |||||
| // Copyright (c) 2013 Mikko Mononen memon@inside.org | |||||
| // | |||||
| // This software is provided 'as-is', without any express or implied | |||||
| // warranty. In no event will the authors 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 software must not be misrepresented; you must not | |||||
| // claim that you wrote the original software. If you use this software | |||||
| // 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 software. | |||||
| // 3. This notice may not be removed or altered from any source distribution. | |||||
| // | |||||
| #ifndef NANOVG_H | |||||
| #define NANOVG_H | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| #define NVG_PI 3.14159265358979323846264338327f | |||||
| #ifdef _MSC_VER | |||||
| #pragma warning(push) | |||||
| #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union | |||||
| #endif | |||||
| typedef struct NVGcontext NVGcontext; | |||||
| struct NVGcolor { | |||||
| union { | |||||
| float rgba[4]; | |||||
| struct { | |||||
| float r,g,b,a; | |||||
| }; | |||||
| }; | |||||
| }; | |||||
| typedef struct NVGcolor NVGcolor; | |||||
| struct NVGpaint { | |||||
| float xform[6]; | |||||
| float extent[2]; | |||||
| float radius; | |||||
| float feather; | |||||
| NVGcolor innerColor; | |||||
| NVGcolor outerColor; | |||||
| int image; | |||||
| }; | |||||
| typedef struct NVGpaint NVGpaint; | |||||
| enum NVGwinding { | |||||
| NVG_CCW = 1, // Winding for solid shapes | |||||
| NVG_CW = 2, // Winding for holes | |||||
| }; | |||||
| enum NVGsolidity { | |||||
| NVG_SOLID = 1, // CCW | |||||
| NVG_HOLE = 2, // CW | |||||
| }; | |||||
| enum NVGlineCap { | |||||
| NVG_BUTT, | |||||
| NVG_ROUND, | |||||
| NVG_SQUARE, | |||||
| NVG_BEVEL, | |||||
| NVG_MITER, | |||||
| }; | |||||
| enum NVGalign { | |||||
| // Horizontal align | |||||
| NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. | |||||
| NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. | |||||
| NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. | |||||
| // Vertical align | |||||
| NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | |||||
| NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | |||||
| NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||||
| NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||||
| }; | |||||
| struct NVGglyphPosition { | |||||
| const char* str; // Position of the glyph in the input string. | |||||
| float x; // The x-coordinate of the logical glyph position. | |||||
| float minx, maxx; // The bounds of the glyph shape. | |||||
| }; | |||||
| typedef struct NVGglyphPosition NVGglyphPosition; | |||||
| struct NVGtextRow { | |||||
| const char* start; // Pointer to the input text where the row starts. | |||||
| const char* end; // Pointer to the input text where the row ends (one past the last character). | |||||
| const char* next; // Pointer to the beginning of the next row. | |||||
| float width; // Logical width of the row. | |||||
| float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. | |||||
| }; | |||||
| typedef struct NVGtextRow NVGtextRow; | |||||
| enum NVGimageFlags { | |||||
| NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||||
| NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | |||||
| NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | |||||
| NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | |||||
| NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||||
| }; | |||||
| // Begin drawing a new frame | |||||
| // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() | |||||
| // nvgBeginFrame() defines the size of the window to render to in relation currently | |||||
| // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to | |||||
| // control the rendering on Hi-DPI devices. | |||||
| // For example, GLFW returns two dimension for an opened window: window size and | |||||
| // frame buffer size. In that case you would set windowWidth/Height to the window size | |||||
| // devicePixelRatio to: frameBufferWidth / windowWidth. | |||||
| void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); | |||||
| // Cancels drawing the current frame. | |||||
| void nvgCancelFrame(NVGcontext* ctx); | |||||
| // Ends drawing flushing remaining render state. | |||||
| void nvgEndFrame(NVGcontext* ctx); | |||||
| // | |||||
| // Color utils | |||||
| // | |||||
| // Colors in NanoVG are stored as unsigned ints in ABGR format. | |||||
| // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). | |||||
| NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); | |||||
| // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. | |||||
| NVGcolor nvgRGBf(float r, float g, float b); | |||||
| // Returns a color value from red, green, blue and alpha values. | |||||
| NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); | |||||
| // Returns a color value from red, green, blue and alpha values. | |||||
| NVGcolor nvgRGBAf(float r, float g, float b, float a); | |||||
| // Linearly interpolates from color c0 to c1, and returns resulting color value. | |||||
| NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); | |||||
| // Sets transparency of a color value. | |||||
| NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); | |||||
| // Sets transparency of a color value. | |||||
| NVGcolor nvgTransRGBAf(NVGcolor c0, float a); | |||||
| // Returns color value specified by hue, saturation and lightness. | |||||
| // HSL values are all in range [0..1], alpha will be set to 255. | |||||
| NVGcolor nvgHSL(float h, float s, float l); | |||||
| // Returns color value specified by hue, saturation and lightness and alpha. | |||||
| // HSL values are all in range [0..1], alpha in range [0..255] | |||||
| NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); | |||||
| // | |||||
| // State Handling | |||||
| // | |||||
| // NanoVG contains state which represents how paths will be rendered. | |||||
| // The state contains transform, fill and stroke styles, text and font styles, | |||||
| // and scissor clipping. | |||||
| // Pushes and saves the current render state into a state stack. | |||||
| // A matching nvgRestore() must be used to restore the state. | |||||
| void nvgSave(NVGcontext* ctx); | |||||
| // Pops and restores current render state. | |||||
| void nvgRestore(NVGcontext* ctx); | |||||
| // Resets current render state to default values. Does not affect the render state stack. | |||||
| void nvgReset(NVGcontext* ctx); | |||||
| // | |||||
| // Render styles | |||||
| // | |||||
| // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. | |||||
| // Solid color is simply defined as a color value, different kinds of paints can be created | |||||
| // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | |||||
| // | |||||
| // Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Sets current stroke style to a solid color. | |||||
| void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | |||||
| // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. | |||||
| void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); | |||||
| // Sets current fill style to a solid color. | |||||
| void nvgFillColor(NVGcontext* ctx, NVGcolor color); | |||||
| // Sets current fill style to a paint, which can be a one of the gradients or a pattern. | |||||
| void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); | |||||
| // Sets the miter limit of the stroke style. | |||||
| // Miter limit controls when a sharp corner is beveled. | |||||
| void nvgMiterLimit(NVGcontext* ctx, float limit); | |||||
| // Sets the stroke width of the stroke style. | |||||
| void nvgStrokeWidth(NVGcontext* ctx, float size); | |||||
| // Sets how the end of the line (cap) is drawn, | |||||
| // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. | |||||
| void nvgLineCap(NVGcontext* ctx, int cap); | |||||
| // Sets how sharp path corners are drawn. | |||||
| // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. | |||||
| void nvgLineJoin(NVGcontext* ctx, int join); | |||||
| // Sets the transparency applied to all rendered shapes. | |||||
| // Already transparent paths will get proportionally more transparent as well. | |||||
| void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||||
| // | |||||
| // Transforms | |||||
| // | |||||
| // The paths, gradients, patterns and scissor region are transformed by an transformation | |||||
| // matrix at the time when they are passed to the API. | |||||
| // The current transformation matrix is a affine matrix: | |||||
| // [sx kx tx] | |||||
| // [ky sy ty] | |||||
| // [ 0 0 1] | |||||
| // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. | |||||
| // The last row is assumed to be 0,0,1 and is not stored. | |||||
| // | |||||
| // Apart from nvgResetTransform(), each transformation function first creates | |||||
| // specific transformation matrix and pre-multiplies the current transformation by it. | |||||
| // | |||||
| // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||||
| // Resets current transform to a identity matrix. | |||||
| void nvgResetTransform(NVGcontext* ctx); | |||||
| // Premultiplies current coordinate system by specified matrix. | |||||
| // The parameters are interpreted as matrix as follows: | |||||
| // [a c e] | |||||
| // [b d f] | |||||
| // [0 0 1] | |||||
| void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); | |||||
| // Translates current coordinate system. | |||||
| void nvgTranslate(NVGcontext* ctx, float x, float y); | |||||
| // Rotates current coordinate system. Angle is specified in radians. | |||||
| void nvgRotate(NVGcontext* ctx, float angle); | |||||
| // Skews the current coordinate system along X axis. Angle is specified in radians. | |||||
| void nvgSkewX(NVGcontext* ctx, float angle); | |||||
| // Skews the current coordinate system along Y axis. Angle is specified in radians. | |||||
| void nvgSkewY(NVGcontext* ctx, float angle); | |||||
| // Scales the current coordinate system. | |||||
| void nvgScale(NVGcontext* ctx, float x, float y); | |||||
| // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. | |||||
| // [a c e] | |||||
| // [b d f] | |||||
| // [0 0 1] | |||||
| // There should be space for 6 floats in the return buffer for the values a-f. | |||||
| void nvgCurrentTransform(NVGcontext* ctx, float* xform); | |||||
| // The following functions can be used to make calculations on 2x3 transformation matrices. | |||||
| // A 2x3 matrix is represented as float[6]. | |||||
| // Sets the transform to identity matrix. | |||||
| void nvgTransformIdentity(float* dst); | |||||
| // Sets the transform to translation matrix matrix. | |||||
| void nvgTransformTranslate(float* dst, float tx, float ty); | |||||
| // Sets the transform to scale matrix. | |||||
| void nvgTransformScale(float* dst, float sx, float sy); | |||||
| // Sets the transform to rotate matrix. Angle is specified in radians. | |||||
| void nvgTransformRotate(float* dst, float a); | |||||
| // Sets the transform to skew-x matrix. Angle is specified in radians. | |||||
| void nvgTransformSkewX(float* dst, float a); | |||||
| // Sets the transform to skew-y matrix. Angle is specified in radians. | |||||
| void nvgTransformSkewY(float* dst, float a); | |||||
| // Sets the transform to the result of multiplication of two transforms, of A = A*B. | |||||
| void nvgTransformMultiply(float* dst, const float* src); | |||||
| // Sets the transform to the result of multiplication of two transforms, of A = B*A. | |||||
| void nvgTransformPremultiply(float* dst, const float* src); | |||||
| // Sets the destination to inverse of specified transform. | |||||
| // Returns 1 if the inverse could be calculated, else 0. | |||||
| int nvgTransformInverse(float* dst, const float* src); | |||||
| // Transform a point by given transform. | |||||
| void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); | |||||
| // Converts degrees to radians and vice versa. | |||||
| float nvgDegToRad(float deg); | |||||
| float nvgRadToDeg(float rad); | |||||
| // | |||||
| // Images | |||||
| // | |||||
| // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. | |||||
| // In addition you can upload your own image. The image loading is provided by stb_image. | |||||
| // The parameter imageFlags is combination of flags defined in NVGimageFlags. | |||||
| // Creates image by loading it from the disk from specified file name. | |||||
| // Returns handle to the image. | |||||
| int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); | |||||
| // Creates image by loading it from the specified chunk of memory. | |||||
| // Returns handle to the image. | |||||
| int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); | |||||
| // Creates image from specified image data. | |||||
| // Returns handle to the image. | |||||
| int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); | |||||
| // Updates image data specified by image handle. | |||||
| void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); | |||||
| // Returns the dimensions of a created image. | |||||
| void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); | |||||
| // Deletes created image. | |||||
| void nvgDeleteImage(NVGcontext* ctx, int image); | |||||
| // | |||||
| // Paints | |||||
| // | |||||
| // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. | |||||
| // These can be used as paints for strokes and fills. | |||||
| // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates | |||||
| // of the linear gradient, icol specifies the start color and ocol the end color. | |||||
| // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||||
| NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, | |||||
| NVGcolor icol, NVGcolor ocol); | |||||
| // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering | |||||
| // drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, | |||||
| // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry | |||||
| // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. | |||||
| // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||||
| NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, | |||||
| float r, float f, NVGcolor icol, NVGcolor ocol); | |||||
| // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify | |||||
| // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. | |||||
| // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||||
| NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, | |||||
| NVGcolor icol, NVGcolor ocol); | |||||
| // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, | |||||
| // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. | |||||
| // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||||
| NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, | |||||
| float angle, int image, float alpha); | |||||
| // | |||||
| // Scissoring | |||||
| // | |||||
| // Scissoring allows you to clip the rendering into a rectangle. This is useful for various | |||||
| // user interface cases like rendering a text edit or a timeline. | |||||
| // Sets the current scissor rectangle. | |||||
| // The scissor rectangle is transformed by the current transform. | |||||
| void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); | |||||
| // Intersects current scissor rectangle with the specified rectangle. | |||||
| // The scissor rectangle is transformed by the current transform. | |||||
| // Note: in case the rotation of previous scissor rect differs from | |||||
| // the current one, the intersection will be done between the specified | |||||
| // rectangle and the previous scissor rectangle transformed in the current | |||||
| // transform space. The resulting shape is always rectangle. | |||||
| void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); | |||||
| // Reset and disables scissoring. | |||||
| void nvgResetScissor(NVGcontext* ctx); | |||||
| // | |||||
| // Paths | |||||
| // | |||||
| // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. | |||||
| // Then you define one or more paths and sub-paths which describe the shape. The are functions | |||||
| // to draw common shapes like rectangles and circles, and lower level step-by-step functions, | |||||
| // which allow to define a path curve by curve. | |||||
| // | |||||
| // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise | |||||
| // winding and holes should have counter clockwise order. To specify winding of a path you can | |||||
| // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. | |||||
| // | |||||
| // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it | |||||
| // with current stroke style by calling nvgStroke(). | |||||
| // | |||||
| // The curve segments and sub-paths are transformed by the current transform. | |||||
| // Clears the current path and sub-paths. | |||||
| void nvgBeginPath(NVGcontext* ctx); | |||||
| // Starts new sub-path with specified point as first point. | |||||
| void nvgMoveTo(NVGcontext* ctx, float x, float y); | |||||
| // Adds line segment from the last point in the path to the specified point. | |||||
| void nvgLineTo(NVGcontext* ctx, float x, float y); | |||||
| // Adds cubic bezier segment from last point in the path via two control points to the specified point. | |||||
| void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); | |||||
| // Adds quadratic bezier segment from last point in the path via a control point to the specified point. | |||||
| void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); | |||||
| // Adds an arc segment at the corner defined by the last path point, and two specified points. | |||||
| void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); | |||||
| // Closes current sub-path with a line segment. | |||||
| void nvgClosePath(NVGcontext* ctx); | |||||
| // Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||||
| void nvgPathWinding(NVGcontext* ctx, int dir); | |||||
| // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | |||||
| // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). | |||||
| // Angles are specified in radians. | |||||
| void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); | |||||
| // Creates new rectangle shaped sub-path. | |||||
| void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); | |||||
| // Creates new rounded rectangle shaped sub-path. | |||||
| void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | |||||
| // Creates new ellipse shaped sub-path. | |||||
| void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | |||||
| // Creates new circle shaped sub-path. | |||||
| void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | |||||
| // Fills the current path with current fill style. | |||||
| void nvgFill(NVGcontext* ctx); | |||||
| // Fills the current path with current stroke style. | |||||
| void nvgStroke(NVGcontext* ctx); | |||||
| // | |||||
| // Text | |||||
| // | |||||
| // NanoVG allows you to load .ttf files and use the font to render text. | |||||
| // | |||||
| // The appearance of the text can be defined by setting the current text style | |||||
| // and by specifying the fill color. Common text and font settings such as | |||||
| // font size, letter spacing and text align are supported. Font blur allows you | |||||
| // to create simple text effects such as drop shadows. | |||||
| // | |||||
| // At render time the font face can be set based on the font handles or name. | |||||
| // | |||||
| // Font measure functions return values in local space, the calculations are | |||||
| // carried in the same resolution as the final rendering. This is done because | |||||
| // the text glyph positions are snapped to the nearest pixels sharp rendering. | |||||
| // | |||||
| // The local space means that values are not rotated or scale as per the current | |||||
| // transformation. For example if you set font size to 12, which would mean that | |||||
| // line height is 16, then regardless of the current scaling and rotation, the | |||||
| // returned line height is always 16. Some measures may vary because of the scaling | |||||
| // since aforementioned pixel snapping. | |||||
| // | |||||
| // While this may sound a little odd, the setup allows you to always render the | |||||
| // same way regardless of scaling. I.e. following works regardless of scaling: | |||||
| // | |||||
| // const char* txt = "Text me up."; | |||||
| // nvgTextBounds(vg, x,y, txt, NULL, bounds); | |||||
| // nvgBeginPath(vg); | |||||
| // nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); | |||||
| // nvgFill(vg); | |||||
| // | |||||
| // Note: currently only solid color fill is supported for text. | |||||
| // Creates font by loading it from the disk from specified file name. | |||||
| // Returns handle to the font. | |||||
| int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | |||||
| // Creates image by loading it from the specified memory chunk. | |||||
| // Returns handle to the font. | |||||
| int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData); | |||||
| // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | |||||
| int nvgFindFont(NVGcontext* ctx, const char* name); | |||||
| // Sets the font size of current text style. | |||||
| void nvgFontSize(NVGcontext* ctx, float size); | |||||
| // Sets the blur of current text style. | |||||
| void nvgFontBlur(NVGcontext* ctx, float blur); | |||||
| // Sets the letter spacing of current text style. | |||||
| void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | |||||
| // Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||||
| void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | |||||
| // Sets the text align of current text style, see NVGalign for options. | |||||
| void nvgTextAlign(NVGcontext* ctx, int align); | |||||
| // Sets the font face based on specified id of current text style. | |||||
| void nvgFontFaceId(NVGcontext* ctx, int font); | |||||
| // Sets the font face based on specified name of current text style. | |||||
| void nvgFontFace(NVGcontext* ctx, const char* font); | |||||
| // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. | |||||
| float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); | |||||
| // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. | |||||
| // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||||
| // Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||||
| void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); | |||||
| // Measures the specified text string. Parameter bounds should be a pointer to float[4], | |||||
| // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] | |||||
| // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). | |||||
| // Measured values are returned in local coordinate space. | |||||
| float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); | |||||
| // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], | |||||
| // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] | |||||
| // Measured values are returned in local coordinate space. | |||||
| void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); | |||||
| // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. | |||||
| // Measured values are returned in local coordinate space. | |||||
| int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); | |||||
| // Returns the vertical metrics based on the current text style. | |||||
| // Measured values are returned in local coordinate space. | |||||
| void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); | |||||
| // Breaks the specified text into lines. If end is specified only the sub-string will be used. | |||||
| // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||||
| // Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||||
| int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); | |||||
| // | |||||
| // Internal Render API | |||||
| // | |||||
| enum NVGtexture { | |||||
| NVG_TEXTURE_ALPHA = 0x01, | |||||
| NVG_TEXTURE_RGBA = 0x02, | |||||
| }; | |||||
| struct NVGscissor { | |||||
| float xform[6]; | |||||
| float extent[2]; | |||||
| }; | |||||
| typedef struct NVGscissor NVGscissor; | |||||
| struct NVGvertex { | |||||
| float x,y,u,v; | |||||
| }; | |||||
| typedef struct NVGvertex NVGvertex; | |||||
| struct NVGpath { | |||||
| int first; | |||||
| int count; | |||||
| unsigned char closed; | |||||
| int nbevel; | |||||
| NVGvertex* fill; | |||||
| int nfill; | |||||
| NVGvertex* stroke; | |||||
| int nstroke; | |||||
| int winding; | |||||
| int convex; | |||||
| }; | |||||
| typedef struct NVGpath NVGpath; | |||||
| struct NVGparams { | |||||
| void* userPtr; | |||||
| int edgeAntiAlias; | |||||
| int (*renderCreate)(void* uptr); | |||||
| int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||||
| int (*renderDeleteTexture)(void* uptr, int image); | |||||
| int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | |||||
| int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | |||||
| void (*renderViewport)(void* uptr, int width, int height); | |||||
| void (*renderCancel)(void* uptr); | |||||
| void (*renderFlush)(void* uptr); | |||||
| void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||||
| void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||||
| void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); | |||||
| void (*renderDelete)(void* uptr); | |||||
| }; | |||||
| typedef struct NVGparams NVGparams; | |||||
| // Constructor and destructor, called by the render back-end. | |||||
| NVGcontext* nvgCreateInternal(NVGparams* params); | |||||
| void nvgDeleteInternal(NVGcontext* ctx); | |||||
| NVGparams* nvgInternalParams(NVGcontext* ctx); | |||||
| // Debug function to dump cached path data. | |||||
| void nvgDebugDumpPathCache(NVGcontext* ctx); | |||||
| #ifdef _MSC_VER | |||||
| #pragma warning(pop) | |||||
| #endif | |||||
| #define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } | |||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #endif // NANOVG_H | |||||
| @@ -0,0 +1,132 @@ | |||||
| // | |||||
| // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org | |||||
| // | |||||
| // This software is provided 'as-is', without any express or implied | |||||
| // warranty. In no event will the authors 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 software must not be misrepresented; you must not | |||||
| // claim that you wrote the original software. If you use this software | |||||
| // 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 software. | |||||
| // 3. This notice may not be removed or altered from any source distribution. | |||||
| // | |||||
| #ifndef NANOVG_GL_UTILS_H | |||||
| #define NANOVG_GL_UTILS_H | |||||
| struct NVGLUframebuffer { | |||||
| NVGcontext* ctx; | |||||
| GLuint fbo; | |||||
| GLuint rbo; | |||||
| GLuint texture; | |||||
| int image; | |||||
| }; | |||||
| typedef struct NVGLUframebuffer NVGLUframebuffer; | |||||
| // Helper function to create GL frame buffer to render to. | |||||
| void nvgluBindFramebuffer(NVGLUframebuffer* fb); | |||||
| NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | |||||
| void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb); | |||||
| #endif // NANOVG_GL_UTILS_H | |||||
| #ifdef NANOVG_GL_IMPLEMENTATION | |||||
| #if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||||
| // FBO is core in OpenGL 3>. | |||||
| # define NANOVG_FBO_VALID 1 | |||||
| #elif defined(NANOVG_GL2) | |||||
| // On OS X including glext defines FBO on GL2 too. | |||||
| # ifdef __APPLE__ | |||||
| # include <OpenGL/glext.h> | |||||
| # define NANOVG_FBO_VALID 1 | |||||
| # endif | |||||
| #endif | |||||
| static GLint defaultFBO = -1; | |||||
| NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags) | |||||
| { | |||||
| #ifdef NANOVG_FBO_VALID | |||||
| GLint defaultFBO; | |||||
| GLint defaultRBO; | |||||
| NVGLUframebuffer* fb = NULL; | |||||
| glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); | |||||
| glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO); | |||||
| fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer)); | |||||
| if (fb == NULL) goto error; | |||||
| memset(fb, 0, sizeof(NVGLUframebuffer)); | |||||
| fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | |||||
| fb->texture = nvglImageHandle(ctx, fb->image); | |||||
| // frame buffer object | |||||
| glGenFramebuffers(1, &fb->fbo); | |||||
| glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); | |||||
| // render buffer object | |||||
| glGenRenderbuffers(1, &fb->rbo); | |||||
| glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); | |||||
| glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h); | |||||
| // combine all | |||||
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | |||||
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | |||||
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; | |||||
| glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | |||||
| glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | |||||
| return fb; | |||||
| error: | |||||
| glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | |||||
| glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | |||||
| nvgluDeleteFramebuffer(ctx, fb); | |||||
| return NULL; | |||||
| #else | |||||
| NVG_NOTUSED(ctx); | |||||
| NVG_NOTUSED(w); | |||||
| NVG_NOTUSED(h); | |||||
| NVG_NOTUSED(imageFlags); | |||||
| return NULL; | |||||
| #endif | |||||
| } | |||||
| void nvgluBindFramebuffer(NVGLUframebuffer* fb) | |||||
| { | |||||
| #ifdef NANOVG_FBO_VALID | |||||
| if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); | |||||
| glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO); | |||||
| #else | |||||
| NVG_NOTUSED(fb); | |||||
| #endif | |||||
| } | |||||
| void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||||
| { | |||||
| #ifdef NANOVG_FBO_VALID | |||||
| if (fb == NULL) return; | |||||
| if (fb->fbo != 0) | |||||
| glDeleteFramebuffers(1, &fb->fbo); | |||||
| if (fb->rbo != 0) | |||||
| glDeleteRenderbuffers(1, &fb->rbo); | |||||
| if (fb->image >= 0) | |||||
| nvgDeleteImage(ctx, fb->image); | |||||
| fb->fbo = 0; | |||||
| fb->rbo = 0; | |||||
| fb->texture = 0; | |||||
| fb->image = -1; | |||||
| free(fb); | |||||
| #else | |||||
| NVG_NOTUSED(ctx); | |||||
| NVG_NOTUSED(fb); | |||||
| #endif | |||||
| } | |||||
| #endif // NANOVG_GL_IMPLEMENTATION | |||||
| @@ -0,0 +1,21 @@ | |||||
| Blendish - Blender 2.5 UI based theming functions for NanoVG | |||||
| Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com> | |||||
| 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. | |||||
| @@ -0,0 +1,15 @@ | |||||
| /* (Auto-generated binary data file). */ | |||||
| #ifndef BINARY_BLENDISH_RESOURCES_H | |||||
| #define BINARY_BLENDISH_RESOURCES_H | |||||
| namespace blendish_resources | |||||
| { | |||||
| extern const char* blender_icons16_png; | |||||
| const unsigned int blender_icons16_png_size = 250706; | |||||
| extern const char* dejavusans_ttf; | |||||
| const unsigned int dejavusans_ttf_size = 741536; | |||||
| }; | |||||
| #endif | |||||
| @@ -0,0 +1,97 @@ | |||||
| Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. | |||||
| Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) | |||||
| Bitstream Vera Fonts Copyright | |||||
| ------------------------------ | |||||
| Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is | |||||
| a trademark of Bitstream, Inc. | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of the fonts accompanying this license ("Fonts") and associated | |||||
| documentation files (the "Font Software"), to reproduce and distribute the | |||||
| Font Software, including without limitation the rights to use, copy, merge, | |||||
| publish, distribute, and/or sell copies of the Font Software, and to permit | |||||
| persons to whom the Font Software is furnished to do so, subject to the | |||||
| following conditions: | |||||
| The above copyright and trademark notices and this permission notice shall | |||||
| be included in all copies of one or more of the Font Software typefaces. | |||||
| The Font Software may be modified, altered, or added to, and in particular | |||||
| the designs of glyphs or characters in the Fonts may be modified and | |||||
| additional glyphs or characters may be added to the Fonts, only if the fonts | |||||
| are renamed to names not containing either the words "Bitstream" or the word | |||||
| "Vera". | |||||
| This License becomes null and void to the extent applicable to Fonts or Font | |||||
| Software that has been modified and is distributed under the "Bitstream | |||||
| Vera" names. | |||||
| The Font Software may be sold as part of a larger software package but no | |||||
| copy of one or more of the Font Software typefaces may be sold by itself. | |||||
| THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||||
| OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, | |||||
| TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME | |||||
| FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING | |||||
| ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, | |||||
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | |||||
| THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE | |||||
| FONT SOFTWARE. | |||||
| Except as contained in this notice, the names of Gnome, the Gnome | |||||
| Foundation, and Bitstream Inc., shall not be used in advertising or | |||||
| otherwise to promote the sale, use or other dealings in this Font Software | |||||
| without prior written authorization from the Gnome Foundation or Bitstream | |||||
| Inc., respectively. For further information, contact: fonts at gnome dot | |||||
| org. | |||||
| Arev Fonts Copyright | |||||
| ------------------------------ | |||||
| Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. | |||||
| Permission is hereby granted, free of charge, to any person obtaining | |||||
| a copy of the fonts accompanying this license ("Fonts") and | |||||
| associated documentation files (the "Font Software"), to reproduce | |||||
| and distribute the modifications to the Bitstream Vera Font Software, | |||||
| including without limitation the rights to use, copy, merge, publish, | |||||
| distribute, and/or sell copies of the Font Software, and to permit | |||||
| persons to whom the Font Software is furnished to do so, subject to | |||||
| the following conditions: | |||||
| The above copyright and trademark notices and this permission notice | |||||
| shall be included in all copies of one or more of the Font Software | |||||
| typefaces. | |||||
| The Font Software may be modified, altered, or added to, and in | |||||
| particular the designs of glyphs or characters in the Fonts may be | |||||
| modified and additional glyphs or characters may be added to the | |||||
| Fonts, only if the fonts are renamed to names not containing either | |||||
| the words "Tavmjong Bah" or the word "Arev". | |||||
| This License becomes null and void to the extent applicable to Fonts | |||||
| or Font Software that has been modified and is distributed under the | |||||
| "Tavmjong Bah Arev" names. | |||||
| The Font Software may be sold as part of a larger software package but | |||||
| no copy of one or more of the Font Software typefaces may be sold by | |||||
| itself. | |||||
| THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF | |||||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT | |||||
| OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL | |||||
| TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |||||
| INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL | |||||
| DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||||
| FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM | |||||
| OTHER DEALINGS IN THE FONT SOFTWARE. | |||||
| Except as contained in this notice, the name of Tavmjong Bah shall not | |||||
| be used in advertising or otherwise to promote the sale, use or other | |||||
| dealings in this Font Software without prior written authorization | |||||
| from Tavmjong Bah. For further information, contact: tavmjong @ free | |||||
| . fr. | |||||
| @@ -0,0 +1,280 @@ | |||||
| GNU GENERAL PUBLIC LICENSE | |||||
| Version 2, June 1991 | |||||
| Copyright (C) 1989, 1991 Free Software Foundation, Inc. | |||||
| 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||||
| Everyone is permitted to copy and distribute verbatim copies | |||||
| of this license document, but changing it is not allowed. | |||||
| Preamble | |||||
| The licenses for most software are designed to take away your | |||||
| freedom to share and change it. By contrast, the GNU General Public | |||||
| License is intended to guarantee your freedom to share and change free | |||||
| software--to make sure the software is free for all its users. This | |||||
| General Public License applies to most of the Free Software | |||||
| Foundation's software and to any other program whose authors commit to | |||||
| using it. (Some other Free Software Foundation software is covered by | |||||
| the GNU Library General Public License instead.) You can apply it to | |||||
| your programs, too. | |||||
| When we speak of free software, we are referring to freedom, not | |||||
| price. Our General Public Licenses are designed to make sure that you | |||||
| have the freedom to distribute copies of free software (and charge for | |||||
| this service if you wish), that you receive source code or can get it | |||||
| if you want it, that you can change the software or use pieces of it | |||||
| in new free programs; and that you know you can do these things. | |||||
| To protect your rights, we need to make restrictions that forbid | |||||
| anyone to deny you these rights or to ask you to surrender the rights. | |||||
| These restrictions translate to certain responsibilities for you if you | |||||
| distribute copies of the software, or if you modify it. | |||||
| For example, if you distribute copies of such a program, whether | |||||
| gratis or for a fee, you must give the recipients all the rights that | |||||
| you have. You must make sure that they, too, receive or can get the | |||||
| source code. And you must show them these terms so they know their | |||||
| rights. | |||||
| We protect your rights with two steps: (1) copyright the software, and | |||||
| (2) offer you this license which gives you legal permission to copy, | |||||
| distribute and/or modify the software. | |||||
| Also, for each author's protection and ours, we want to make certain | |||||
| that everyone understands that there is no warranty for this free | |||||
| software. If the software is modified by someone else and passed on, we | |||||
| want its recipients to know that what they have is not the original, so | |||||
| that any problems introduced by others will not reflect on the original | |||||
| authors' reputations. | |||||
| Finally, any free program is threatened constantly by software | |||||
| patents. We wish to avoid the danger that redistributors of a free | |||||
| program will individually obtain patent licenses, in effect making the | |||||
| program proprietary. To prevent this, we have made it clear that any | |||||
| patent must be licensed for everyone's free use or not licensed at all. | |||||
| The precise terms and conditions for copying, distribution and | |||||
| modification follow. | |||||
| GNU GENERAL PUBLIC LICENSE | |||||
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||||
| 0. This License applies to any program or other work which contains | |||||
| a notice placed by the copyright holder saying it may be distributed | |||||
| under the terms of this General Public License. The "Program", below, | |||||
| refers to any such program or work, and a "work based on the Program" | |||||
| means either the Program or any derivative work under copyright law: | |||||
| that is to say, a work containing the Program or a portion of it, | |||||
| either verbatim or with modifications and/or translated into another | |||||
| language. (Hereinafter, translation is included without limitation in | |||||
| the term "modification".) Each licensee is addressed as "you". | |||||
| Activities other than copying, distribution and modification are not | |||||
| covered by this License; they are outside its scope. The act of | |||||
| running the Program is not restricted, and the output from the Program | |||||
| is covered only if its contents constitute a work based on the | |||||
| Program (independent of having been made by running the Program). | |||||
| Whether that is true depends on what the Program does. | |||||
| 1. You may copy and distribute verbatim copies of the Program's | |||||
| source code as you receive it, in any medium, provided that you | |||||
| conspicuously and appropriately publish on each copy an appropriate | |||||
| copyright notice and disclaimer of warranty; keep intact all the | |||||
| notices that refer to this License and to the absence of any warranty; | |||||
| and give any other recipients of the Program a copy of this License | |||||
| along with the Program. | |||||
| You may charge a fee for the physical act of transferring a copy, and | |||||
| you may at your option offer warranty protection in exchange for a fee. | |||||
| 2. You may modify your copy or copies of the Program or any portion | |||||
| of it, thus forming a work based on the Program, and copy and | |||||
| distribute such modifications or work under the terms of Section 1 | |||||
| above, provided that you also meet all of these conditions: | |||||
| a) You must cause the modified files to carry prominent notices | |||||
| stating that you changed the files and the date of any change. | |||||
| b) You must cause any work that you distribute or publish, that in | |||||
| whole or in part contains or is derived from the Program or any | |||||
| part thereof, to be licensed as a whole at no charge to all third | |||||
| parties under the terms of this License. | |||||
| c) If the modified program normally reads commands interactively | |||||
| when run, you must cause it, when started running for such | |||||
| interactive use in the most ordinary way, to print or display an | |||||
| announcement including an appropriate copyright notice and a | |||||
| notice that there is no warranty (or else, saying that you provide | |||||
| a warranty) and that users may redistribute the program under | |||||
| these conditions, and telling the user how to view a copy of this | |||||
| License. (Exception: if the Program itself is interactive but | |||||
| does not normally print such an announcement, your work based on | |||||
| the Program is not required to print an announcement.) | |||||
| These requirements apply to the modified work as a whole. If | |||||
| identifiable sections of that work are not derived from the Program, | |||||
| and can be reasonably considered independent and separate works in | |||||
| themselves, then this License, and its terms, do not apply to those | |||||
| sections when you distribute them as separate works. But when you | |||||
| distribute the same sections as part of a whole which is a work based | |||||
| on the Program, the distribution of the whole must be on the terms of | |||||
| this License, whose permissions for other licensees extend to the | |||||
| entire whole, and thus to each and every part regardless of who wrote it. | |||||
| Thus, it is not the intent of this section to claim rights or contest | |||||
| your rights to work written entirely by you; rather, the intent is to | |||||
| exercise the right to control the distribution of derivative or | |||||
| collective works based on the Program. | |||||
| In addition, mere aggregation of another work not based on the Program | |||||
| with the Program (or with a work based on the Program) on a volume of | |||||
| a storage or distribution medium does not bring the other work under | |||||
| the scope of this License. | |||||
| 3. You may copy and distribute the Program (or a work based on it, | |||||
| under Section 2) in object code or executable form under the terms of | |||||
| Sections 1 and 2 above provided that you also do one of the following: | |||||
| a) Accompany it with the complete corresponding machine-readable | |||||
| source code, which must be distributed under the terms of Sections | |||||
| 1 and 2 above on a medium customarily used for software interchange; or, | |||||
| b) Accompany it with a written offer, valid for at least three | |||||
| years, to give any third party, for a charge no more than your | |||||
| cost of physically performing source distribution, a complete | |||||
| machine-readable copy of the corresponding source code, to be | |||||
| distributed under the terms of Sections 1 and 2 above on a medium | |||||
| customarily used for software interchange; or, | |||||
| c) Accompany it with the information you received as to the offer | |||||
| to distribute corresponding source code. (This alternative is | |||||
| allowed only for noncommercial distribution and only if you | |||||
| received the program in object code or executable form with such | |||||
| an offer, in accord with Subsection b above.) | |||||
| The source code for a work means the preferred form of the work for | |||||
| making modifications to it. For an executable work, complete source | |||||
| code means all the source code for all modules it contains, plus any | |||||
| associated interface definition files, plus the scripts used to | |||||
| control compilation and installation of the executable. However, as a | |||||
| special exception, the source code distributed need not include | |||||
| anything that is normally distributed (in either source or binary | |||||
| form) with the major components (compiler, kernel, and so on) of the | |||||
| operating system on which the executable runs, unless that component | |||||
| itself accompanies the executable. | |||||
| If distribution of executable or object code is made by offering | |||||
| access to copy from a designated place, then offering equivalent | |||||
| access to copy the source code from the same place counts as | |||||
| distribution of the source code, even though third parties are not | |||||
| compelled to copy the source along with the object code. | |||||
| 4. You may not copy, modify, sublicense, or distribute the Program | |||||
| except as expressly provided under this License. Any attempt | |||||
| otherwise to copy, modify, sublicense or distribute the Program is | |||||
| void, and will automatically terminate your rights under this License. | |||||
| However, parties who have received copies, or rights, from you under | |||||
| this License will not have their licenses terminated so long as such | |||||
| parties remain in full compliance. | |||||
| 5. You are not required to accept this License, since you have not | |||||
| signed it. However, nothing else grants you permission to modify or | |||||
| distribute the Program or its derivative works. These actions are | |||||
| prohibited by law if you do not accept this License. Therefore, by | |||||
| modifying or distributing the Program (or any work based on the | |||||
| Program), you indicate your acceptance of this License to do so, and | |||||
| all its terms and conditions for copying, distributing or modifying | |||||
| the Program or works based on it. | |||||
| 6. Each time you redistribute the Program (or any work based on the | |||||
| Program), the recipient automatically receives a license from the | |||||
| original licensor to copy, distribute or modify the Program subject to | |||||
| these terms and conditions. You may not impose any further | |||||
| restrictions on the recipients' exercise of the rights granted herein. | |||||
| You are not responsible for enforcing compliance by third parties to | |||||
| this License. | |||||
| 7. If, as a consequence of a court judgment or allegation of patent | |||||
| infringement or for any other reason (not limited to patent issues), | |||||
| conditions are imposed on you (whether by court order, agreement or | |||||
| otherwise) that contradict the conditions of this License, they do not | |||||
| excuse you from the conditions of this License. If you cannot | |||||
| distribute so as to satisfy simultaneously your obligations under this | |||||
| License and any other pertinent obligations, then as a consequence you | |||||
| may not distribute the Program at all. For example, if a patent | |||||
| license would not permit royalty-free redistribution of the Program by | |||||
| all those who receive copies directly or indirectly through you, then | |||||
| the only way you could satisfy both it and this License would be to | |||||
| refrain entirely from distribution of the Program. | |||||
| If any portion of this section is held invalid or unenforceable under | |||||
| any particular circumstance, the balance of the section is intended to | |||||
| apply and the section as a whole is intended to apply in other | |||||
| circumstances. | |||||
| It is not the purpose of this section to induce you to infringe any | |||||
| patents or other property right claims or to contest validity of any | |||||
| such claims; this section has the sole purpose of protecting the | |||||
| integrity of the free software distribution system, which is | |||||
| implemented by public license practices. Many people have made | |||||
| generous contributions to the wide range of software distributed | |||||
| through that system in reliance on consistent application of that | |||||
| system; it is up to the author/donor to decide if he or she is willing | |||||
| to distribute software through any other system and a licensee cannot | |||||
| impose that choice. | |||||
| This section is intended to make thoroughly clear what is believed to | |||||
| be a consequence of the rest of this License. | |||||
| 8. If the distribution and/or use of the Program is restricted in | |||||
| certain countries either by patents or by copyrighted interfaces, the | |||||
| original copyright holder who places the Program under this License | |||||
| may add an explicit geographical distribution limitation excluding | |||||
| those countries, so that distribution is permitted only in or among | |||||
| countries not thus excluded. In such case, this License incorporates | |||||
| the limitation as if written in the body of this License. | |||||
| 9. The Free Software Foundation may publish revised and/or new versions | |||||
| of the General Public License from time to time. Such new versions will | |||||
| be similar in spirit to the present version, but may differ in detail to | |||||
| address new problems or concerns. | |||||
| Each version is given a distinguishing version number. If the Program | |||||
| specifies a version number of this License which applies to it and "any | |||||
| later version", you have the option of following the terms and conditions | |||||
| either of that version or of any later version published by the Free | |||||
| Software Foundation. If the Program does not specify a version number of | |||||
| this License, you may choose any version ever published by the Free Software | |||||
| Foundation. | |||||
| 10. If you wish to incorporate parts of the Program into other free | |||||
| programs whose distribution conditions are different, write to the author | |||||
| to ask for permission. For software which is copyrighted by the Free | |||||
| Software Foundation, write to the Free Software Foundation; we sometimes | |||||
| make exceptions for this. Our decision will be guided by the two goals | |||||
| of preserving the free status of all derivatives of our free software and | |||||
| of promoting the sharing and reuse of software generally. | |||||
| NO WARRANTY | |||||
| 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |||||
| PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |||||
| OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |||||
| TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |||||
| REPAIR OR CORRECTION. | |||||
| 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | |||||
| REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | |||||
| INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | |||||
| OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | |||||
| TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | |||||
| YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | |||||
| POSSIBILITY OF SUCH DAMAGES. | |||||
| END OF TERMS AND CONDITIONS | |||||
| @@ -0,0 +1,121 @@ | |||||
| /* | |||||
| Copyright 2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_COMMON_H_INCLUDED | |||||
| #define PUGL_COMMON_H_INCLUDED | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** | |||||
| @addtogroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| A Pugl view. | |||||
| */ | |||||
| typedef struct PuglViewImpl PuglView; | |||||
| /** | |||||
| A native window handle. | |||||
| On X11, this is a Window. | |||||
| On OSX, this is an NSView*. | |||||
| On Windows, this is a HWND. | |||||
| */ | |||||
| typedef intptr_t PuglNativeWindow; | |||||
| /** | |||||
| Handle for opaque user data. | |||||
| */ | |||||
| typedef void* PuglHandle; | |||||
| /** | |||||
| Return status code. | |||||
| */ | |||||
| typedef enum { | |||||
| PUGL_SUCCESS = 0 | |||||
| } PuglStatus; | |||||
| /** | |||||
| Drawing context type. | |||||
| */ | |||||
| typedef enum { | |||||
| PUGL_GL, | |||||
| PUGL_CAIRO | |||||
| } PuglContextType; | |||||
| /** | |||||
| Convenience symbols for ASCII control characters. | |||||
| */ | |||||
| typedef enum { | |||||
| PUGL_CHAR_BACKSPACE = 0x08, | |||||
| PUGL_CHAR_ESCAPE = 0x1B, | |||||
| PUGL_CHAR_DELETE = 0x7F | |||||
| } PuglChar; | |||||
| /** | |||||
| Keyboard modifier flags. | |||||
| */ | |||||
| typedef enum { | |||||
| PUGL_MOD_SHIFT = 1 << 0, /**< Shift key */ | |||||
| PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||||
| PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||||
| PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||||
| } PuglMod; | |||||
| /** | |||||
| Special (non-Unicode) keyboard keys. | |||||
| */ | |||||
| typedef enum { | |||||
| PUGL_KEY_F1 = 1, | |||||
| PUGL_KEY_F2, | |||||
| PUGL_KEY_F3, | |||||
| PUGL_KEY_F4, | |||||
| PUGL_KEY_F5, | |||||
| PUGL_KEY_F6, | |||||
| PUGL_KEY_F7, | |||||
| PUGL_KEY_F8, | |||||
| PUGL_KEY_F9, | |||||
| PUGL_KEY_F10, | |||||
| PUGL_KEY_F11, | |||||
| PUGL_KEY_F12, | |||||
| PUGL_KEY_LEFT, | |||||
| PUGL_KEY_UP, | |||||
| PUGL_KEY_RIGHT, | |||||
| PUGL_KEY_DOWN, | |||||
| PUGL_KEY_PAGE_UP, | |||||
| PUGL_KEY_PAGE_DOWN, | |||||
| PUGL_KEY_HOME, | |||||
| PUGL_KEY_END, | |||||
| PUGL_KEY_INSERT, | |||||
| PUGL_KEY_SHIFT, | |||||
| PUGL_KEY_CTRL, | |||||
| PUGL_KEY_ALT, | |||||
| PUGL_KEY_SUPER | |||||
| } PuglKey; | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* PUGL_COMMON_H_INCLUDED */ | |||||
| @@ -0,0 +1,41 @@ | |||||
| /* | |||||
| Copyright 2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_EVENT_H_INCLUDED | |||||
| #define PUGL_EVENT_H_INCLUDED | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| #include "pugl/common.h" | |||||
| /** | |||||
| @addtogroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* PUGL_EVENT_H_INCLUDED */ | |||||
| @@ -0,0 +1,32 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file gl.h Portable header wrapper for gl.h. | |||||
| Unfortunately, GL includes vary across platforms so this header allows for | |||||
| pure portable programs. | |||||
| */ | |||||
| #ifdef __APPLE__ | |||||
| # include "OpenGL/gl.h" | |||||
| #else | |||||
| # ifdef _WIN32 | |||||
| # include <windows.h> /* Broken Windows GL headers require this */ | |||||
| # endif | |||||
| # include "GL/gl.h" | |||||
| #endif | |||||
| @@ -0,0 +1,32 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file glu.h Portable header wrapper for glu.h. | |||||
| Unfortunately, GL includes vary across platforms so this header allows for | |||||
| pure portable programs. | |||||
| */ | |||||
| #ifdef __APPLE__ | |||||
| # include "OpenGL/glu.h" | |||||
| #else | |||||
| # ifdef _WIN32 | |||||
| # include <windows.h> /* Broken Windows GL headers require this */ | |||||
| # endif | |||||
| # include "GL/glu.h" | |||||
| #endif | |||||
| @@ -0,0 +1,383 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file pugl.h API for Pugl, a minimal portable API for OpenGL. | |||||
| */ | |||||
| #ifndef PUGL_H_INCLUDED | |||||
| #define PUGL_H_INCLUDED | |||||
| #include <stdint.h> | |||||
| #include "pugl/common.h" | |||||
| #include "pugl/event.h" | |||||
| #ifdef PUGL_SHARED | |||||
| # ifdef _WIN32 | |||||
| # define PUGL_LIB_IMPORT __declspec(dllimport) | |||||
| # define PUGL_LIB_EXPORT __declspec(dllexport) | |||||
| # else | |||||
| # define PUGL_LIB_IMPORT __attribute__((visibility("default"))) | |||||
| # define PUGL_LIB_EXPORT __attribute__((visibility("default"))) | |||||
| # endif | |||||
| # ifdef PUGL_INTERNAL | |||||
| # define PUGL_API PUGL_LIB_EXPORT | |||||
| # else | |||||
| # define PUGL_API PUGL_LIB_IMPORT | |||||
| # endif | |||||
| #else | |||||
| # ifdef _WIN32 | |||||
| # define PUGL_API | |||||
| # else | |||||
| # define PUGL_API __attribute__((visibility("hidden"))) | |||||
| # endif | |||||
| #endif | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** | |||||
| @defgroup pugl Pugl | |||||
| A minimal portable API for OpenGL. | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| A function called when the window is closed. | |||||
| */ | |||||
| typedef void (*PuglCloseFunc)(PuglView* view); | |||||
| /** | |||||
| A function called to draw the view contents with OpenGL. | |||||
| */ | |||||
| typedef void (*PuglDisplayFunc)(PuglView* view); | |||||
| /** | |||||
| A function called when a key is pressed or released. | |||||
| @param view The view the event occured in. | |||||
| @param press True if the key was pressed, false if released. | |||||
| @param key Unicode point of the key pressed. | |||||
| */ | |||||
| typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); | |||||
| /** | |||||
| A function called when the pointer moves. | |||||
| @param view The view the event occured in. | |||||
| @param x The window-relative x coordinate of the pointer. | |||||
| @param y The window-relative y coordinate of the pointer. | |||||
| */ | |||||
| typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); | |||||
| /** | |||||
| A function called when a mouse button is pressed or released. | |||||
| @param view The view the event occured in. | |||||
| @param button The button number (1 = left, 2 = middle, 3 = right). | |||||
| @param press True if the key was pressed, false if released. | |||||
| @param x The window-relative x coordinate of the pointer. | |||||
| @param y The window-relative y coordinate of the pointer. | |||||
| */ | |||||
| typedef void (*PuglMouseFunc)( | |||||
| PuglView* view, int button, bool press, int x, int y); | |||||
| /** | |||||
| A function called when the view is resized. | |||||
| @param view The view being resized. | |||||
| @param width The new view width. | |||||
| @param height The new view height. | |||||
| */ | |||||
| typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | |||||
| /** | |||||
| A function called on scrolling (e.g. mouse wheel or track pad). | |||||
| The distances used here are in "lines", a single tick of a clicking mouse | |||||
| wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and | |||||
| devices support finer resolution and/or higher values for fast scrolls, | |||||
| so programs should handle any value gracefully. | |||||
| @param view The view being scrolled. | |||||
| @param x The window-relative x coordinate of the pointer. | |||||
| @param y The window-relative y coordinate of the pointer. | |||||
| @param dx The scroll x distance. | |||||
| @param dx The scroll y distance. | |||||
| */ | |||||
| typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy); | |||||
| /** | |||||
| A function called when a special key is pressed or released. | |||||
| This callback allows the use of keys that do not have unicode points. | |||||
| Note that some are non-printable keys. | |||||
| @param view The view the event occured in. | |||||
| @param press True if the key was pressed, false if released. | |||||
| @param key The key pressed. | |||||
| */ | |||||
| typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | |||||
| /** | |||||
| A function called when a filename is selected via file-browser. | |||||
| @param view The view the event occured in. | |||||
| @param filename The selected file name or NULL if the dialog was canceled. | |||||
| */ | |||||
| typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); | |||||
| /** | |||||
| @name Initialization | |||||
| Configuration functions which must be called before creating a window. | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Create a Pugl context. | |||||
| To create a window, call the various puglInit* functions as necessary, then | |||||
| call puglCreateWindow(). | |||||
| */ | |||||
| PUGL_API PuglView* | |||||
| puglInit(void); | |||||
| /** | |||||
| Set the parent window before creating a window (for embedding). | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitWindowParent(PuglView* view, PuglNativeWindow parent); | |||||
| /** | |||||
| Set the window size before creating a window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitWindowSize(PuglView* view, int width, int height); | |||||
| /** | |||||
| Set the minimum window size before creating a window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitWindowMinSize(PuglView* view, int width, int height); | |||||
| /** | |||||
| Enable or disable resizing before creating a window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitUserResizable(PuglView* view, bool resizable); | |||||
| /** | |||||
| Set transient parent before creating a window. | |||||
| On X11, parent_id must be a Window. | |||||
| On OSX, parent_id must be an NSView*. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitTransientFor(PuglView* view, uintptr_t parent); | |||||
| /** | |||||
| Set the context type before creating a window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglInitContextType(PuglView* view, PuglContextType type); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| /** | |||||
| @name Windows | |||||
| Window management functions. | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Create a window with the settings given by the various puglInit functions. | |||||
| @return 1 (pugl does not currently support multiple windows). | |||||
| */ | |||||
| PUGL_API int | |||||
| puglCreateWindow(PuglView* view, const char* title); | |||||
| /** | |||||
| Show the current window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglShowWindow(PuglView* view); | |||||
| /** | |||||
| Hide the current window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglHideWindow(PuglView* view); | |||||
| /** | |||||
| Return the native window handle. | |||||
| */ | |||||
| PUGL_API PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| /** | |||||
| Set the handle to be passed to all callbacks. | |||||
| This is generally a pointer to a struct which contains all necessary state. | |||||
| Everything needed in callbacks should be here, not in static variables. | |||||
| Note the lack of this facility makes GLUT unsuitable for plugins or | |||||
| non-trivial programs; this mistake is largely why Pugl exists. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetHandle(PuglView* view, PuglHandle handle); | |||||
| /** | |||||
| Get the handle to be passed to all callbacks. | |||||
| */ | |||||
| PUGL_API PuglHandle | |||||
| puglGetHandle(PuglView* view); | |||||
| /** | |||||
| Get the drawing context. | |||||
| For PUGL_GL contexts, this is unused and returns NULL. | |||||
| For PUGL_CAIRO contexts, this returns a pointer to a cairo_t. | |||||
| */ | |||||
| PUGL_API void* | |||||
| puglGetContext(PuglView* view); | |||||
| /** | |||||
| Return the timestamp (if any) of the currently-processing event. | |||||
| */ | |||||
| PUGL_API uint32_t | |||||
| puglGetEventTimestamp(PuglView* view); | |||||
| /** | |||||
| Get the currently active modifiers (PuglMod flags). | |||||
| This should only be called from an event handler. | |||||
| */ | |||||
| PUGL_API int | |||||
| puglGetModifiers(PuglView* view); | |||||
| /** | |||||
| Ignore synthetic repeated key events. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore); | |||||
| /** | |||||
| @name Event Callbacks | |||||
| Functions to set event callbacks for handling user input. | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Set the function to call when the window is closed. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); | |||||
| /** | |||||
| Set the display function which should draw the UI using GL. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); | |||||
| /** | |||||
| Set the function to call on keyboard events. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); | |||||
| /** | |||||
| Set the function to call on mouse motion. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); | |||||
| /** | |||||
| Set the function to call on mouse button events. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); | |||||
| /** | |||||
| Set the function to call on scroll events. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); | |||||
| /** | |||||
| Set the function to call on special events. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||||
| /** | |||||
| Set the function to call when the window size changes. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||||
| /** | |||||
| Set the function to call on file-browser selections. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| /** | |||||
| Grab the input focus. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglGrabFocus(PuglView* view); | |||||
| /** | |||||
| Process all pending window events. | |||||
| This handles input events as well as rendering, so it should be called | |||||
| regularly and rapidly enough to keep the UI responsive. | |||||
| */ | |||||
| PUGL_API PuglStatus | |||||
| puglProcessEvents(PuglView* view); | |||||
| /** | |||||
| Request a redisplay on the next call to puglProcessEvents(). | |||||
| */ | |||||
| PUGL_API void | |||||
| puglPostRedisplay(PuglView* view); | |||||
| /** | |||||
| Destroy a GL window. | |||||
| */ | |||||
| PUGL_API void | |||||
| puglDestroy(PuglView* view); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* PUGL_H_INCLUDED */ | |||||
| @@ -0,0 +1,352 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file pugl_internal.h Private platform-independent definitions. | |||||
| Note this file contains function definitions, so it must be compiled into | |||||
| the final binary exactly once. Each platform specific implementation file | |||||
| including it once should achieve this. | |||||
| If you are copying the pugl code into your source tree, the following | |||||
| symbols can be defined to tweak pugl behaviour: | |||||
| PUGL_HAVE_CAIRO: Include Cairo support code. | |||||
| PUGL_HAVE_GL: Include OpenGL support code. | |||||
| PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | |||||
| PUGL_VERBOSE: Print GL information to console. | |||||
| */ | |||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/event.h" | |||||
| #ifdef PUGL_VERBOSE | |||||
| # include <stdio.h> | |||||
| # define PUGL_LOG(str) fprintf(stderr, "pugl: " str) | |||||
| # define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__) | |||||
| #else | |||||
| # define PUGL_LOG(str) | |||||
| # define PUGL_LOGF(fmt, ...) | |||||
| #endif | |||||
| typedef struct PuglInternalsImpl PuglInternals; | |||||
| struct PuglViewImpl { | |||||
| PuglHandle handle; | |||||
| PuglCloseFunc closeFunc; | |||||
| PuglDisplayFunc displayFunc; | |||||
| PuglKeyboardFunc keyboardFunc; | |||||
| PuglMotionFunc motionFunc; | |||||
| PuglMouseFunc mouseFunc; | |||||
| PuglReshapeFunc reshapeFunc; | |||||
| PuglScrollFunc scrollFunc; | |||||
| PuglSpecialFunc specialFunc; | |||||
| PuglFileSelectedFunc fileSelectedFunc; | |||||
| PuglInternals* impl; | |||||
| PuglNativeWindow parent; | |||||
| PuglContextType ctx_type; | |||||
| uintptr_t transient_parent; | |||||
| int width; | |||||
| int height; | |||||
| int min_width; | |||||
| int min_height; | |||||
| int mods; | |||||
| bool mouse_in_view; | |||||
| bool ignoreKeyRepeat; | |||||
| bool redisplay; | |||||
| bool resizable; | |||||
| uint32_t event_timestamp_ms; | |||||
| }; | |||||
| PuglInternals* puglInitInternals(void); | |||||
| PuglView* | |||||
| puglInit(void) | |||||
| { | |||||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
| if (!view) { | |||||
| return NULL; | |||||
| } | |||||
| PuglInternals* impl = puglInitInternals(); | |||||
| if (!impl) { | |||||
| free(view); | |||||
| return NULL; | |||||
| } | |||||
| view->impl = impl; | |||||
| view->width = 640; | |||||
| view->height = 480; | |||||
| return view; | |||||
| } | |||||
| void | |||||
| puglInitWindowSize(PuglView* view, int width, int height) | |||||
| { | |||||
| view->width = width; | |||||
| view->height = height; | |||||
| } | |||||
| void | |||||
| puglInitWindowMinSize(PuglView* view, int width, int height) | |||||
| { | |||||
| view->min_width = width; | |||||
| view->min_height = height; | |||||
| } | |||||
| void | |||||
| puglInitWindowParent(PuglView* view, PuglNativeWindow parent) | |||||
| { | |||||
| view->parent = parent; | |||||
| } | |||||
| void | |||||
| puglInitUserResizable(PuglView* view, bool resizable) | |||||
| { | |||||
| view->resizable = resizable; | |||||
| } | |||||
| void | |||||
| puglInitTransientFor(PuglView* view, uintptr_t parent) | |||||
| { | |||||
| view->transient_parent = parent; | |||||
| } | |||||
| void | |||||
| puglInitContextType(PuglView* view, PuglContextType type) | |||||
| { | |||||
| view->ctx_type = type; | |||||
| } | |||||
| void | |||||
| puglSetHandle(PuglView* view, PuglHandle handle) | |||||
| { | |||||
| view->handle = handle; | |||||
| } | |||||
| PuglHandle | |||||
| puglGetHandle(PuglView* view) | |||||
| { | |||||
| return view->handle; | |||||
| } | |||||
| uint32_t | |||||
| puglGetEventTimestamp(PuglView* view) | |||||
| { | |||||
| return view->event_timestamp_ms; | |||||
| } | |||||
| int | |||||
| puglGetModifiers(PuglView* view) | |||||
| { | |||||
| return view->mods; | |||||
| } | |||||
| void | |||||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||||
| { | |||||
| view->ignoreKeyRepeat = ignore; | |||||
| } | |||||
| void | |||||
| puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) | |||||
| { | |||||
| view->closeFunc = closeFunc; | |||||
| } | |||||
| void | |||||
| puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) | |||||
| { | |||||
| view->displayFunc = displayFunc; | |||||
| } | |||||
| void | |||||
| puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) | |||||
| { | |||||
| view->keyboardFunc = keyboardFunc; | |||||
| } | |||||
| void | |||||
| puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) | |||||
| { | |||||
| view->motionFunc = motionFunc; | |||||
| } | |||||
| void | |||||
| puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) | |||||
| { | |||||
| view->mouseFunc = mouseFunc; | |||||
| } | |||||
| void | |||||
| puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) | |||||
| { | |||||
| view->reshapeFunc = reshapeFunc; | |||||
| } | |||||
| void | |||||
| puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | |||||
| { | |||||
| view->scrollFunc = scrollFunc; | |||||
| } | |||||
| void | |||||
| puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||||
| { | |||||
| view->specialFunc = specialFunc; | |||||
| } | |||||
| void | |||||
| puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) | |||||
| { | |||||
| view->fileSelectedFunc = fileSelectedFunc; | |||||
| } | |||||
| void | |||||
| puglEnterContext(PuglView* view); | |||||
| void | |||||
| puglLeaveContext(PuglView* view, bool flush); | |||||
| #if 0 | |||||
| /** Return the code point for buf, or the replacement character on error. */ | |||||
| static uint32_t | |||||
| puglDecodeUTF8(const uint8_t* buf) | |||||
| { | |||||
| #define FAIL_IF(cond) { if (cond) return 0xFFFD; } | |||||
| /* http://en.wikipedia.org/wiki/UTF-8 */ | |||||
| if (buf[0] < 0x80) { | |||||
| return buf[0]; | |||||
| } else if (buf[0] < 0xC2) { | |||||
| return 0xFFFD; | |||||
| } else if (buf[0] < 0xE0) { | |||||
| FAIL_IF((buf[1] & 0xC0) != 0x80); | |||||
| return (buf[0] << 6) + buf[1] - 0x3080; | |||||
| } else if (buf[0] < 0xF0) { | |||||
| FAIL_IF((buf[1] & 0xC0) != 0x80); | |||||
| FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0); | |||||
| FAIL_IF((buf[2] & 0xC0) != 0x80); | |||||
| return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080; | |||||
| } else if (buf[0] < 0xF5) { | |||||
| FAIL_IF((buf[1] & 0xC0) != 0x80); | |||||
| FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90); | |||||
| FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90); | |||||
| FAIL_IF((buf[2] & 0xC0) != 0x80); | |||||
| FAIL_IF((buf[3] & 0xC0) != 0x80); | |||||
| return ((buf[0] << 18) + | |||||
| (buf[1] << 12) + | |||||
| (buf[2] << 6) + | |||||
| buf[3] - 0x3C82080); | |||||
| } | |||||
| return 0xFFFD; | |||||
| } | |||||
| #endif | |||||
| static void | |||||
| puglDefaultReshape(PuglView* view, int width, int height) | |||||
| { | |||||
| glMatrixMode(GL_PROJECTION); | |||||
| glLoadIdentity(); | |||||
| glOrtho(0, width, height, 0, 0, 1); | |||||
| glViewport(0, 0, width, height); | |||||
| glMatrixMode(GL_MODELVIEW); | |||||
| glLoadIdentity(); | |||||
| return; | |||||
| // unused | |||||
| (void)view; | |||||
| } | |||||
| #if 0 | |||||
| static void | |||||
| puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||||
| { | |||||
| if (view->eventFunc) { | |||||
| view->eventFunc(view, event); | |||||
| } | |||||
| switch (event->type) { | |||||
| case PUGL_CONFIGURE: | |||||
| puglEnterContext(view); | |||||
| view->width = event->configure.width; | |||||
| view->height = event->configure.height; | |||||
| if (view->reshapeFunc) { | |||||
| view->reshapeFunc(view, view->width, view->height); | |||||
| } | |||||
| puglLeaveContext(view, false); | |||||
| break; | |||||
| case PUGL_EXPOSE: | |||||
| if (event->expose.count == 0) { | |||||
| puglEnterContext(view); | |||||
| if (view->displayFunc) { | |||||
| view->displayFunc(view); | |||||
| } | |||||
| view->redisplay = false; | |||||
| puglLeaveContext(view, true); | |||||
| } | |||||
| break; | |||||
| case PUGL_MOTION_NOTIFY: | |||||
| view->event_timestamp_ms = event->motion.time; | |||||
| view->mods = event->motion.state; | |||||
| if (view->motionFunc) { | |||||
| view->motionFunc(view, event->motion.x, event->motion.y); | |||||
| } | |||||
| break; | |||||
| case PUGL_SCROLL: | |||||
| if (view->scrollFunc) { | |||||
| view->scrollFunc(view, | |||||
| event->scroll.x, event->scroll.y, | |||||
| event->scroll.dx, event->scroll.dy); | |||||
| } | |||||
| break; | |||||
| case PUGL_BUTTON_PRESS: | |||||
| case PUGL_BUTTON_RELEASE: | |||||
| view->event_timestamp_ms = event->button.time; | |||||
| view->mods = event->button.state; | |||||
| if (view->mouseFunc) { | |||||
| view->mouseFunc(view, | |||||
| event->button.button, | |||||
| event->type == PUGL_BUTTON_PRESS, | |||||
| event->button.x, | |||||
| event->button.y); | |||||
| } | |||||
| break; | |||||
| case PUGL_KEY_PRESS: | |||||
| case PUGL_KEY_RELEASE: | |||||
| view->event_timestamp_ms = event->key.time; | |||||
| view->mods = event->key.state; | |||||
| if (event->key.special && view->specialFunc) { | |||||
| view->specialFunc(view, | |||||
| event->type == PUGL_KEY_PRESS, | |||||
| event->key.special); | |||||
| } else if (event->key.character && view->keyboardFunc) { | |||||
| view->keyboardFunc(view, | |||||
| event->type == PUGL_KEY_PRESS, | |||||
| event->key.character); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| @@ -0,0 +1,573 @@ | |||||
| /* | |||||
| Copyright 2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file pugl_osx.m OSX/Cocoa Pugl Implementation. | |||||
| */ | |||||
| #include <stdlib.h> | |||||
| #import <Cocoa/Cocoa.h> | |||||
| #include "pugl_internal.h" | |||||
| @interface PuglWindow : NSWindow | |||||
| { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| } | |||||
| - (id) initWithContentRect:(NSRect)contentRect | |||||
| styleMask:(unsigned int)aStyle | |||||
| backing:(NSBackingStoreType)bufferingType | |||||
| defer:(BOOL)flag; | |||||
| - (void) setPuglview:(PuglView*)view; | |||||
| - (BOOL) canBecomeKeyWindow; | |||||
| - (BOOL) windowShouldClose:(id)sender; | |||||
| @end | |||||
| @implementation PuglWindow | |||||
| - (id)initWithContentRect:(NSRect)contentRect | |||||
| styleMask:(unsigned int)aStyle | |||||
| backing:(NSBackingStoreType)bufferingType | |||||
| defer:(BOOL)flag | |||||
| { | |||||
| NSWindow* result = [super initWithContentRect:contentRect | |||||
| styleMask:(NSClosableWindowMask | | |||||
| NSTitledWindowMask | | |||||
| NSResizableWindowMask) | |||||
| backing:NSBackingStoreBuffered defer:NO]; | |||||
| [result setAcceptsMouseMovedEvents:YES]; | |||||
| [result setLevel: CGShieldingWindowLevel() + 1]; | |||||
| return (PuglWindow*)result; | |||||
| // unused | |||||
| (void)aStyle; (void)bufferingType; (void)flag; | |||||
| } | |||||
| - (void)setPuglview:(PuglView*)view | |||||
| { | |||||
| puglview = view; | |||||
| [self setContentSize:NSMakeSize(view->width, view->height)]; | |||||
| } | |||||
| - (BOOL)canBecomeKeyWindow | |||||
| { | |||||
| return YES; | |||||
| } | |||||
| - (BOOL)windowShouldClose:(id)sender | |||||
| { | |||||
| if (puglview->closeFunc) | |||||
| puglview->closeFunc(puglview); | |||||
| return YES; | |||||
| // unused | |||||
| (void)sender; | |||||
| } | |||||
| @end | |||||
| static void | |||||
| puglDisplay(PuglView* view) | |||||
| { | |||||
| view->redisplay = false; | |||||
| if (view->displayFunc) { | |||||
| view->displayFunc(view); | |||||
| } | |||||
| } | |||||
| @interface PuglOpenGLView : NSOpenGLView | |||||
| { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| NSTrackingArea* trackingArea; | |||||
| bool doubleBuffered; | |||||
| } | |||||
| - (BOOL) acceptsFirstMouse:(NSEvent*)e; | |||||
| - (BOOL) acceptsFirstResponder; | |||||
| - (BOOL) isFlipped; | |||||
| - (BOOL) isOpaque; | |||||
| - (BOOL) preservesContentInLiveResize; | |||||
| - (id) initWithFrame:(NSRect)frame; | |||||
| - (void) reshape; | |||||
| - (void) drawRect:(NSRect)r; | |||||
| - (void) cursorUpdate:(NSEvent*)e; | |||||
| - (void) updateTrackingAreas; | |||||
| - (void) viewWillMoveToWindow:(NSWindow*)newWindow; | |||||
| - (void) mouseMoved:(NSEvent*)event; | |||||
| - (void) mouseDragged:(NSEvent*)event; | |||||
| - (void) rightMouseDragged:(NSEvent*)event; | |||||
| - (void) otherMouseDragged:(NSEvent*)event; | |||||
| - (void) mouseDown:(NSEvent*)event; | |||||
| - (void) rightMouseDown:(NSEvent*)event; | |||||
| - (void) otherMouseDown:(NSEvent*)event; | |||||
| - (void) mouseUp:(NSEvent*)event; | |||||
| - (void) rightMouseUp:(NSEvent*)event; | |||||
| - (void) otherMouseUp:(NSEvent*)event; | |||||
| - (void) scrollWheel:(NSEvent*)event; | |||||
| - (void) keyDown:(NSEvent*)event; | |||||
| - (void) keyUp:(NSEvent*)event; | |||||
| - (void) flagsChanged:(NSEvent*)event; | |||||
| @end | |||||
| @implementation PuglOpenGLView | |||||
| - (BOOL) acceptsFirstMouse:(NSEvent*)e | |||||
| { | |||||
| return YES; | |||||
| // unused | |||||
| (void)e; | |||||
| } | |||||
| - (BOOL) acceptsFirstResponder | |||||
| { | |||||
| return YES; | |||||
| } | |||||
| - (BOOL) isFlipped | |||||
| { | |||||
| return YES; | |||||
| } | |||||
| - (BOOL) isOpaque | |||||
| { | |||||
| return YES; | |||||
| } | |||||
| - (BOOL) preservesContentInLiveResize | |||||
| { | |||||
| return NO; | |||||
| } | |||||
| - (id) initWithFrame:(NSRect)frame | |||||
| { | |||||
| puglview = nil; | |||||
| trackingArea = nil; | |||||
| doubleBuffered = true; | |||||
| NSOpenGLPixelFormatAttribute pixelAttribs[] = { | |||||
| NSOpenGLPFAColorSize, 24, | |||||
| NSOpenGLPFAAlphaSize, 8, | |||||
| NSOpenGLPFADepthSize, 16, | |||||
| NSOpenGLPFADoubleBuffer, | |||||
| NSOpenGLPFAAccelerated, | |||||
| 0 | |||||
| }; | |||||
| NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] | |||||
| initWithAttributes:pixelAttribs]; | |||||
| if (pixelFormat) { | |||||
| self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||||
| [pixelFormat release]; | |||||
| printf("Is doubleBuffered? TRUE\n"); | |||||
| } else { | |||||
| self = [super initWithFrame:frame]; | |||||
| doubleBuffered = false; | |||||
| printf("Is doubleBuffered? FALSE\n"); | |||||
| } | |||||
| if (self) { | |||||
| GLint swapInterval = 1; | |||||
| [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; | |||||
| [self reshape]; | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (void) reshape | |||||
| { | |||||
| [[self openGLContext] update]; | |||||
| if (!puglview) { | |||||
| /* NOTE: Apparently reshape gets called when the GC gets around to | |||||
| deleting the view (?), so we must have reset puglview to NULL when | |||||
| this comes around. | |||||
| */ | |||||
| return; | |||||
| } | |||||
| NSRect bounds = [self bounds]; | |||||
| int width = bounds.size.width; | |||||
| int height = bounds.size.height; | |||||
| puglEnterContext(puglview); | |||||
| if (puglview->reshapeFunc) { | |||||
| puglview->reshapeFunc(puglview, width, height); | |||||
| } else { | |||||
| puglDefaultReshape(puglview, width, height); | |||||
| } | |||||
| puglLeaveContext(puglview, false); | |||||
| puglview->width = width; | |||||
| puglview->height = height; | |||||
| } | |||||
| - (void) drawRect:(NSRect)r | |||||
| { | |||||
| puglEnterContext(puglview); | |||||
| puglDisplay(puglview); | |||||
| puglLeaveContext(puglview, true); | |||||
| // unused | |||||
| return; (void)r; | |||||
| } | |||||
| - (void) cursorUpdate:(NSEvent*)e | |||||
| { | |||||
| [[NSCursor arrowCursor] set]; | |||||
| // unused | |||||
| return; (void)e; | |||||
| } | |||||
| - (void) updateTrackingAreas | |||||
| { | |||||
| static const int opts = NSTrackingMouseEnteredAndExited | |||||
| | NSTrackingMouseMoved | |||||
| | NSTrackingEnabledDuringMouseDrag | |||||
| | NSTrackingInVisibleRect | |||||
| | NSTrackingActiveAlways | |||||
| | NSTrackingCursorUpdate; | |||||
| if (trackingArea != nil) { | |||||
| [self removeTrackingArea:trackingArea]; | |||||
| [trackingArea release]; | |||||
| } | |||||
| trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] | |||||
| options:opts | |||||
| owner:self | |||||
| userInfo:nil]; | |||||
| [self addTrackingArea:trackingArea]; | |||||
| [super updateTrackingAreas]; | |||||
| } | |||||
| - (void) viewWillMoveToWindow:(NSWindow*)newWindow | |||||
| { | |||||
| if (newWindow != nil) { | |||||
| [newWindow setAcceptsMouseMovedEvents:YES]; | |||||
| [newWindow makeFirstResponder:self]; | |||||
| } | |||||
| [super viewWillMoveToWindow:newWindow]; | |||||
| } | |||||
| static unsigned | |||||
| getModifiers(PuglView* view, NSEvent* ev) | |||||
| { | |||||
| const unsigned modifierFlags = [ev modifierFlags]; | |||||
| view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); | |||||
| unsigned mods = 0; | |||||
| mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; | |||||
| mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; | |||||
| mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; | |||||
| mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; | |||||
| return mods; | |||||
| } | |||||
| static int | |||||
| getFixedAppKitButton(NSInteger button) | |||||
| { | |||||
| switch (button) { | |||||
| case 0: return 1; | |||||
| case 1: return 3; | |||||
| case 2: return 2; | |||||
| default: return button; | |||||
| } | |||||
| } | |||||
| - (void) mouseMoved:(NSEvent*)event | |||||
| { | |||||
| if (puglview->motionFunc) { | |||||
| NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->motionFunc(puglview, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) mouseDragged:(NSEvent*)event | |||||
| { | |||||
| [self mouseMoved:event]; | |||||
| } | |||||
| - (void) rightMouseDragged:(NSEvent*)event | |||||
| { | |||||
| [self mouseDragged:event]; | |||||
| } | |||||
| - (void) otherMouseDragged:(NSEvent*)event | |||||
| { | |||||
| [self mouseDragged:event]; | |||||
| } | |||||
| - (void) mouseDown:(NSEvent*)event | |||||
| { | |||||
| if (puglview->mouseFunc) { | |||||
| NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) rightMouseDown:(NSEvent*)event | |||||
| { | |||||
| [self mouseDown:event]; | |||||
| } | |||||
| - (void) otherMouseDown:(NSEvent*)event | |||||
| { | |||||
| [self mouseDown:event]; | |||||
| } | |||||
| - (void) mouseUp:(NSEvent*)event | |||||
| { | |||||
| if (puglview->mouseFunc) { | |||||
| NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y); | |||||
| } | |||||
| } | |||||
| - (void) rightMouseUp:(NSEvent*)event | |||||
| { | |||||
| [self mouseUp:event]; | |||||
| } | |||||
| - (void) otherMouseUp:(NSEvent*)event | |||||
| { | |||||
| [self mouseUp:event]; | |||||
| } | |||||
| - (void) scrollWheel:(NSEvent*)event | |||||
| { | |||||
| if (puglview->scrollFunc) { | |||||
| NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->scrollFunc(puglview, | |||||
| loc.x, loc.y, | |||||
| [event deltaX], [event deltaY]); | |||||
| } | |||||
| } | |||||
| - (void) keyDown:(NSEvent*)event | |||||
| { | |||||
| if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { | |||||
| NSString* chars = [event characters]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); | |||||
| } | |||||
| } | |||||
| - (void) keyUp:(NSEvent*)event | |||||
| { | |||||
| if (puglview->keyboardFunc) { | |||||
| NSString* chars = [event characters]; | |||||
| puglview->mods = getModifiers(puglview, event); | |||||
| puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); | |||||
| } | |||||
| } | |||||
| - (void) flagsChanged:(NSEvent*)event | |||||
| { | |||||
| if (puglview->specialFunc) { | |||||
| const unsigned mods = getModifiers(puglview, event); | |||||
| if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { | |||||
| puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); | |||||
| } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { | |||||
| puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); | |||||
| } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { | |||||
| puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); | |||||
| } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { | |||||
| puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); | |||||
| } | |||||
| puglview->mods = mods; | |||||
| } | |||||
| } | |||||
| @end | |||||
| struct PuglInternalsImpl { | |||||
| PuglOpenGLView* glview; | |||||
| id window; | |||||
| }; | |||||
| PuglInternals* | |||||
| puglInitInternals() | |||||
| { | |||||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
| } | |||||
| void | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| [[view->impl->glview openGLContext] makeCurrentContext]; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| void | |||||
| puglLeaveContext(PuglView* view, bool flush) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL && flush) { | |||||
| if (view->impl->glview->doubleBuffered) { | |||||
| [[view->impl->glview openGLContext] flushBuffer]; | |||||
| } else { | |||||
| glFlush(); | |||||
| } | |||||
| //[NSOpenGLContext clearCurrentContext]; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| int | |||||
| puglCreateWindow(PuglView* view, const char* title) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| [NSAutoreleasePool new]; | |||||
| [NSApplication sharedApplication]; | |||||
| impl->glview = [PuglOpenGLView new]; | |||||
| if (!impl->glview) { | |||||
| return 1; | |||||
| } | |||||
| impl->glview->puglview = view; | |||||
| if (view->resizable) { | |||||
| [impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | |||||
| } | |||||
| if (view->parent) { | |||||
| [impl->glview retain]; | |||||
| NSView* pview = (NSView*)view->parent; | |||||
| [pview addSubview:impl->glview]; | |||||
| return 0; | |||||
| } | |||||
| id window = [[PuglWindow new]retain]; | |||||
| if (title) { | |||||
| NSString* titleString = [[NSString alloc] | |||||
| initWithBytes:title | |||||
| length:strlen(title) | |||||
| encoding:NSUTF8StringEncoding]; | |||||
| [window setTitle:titleString]; | |||||
| } | |||||
| [window setPuglview:view]; | |||||
| [window setContentView:impl->glview]; | |||||
| [window makeFirstResponder:impl->glview]; | |||||
| [window makeKeyAndOrderFront:window]; | |||||
| // wait for first puglShowWindow | |||||
| [window setIsVisible:NO]; | |||||
| [NSApp activateIgnoringOtherApps:YES]; | |||||
| [window center]; | |||||
| impl->window = window; | |||||
| return 0; | |||||
| } | |||||
| void | |||||
| puglShowWindow(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| if (impl->window) { | |||||
| [impl->window setIsVisible:YES]; | |||||
| } else { | |||||
| [view->impl->glview setHidden:NO]; | |||||
| } | |||||
| } | |||||
| void | |||||
| puglHideWindow(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| if (impl->window) { | |||||
| [impl->window setIsVisible:NO]; | |||||
| } else { | |||||
| [impl->glview setHidden:YES]; | |||||
| } | |||||
| } | |||||
| void | |||||
| puglDestroy(PuglView* view) | |||||
| { | |||||
| view->impl->glview->puglview = NULL; | |||||
| if (view->impl->window) { | |||||
| [view->impl->window close]; | |||||
| [view->impl->glview release]; | |||||
| [view->impl->window release]; | |||||
| } else { | |||||
| [view->impl->glview release]; | |||||
| } | |||||
| free(view->impl); | |||||
| free(view); | |||||
| } | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| return PUGL_SUCCESS; | |||||
| // unused | |||||
| (void)view; | |||||
| } | |||||
| void | |||||
| puglPostRedisplay(PuglView* view) | |||||
| { | |||||
| view->redisplay = true; | |||||
| [view->impl->glview setNeedsDisplay:YES]; | |||||
| } | |||||
| PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view) | |||||
| { | |||||
| return (PuglNativeWindow)view->impl->glview; | |||||
| } | |||||
| void* | |||||
| puglGetContext(PuglView* view) | |||||
| { | |||||
| return NULL; | |||||
| } | |||||
| @@ -0,0 +1,481 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file pugl_win.cpp Windows/WGL Pugl Implementation. | |||||
| */ | |||||
| #include <windows.h> | |||||
| #include <windowsx.h> | |||||
| #include <GL/gl.h> | |||||
| #include <ctime> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include "pugl/pugl_internal.h" | |||||
| #ifndef WM_MOUSEWHEEL | |||||
| # define WM_MOUSEWHEEL 0x020A | |||||
| #endif | |||||
| #ifndef WM_MOUSEHWHEEL | |||||
| # define WM_MOUSEHWHEEL 0x020E | |||||
| #endif | |||||
| #ifndef WHEEL_DELTA | |||||
| # define WHEEL_DELTA 120 | |||||
| #endif | |||||
| #ifndef GWLP_USERDATA | |||||
| # define GWLP_USERDATA (-21) | |||||
| #endif | |||||
| #define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) | |||||
| HINSTANCE hInstance = NULL; | |||||
| struct PuglInternalsImpl { | |||||
| HWND hwnd; | |||||
| HDC hdc; | |||||
| HGLRC hglrc; | |||||
| WNDCLASS wc; | |||||
| }; | |||||
| LRESULT CALLBACK | |||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||||
| #if 0 | |||||
| extern "C" { | |||||
| BOOL WINAPI | |||||
| DllMain(HINSTANCE hInst, DWORD, LPVOID) | |||||
| { | |||||
| hInstance = hInst; | |||||
| return 1; | |||||
| } | |||||
| } // extern "C" | |||||
| #endif | |||||
| PuglInternals* | |||||
| puglInitInternals() | |||||
| { | |||||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
| } | |||||
| void | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| void | |||||
| puglLeaveContext(PuglView* view, bool flush) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL && flush) { | |||||
| glFlush(); | |||||
| SwapBuffers(view->impl->hdc); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| int | |||||
| puglCreateWindow(PuglView* view, const char* title) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| if (!title) { | |||||
| title = "Window"; | |||||
| } | |||||
| // FIXME: This is nasty, and pugl should not have static anything. | |||||
| // Should class be a parameter? Does this make sense on other platforms? | |||||
| static int wc_count = 0; | |||||
| char classNameBuf[256]; | |||||
| std::srand((std::time(NULL))); | |||||
| _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||||
| classNameBuf[sizeof(classNameBuf)-1] = '\0'; | |||||
| impl->wc.style = CS_OWNDC; | |||||
| impl->wc.lpfnWndProc = wndProc; | |||||
| impl->wc.cbClsExtra = 0; | |||||
| impl->wc.cbWndExtra = 0; | |||||
| impl->wc.hInstance = hInstance; | |||||
| impl->wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); | |||||
| impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW); | |||||
| impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||||
| impl->wc.lpszMenuName = NULL; | |||||
| impl->wc.lpszClassName = strdup(classNameBuf); | |||||
| if (!RegisterClass(&impl->wc)) { | |||||
| free((void*)impl->wc.lpszClassName); | |||||
| free(impl); | |||||
| free(view); | |||||
| return 1; | |||||
| } | |||||
| int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||||
| if (view->resizable) { | |||||
| winFlags |= WS_SIZEBOX; | |||||
| if (view->min_width > 0 && view->min_height > 0) { | |||||
| // Adjust the minimum window size to accomodate requested view size | |||||
| RECT mr = { 0, 0, view->min_width, view->min_height }; | |||||
| AdjustWindowRectEx(&mr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||||
| view->min_width = mr.right - mr.left; | |||||
| view->min_height = mr.bottom - mr.top; | |||||
| } | |||||
| } | |||||
| // Adjust the window size to accomodate requested view size | |||||
| RECT wr = { 0, 0, view->width, view->height }; | |||||
| AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||||
| impl->hwnd = CreateWindowEx( | |||||
| WS_EX_TOPMOST, | |||||
| classNameBuf, title, | |||||
| view->parent ? (WS_CHILD | WS_VISIBLE) : winFlags, | |||||
| CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, | |||||
| (HWND)view->parent, NULL, hInstance, NULL); | |||||
| if (!impl->hwnd) { | |||||
| UnregisterClass(impl->wc.lpszClassName, NULL); | |||||
| free((void*)impl->wc.lpszClassName); | |||||
| free(impl); | |||||
| free(view); | |||||
| return 1; | |||||
| } | |||||
| SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | |||||
| impl->hdc = GetDC(impl->hwnd); | |||||
| PIXELFORMATDESCRIPTOR pfd; | |||||
| ZeroMemory(&pfd, sizeof(pfd)); | |||||
| pfd.nSize = sizeof(pfd); | |||||
| pfd.nVersion = 1; | |||||
| pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; | |||||
| pfd.iPixelType = PFD_TYPE_RGBA; | |||||
| pfd.cColorBits = 24; | |||||
| pfd.cDepthBits = 16; | |||||
| pfd.iLayerType = PFD_MAIN_PLANE; | |||||
| int format = ChoosePixelFormat(impl->hdc, &pfd); | |||||
| SetPixelFormat(impl->hdc, format, &pfd); | |||||
| impl->hglrc = wglCreateContext(impl->hdc); | |||||
| if (!impl->hglrc) { | |||||
| ReleaseDC (impl->hwnd, impl->hdc); | |||||
| DestroyWindow (impl->hwnd); | |||||
| UnregisterClass (impl->wc.lpszClassName, NULL); | |||||
| free((void*)impl->wc.lpszClassName); | |||||
| free(impl); | |||||
| free(view); | |||||
| return 1; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| void | |||||
| puglShowWindow(PuglView* view) | |||||
| { | |||||
| ShowWindow(view->impl->hwnd, SW_SHOWNORMAL); | |||||
| } | |||||
| void | |||||
| puglHideWindow(PuglView* view) | |||||
| { | |||||
| ShowWindow(view->impl->hwnd, SW_HIDE); | |||||
| } | |||||
| void | |||||
| puglDestroy(PuglView* view) | |||||
| { | |||||
| wglMakeCurrent(NULL, NULL); | |||||
| wglDeleteContext(view->impl->hglrc); | |||||
| ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||||
| DestroyWindow(view->impl->hwnd); | |||||
| UnregisterClass(view->impl->wc.lpszClassName, NULL); | |||||
| free((void*)view->impl->wc.lpszClassName); | |||||
| free(view->impl); | |||||
| free(view); | |||||
| } | |||||
| static void | |||||
| puglReshape(PuglView* view, int width, int height) | |||||
| { | |||||
| puglEnterContext(view); | |||||
| if (view->reshapeFunc) { | |||||
| view->reshapeFunc(view, width, height); | |||||
| } else { | |||||
| puglDefaultReshape(view, width, height); | |||||
| } | |||||
| view->width = width; | |||||
| view->height = height; | |||||
| } | |||||
| static void | |||||
| puglDisplay(PuglView* view) | |||||
| { | |||||
| puglEnterContext(view); | |||||
| view->redisplay = false; | |||||
| if (view->displayFunc) { | |||||
| view->displayFunc(view); | |||||
| } | |||||
| puglLeaveContext(view, true); | |||||
| } | |||||
| static PuglKey | |||||
| keySymToSpecial(int sym) | |||||
| { | |||||
| switch (sym) { | |||||
| case VK_F1: return PUGL_KEY_F1; | |||||
| case VK_F2: return PUGL_KEY_F2; | |||||
| case VK_F3: return PUGL_KEY_F3; | |||||
| case VK_F4: return PUGL_KEY_F4; | |||||
| case VK_F5: return PUGL_KEY_F5; | |||||
| case VK_F6: return PUGL_KEY_F6; | |||||
| case VK_F7: return PUGL_KEY_F7; | |||||
| case VK_F8: return PUGL_KEY_F8; | |||||
| case VK_F9: return PUGL_KEY_F9; | |||||
| case VK_F10: return PUGL_KEY_F10; | |||||
| case VK_F11: return PUGL_KEY_F11; | |||||
| case VK_F12: return PUGL_KEY_F12; | |||||
| case VK_LEFT: return PUGL_KEY_LEFT; | |||||
| case VK_UP: return PUGL_KEY_UP; | |||||
| case VK_RIGHT: return PUGL_KEY_RIGHT; | |||||
| case VK_DOWN: return PUGL_KEY_DOWN; | |||||
| case VK_PRIOR: return PUGL_KEY_PAGE_UP; | |||||
| case VK_NEXT: return PUGL_KEY_PAGE_DOWN; | |||||
| case VK_HOME: return PUGL_KEY_HOME; | |||||
| case VK_END: return PUGL_KEY_END; | |||||
| case VK_INSERT: return PUGL_KEY_INSERT; | |||||
| case VK_SHIFT: return PUGL_KEY_SHIFT; | |||||
| case VK_CONTROL: return PUGL_KEY_CTRL; | |||||
| case VK_MENU: return PUGL_KEY_ALT; | |||||
| case VK_LWIN: return PUGL_KEY_SUPER; | |||||
| case VK_RWIN: return PUGL_KEY_SUPER; | |||||
| } | |||||
| return (PuglKey)0; | |||||
| } | |||||
| static void | |||||
| processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) | |||||
| { | |||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| if (press) { | |||||
| SetCapture(view->impl->hwnd); | |||||
| } else { | |||||
| ReleaseCapture(); | |||||
| } | |||||
| if (view->mouseFunc) { | |||||
| view->mouseFunc(view, button, press, | |||||
| GET_X_LPARAM(lParam), | |||||
| GET_Y_LPARAM(lParam)); | |||||
| } | |||||
| } | |||||
| static void | |||||
| setModifiers(PuglView* view) | |||||
| { | |||||
| view->mods = 0; | |||||
| view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; | |||||
| view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; | |||||
| view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; | |||||
| view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||||
| view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||||
| } | |||||
| static LRESULT | |||||
| handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| { | |||||
| PAINTSTRUCT ps; | |||||
| PuglKey key; | |||||
| RECT rect; | |||||
| MINMAXINFO* mmi; | |||||
| setModifiers(view); | |||||
| switch (message) { | |||||
| case WM_CREATE: | |||||
| case WM_SHOWWINDOW: | |||||
| case WM_SIZE: | |||||
| GetClientRect(view->impl->hwnd, &rect); | |||||
| puglReshape(view, rect.right, rect.bottom); | |||||
| view->width = rect.right; | |||||
| view->height = rect.bottom; | |||||
| break; | |||||
| case WM_GETMINMAXINFO: | |||||
| mmi = (MINMAXINFO*)lParam; | |||||
| mmi->ptMinTrackSize.x = view->min_width; | |||||
| mmi->ptMinTrackSize.y = view->min_height; | |||||
| break; | |||||
| case WM_PAINT: | |||||
| BeginPaint(view->impl->hwnd, &ps); | |||||
| puglDisplay(view); | |||||
| EndPaint(view->impl->hwnd, &ps); | |||||
| break; | |||||
| case WM_MOUSEMOVE: | |||||
| if (view->motionFunc) { | |||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |||||
| } | |||||
| break; | |||||
| case WM_LBUTTONDOWN: | |||||
| processMouseEvent(view, 1, true, lParam); | |||||
| break; | |||||
| case WM_MBUTTONDOWN: | |||||
| processMouseEvent(view, 2, true, lParam); | |||||
| break; | |||||
| case WM_RBUTTONDOWN: | |||||
| processMouseEvent(view, 3, true, lParam); | |||||
| break; | |||||
| case WM_LBUTTONUP: | |||||
| processMouseEvent(view, 1, false, lParam); | |||||
| break; | |||||
| case WM_MBUTTONUP: | |||||
| processMouseEvent(view, 2, false, lParam); | |||||
| break; | |||||
| case WM_RBUTTONUP: | |||||
| processMouseEvent(view, 3, false, lParam); | |||||
| break; | |||||
| case WM_MOUSEWHEEL: | |||||
| if (view->scrollFunc) { | |||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||||
| ScreenToClient(view->impl->hwnd, &pt); | |||||
| view->scrollFunc( | |||||
| view, pt.x, pt.y, | |||||
| 0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); | |||||
| } | |||||
| break; | |||||
| case WM_MOUSEHWHEEL: | |||||
| if (view->scrollFunc) { | |||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||||
| ScreenToClient(view->impl->hwnd, &pt); | |||||
| view->scrollFunc( | |||||
| view, pt.x, pt.y, | |||||
| GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); | |||||
| } | |||||
| break; | |||||
| case WM_KEYDOWN: | |||||
| if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { | |||||
| break; | |||||
| } // else nobreak | |||||
| case WM_KEYUP: | |||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| if ((key = keySymToSpecial(wParam))) { | |||||
| if (view->specialFunc) { | |||||
| view->specialFunc(view, message == WM_KEYDOWN, key); | |||||
| } | |||||
| } else if (view->keyboardFunc) { | |||||
| static BYTE kbs[256]; | |||||
| if (GetKeyboardState(kbs) != FALSE) { | |||||
| char lb[2]; | |||||
| UINT scanCode = (lParam >> 8) & 0xFFFFFF00; | |||||
| if ( 1 == ToAscii(wParam, scanCode, kbs, (LPWORD)lb, 0)) { | |||||
| view->keyboardFunc(view, message == WM_KEYDOWN, (char)lb[0]); | |||||
| } | |||||
| } | |||||
| } | |||||
| break; | |||||
| case WM_QUIT: | |||||
| case PUGL_LOCAL_CLOSE_MSG: | |||||
| if (view->closeFunc) { | |||||
| view->closeFunc(view); | |||||
| view->redisplay = false; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| return DefWindowProc( | |||||
| view->impl->hwnd, message, wParam, lParam); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| void | |||||
| puglGrabFocus(PuglView* view) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| MSG msg; | |||||
| while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { | |||||
| handleMessage(view, msg.message, msg.wParam, msg.lParam); | |||||
| } | |||||
| if (view->redisplay) { | |||||
| InvalidateRect(view->impl->hwnd, NULL, FALSE); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| LRESULT CALLBACK | |||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| { | |||||
| PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | |||||
| switch (message) { | |||||
| case WM_CREATE: | |||||
| PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | |||||
| return 0; | |||||
| case WM_CLOSE: | |||||
| PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); | |||||
| return 0; | |||||
| case WM_DESTROY: | |||||
| return 0; | |||||
| default: | |||||
| if (view && hwnd == view->impl->hwnd) { | |||||
| return handleMessage(view, message, wParam, lParam); | |||||
| } else { | |||||
| return DefWindowProc(hwnd, message, wParam, lParam); | |||||
| } | |||||
| } | |||||
| } | |||||
| void | |||||
| puglPostRedisplay(PuglView* view) | |||||
| { | |||||
| view->redisplay = true; | |||||
| } | |||||
| PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view) | |||||
| { | |||||
| return (PuglNativeWindow)view->impl->hwnd; | |||||
| } | |||||
| void* | |||||
| puglGetContext(PuglView* view) | |||||
| { | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| if (view->ctx_type == PUGL_CAIRO) { | |||||
| // TODO | |||||
| } | |||||
| #endif | |||||
| return NULL; | |||||
| } | |||||
| @@ -0,0 +1,609 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Copyright 2013 Robin Gareus <robin@gareus.org> | |||||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file pugl_x11.c X11 Pugl Implementation. | |||||
| */ | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include <X11/Xatom.h> | |||||
| #include <X11/Xlib.h> | |||||
| #include <X11/Xutil.h> | |||||
| #include <X11/keysym.h> | |||||
| #ifdef PUGL_HAVE_GL | |||||
| #include <GL/gl.h> | |||||
| #include <GL/glx.h> | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| #include <cairo/cairo.h> | |||||
| #include <cairo/cairo-xlib.h> | |||||
| #endif | |||||
| #include "pugl/pugl_internal.h" | |||||
| #define SOFD_HAVE_X11 | |||||
| #include "../sofd/libsofd.h" | |||||
| #include "../sofd/libsofd.c" | |||||
| struct PuglInternalsImpl { | |||||
| Display* display; | |||||
| int screen; | |||||
| Window win; | |||||
| XIM xim; | |||||
| XIC xic; | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| cairo_t* cr; | |||||
| cairo_surface_t* surface; | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_GL | |||||
| GLXContext ctx; | |||||
| Bool doubleBuffered; | |||||
| #endif | |||||
| }; | |||||
| PuglInternals* | |||||
| puglInitInternals(void) | |||||
| { | |||||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
| } | |||||
| static XVisualInfo* | |||||
| getVisual(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| XVisualInfo* vi = NULL; | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| /** | |||||
| Attributes for single-buffered RGBA with at least | |||||
| 4 bits per color and a 16 bit depth buffer. | |||||
| */ | |||||
| int attrListSgl[] = { | |||||
| GLX_RGBA, | |||||
| GLX_RED_SIZE, 4, | |||||
| GLX_GREEN_SIZE, 4, | |||||
| GLX_BLUE_SIZE, 4, | |||||
| GLX_DEPTH_SIZE, 16, | |||||
| GLX_ARB_multisample, 1, | |||||
| None | |||||
| }; | |||||
| /** | |||||
| Attributes for double-buffered RGBA with at least | |||||
| 4 bits per color and a 16 bit depth buffer. | |||||
| */ | |||||
| int attrListDbl[] = { | |||||
| GLX_RGBA, | |||||
| GLX_DOUBLEBUFFER, | |||||
| GLX_RED_SIZE, 4, | |||||
| GLX_GREEN_SIZE, 4, | |||||
| GLX_BLUE_SIZE, 4, | |||||
| GLX_DEPTH_SIZE, 16, | |||||
| GLX_ARB_multisample, 1, | |||||
| None | |||||
| }; | |||||
| /** | |||||
| Attributes for double-buffered RGBA with multi-sampling | |||||
| (antialiasing) | |||||
| */ | |||||
| int attrListDblMS[] = { | |||||
| GLX_RGBA, | |||||
| GLX_DOUBLEBUFFER, | |||||
| GLX_RED_SIZE, 4, | |||||
| GLX_GREEN_SIZE, 4, | |||||
| GLX_BLUE_SIZE, 4, | |||||
| GLX_ALPHA_SIZE, 4, | |||||
| GLX_DEPTH_SIZE, 16, | |||||
| GLX_SAMPLE_BUFFERS, 1, | |||||
| GLX_SAMPLES, 4, | |||||
| None | |||||
| }; | |||||
| impl->doubleBuffered = True; | |||||
| vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||||
| if (vi == NULL) { | |||||
| vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||||
| PUGL_LOG("multisampling (antialiasing) is not available\n"); | |||||
| } | |||||
| if (vi == NULL) { | |||||
| vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||||
| impl->doubleBuffered = False; | |||||
| PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n"); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| if (view->ctx_type == PUGL_CAIRO) { | |||||
| XVisualInfo pat; | |||||
| int n; | |||||
| pat.screen = impl->screen; | |||||
| vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); | |||||
| } | |||||
| #endif | |||||
| return vi; | |||||
| } | |||||
| static bool | |||||
| createContext(PuglView* view, XVisualInfo* vi) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||||
| return (impl->ctx != NULL); | |||||
| } | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| if (view->ctx_type == PUGL_CAIRO) { | |||||
| impl->surface = cairo_xlib_surface_create( | |||||
| impl->display, impl->win, vi->visual, view->width, view->height); | |||||
| if (impl->surface == NULL) { | |||||
| PUGL_LOG("failed to create cairo surface\n"); | |||||
| return false; | |||||
| } | |||||
| impl->cr = cairo_create(impl->surface); | |||||
| if (impl->cr == NULL) { | |||||
| cairo_surface_destroy(impl->surface); | |||||
| impl->surface = NULL; | |||||
| PUGL_LOG("failed to create cairo context\n"); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| #endif | |||||
| return false; | |||||
| } | |||||
| static void | |||||
| destroyContext(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| glXDestroyContext(impl->display, impl->ctx); | |||||
| impl->ctx = NULL; | |||||
| } | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| if (view->ctx_type == PUGL_CAIRO) { | |||||
| cairo_destroy(impl->cr); | |||||
| impl->cr = NULL; | |||||
| cairo_surface_destroy(impl->surface); | |||||
| impl->surface = NULL; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| void | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL) { | |||||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| void | |||||
| puglLeaveContext(PuglView* view, bool flush) | |||||
| { | |||||
| #ifdef PUGL_HAVE_GL | |||||
| if (view->ctx_type == PUGL_GL && flush) { | |||||
| glFlush(); | |||||
| if (view->impl->doubleBuffered) { | |||||
| glXSwapBuffers(view->impl->display, view->impl->win); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| } | |||||
| int | |||||
| puglCreateWindow(PuglView* view, const char* title) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| impl->display = XOpenDisplay(NULL); | |||||
| impl->screen = DefaultScreen(impl->display); | |||||
| XVisualInfo* const vi = getVisual(view); | |||||
| if (!vi) { | |||||
| XCloseDisplay(impl->display); | |||||
| impl->display = NULL; | |||||
| return 1; | |||||
| } | |||||
| #ifdef PUGL_HAVE_GL | |||||
| int glxMajor, glxMinor; | |||||
| glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||||
| PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||||
| #endif | |||||
| Window xParent = view->parent | |||||
| ? (Window)view->parent | |||||
| : RootWindow(impl->display, impl->screen); | |||||
| Colormap cmap = XCreateColormap( | |||||
| impl->display, xParent, vi->visual, AllocNone); | |||||
| XSetWindowAttributes attr; | |||||
| memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||||
| attr.background_pixel = BlackPixel(impl->display, impl->screen); | |||||
| attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||||
| attr.colormap = cmap; | |||||
| attr.event_mask = (ExposureMask | StructureNotifyMask | | |||||
| EnterWindowMask | LeaveWindowMask | | |||||
| KeyPressMask | KeyReleaseMask | | |||||
| ButtonPressMask | ButtonReleaseMask | | |||||
| PointerMotionMask | FocusChangeMask); | |||||
| impl->win = XCreateWindow( | |||||
| impl->display, xParent, | |||||
| 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||||
| CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr); | |||||
| if (!createContext(view, vi)) { | |||||
| XDestroyWindow(impl->display, impl->win); | |||||
| impl->win = 0; | |||||
| XCloseDisplay(impl->display); | |||||
| impl->display = NULL; | |||||
| return 1; | |||||
| } | |||||
| XSizeHints sizeHints; | |||||
| memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
| if (!view->resizable) { | |||||
| sizeHints.flags = PMinSize|PMaxSize; | |||||
| sizeHints.min_width = view->width; | |||||
| sizeHints.min_height = view->height; | |||||
| sizeHints.max_width = view->width; | |||||
| sizeHints.max_height = view->height; | |||||
| XSetNormalHints(impl->display, impl->win, &sizeHints); | |||||
| } else if (view->min_width > 0 && view->min_height > 0) { | |||||
| sizeHints.flags = PMinSize; | |||||
| sizeHints.min_width = view->min_width; | |||||
| sizeHints.min_height = view->min_height; | |||||
| XSetNormalHints(impl->display, impl->win, &sizeHints); | |||||
| } | |||||
| if (title) { | |||||
| XStoreName(impl->display, impl->win, title); | |||||
| } | |||||
| if (!view->parent) { | |||||
| Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||||
| XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||||
| } | |||||
| if (glXIsDirect(impl->display, impl->ctx)) { | |||||
| PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||||
| } else { | |||||
| PUGL_LOG("No DRI available\n"); | |||||
| } | |||||
| XFree(vi); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| void | |||||
| puglShowWindow(PuglView* view) | |||||
| { | |||||
| XMapRaised(view->impl->display, view->impl->win); | |||||
| } | |||||
| void | |||||
| puglHideWindow(PuglView* view) | |||||
| { | |||||
| XUnmapWindow(view->impl->display, view->impl->win); | |||||
| } | |||||
| void | |||||
| puglDestroy(PuglView* view) | |||||
| { | |||||
| if (!view) { | |||||
| return; | |||||
| } | |||||
| x_fib_close(view->impl->display); | |||||
| destroyContext(view); | |||||
| XDestroyWindow(view->impl->display, view->impl->win); | |||||
| XCloseDisplay(view->impl->display); | |||||
| free(view->impl); | |||||
| free(view); | |||||
| } | |||||
| static void | |||||
| puglReshape(PuglView* view, int width, int height) | |||||
| { | |||||
| puglEnterContext(view); | |||||
| if (view->reshapeFunc) { | |||||
| view->reshapeFunc(view, width, height); | |||||
| } else { | |||||
| puglDefaultReshape(view, width, height); | |||||
| } | |||||
| puglLeaveContext(view, false); | |||||
| view->width = width; | |||||
| view->height = height; | |||||
| } | |||||
| static void | |||||
| puglDisplay(PuglView* view) | |||||
| { | |||||
| puglEnterContext(view); | |||||
| view->redisplay = false; | |||||
| if (view->displayFunc) { | |||||
| view->displayFunc(view); | |||||
| } | |||||
| puglLeaveContext(view, true); | |||||
| } | |||||
| static PuglKey | |||||
| keySymToSpecial(KeySym sym) | |||||
| { | |||||
| switch (sym) { | |||||
| case XK_F1: return PUGL_KEY_F1; | |||||
| case XK_F2: return PUGL_KEY_F2; | |||||
| case XK_F3: return PUGL_KEY_F3; | |||||
| case XK_F4: return PUGL_KEY_F4; | |||||
| case XK_F5: return PUGL_KEY_F5; | |||||
| case XK_F6: return PUGL_KEY_F6; | |||||
| case XK_F7: return PUGL_KEY_F7; | |||||
| case XK_F8: return PUGL_KEY_F8; | |||||
| case XK_F9: return PUGL_KEY_F9; | |||||
| case XK_F10: return PUGL_KEY_F10; | |||||
| case XK_F11: return PUGL_KEY_F11; | |||||
| case XK_F12: return PUGL_KEY_F12; | |||||
| case XK_Left: return PUGL_KEY_LEFT; | |||||
| case XK_Up: return PUGL_KEY_UP; | |||||
| case XK_Right: return PUGL_KEY_RIGHT; | |||||
| case XK_Down: return PUGL_KEY_DOWN; | |||||
| case XK_Page_Up: return PUGL_KEY_PAGE_UP; | |||||
| case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; | |||||
| case XK_Home: return PUGL_KEY_HOME; | |||||
| case XK_End: return PUGL_KEY_END; | |||||
| case XK_Insert: return PUGL_KEY_INSERT; | |||||
| case XK_Shift_L: return PUGL_KEY_SHIFT; | |||||
| case XK_Shift_R: return PUGL_KEY_SHIFT; | |||||
| case XK_Control_L: return PUGL_KEY_CTRL; | |||||
| case XK_Control_R: return PUGL_KEY_CTRL; | |||||
| case XK_Alt_L: return PUGL_KEY_ALT; | |||||
| case XK_Alt_R: return PUGL_KEY_ALT; | |||||
| case XK_Super_L: return PUGL_KEY_SUPER; | |||||
| case XK_Super_R: return PUGL_KEY_SUPER; | |||||
| } | |||||
| return (PuglKey)0; | |||||
| } | |||||
| static void | |||||
| setModifiers(PuglView* view, unsigned xstate, unsigned xtime) | |||||
| { | |||||
| view->event_timestamp_ms = xtime; | |||||
| view->mods = 0; | |||||
| view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; | |||||
| view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; | |||||
| view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; | |||||
| view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; | |||||
| } | |||||
| static void | |||||
| dispatchKey(PuglView* view, XEvent* event, bool press) | |||||
| { | |||||
| KeySym sym; | |||||
| char str[5]; | |||||
| const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); | |||||
| if (sym == XK_Escape && view->closeFunc && !press && !view->parent) { | |||||
| view->closeFunc(view); | |||||
| view->redisplay = false; | |||||
| return; | |||||
| } | |||||
| if (n == 0) { | |||||
| return; | |||||
| } | |||||
| if (n > 1) { | |||||
| fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); | |||||
| return; | |||||
| } | |||||
| const PuglKey special = keySymToSpecial(sym); | |||||
| if (special && view->specialFunc) { | |||||
| view->specialFunc(view, press, special); | |||||
| } else if (!special && view->keyboardFunc) { | |||||
| view->keyboardFunc(view, press, str[0]); | |||||
| } | |||||
| } | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| XEvent event; | |||||
| while (XPending(view->impl->display) > 0) { | |||||
| XNextEvent(view->impl->display, &event); | |||||
| if (x_fib_handle_events(view->impl->display, &event)) { | |||||
| const int status = x_fib_status(); | |||||
| if (status > 0) { | |||||
| char* const filename = x_fib_filename(); | |||||
| x_fib_close(view->impl->display); | |||||
| if (view->fileSelectedFunc) { | |||||
| view->fileSelectedFunc(view, filename); | |||||
| } | |||||
| free(filename); | |||||
| } else if (status < 0) { | |||||
| x_fib_close(view->impl->display); | |||||
| if (view->fileSelectedFunc) { | |||||
| view->fileSelectedFunc(view, NULL); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| if (event.xany.window != view->impl->win) { | |||||
| continue; | |||||
| } | |||||
| switch (event.type) { | |||||
| case MapNotify: | |||||
| puglReshape(view, view->width, view->height); | |||||
| break; | |||||
| case ConfigureNotify: | |||||
| if ((event.xconfigure.width != view->width) || | |||||
| (event.xconfigure.height != view->height)) { | |||||
| puglReshape(view, | |||||
| event.xconfigure.width, | |||||
| event.xconfigure.height); | |||||
| } | |||||
| break; | |||||
| case Expose: | |||||
| if (event.xexpose.count != 0) { | |||||
| break; | |||||
| } | |||||
| puglDisplay(view); | |||||
| break; | |||||
| case MotionNotify: | |||||
| setModifiers(view, event.xmotion.state, event.xmotion.time); | |||||
| if (view->motionFunc) { | |||||
| view->motionFunc(view, event.xmotion.x, event.xmotion.y); | |||||
| } | |||||
| break; | |||||
| case ButtonPress: | |||||
| setModifiers(view, event.xbutton.state, event.xbutton.time); | |||||
| if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { | |||||
| if (view->scrollFunc) { | |||||
| float dx = 0, dy = 0; | |||||
| switch (event.xbutton.button) { | |||||
| case 4: dy = 1.0f; break; | |||||
| case 5: dy = -1.0f; break; | |||||
| case 6: dx = -1.0f; break; | |||||
| case 7: dx = 1.0f; break; | |||||
| } | |||||
| view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy); | |||||
| } | |||||
| break; | |||||
| } | |||||
| // nobreak | |||||
| case ButtonRelease: | |||||
| setModifiers(view, event.xbutton.state, event.xbutton.time); | |||||
| if (view->mouseFunc && | |||||
| (event.xbutton.button < 4 || event.xbutton.button > 7)) { | |||||
| view->mouseFunc(view, | |||||
| event.xbutton.button, event.type == ButtonPress, | |||||
| event.xbutton.x, event.xbutton.y); | |||||
| } | |||||
| break; | |||||
| case KeyPress: | |||||
| setModifiers(view, event.xkey.state, event.xkey.time); | |||||
| dispatchKey(view, &event, true); | |||||
| break; | |||||
| case KeyRelease: { | |||||
| setModifiers(view, event.xkey.state, event.xkey.time); | |||||
| bool repeated = false; | |||||
| if (view->ignoreKeyRepeat && | |||||
| XEventsQueued(view->impl->display, QueuedAfterReading)) { | |||||
| XEvent next; | |||||
| XPeekEvent(view->impl->display, &next); | |||||
| if (next.type == KeyPress && | |||||
| next.xkey.time == event.xkey.time && | |||||
| next.xkey.keycode == event.xkey.keycode) { | |||||
| XNextEvent(view->impl->display, &event); | |||||
| repeated = true; | |||||
| } | |||||
| } | |||||
| if (!repeated) { | |||||
| dispatchKey(view, &event, false); | |||||
| } | |||||
| } break; | |||||
| case ClientMessage: { | |||||
| char* type = XGetAtomName(view->impl->display, | |||||
| event.xclient.message_type); | |||||
| if (!strcmp(type, "WM_PROTOCOLS")) { | |||||
| if (view->closeFunc) { | |||||
| view->closeFunc(view); | |||||
| view->redisplay = false; | |||||
| } | |||||
| } | |||||
| XFree(type); | |||||
| } break; | |||||
| #ifdef PUGL_GRAB_FOCUS | |||||
| case EnterNotify: | |||||
| XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); | |||||
| break; | |||||
| #endif | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (view->redisplay) { | |||||
| puglDisplay(view); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| void | |||||
| puglPostRedisplay(PuglView* view) | |||||
| { | |||||
| view->redisplay = true; | |||||
| } | |||||
| PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view) | |||||
| { | |||||
| return view->impl->win; | |||||
| } | |||||
| void* | |||||
| puglGetContext(PuglView* view) | |||||
| { | |||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| if (view->ctx_type == PUGL_CAIRO) { | |||||
| return view->impl->cr; | |||||
| } | |||||
| #endif | |||||
| return NULL; | |||||
| // may be unused | |||||
| (void)view; | |||||
| } | |||||
| @@ -0,0 +1,175 @@ | |||||
| /* 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 | |||||
| #include <X11/Xlib.h> | |||||
| /////////////////////////////////////////////////////////////////////////////// | |||||
| /* 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); | |||||
| /** 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*)); | |||||
| /* '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); | |||||
| #endif // LIBSOFD_H | |||||
| @@ -0,0 +1,571 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifdef DOXYGEN | |||||
| #include "src/DistrhoDefines.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Intro */ | |||||
| /** | |||||
| @mainpage DISTRHO %Plugin Framework | |||||
| DISTRHO %Plugin Framework (or @b DPF for short) | |||||
| is a plugin framework designed to make development of new plugins an easy and enjoyable task.@n | |||||
| It allows developers to create plugins with custom UIs using a simple C++ API.@n | |||||
| The framework facilitates exporting various different plugin formats from the same code-base. | |||||
| DPF can build for LADSPA, DSSI, LV2 and VST2 formats.@n | |||||
| A JACK/Standalone mode is also available, allowing you to quickly test plugins. | |||||
| @section Macros | |||||
| You start by creating a "DistrhoPluginInfo.h" file describing the plugin via macros, see @ref PluginMacros.@n | |||||
| This file is included in the main DPF code to select which features to activate for each plugin format. | |||||
| For example, a plugin (with %UI) that use states will require LV2 hosts to support Atom and Worker extensions for | |||||
| message passing from the %UI to the plugin.@n | |||||
| If your plugin does not make use of states, the Worker extension is not set as a required feature. | |||||
| @section Plugin | |||||
| The next step is to create your plugin code by subclassing DPF's Plugin class.@n | |||||
| You need to pass the number of parameters in the constructor and also the number of programs and states, if any. | |||||
| Here's an example of an audio plugin that simply mutes the host output: | |||||
| @code | |||||
| class MutePlugin : public Plugin | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Plugin class constructor. | |||||
| */ | |||||
| MutePlugin() | |||||
| : Plugin(0, 0, 0) // 0 parameters, 0 programs and 0 states | |||||
| { | |||||
| } | |||||
| protected: | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Information */ | |||||
| /** | |||||
| Get the plugin label. | |||||
| This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. | |||||
| */ | |||||
| const char* getLabel() const override | |||||
| { | |||||
| return "Mute"; | |||||
| } | |||||
| /** | |||||
| Get the plugin author/maker. | |||||
| */ | |||||
| const char* getMaker() const override | |||||
| { | |||||
| return "DPF"; | |||||
| } | |||||
| /** | |||||
| Get the plugin license name (a single line of text). | |||||
| For commercial plugins this should return some short copyright information. | |||||
| */ | |||||
| const char* getLicense() const override | |||||
| { | |||||
| return "MIT"; | |||||
| } | |||||
| /** | |||||
| Get the plugin version, in hexadecimal. | |||||
| TODO format to be defined | |||||
| */ | |||||
| uint32_t getVersion() const override | |||||
| { | |||||
| return 0x1000; | |||||
| } | |||||
| /** | |||||
| Get the plugin unique Id. | |||||
| This value is used by LADSPA, DSSI and VST plugin formats. | |||||
| */ | |||||
| int64_t getUniqueId() const override | |||||
| { | |||||
| return cconst('M', 'u', 't', 'e'); | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * This example has no parameters, so skip parameter stuff */ | |||||
| void initParameter(uint32_t, Parameter&) override {} | |||||
| float getParameterValue(uint32_t) const override { return 0.0f; } | |||||
| void setParameterValue(uint32_t, float) override {} | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Audio/MIDI Processing */ | |||||
| /** | |||||
| Run/process function for plugins without MIDI input. | |||||
| NOTE: Some parameters might be null if there are no audio inputs or outputs. | |||||
| */ | |||||
| void run(const float**, float** outputs, uint32_t frames) override | |||||
| { | |||||
| // get the left and right audio outputs | |||||
| float* const outL = outputs[0]; | |||||
| float* const outR = outputs[1]; | |||||
| // mute audio | |||||
| std::memset(outL, 0, sizeof(float)*frames); | |||||
| std::memset(outR, 0, sizeof(float)*frames); | |||||
| } | |||||
| }; | |||||
| @endcode | |||||
| See the Plugin class for more information and to understand what each function does. | |||||
| @section Parameters | |||||
| A plugin is nothing without parameters.@n | |||||
| In DPF parameters can be inputs or outputs.@n | |||||
| They have hints to describe how they behave plus a name and a symbol identifying them.@n | |||||
| Parameters also have 'ranges' – a minimum, maximum and default value. | |||||
| Input parameters are "read-only": the plugin can read them but not change them. | |||||
| (the exception being when changing programs, more on that below)@n | |||||
| It's the host responsibility to save, restore and set input parameters. | |||||
| Output parameters can be changed at anytime by the plugin.@n | |||||
| The host will simply read their values and not change them. | |||||
| Here's an example of an audio plugin that has 1 input parameter: | |||||
| @code | |||||
| class GainPlugin : public Plugin | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Plugin class constructor. | |||||
| You must set all parameter values to their defaults, matching ParameterRanges::def. | |||||
| */ | |||||
| GainPlugin() | |||||
| : Plugin(1, 0, 0), // 1 parameter, 0 programs and 0 states | |||||
| fGain(1.0f) | |||||
| { | |||||
| } | |||||
| protected: | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Information */ | |||||
| const char* getLabel() const override | |||||
| { | |||||
| return "Gain"; | |||||
| } | |||||
| const char* getMaker() const override | |||||
| { | |||||
| return "DPF"; | |||||
| } | |||||
| const char* getLicense() const override | |||||
| { | |||||
| return "MIT"; | |||||
| } | |||||
| uint32_t getVersion() const override | |||||
| { | |||||
| return 0x1000; | |||||
| } | |||||
| int64_t getUniqueId() const override | |||||
| { | |||||
| return cconst('G', 'a', 'i', 'n'); | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Init */ | |||||
| /** | |||||
| Initialize a parameter. | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| void initParameter(uint32_t index, Parameter& parameter) override | |||||
| { | |||||
| // we only have one parameter so we can skip checking the index | |||||
| parameter.hints = kParameterIsAutomable; | |||||
| parameter.name = "Gain"; | |||||
| parameter.symbol = "gain"; | |||||
| parameter.ranges.min = 0.0f; | |||||
| parameter.ranges.max = 2.0f; | |||||
| parameter.ranges.def = 1.0f; | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Internal data */ | |||||
| /** | |||||
| Get the current value of a parameter. | |||||
| */ | |||||
| float getParameterValue(uint32_t index) const override | |||||
| { | |||||
| // same as before, ignore index check | |||||
| return fGain; | |||||
| } | |||||
| /** | |||||
| Change a parameter value. | |||||
| */ | |||||
| void setParameterValue(uint32_t index, float value) override | |||||
| { | |||||
| // same as before, ignore index check | |||||
| fGain = value; | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Audio/MIDI Processing */ | |||||
| void run(const float**, float** outputs, uint32_t frames) override | |||||
| { | |||||
| // get the mono input and output | |||||
| const float* const in = inputs[0]; | |||||
| /* */ float* const out = outputs[0]; | |||||
| // apply gain against all samples | |||||
| for (uint32_t i=0; i < frames; ++i) | |||||
| out[i] = in[i] * fGain; | |||||
| } | |||||
| private: | |||||
| float fGain; | |||||
| }; | |||||
| @endcode | |||||
| See the Parameter struct for more information about parameters. | |||||
| @section Programs | |||||
| Programs in DPF refer to plugin-side presets (usually called "factory presets"), | |||||
| an initial set of presets provided by plugin authors included in the actual plugin. | |||||
| To use programs you must first enable them by setting @ref DISTRHO_PLUGIN_WANT_PROGRAMS to 1 in your DistrhoPluginInfo.h file.@n | |||||
| When enabled you'll need to override 2 new function in your plugin code, | |||||
| Plugin::initProgramName(uint32_t, String&) and Plugin::loadProgram(uint32_t). | |||||
| Here's an example of a plugin with a "default" program: | |||||
| @code | |||||
| class PluginWithPresets : public Plugin | |||||
| { | |||||
| public: | |||||
| PluginWithPresets() | |||||
| : Plugin(2, 1, 0), // 2 parameters, 1 program and 0 states | |||||
| fGainL(1.0f), | |||||
| fGainR(1.0f), | |||||
| { | |||||
| } | |||||
| protected: | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Information */ | |||||
| const char* getLabel() const override | |||||
| { | |||||
| return "Prog"; | |||||
| } | |||||
| const char* getMaker() const override | |||||
| { | |||||
| return "DPF"; | |||||
| } | |||||
| const char* getLicense() const override | |||||
| { | |||||
| return "MIT"; | |||||
| } | |||||
| uint32_t getVersion() const override | |||||
| { | |||||
| return 0x1000; | |||||
| } | |||||
| int64_t getUniqueId() const override | |||||
| { | |||||
| return cconst('P', 'r', 'o', 'g'); | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Init */ | |||||
| /** | |||||
| Initialize a parameter. | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| void initParameter(uint32_t index, Parameter& parameter) override | |||||
| { | |||||
| parameter.hints = kParameterIsAutomable; | |||||
| parameter.ranges.min = 0.0f; | |||||
| parameter.ranges.max = 2.0f; | |||||
| parameter.ranges.def = 1.0f; | |||||
| switch (index) | |||||
| { | |||||
| case 0; | |||||
| parameter.name = "Gain Right"; | |||||
| parameter.symbol = "gainR"; | |||||
| break; | |||||
| case 1; | |||||
| parameter.name = "Gain Left"; | |||||
| parameter.symbol = "gainL"; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /** | |||||
| Set the name of the program @a index. | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| void initProgramName(uint32_t index, String& programName) | |||||
| { | |||||
| switch(index) | |||||
| { | |||||
| case 0: | |||||
| programName = "Default"; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Internal data */ | |||||
| /** | |||||
| Get the current value of a parameter. | |||||
| */ | |||||
| float getParameterValue(uint32_t index) const override | |||||
| { | |||||
| switch (index) | |||||
| { | |||||
| case 0; | |||||
| return fGainL; | |||||
| case 1; | |||||
| return fGainR; | |||||
| } | |||||
| } | |||||
| /** | |||||
| Change a parameter value. | |||||
| */ | |||||
| void setParameterValue(uint32_t index, float value) override | |||||
| { | |||||
| switch (index) | |||||
| { | |||||
| case 0; | |||||
| fGainL = value; | |||||
| break; | |||||
| case 1; | |||||
| fGainR = value; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /** | |||||
| Load a program. | |||||
| */ | |||||
| void loadProgram(uint32_t index) | |||||
| { | |||||
| switch(index) | |||||
| { | |||||
| case 0: | |||||
| fGainL = 1.0f; | |||||
| fGainR = 1.0f; | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* ---------------------------------------------------------------------------------------- | |||||
| * Audio/MIDI Processing */ | |||||
| void run(const float**, float** outputs, uint32_t frames) override | |||||
| { | |||||
| // get the left and right audio buffers | |||||
| const float* const inL = inputs[0]; | |||||
| const float* const inR = inputs[0]; | |||||
| /* */ float* const outL = outputs[0]; | |||||
| /* */ float* const outR = outputs[0]; | |||||
| // apply gain against all samples | |||||
| for (uint32_t i=0; i < frames; ++i) | |||||
| { | |||||
| outL[i] = inL[i] * fGainL; | |||||
| outR[i] = inR[i] * fGainR; | |||||
| } | |||||
| } | |||||
| private: | |||||
| float fGainL, fGainR; | |||||
| }; | |||||
| @endcode | |||||
| @section States | |||||
| describe them | |||||
| @section MIDI | |||||
| describe them | |||||
| @section Latency | |||||
| describe it | |||||
| @section Time-Position | |||||
| describe it | |||||
| @section UI | |||||
| describe them | |||||
| */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Plugin Macros */ | |||||
| /** | |||||
| @defgroup PluginMacros Plugin Macros | |||||
| C Macros that describe your plugin. (defined in the "DistrhoPluginInfo.h" file) | |||||
| With these macros you can tell the host what features your plugin requires.@n | |||||
| Depending on which macros you enable, new functions will be available to call and/or override. | |||||
| All values are either integer or strings.@n | |||||
| For boolean-like values 1 means 'on' and 0 means 'off'. | |||||
| The values defined in this group are for documentation purposes only.@n | |||||
| All macros are disabled by default. | |||||
| Only 4 macros are required, they are: | |||||
| - @ref DISTRHO_PLUGIN_NAME | |||||
| - @ref DISTRHO_PLUGIN_NUM_INPUTS | |||||
| - @ref DISTRHO_PLUGIN_NUM_OUTPUTS | |||||
| - @ref DISTRHO_PLUGIN_URI | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| The plugin name.@n | |||||
| This is used to identify your plugin before a Plugin instance can be created. | |||||
| @note This macro is required. | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_NAME "Plugin Name" | |||||
| /** | |||||
| Number of audio inputs the plugin has. | |||||
| @note This macro is required. | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||||
| /** | |||||
| Number of audio outputs the plugin has. | |||||
| @note This macro is required. | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||||
| /** | |||||
| The plugin URI when exporting in LV2 format. | |||||
| @note This macro is required. | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_URI "urn:distrho:name" | |||||
| /** | |||||
| Wherever the plugin has a custom %UI. | |||||
| @see DISTRHO_UI_USE_NANOVG | |||||
| @see UI | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_HAS_UI 1 | |||||
| /** | |||||
| Wherever the plugin processing is realtime-safe.@n | |||||
| TODO - list rtsafe requirements | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_IS_RT_SAFE 1 | |||||
| /** | |||||
| Wherever the plugin is a synth.@n | |||||
| @ref DISTRHO_PLUGIN_WANT_MIDI_INPUT is automatically enabled when this is too. | |||||
| @see DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_IS_SYNTH 1 | |||||
| /** | |||||
| Enable direct access between the %UI and plugin code. | |||||
| @see UI::getPluginInstancePointer() | |||||
| @note DO NOT USE THIS UNLESS STRICTLY NECESSARY!! | |||||
| Try to avoid it at all costs! | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 | |||||
| /** | |||||
| Wherever the plugin introduces latency during audio or midi processing. | |||||
| @see Plugin::setLatency(uint32_t) | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_LATENCY 1 | |||||
| /** | |||||
| Wherever the plugin wants MIDI input.@n | |||||
| This is automatically enabled if @ref DISTRHO_PLUGIN_IS_SYNTH is true. | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 | |||||
| /** | |||||
| Wherever the plugin wants MIDI output. | |||||
| @see Plugin::writeMidiEvent(const MidiEvent&) | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 | |||||
| /** | |||||
| Wherever the plugin provides its own internal programs. | |||||
| @see Plugin::initProgramName(uint32_t, String&) | |||||
| @see Plugin::loadProgram(uint32_t) | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 | |||||
| /** | |||||
| Wherever the plugin uses internal non-parameter data. | |||||
| @see Plugin::initState(uint32_t, String&, String&) | |||||
| @see Plugin::setState(const char*, const char*) | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_STATE 1 | |||||
| /** | |||||
| Wherever the plugin wants time position information from the host. | |||||
| @see Plugin::getTimePosition() | |||||
| */ | |||||
| #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 | |||||
| /** | |||||
| Wherever the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n | |||||
| When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget. | |||||
| */ | |||||
| #define DISTRHO_UI_USE_NANOVG 1 | |||||
| /** | |||||
| The %UI URI when exporting in LV2 format.@n | |||||
| By default this is set to @ref DISTRHO_PLUGIN_URI with "#UI" as suffix. | |||||
| */ | |||||
| #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||||
| /** @} */ | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DOXYGEN | |||||
| @@ -0,0 +1,720 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_PLUGIN_HPP_INCLUDED | |||||
| #define DISTRHO_PLUGIN_HPP_INCLUDED | |||||
| #include "extra/String.hpp" | |||||
| #include "extra/LeakDetector.hpp" | |||||
| #include "src/DistrhoPluginChecks.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Audio Port Hints */ | |||||
| /** | |||||
| @defgroup AudioPortHints Audio Port Hints | |||||
| Various audio port hints. | |||||
| @see AudioPort::hints | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Audio port can be used as control voltage (LV2 only). | |||||
| */ | |||||
| static const uint32_t kAudioPortIsCV = 0x1; | |||||
| /** | |||||
| Audio port should be used as sidechan (LV2 only). | |||||
| */ | |||||
| static const uint32_t kAudioPortIsSidechain = 0x2; | |||||
| /** @} */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Parameter Hints */ | |||||
| /** | |||||
| @defgroup ParameterHints Parameter Hints | |||||
| Various parameter hints. | |||||
| @see Parameter::hints | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Parameter is automable (real-time safe). | |||||
| @see Plugin::setParameterValue(uint32_t, float) | |||||
| */ | |||||
| static const uint32_t kParameterIsAutomable = 0x01; | |||||
| /** | |||||
| Parameter value is boolean.@n | |||||
| It's always at either minimum or maximum value. | |||||
| */ | |||||
| static const uint32_t kParameterIsBoolean = 0x02; | |||||
| /** | |||||
| Parameter value is integer. | |||||
| */ | |||||
| static const uint32_t kParameterIsInteger = 0x04; | |||||
| /** | |||||
| Parameter value is logarithmic. | |||||
| */ | |||||
| static const uint32_t kParameterIsLogarithmic = 0x08; | |||||
| /** | |||||
| Parameter is of output type.@n | |||||
| When unset, parameter is assumed to be of input type. | |||||
| Parameter inputs are changed by the host and must not be changed by the plugin.@n | |||||
| The only exception being when changing programs, see Plugin::loadProgram().@n | |||||
| Outputs are changed by the plugin and never modified by the host. | |||||
| */ | |||||
| static const uint32_t kParameterIsOutput = 0x10; | |||||
| /** @} */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Base Plugin structs */ | |||||
| /** | |||||
| @defgroup BasePluginStructs Base Plugin Structs | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Audio Port. | |||||
| */ | |||||
| struct AudioPort { | |||||
| /** | |||||
| Hints describing this audio port. | |||||
| @see AudioPortHints | |||||
| */ | |||||
| uint32_t hints; | |||||
| /** | |||||
| The name of this audio port.@n | |||||
| An audio port name can contain any character, but hosts might have a hard time with non-ascii ones.@n | |||||
| The name doesn't have to be unique within a plugin instance, but it's recommended. | |||||
| */ | |||||
| String name; | |||||
| /** | |||||
| The symbol of this audio port.@n | |||||
| An audio port symbol is a short restricted name used as a machine and human readable identifier.@n | |||||
| The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. | |||||
| @note Audio port and parameter symbols MUST be unique within a plugin instance. | |||||
| */ | |||||
| String symbol; | |||||
| /** | |||||
| Default constructor for a regular audio port. | |||||
| */ | |||||
| AudioPort() noexcept | |||||
| : hints(0x0), | |||||
| name(), | |||||
| symbol() {} | |||||
| }; | |||||
| /** | |||||
| Parameter ranges.@n | |||||
| This is used to set the default, minimum and maximum values of a parameter. | |||||
| By default a parameter has 0.0 as minimum, 1.0 as maximum and 0.0 as default.@n | |||||
| When changing this struct values you must ensure maximum > minimum and default is within range. | |||||
| */ | |||||
| struct ParameterRanges { | |||||
| /** | |||||
| Default value. | |||||
| */ | |||||
| float def; | |||||
| /** | |||||
| Minimum value. | |||||
| */ | |||||
| float min; | |||||
| /** | |||||
| Maximum value. | |||||
| */ | |||||
| float max; | |||||
| /** | |||||
| Default constructor, using 0.0 as minimum, 1.0 as maximum and 0.0 as default. | |||||
| */ | |||||
| ParameterRanges() noexcept | |||||
| : def(0.0f), | |||||
| min(0.0f), | |||||
| max(1.0f) {} | |||||
| /** | |||||
| Constructor using custom values. | |||||
| */ | |||||
| ParameterRanges(float df, float mn, float mx) noexcept | |||||
| : def(df), | |||||
| min(mn), | |||||
| max(mx) {} | |||||
| /** | |||||
| Fix the default value within range. | |||||
| */ | |||||
| void fixDefault() noexcept | |||||
| { | |||||
| fixValue(def); | |||||
| } | |||||
| /** | |||||
| Fix a value within range. | |||||
| */ | |||||
| void fixValue(float& value) const noexcept | |||||
| { | |||||
| if (value < min) | |||||
| value = min; | |||||
| else if (value > max) | |||||
| value = max; | |||||
| } | |||||
| /** | |||||
| Get a fixed value within range. | |||||
| */ | |||||
| const float& getFixedValue(const float& value) const noexcept | |||||
| { | |||||
| if (value <= min) | |||||
| return min; | |||||
| if (value >= max) | |||||
| return max; | |||||
| return value; | |||||
| } | |||||
| /** | |||||
| Get a value normalized to 0.0<->1.0. | |||||
| */ | |||||
| float getNormalizedValue(const float& value) const noexcept | |||||
| { | |||||
| const float normValue((value - min) / (max - min)); | |||||
| if (normValue <= 0.0f) | |||||
| return 0.0f; | |||||
| if (normValue >= 1.0f) | |||||
| return 1.0f; | |||||
| return normValue; | |||||
| } | |||||
| /** | |||||
| Get a value normalized to 0.0<->1.0, fixed within range. | |||||
| */ | |||||
| float getFixedAndNormalizedValue(const float& value) const noexcept | |||||
| { | |||||
| if (value <= min) | |||||
| return 0.0f; | |||||
| if (value >= max) | |||||
| return 1.0f; | |||||
| const float normValue((value - min) / (max - min)); | |||||
| if (normValue <= 0.0f) | |||||
| return 0.0f; | |||||
| if (normValue >= 1.0f) | |||||
| return 1.0f; | |||||
| return normValue; | |||||
| } | |||||
| /** | |||||
| Get a proper value previously normalized to 0.0<->1.0. | |||||
| */ | |||||
| float getUnnormalizedValue(const float& value) const noexcept | |||||
| { | |||||
| if (value <= 0.0f) | |||||
| return min; | |||||
| if (value >= 1.0f) | |||||
| return max; | |||||
| return value * (max - min) + min; | |||||
| } | |||||
| }; | |||||
| /** | |||||
| Parameter. | |||||
| */ | |||||
| struct Parameter { | |||||
| /** | |||||
| Hints describing this parameter. | |||||
| @see ParameterHints | |||||
| */ | |||||
| uint32_t hints; | |||||
| /** | |||||
| The name of this parameter.@n | |||||
| A parameter name can contain any character, but hosts might have a hard time with non-ascii ones.@n | |||||
| The name doesn't have to be unique within a plugin instance, but it's recommended. | |||||
| */ | |||||
| String name; | |||||
| /** | |||||
| The symbol of this parameter.@n | |||||
| A parameter symbol is a short restricted name used as a machine and human readable identifier.@n | |||||
| The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. | |||||
| @note Parameter symbols MUST be unique within a plugin instance. | |||||
| */ | |||||
| String symbol; | |||||
| /** | |||||
| The unit of this parameter.@n | |||||
| This means something like "dB", "kHz" and "ms".@n | |||||
| Can be left blank if a unit does not apply to this parameter. | |||||
| */ | |||||
| String unit; | |||||
| /** | |||||
| Ranges of this parameter.@n | |||||
| The ranges describe the default, minimum and maximum values. | |||||
| */ | |||||
| ParameterRanges ranges; | |||||
| /** | |||||
| Default constructor for a null parameter. | |||||
| */ | |||||
| Parameter() noexcept | |||||
| : hints(0x0), | |||||
| name(), | |||||
| symbol(), | |||||
| unit(), | |||||
| ranges() {} | |||||
| /** | |||||
| Constructor using custom values. | |||||
| */ | |||||
| Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept | |||||
| : hints(h), | |||||
| name(n), | |||||
| symbol(s), | |||||
| unit(u), | |||||
| ranges(def, min, max) {} | |||||
| }; | |||||
| /** | |||||
| MIDI event. | |||||
| */ | |||||
| struct MidiEvent { | |||||
| /** | |||||
| Size of internal data. | |||||
| */ | |||||
| static const uint32_t kDataSize = 4; | |||||
| /** | |||||
| Time offset in frames. | |||||
| */ | |||||
| uint32_t frame; | |||||
| /** | |||||
| Number of bytes used. | |||||
| */ | |||||
| uint32_t size; | |||||
| /** | |||||
| MIDI data.@n | |||||
| If size > kDataSize, dataExt is used (otherwise null). | |||||
| */ | |||||
| uint8_t data[kDataSize]; | |||||
| const uint8_t* dataExt; | |||||
| }; | |||||
| /** | |||||
| Time position.@n | |||||
| The @a playing and @a frame values are always valid.@n | |||||
| BBT values are only valid when @a bbt.valid is true. | |||||
| This struct is inspired by the JACK Transport API. | |||||
| */ | |||||
| struct TimePosition { | |||||
| /** | |||||
| Wherever the host transport is playing/rolling. | |||||
| */ | |||||
| bool playing; | |||||
| /** | |||||
| Current host transport position in frames. | |||||
| */ | |||||
| uint64_t frame; | |||||
| /** | |||||
| Bar-Beat-Tick time position. | |||||
| */ | |||||
| struct BarBeatTick { | |||||
| /** | |||||
| Wherever the host transport is using BBT.@n | |||||
| If false you must not read from this struct. | |||||
| */ | |||||
| bool valid; | |||||
| /** | |||||
| Current bar.@n | |||||
| Should always be > 0.@n | |||||
| The first bar is bar '1'. | |||||
| */ | |||||
| int32_t bar; | |||||
| /** | |||||
| Current beat within bar.@n | |||||
| Should always be > 0 and <= @a beatsPerBar.@n | |||||
| The first beat is beat '1'. | |||||
| */ | |||||
| int32_t beat; | |||||
| /** | |||||
| Current tick within beat.@n | |||||
| Should always be > 0 and <= @a ticksPerBeat.@n | |||||
| The first tick is tick '0'. | |||||
| */ | |||||
| int32_t tick; | |||||
| /** | |||||
| Number of ticks that have elapsed between frame 0 and the first beat of the current measure. | |||||
| */ | |||||
| double barStartTick; | |||||
| /** | |||||
| Time signature "numerator". | |||||
| */ | |||||
| float beatsPerBar; | |||||
| /** | |||||
| Time signature "denominator". | |||||
| */ | |||||
| float beatType; | |||||
| /** | |||||
| Number of ticks within a bar.@n | |||||
| Usually a moderately large integer with many denominators, such as 1920.0. | |||||
| */ | |||||
| double ticksPerBeat; | |||||
| /** | |||||
| Number of beats per minute. | |||||
| */ | |||||
| double beatsPerMinute; | |||||
| /** | |||||
| Default constructor for a null BBT time position. | |||||
| */ | |||||
| BarBeatTick() noexcept | |||||
| : valid(false), | |||||
| bar(0), | |||||
| beat(0), | |||||
| tick(0), | |||||
| barStartTick(0.0), | |||||
| beatsPerBar(0.0f), | |||||
| beatType(0.0f), | |||||
| ticksPerBeat(0.0), | |||||
| beatsPerMinute(0.0) {} | |||||
| } bbt; | |||||
| /** | |||||
| Default constructor for a time position. | |||||
| */ | |||||
| TimePosition() noexcept | |||||
| : playing(false), | |||||
| frame(0), | |||||
| bbt() {} | |||||
| }; | |||||
| /** @} */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * DPF Plugin */ | |||||
| /** | |||||
| @defgroup MainClasses Main Classes | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| DPF Plugin class from where plugin instances are created. | |||||
| The public methods (Host state) are called from the plugin to get or set host information.@n | |||||
| They can be called from a plugin instance at anytime unless stated otherwise.@n | |||||
| All other methods are to be implemented by the plugin and will be called by the host. | |||||
| Shortly after a plugin instance is created, the various init* functions will be called by the host.@n | |||||
| Host will call activate() before run(), and deactivate() before the plugin instance is destroyed.@n | |||||
| The host may call deactivate right after activate and vice-versa, but never activate/deactivate consecutively.@n | |||||
| There is no limit on how many times run() is called, only that activate/deactivate will be called in between. | |||||
| The buffer size and sample rate values will remain constant between activate and deactivate.@n | |||||
| Buffer size is only a hint though, the host might call run() with a higher or lower number of frames. | |||||
| Some of this class functions are only available according to some macros. | |||||
| DISTRHO_PLUGIN_WANT_PROGRAMS activates program related features.@n | |||||
| When enabled you need to implement initProgramName() and loadProgram(). | |||||
| DISTRHO_PLUGIN_WANT_STATE activates internal state features.@n | |||||
| When enabled you need to implement initStateKey() and setState(). | |||||
| The process function run() changes wherever DISTRHO_PLUGIN_WANT_MIDI_INPUT is enabled or not.@n | |||||
| When enabled it provides midi input events. | |||||
| */ | |||||
| class Plugin | |||||
| { | |||||
| public: | |||||
| /** | |||||
| Plugin class constructor.@n | |||||
| You must set all parameter values to their defaults, matching ParameterRanges::def. | |||||
| */ | |||||
| Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| virtual ~Plugin(); | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Host state */ | |||||
| /** | |||||
| Get the current buffer size that will probably be used during processing, in frames.@n | |||||
| This value will remain constant between activate and deactivate. | |||||
| @note This value is only a hint!@n | |||||
| Hosts might call run() with a higher or lower number of frames. | |||||
| @see bufferSizeChanged(uint32_t) | |||||
| */ | |||||
| uint32_t getBufferSize() const noexcept; | |||||
| /** | |||||
| Get the current sample rate that will be used during processing.@n | |||||
| This value will remain constant between activate and deactivate. | |||||
| @see sampleRateChanged(double) | |||||
| */ | |||||
| double getSampleRate() const noexcept; | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| /** | |||||
| Get the current host transport time position.@n | |||||
| This function should only be called during run().@n | |||||
| You can call this during other times, but the returned position is not guaranteed to be in sync. | |||||
| @note TimePosition is not supported in LADSPA and DSSI plugin formats. | |||||
| */ | |||||
| const TimePosition& getTimePosition() const noexcept; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| /** | |||||
| Change the plugin audio output latency to @a frames.@n | |||||
| This function should only be called in the constructor, activate() and run(). | |||||
| @note This function is only available if DISTRHO_PLUGIN_WANT_LATENCY is enabled. | |||||
| */ | |||||
| void setLatency(uint32_t frames) noexcept; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| /** | |||||
| Write a MIDI output event.@n | |||||
| This function must only be called during run().@n | |||||
| Returns false when the host buffer is full, in which case do not call this again until the next run(). | |||||
| */ | |||||
| bool writeMidiEvent(const MidiEvent& midiEvent) noexcept; | |||||
| #endif | |||||
| protected: | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Information */ | |||||
| /** | |||||
| Get the plugin name.@n | |||||
| Returns DISTRHO_PLUGIN_NAME by default. | |||||
| */ | |||||
| virtual const char* getName() const { return DISTRHO_PLUGIN_NAME; } | |||||
| /** | |||||
| Get the plugin label.@n | |||||
| This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. | |||||
| */ | |||||
| virtual const char* getLabel() const = 0; | |||||
| /** | |||||
| Get the plugin author/maker. | |||||
| */ | |||||
| virtual const char* getMaker() const = 0; | |||||
| /** | |||||
| Get the plugin license name (a single line of text).@n | |||||
| For commercial plugins this should return some short copyright information. | |||||
| */ | |||||
| virtual const char* getLicense() const = 0; | |||||
| /** | |||||
| Get the plugin version, in hexadecimal.@n | |||||
| TODO format to be defined | |||||
| */ | |||||
| virtual uint32_t getVersion() const = 0; | |||||
| /** | |||||
| Get the plugin unique Id.@n | |||||
| This value is used by LADSPA, DSSI and VST plugin formats. | |||||
| */ | |||||
| virtual int64_t getUniqueId() const = 0; | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Init */ | |||||
| /** | |||||
| Initialize the audio port @a index.@n | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| virtual void initAudioPort(bool input, uint32_t index, AudioPort& port); | |||||
| /** | |||||
| Initialize the parameter @a index.@n | |||||
| This function will be called once, shortly after the plugin is created. | |||||
| */ | |||||
| virtual void initParameter(uint32_t index, Parameter& parameter) = 0; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| /** | |||||
| Set the name of the program @a index.@n | |||||
| This function will be called once, shortly after the plugin is created.@n | |||||
| Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_PROGRAMS is enabled. | |||||
| */ | |||||
| virtual void initProgramName(uint32_t index, String& programName) = 0; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| /** | |||||
| Set the state key and default value of @a index.@n | |||||
| This function will be called once, shortly after the plugin is created.@n | |||||
| Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. | |||||
| */ | |||||
| virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; | |||||
| #endif | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Internal data */ | |||||
| /** | |||||
| Get the current value of a parameter.@n | |||||
| The host may call this function from any context, including realtime processing. | |||||
| */ | |||||
| virtual float getParameterValue(uint32_t index) const = 0; | |||||
| /** | |||||
| Change a parameter value.@n | |||||
| The host may call this function from any context, including realtime processing.@n | |||||
| When a parameter is marked as automable, you must ensure no non-realtime operations are performed. | |||||
| @note This function will only be called for parameter inputs. | |||||
| */ | |||||
| virtual void setParameterValue(uint32_t index, float value) = 0; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| /** | |||||
| Load a program.@n | |||||
| The host may call this function from any context, including realtime processing.@n | |||||
| Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_PROGRAMS is enabled. | |||||
| */ | |||||
| virtual void loadProgram(uint32_t index) = 0; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| /** | |||||
| Change an internal state @a key to @a value.@n | |||||
| Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. | |||||
| */ | |||||
| virtual void setState(const char* key, const char* value) = 0; | |||||
| #endif | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Audio/MIDI Processing */ | |||||
| /** | |||||
| Activate this plugin. | |||||
| */ | |||||
| virtual void activate() {} | |||||
| /** | |||||
| Deactivate this plugin. | |||||
| */ | |||||
| virtual void deactivate() {} | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| /** | |||||
| Run/process function for plugins with MIDI input. | |||||
| @note Some parameters might be null if there are no audio inputs/outputs or MIDI events. | |||||
| */ | |||||
| virtual void run(const float** inputs, float** outputs, uint32_t frames, | |||||
| const MidiEvent* midiEvents, uint32_t midiEventCount) = 0; | |||||
| #else | |||||
| /** | |||||
| Run/process function for plugins without MIDI input. | |||||
| @note Some parameters might be null if there are no audio inputs or outputs. | |||||
| */ | |||||
| virtual void run(const float** inputs, float** outputs, uint32_t frames) = 0; | |||||
| #endif | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Callbacks (optional) */ | |||||
| /** | |||||
| Optional callback to inform the plugin about a buffer size change.@n | |||||
| This function will only be called when the plugin is deactivated. | |||||
| @note This value is only a hint!@n | |||||
| Hosts might call run() with a higher or lower number of frames. | |||||
| @see getBufferSize() | |||||
| */ | |||||
| virtual void bufferSizeChanged(uint32_t newBufferSize); | |||||
| /** | |||||
| Optional callback to inform the plugin about a sample rate change.@n | |||||
| This function will only be called when the plugin is deactivated. | |||||
| @see getSampleRate() | |||||
| */ | |||||
| virtual void sampleRateChanged(double newSampleRate); | |||||
| // ------------------------------------------------------------------------------------------------------- | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| friend class PluginExporter; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Plugin) | |||||
| }; | |||||
| /** @} */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Create plugin, entry point */ | |||||
| /** | |||||
| @defgroup EntryPoints Entry Points | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| TODO. | |||||
| */ | |||||
| extern Plugin* createPlugin(); | |||||
| /** @} */ | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_PLUGIN_HPP_INCLUDED | |||||
| @@ -0,0 +1,30 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "src/DistrhoPlugin.cpp" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||||
| # include "src/DistrhoPluginCarla.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||||
| # include "src/DistrhoPluginJack.cpp" | |||||
| #elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) | |||||
| # include "src/DistrhoPluginLADSPA+DSSI.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||||
| # include "src/DistrhoPluginLV2.cpp" | |||||
| # include "src/DistrhoPluginLV2export.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST) | |||||
| # include "src/DistrhoPluginVST.cpp" | |||||
| #endif | |||||
| @@ -0,0 +1,209 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_UI_HPP_INCLUDED | |||||
| #define DISTRHO_UI_HPP_INCLUDED | |||||
| #include "extra/LeakDetector.hpp" | |||||
| #include "src/DistrhoPluginChecks.h" | |||||
| #if DISTRHO_UI_USE_NANOVG | |||||
| # include "../dgl/NanoVG.hpp" | |||||
| typedef DGL::NanoWidget UIWidget; | |||||
| #else | |||||
| # include "../dgl/Widget.hpp" | |||||
| typedef DGL::Widget UIWidget; | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * DPF UI */ | |||||
| /** | |||||
| @addtogroup MainClasses | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| DPF UI class from where UI instances are created. | |||||
| TODO. | |||||
| must call setSize during construction, | |||||
| */ | |||||
| class UI : public UIWidget | |||||
| { | |||||
| public: | |||||
| /** | |||||
| UI class constructor. | |||||
| The UI should be initialized to a default state that matches the plugin side. | |||||
| */ | |||||
| UI(uint width = 0, uint height = 0); | |||||
| /** | |||||
| Destructor. | |||||
| */ | |||||
| virtual ~UI(); | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Host state */ | |||||
| /** | |||||
| Get the current sample rate used in plugin processing. | |||||
| @see sampleRateChanged(double) | |||||
| */ | |||||
| double getSampleRate() const noexcept; | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| void editParameter(uint32_t index, bool started); | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| void setParameterValue(uint32_t index, float value); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| void setState(const char* key, const char* value); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */ | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| void* getPluginInstancePointer() const noexcept; | |||||
| #endif | |||||
| protected: | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * DSP/Plugin Callbacks */ | |||||
| /** | |||||
| A parameter has changed on the plugin side. | |||||
| This is called by the host to inform the UI about parameter changes. | |||||
| */ | |||||
| virtual void parameterChanged(uint32_t index, float value) = 0; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| /** | |||||
| A program has been loaded on the plugin side. | |||||
| This is called by the host to inform the UI about program changes. | |||||
| */ | |||||
| virtual void programLoaded(uint32_t index) = 0; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| /** | |||||
| A state has changed on the plugin side. | |||||
| This is called by the host to inform the UI about state changes. | |||||
| */ | |||||
| virtual void stateChanged(const char* key, const char* value) = 0; | |||||
| #endif | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * DSP/Plugin Callbacks (optional) */ | |||||
| /** | |||||
| Optional callback to inform the UI about a sample rate change on the plugin side. | |||||
| @see getSampleRate() | |||||
| */ | |||||
| virtual void sampleRateChanged(double newSampleRate); | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * UI Callbacks (optional) */ | |||||
| /** | |||||
| TODO: Document this. | |||||
| */ | |||||
| virtual void uiIdle() {} | |||||
| /** | |||||
| File browser selected function. | |||||
| @see Window::fileBrowserSelected(const char*) | |||||
| */ | |||||
| virtual void uiFileBrowserSelected(const char* filename); | |||||
| /** | |||||
| OpenGL window reshape function, called when parent window is resized. | |||||
| You can reimplement this function for a custom OpenGL state. | |||||
| @see Window::onReshape(uint,uint) | |||||
| */ | |||||
| virtual void uiReshape(uint width, uint height); | |||||
| /* -------------------------------------------------------------------------------------------------------- | |||||
| * UI Resize Handling, internal */ | |||||
| /** | |||||
| OpenGL widget resize function, called when the widget is resized. | |||||
| This is overriden here so the host knows when the UI is resized by you. | |||||
| @see Widget::onResize(const ResizeEvent&) | |||||
| */ | |||||
| void onResize(const ResizeEvent& ev) override; | |||||
| // ------------------------------------------------------------------------------------------------------- | |||||
| private: | |||||
| struct PrivateData; | |||||
| PrivateData* const pData; | |||||
| friend class UIExporter; | |||||
| friend class UIExporterWindow; | |||||
| // these should not be used | |||||
| void setAbsoluteX(int) const noexcept {} | |||||
| void setAbsoluteY(int) const noexcept {} | |||||
| void setAbsolutePos(int, int) const noexcept {} | |||||
| void setAbsolutePos(const DGL::Point<int>&) const noexcept {} | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) | |||||
| }; | |||||
| /** @} */ | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Create UI, entry point */ | |||||
| /** | |||||
| @addtogroup EntryPoints | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| TODO. | |||||
| */ | |||||
| extern UI* createUI(); | |||||
| /** @} */ | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_HPP_INCLUDED | |||||
| @@ -0,0 +1,29 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "src/DistrhoUI.cpp" | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||||
| // nothing | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||||
| // nothing | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||||
| # include "src/DistrhoUIDSSI.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||||
| # include "src/DistrhoUILV2.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST) | |||||
| // nothing | |||||
| #endif | |||||
| @@ -0,0 +1,233 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_UTILS_HPP_INCLUDED | |||||
| #define DISTRHO_UTILS_HPP_INCLUDED | |||||
| #include "src/DistrhoDefines.h" | |||||
| #include <cstdarg> | |||||
| #include <cstdio> | |||||
| #include <cstdlib> | |||||
| #include <cstring> | |||||
| #include <cmath> | |||||
| #include <limits> | |||||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # include <cstdint> | |||||
| #else | |||||
| # include <stdint.h> | |||||
| #endif | |||||
| #if defined(DISTRHO_OS_MAC) && ! defined(CARLA_OS_MAC) | |||||
| namespace std { | |||||
| inline float fmin(float __x, float __y) | |||||
| { return __builtin_fminf(__x, __y); } | |||||
| inline float fmax(float __x, float __y) | |||||
| { return __builtin_fmaxf(__x, __y); } | |||||
| inline float rint(float __x) | |||||
| { return __builtin_rintf(__x); } | |||||
| inline float round(float __x) | |||||
| { return __builtin_roundf(__x); } | |||||
| } | |||||
| #endif | |||||
| #ifndef M_PI | |||||
| # define M_PI 3.14159265358979323846 | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // misc functions | |||||
| /* | |||||
| * Return a 64-bit number from 4 8-bit numbers. | |||||
| */ | |||||
| static inline | |||||
| int64_t d_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) noexcept | |||||
| { | |||||
| return (a << 24) | (b << 16) | (c << 8) | (d << 0); | |||||
| } | |||||
| /* | |||||
| * Dummy function. | |||||
| */ | |||||
| static inline | |||||
| void d_pass() noexcept {} | |||||
| // ----------------------------------------------------------------------- | |||||
| // string print functions | |||||
| /* | |||||
| * Print a string to stdout with newline (gray color). | |||||
| * Does nothing if DEBUG is not defined. | |||||
| */ | |||||
| #ifndef DEBUG | |||||
| # define d_debug(...) | |||||
| #else | |||||
| static inline | |||||
| void d_debug(const char* const fmt, ...) noexcept | |||||
| { | |||||
| try { | |||||
| ::va_list args; | |||||
| ::va_start(args, fmt); | |||||
| std::fprintf(stdout, "\x1b[30;1m"); | |||||
| std::vfprintf(stdout, fmt, args); | |||||
| std::fprintf(stdout, "\x1b[0m\n"); | |||||
| ::va_end(args); | |||||
| } catch (...) {} | |||||
| } | |||||
| #endif | |||||
| /* | |||||
| * Print a string to stdout with newline. | |||||
| */ | |||||
| static inline | |||||
| void d_stdout(const char* const fmt, ...) noexcept | |||||
| { | |||||
| try { | |||||
| ::va_list args; | |||||
| ::va_start(args, fmt); | |||||
| std::vfprintf(stdout, fmt, args); | |||||
| std::fprintf(stdout, "\n"); | |||||
| ::va_end(args); | |||||
| } catch (...) {} | |||||
| } | |||||
| /* | |||||
| * Print a string to stderr with newline. | |||||
| */ | |||||
| static inline | |||||
| void d_stderr(const char* const fmt, ...) noexcept | |||||
| { | |||||
| try { | |||||
| ::va_list args; | |||||
| ::va_start(args, fmt); | |||||
| std::vfprintf(stderr, fmt, args); | |||||
| std::fprintf(stderr, "\n"); | |||||
| ::va_end(args); | |||||
| } catch (...) {} | |||||
| } | |||||
| /* | |||||
| * Print a string to stderr with newline (red color). | |||||
| */ | |||||
| static inline | |||||
| void d_stderr2(const char* const fmt, ...) noexcept | |||||
| { | |||||
| try { | |||||
| ::va_list args; | |||||
| ::va_start(args, fmt); | |||||
| std::fprintf(stderr, "\x1b[31m"); | |||||
| std::vfprintf(stderr, fmt, args); | |||||
| std::fprintf(stderr, "\x1b[0m\n"); | |||||
| ::va_end(args); | |||||
| } catch (...) {} | |||||
| } | |||||
| /* | |||||
| * Print a safe assertion error message. | |||||
| */ | |||||
| static inline | |||||
| void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept | |||||
| { | |||||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line); | |||||
| } | |||||
| /* | |||||
| * Print a safe exception error message. | |||||
| */ | |||||
| static inline | |||||
| void d_safe_exception(const char* const exception, const char* const file, const int line) noexcept | |||||
| { | |||||
| d_stderr2("exception caught: \"%s\" in file %s, line %i", exception, file, line); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // math functions | |||||
| /* | |||||
| * Safely compare two floating point numbers. | |||||
| * Returns true if they match. | |||||
| */ | |||||
| template<typename T> | |||||
| static inline | |||||
| bool d_isEqual(const T& v1, const T& v2) | |||||
| { | |||||
| return std::abs(v1-v2) < std::numeric_limits<T>::epsilon(); | |||||
| } | |||||
| /* | |||||
| * Safely compare two floating point numbers. | |||||
| * Returns true if they don't match. | |||||
| */ | |||||
| template<typename T> | |||||
| static inline | |||||
| bool d_isNotEqual(const T& v1, const T& v2) | |||||
| { | |||||
| return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon(); | |||||
| } | |||||
| /* | |||||
| * Safely check if a floating point number is zero. | |||||
| */ | |||||
| template<typename T> | |||||
| static inline | |||||
| bool d_isZero(const T& value) | |||||
| { | |||||
| return std::abs(value) < std::numeric_limits<T>::epsilon(); | |||||
| } | |||||
| /* | |||||
| * Safely check if a floating point number is not zero. | |||||
| */ | |||||
| template<typename T> | |||||
| static inline | |||||
| bool d_isNotZero(const T& value) | |||||
| { | |||||
| return std::abs(value) >= std::numeric_limits<T>::epsilon(); | |||||
| } | |||||
| /* | |||||
| * Get next power of 2. | |||||
| */ | |||||
| static inline | |||||
| uint32_t d_nextPowerOf2(uint32_t size) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(size > 0, 0); | |||||
| // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 | |||||
| --size; | |||||
| size |= size >> 1; | |||||
| size |= size >> 2; | |||||
| size |= size >> 4; | |||||
| size |= size >> 8; | |||||
| size |= size >> 16; | |||||
| return ++size; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| #ifndef DONT_SET_USING_DISTRHO_NAMESPACE | |||||
| // If your code uses a lot of DISTRHO classes, then this will obviously save you | |||||
| // a lot of typing, but can be disabled by setting DONT_SET_USING_DISTRHO_NAMESPACE. | |||||
| namespace DISTRHO_NAMESPACE {} | |||||
| using namespace DISTRHO_NAMESPACE; | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| #endif // DISTRHO_UTILS_HPP_INCLUDED | |||||
| @@ -0,0 +1,127 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 <cctype> | |||||
| #include <vector> | |||||
| // ----------------------------------------------------------------------- | |||||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||||
| // Copyright (C) 2004-2008 René Nyffenegger | |||||
| // ----------------------------------------------------------------------- | |||||
| // Helpers | |||||
| #ifndef DOXYGEN | |||||
| namespace DistrhoBase64Helpers { | |||||
| static 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 inline | |||||
| bool isBase64Char(const char c) | |||||
| { | |||||
| return (std::isalnum(c) || (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(CarlaBase64Helpers::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 | |||||
| @@ -0,0 +1,141 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_LEAK_DETECTOR_HPP_INCLUDED | |||||
| #define DISTRHO_LEAK_DETECTOR_HPP_INCLUDED | |||||
| #include "../DistrhoUtils.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // The following code was based from juce-core LeakDetector class | |||||
| // Copyright (C) 2013 Raw Material Software Ltd. | |||||
| /** A good old-fashioned C macro concatenation helper. | |||||
| This combines two items (which may themselves be macros) into a single string, | |||||
| avoiding the pitfalls of the ## macro operator. | |||||
| */ | |||||
| #define DISTRHO_JOIN_MACRO_HELPER(a, b) a ## b | |||||
| #define DISTRHO_JOIN_MACRO(item1, item2) DISTRHO_JOIN_MACRO_HELPER(item1, item2) | |||||
| /** This macro lets you embed a leak-detecting object inside a class.\n | |||||
| To use it, simply declare a DISTRHO_LEAK_DETECTOR(YourClassName) inside a private section | |||||
| of the class declaration. E.g. | |||||
| \code | |||||
| class MyClass | |||||
| { | |||||
| public: | |||||
| MyClass(); | |||||
| void blahBlah(); | |||||
| private: | |||||
| DISTRHO_LEAK_DETECTOR(MyClass) | |||||
| }; | |||||
| \endcode | |||||
| */ | |||||
| #define DISTRHO_LEAK_DETECTOR(ClassName) \ | |||||
| friend class DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName>; \ | |||||
| static const char* getLeakedObjectClassName() noexcept { return #ClassName; } \ | |||||
| DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName> DISTRHO_JOIN_MACRO(leakDetector_, ClassName); | |||||
| #define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \ | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ | |||||
| DISTRHO_LEAK_DETECTOR(ClassName) | |||||
| //============================================================================== | |||||
| /** | |||||
| Embedding an instance of this class inside another class can be used as a low-overhead | |||||
| way of detecting leaked instances. | |||||
| This class keeps an internal static count of the number of instances that are | |||||
| active, so that when the app is shutdown and the static destructors are called, | |||||
| it can check whether there are any left-over instances that may have been leaked. | |||||
| To use it, use the DISTRHO_LEAK_DETECTOR macro as a simple way to put one in your | |||||
| class declaration. | |||||
| */ | |||||
| template<class OwnerClass> | |||||
| class LeakedObjectDetector | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| LeakedObjectDetector() noexcept { ++(getCounter().numObjects); } | |||||
| LeakedObjectDetector(const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } | |||||
| ~LeakedObjectDetector() noexcept | |||||
| { | |||||
| if (--(getCounter().numObjects) < 0) | |||||
| { | |||||
| /** If you hit this, then you've managed to delete more instances of this class than you've | |||||
| created.. That indicates that you're deleting some dangling pointers. | |||||
| Note that although this assertion will have been triggered during a destructor, it might | |||||
| not be this particular deletion that's at fault - the incorrect one may have happened | |||||
| at an earlier point in the program, and simply not been detected until now. | |||||
| Most errors like this are caused by using old-fashioned, non-RAII techniques for | |||||
| your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, | |||||
| ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! | |||||
| */ | |||||
| d_stderr2("*** Dangling pointer deletion! Class: '%s', Count: %i", getLeakedObjectClassName(), getCounter().numObjects); | |||||
| } | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| class LeakCounter | |||||
| { | |||||
| public: | |||||
| LeakCounter() noexcept | |||||
| : numObjects(0) {} | |||||
| ~LeakCounter() noexcept | |||||
| { | |||||
| if (numObjects > 0) | |||||
| { | |||||
| /** If you hit this, then you've leaked one or more objects of the type specified by | |||||
| the 'OwnerClass' template parameter - the name should have been printed by the line above. | |||||
| If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for | |||||
| your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, | |||||
| ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! | |||||
| */ | |||||
| d_stderr2("*** Leaked objects detected: %i instance(s) of class '%s'", numObjects, getLeakedObjectClassName()); | |||||
| } | |||||
| } | |||||
| // this should be an atomic... | |||||
| volatile int numObjects; | |||||
| }; | |||||
| static const char* getLeakedObjectClassName() noexcept | |||||
| { | |||||
| return OwnerClass::getLeakedObjectClassName(); | |||||
| } | |||||
| static LeakCounter& getCounter() noexcept | |||||
| { | |||||
| static LeakCounter counter; | |||||
| return counter; | |||||
| } | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_LEAK_DETECTOR_HPP_INCLUDED | |||||
| @@ -0,0 +1,274 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_MUTEX_HPP_INCLUDED | |||||
| #define DISTRHO_MUTEX_HPP_INCLUDED | |||||
| #include "../DistrhoUtils.hpp" | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| # include <winsock2.h> | |||||
| # include <windows.h> | |||||
| #endif | |||||
| #include <pthread.h> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // Mutex class | |||||
| class Mutex | |||||
| { | |||||
| public: | |||||
| /* | |||||
| * Constructor. | |||||
| */ | |||||
| Mutex() noexcept | |||||
| : fMutex() | |||||
| { | |||||
| pthread_mutex_init(&fMutex, nullptr); | |||||
| } | |||||
| /* | |||||
| * Destructor. | |||||
| */ | |||||
| ~Mutex() noexcept | |||||
| { | |||||
| pthread_mutex_destroy(&fMutex); | |||||
| } | |||||
| /* | |||||
| * Lock the mutex. | |||||
| */ | |||||
| void lock() const noexcept | |||||
| { | |||||
| pthread_mutex_lock(&fMutex); | |||||
| } | |||||
| /* | |||||
| * Try to lock the mutex. | |||||
| * Returns true if successful. | |||||
| */ | |||||
| bool tryLock() const noexcept | |||||
| { | |||||
| return (pthread_mutex_trylock(&fMutex) == 0); | |||||
| } | |||||
| /* | |||||
| * Unlock the mutex. | |||||
| */ | |||||
| void unlock() const noexcept | |||||
| { | |||||
| pthread_mutex_unlock(&fMutex); | |||||
| } | |||||
| private: | |||||
| mutable pthread_mutex_t fMutex; | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(Mutex) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // RecursiveMutex class | |||||
| class RecursiveMutex | |||||
| { | |||||
| public: | |||||
| /* | |||||
| * Constructor. | |||||
| */ | |||||
| RecursiveMutex() noexcept | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| : fSection() | |||||
| #else | |||||
| : fMutex() | |||||
| #endif | |||||
| { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| InitializeCriticalSection(&fSection); | |||||
| #else | |||||
| pthread_mutexattr_t atts; | |||||
| pthread_mutexattr_init(&atts); | |||||
| pthread_mutexattr_settype(&atts, PTHREAD_MUTEX_RECURSIVE); | |||||
| pthread_mutex_init(&fMutex, &atts); | |||||
| pthread_mutexattr_destroy(&atts); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Destructor. | |||||
| */ | |||||
| ~RecursiveMutex() noexcept | |||||
| { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| DeleteCriticalSection(&fSection); | |||||
| #else | |||||
| pthread_mutex_destroy(&fMutex); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Lock the mutex. | |||||
| */ | |||||
| void lock() const noexcept | |||||
| { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| EnterCriticalSection(&fSection); | |||||
| #else | |||||
| pthread_mutex_lock(&fMutex); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Try to lock the mutex. | |||||
| * Returns true if successful. | |||||
| */ | |||||
| bool tryLock() const noexcept | |||||
| { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| return (TryEnterCriticalSection(&fSection) != FALSE); | |||||
| #else | |||||
| return (pthread_mutex_trylock(&fMutex) == 0); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Unlock the mutex. | |||||
| */ | |||||
| void unlock() const noexcept | |||||
| { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| LeaveCriticalSection(&fSection); | |||||
| #else | |||||
| pthread_mutex_unlock(&fMutex); | |||||
| #endif | |||||
| } | |||||
| private: | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| mutable CRITICAL_SECTION fSection; | |||||
| #else | |||||
| mutable pthread_mutex_t fMutex; | |||||
| #endif | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Helper class to lock&unlock a mutex during a function scope. | |||||
| template <class Mutex> | |||||
| class ScopeLocker | |||||
| { | |||||
| public: | |||||
| ScopeLocker(const Mutex& mutex) noexcept | |||||
| : fMutex(mutex) | |||||
| { | |||||
| fMutex.lock(); | |||||
| } | |||||
| ~ScopeLocker() noexcept | |||||
| { | |||||
| fMutex.unlock(); | |||||
| } | |||||
| private: | |||||
| const Mutex& fMutex; | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Helper class to try-lock&unlock a mutex during a function scope. | |||||
| template <class Mutex> | |||||
| class ScopeTryLocker | |||||
| { | |||||
| public: | |||||
| ScopeTryLocker(const Mutex& mutex) noexcept | |||||
| : fMutex(mutex), | |||||
| fLocked(mutex.tryLock()) {} | |||||
| ~ScopeTryLocker() noexcept | |||||
| { | |||||
| if (fLocked) | |||||
| fMutex.unlock(); | |||||
| } | |||||
| bool wasLocked() const noexcept | |||||
| { | |||||
| return fLocked; | |||||
| } | |||||
| bool wasNotLocked() const noexcept | |||||
| { | |||||
| return !fLocked; | |||||
| } | |||||
| private: | |||||
| const Mutex& fMutex; | |||||
| const bool fLocked; | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Helper class to unlock&lock a mutex during a function scope. | |||||
| template <class Mutex> | |||||
| class ScopeUnlocker | |||||
| { | |||||
| public: | |||||
| ScopeUnlocker(const Mutex& mutex) noexcept | |||||
| : fMutex(mutex) | |||||
| { | |||||
| fMutex.unlock(); | |||||
| } | |||||
| ~ScopeUnlocker() noexcept | |||||
| { | |||||
| fMutex.lock(); | |||||
| } | |||||
| private: | |||||
| const Mutex& fMutex; | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Define types | |||||
| typedef ScopeLocker<Mutex> MutexLocker; | |||||
| typedef ScopeLocker<RecursiveMutex> RecursiveMutexLocker; | |||||
| typedef ScopeTryLocker<Mutex> MutexTryLocker; | |||||
| typedef ScopeTryLocker<RecursiveMutex> RecursiveMutexTryLocker; | |||||
| typedef ScopeUnlocker<Mutex> MutexUnlocker; | |||||
| typedef ScopeUnlocker<RecursiveMutex> RecursiveMutexUnlocker; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_MUTEX_HPP_INCLUDED | |||||
| @@ -0,0 +1,230 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_SCOPED_POINTER_HPP_INCLUDED | |||||
| #define DISTRHO_SCOPED_POINTER_HPP_INCLUDED | |||||
| #include "../DistrhoUtils.hpp" | |||||
| #include <algorithm> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // The following code was based from juce-core ScopedPointer class | |||||
| // Copyright (C) 2013 Raw Material Software Ltd. | |||||
| //============================================================================== | |||||
| /** | |||||
| This class holds a pointer which is automatically deleted when this object goes | |||||
| out of scope. | |||||
| Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer | |||||
| gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or | |||||
| as member variables is a good way to use RAII to avoid accidentally leaking dynamically | |||||
| created objects. | |||||
| A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer | |||||
| to an object. If you use the assignment operator to assign a different object to a | |||||
| ScopedPointer, the old one will be automatically deleted. | |||||
| A const ScopedPointer is guaranteed not to lose ownership of its object or change the | |||||
| object to which it points during its lifetime. This means that making a copy of a const | |||||
| ScopedPointer is impossible, as that would involve the new copy taking ownership from the | |||||
| old one. | |||||
| If you need to get a pointer out of a ScopedPointer without it being deleted, you | |||||
| can use the release() method. | |||||
| Something to note is the main difference between this class and the std::auto_ptr class, | |||||
| which is that ScopedPointer provides a cast-to-object operator, wheras std::auto_ptr | |||||
| requires that you always call get() to retrieve the pointer. The advantages of providing | |||||
| the cast is that you don't need to call get(), so can use the ScopedPointer in pretty much | |||||
| exactly the same way as a raw pointer. The disadvantage is that the compiler is free to | |||||
| use the cast in unexpected and sometimes dangerous ways - in particular, it becomes difficult | |||||
| to return a ScopedPointer as the result of a function. To avoid this causing errors, | |||||
| ScopedPointer contains an overloaded constructor that should cause a syntax error in these | |||||
| circumstances, but it does mean that instead of returning a ScopedPointer from a function, | |||||
| you'd need to return a raw pointer (or use a std::auto_ptr instead). | |||||
| */ | |||||
| template<class ObjectType> | |||||
| class ScopedPointer | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| /** Creates a ScopedPointer containing a null pointer. */ | |||||
| ScopedPointer() noexcept | |||||
| : object(nullptr) {} | |||||
| /** Creates a ScopedPointer that owns the specified object. */ | |||||
| ScopedPointer(ObjectType* const objectToTakePossessionOf) noexcept | |||||
| : object(objectToTakePossessionOf) {} | |||||
| /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. | |||||
| Because a pointer can only belong to one ScopedPointer, this transfers | |||||
| the pointer from the other object to this one, and the other object is reset to | |||||
| be a null pointer. | |||||
| */ | |||||
| ScopedPointer(ScopedPointer& objectToTransferFrom) noexcept | |||||
| : object(objectToTransferFrom.object) | |||||
| { | |||||
| objectToTransferFrom.object = nullptr; | |||||
| } | |||||
| /** Destructor. | |||||
| This will delete the object that this ScopedPointer currently refers to. | |||||
| */ | |||||
| ~ScopedPointer() | |||||
| { | |||||
| delete object; | |||||
| } | |||||
| /** Changes this ScopedPointer to point to a new object. | |||||
| Because a pointer can only belong to one ScopedPointer, this transfers | |||||
| the pointer from the other object to this one, and the other object is reset to | |||||
| be a null pointer. | |||||
| If this ScopedPointer already points to an object, that object | |||||
| will first be deleted. | |||||
| */ | |||||
| ScopedPointer& operator=(ScopedPointer& objectToTransferFrom) | |||||
| { | |||||
| if (this != objectToTransferFrom.getAddress()) | |||||
| { | |||||
| // Two ScopedPointers should never be able to refer to the same object - if | |||||
| // this happens, you must have done something dodgy! | |||||
| DISTRHO_SAFE_ASSERT_RETURN(object == nullptr || object != objectToTransferFrom.object, *this); | |||||
| ObjectType* const oldObject = object; | |||||
| object = objectToTransferFrom.object; | |||||
| objectToTransferFrom.object = nullptr; | |||||
| delete oldObject; | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| /** Changes this ScopedPointer to point to a new object. | |||||
| If this ScopedPointer already points to an object, that object | |||||
| will first be deleted. | |||||
| The pointer that you pass in may be a nullptr. | |||||
| */ | |||||
| ScopedPointer& operator=(ObjectType* const newObjectToTakePossessionOf) | |||||
| { | |||||
| if (object != newObjectToTakePossessionOf) | |||||
| { | |||||
| ObjectType* const oldObject = object; | |||||
| object = newObjectToTakePossessionOf; | |||||
| delete oldObject; | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| //============================================================================== | |||||
| /** Returns the object that this ScopedPointer refers to. */ | |||||
| operator ObjectType*() const noexcept { return object; } | |||||
| /** Returns the object that this ScopedPointer refers to. */ | |||||
| ObjectType* get() const noexcept { return object; } | |||||
| /** Returns the object that this ScopedPointer refers to. */ | |||||
| ObjectType& operator*() const noexcept { return *object; } | |||||
| /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ | |||||
| ObjectType* operator->() const noexcept { return object; } | |||||
| //============================================================================== | |||||
| /** Removes the current object from this ScopedPointer without deleting it. | |||||
| This will return the current object, and set the ScopedPointer to a null pointer. | |||||
| */ | |||||
| ObjectType* release() noexcept { ObjectType* const o = object; object = nullptr; return o; } | |||||
| //============================================================================== | |||||
| /** Swaps this object with that of another ScopedPointer. | |||||
| The two objects simply exchange their pointers. | |||||
| */ | |||||
| void swapWith(ScopedPointer<ObjectType>& other) noexcept | |||||
| { | |||||
| // Two ScopedPointers should never be able to refer to the same object - if | |||||
| // this happens, you must have done something dodgy! | |||||
| DISTRHO_SAFE_ASSERT_RETURN(object != other.object || this == other.getAddress() || object == nullptr,); | |||||
| std::swap(object, other.object); | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| ObjectType* object; | |||||
| // (Required as an alternative to the overloaded & operator). | |||||
| const ScopedPointer* getAddress() const noexcept { return this; } | |||||
| #ifndef _MSC_VER // (MSVC can't deal with multiple copy constructors) | |||||
| /* The copy constructors are private to stop people accidentally copying a const ScopedPointer | |||||
| (the compiler would let you do so by implicitly casting the source to its raw object pointer). | |||||
| A side effect of this is that in a compiler that doesn't support C++11, you may hit an | |||||
| error when you write something like this: | |||||
| ScopedPointer<MyClass> m = new MyClass(); // Compile error: copy constructor is private. | |||||
| Even though the compiler would normally ignore the assignment here, it can't do so when the | |||||
| copy constructor is private. It's very easy to fix though - just write it like this: | |||||
| ScopedPointer<MyClass> m (new MyClass()); // Compiles OK | |||||
| It's probably best to use the latter form when writing your object declarations anyway, as | |||||
| this is a better representation of the code that you actually want the compiler to produce. | |||||
| */ | |||||
| # ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| ScopedPointer(const ScopedPointer&) = delete; | |||||
| ScopedPointer& operator=(const ScopedPointer&) = delete; | |||||
| # else | |||||
| ScopedPointer(const ScopedPointer&); | |||||
| ScopedPointer& operator=(const ScopedPointer&); | |||||
| # endif | |||||
| #endif | |||||
| }; | |||||
| //============================================================================== | |||||
| /** Compares a ScopedPointer with another pointer. | |||||
| This can be handy for checking whether this is a null pointer. | |||||
| */ | |||||
| template<class ObjectType> | |||||
| bool operator==(const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept | |||||
| { | |||||
| return static_cast<ObjectType*>(pointer1) == pointer2; | |||||
| } | |||||
| /** Compares a ScopedPointer with another pointer. | |||||
| This can be handy for checking whether this is a null pointer. | |||||
| */ | |||||
| template<class ObjectType> | |||||
| bool operator!=(const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept | |||||
| { | |||||
| return static_cast<ObjectType*>(pointer1) != pointer2; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_SCOPED_POINTER_HPP_INCLUDED | |||||
| @@ -0,0 +1,68 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_SLEEP_HPP_INCLUDED | |||||
| #define DISTRHO_SLEEP_HPP_INCLUDED | |||||
| #include "../DistrhoUtils.hpp" | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| # include <winsock2.h> | |||||
| # include <windows.h> | |||||
| #else | |||||
| # include <unistd.h> | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // d_*sleep | |||||
| /* | |||||
| * Sleep for 'secs' seconds. | |||||
| */ | |||||
| static inline | |||||
| void d_sleep(const uint secs) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(secs > 0,); | |||||
| try { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| ::Sleep(secs * 1000); | |||||
| #else | |||||
| ::sleep(secs); | |||||
| #endif | |||||
| } DISTRHO_SAFE_EXCEPTION("d_sleep"); | |||||
| } | |||||
| /* | |||||
| * Sleep for 'msecs' milliseconds. | |||||
| */ | |||||
| static inline | |||||
| void d_msleep(const uint msecs) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(msecs > 0,); | |||||
| try { | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| ::Sleep(msecs); | |||||
| #else | |||||
| ::usleep(msecs * 1000); | |||||
| #endif | |||||
| } DISTRHO_SAFE_EXCEPTION("d_msleep"); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| #endif // DISTRHO_SLEEP_HPP_INCLUDED | |||||
| @@ -0,0 +1,840 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_STRING_HPP_INCLUDED | |||||
| #define DISTRHO_STRING_HPP_INCLUDED | |||||
| #include "../DistrhoUtils.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // String class | |||||
| class String | |||||
| { | |||||
| public: | |||||
| // ------------------------------------------------------------------- | |||||
| // constructors (no explicit conversions allowed) | |||||
| /* | |||||
| * Empty string. | |||||
| */ | |||||
| explicit String() noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) {} | |||||
| /* | |||||
| * Simple character. | |||||
| */ | |||||
| explicit String(const char c) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char ch[2]; | |||||
| ch[0] = c; | |||||
| ch[1] = '\0'; | |||||
| _dup(ch); | |||||
| } | |||||
| /* | |||||
| * Simple char string. | |||||
| */ | |||||
| explicit String(char* const strBuf) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Simple const char string. | |||||
| */ | |||||
| explicit String(const char* const strBuf) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Integer. | |||||
| */ | |||||
| explicit String(const int value) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, "%d", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Unsigned integer, possibly in hexadecimal. | |||||
| */ | |||||
| explicit String(const unsigned int value, const bool hexadecimal = false) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Long integer. | |||||
| */ | |||||
| explicit String(const long value) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, "%ld", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Long unsigned integer, possibly hexadecimal. | |||||
| */ | |||||
| explicit String(const unsigned long value, const bool hexadecimal = false) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Long long integer. | |||||
| */ | |||||
| explicit String(const long long value) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, "%lld", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Long long unsigned integer, possibly hexadecimal. | |||||
| */ | |||||
| explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Single-precision floating point number. | |||||
| */ | |||||
| explicit String(const float value) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, "%f", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| /* | |||||
| * Double-precision floating point number. | |||||
| */ | |||||
| explicit String(const double value) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| std::snprintf(strBuf, 0xff, "%g", value); | |||||
| strBuf[0xff] = '\0'; | |||||
| _dup(strBuf); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // non-explicit constructor | |||||
| /* | |||||
| * Create string from another string. | |||||
| */ | |||||
| String(const String& str) noexcept | |||||
| : fBuffer(_null()), | |||||
| fBufferLen(0) | |||||
| { | |||||
| _dup(str.fBuffer); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // destructor | |||||
| /* | |||||
| * Destructor. | |||||
| */ | |||||
| ~String() noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,); | |||||
| if (fBuffer == _null()) | |||||
| return; | |||||
| std::free(fBuffer); | |||||
| fBuffer = nullptr; | |||||
| fBufferLen = 0; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // public methods | |||||
| /* | |||||
| * Get length of the string. | |||||
| */ | |||||
| std::size_t length() const noexcept | |||||
| { | |||||
| return fBufferLen; | |||||
| } | |||||
| /* | |||||
| * Check if the string is empty. | |||||
| */ | |||||
| bool isEmpty() const noexcept | |||||
| { | |||||
| return (fBufferLen == 0); | |||||
| } | |||||
| /* | |||||
| * Check if the string is not empty. | |||||
| */ | |||||
| bool isNotEmpty() const noexcept | |||||
| { | |||||
| return (fBufferLen != 0); | |||||
| } | |||||
| /* | |||||
| * Check if the string contains another string, optionally ignoring case. | |||||
| */ | |||||
| bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false); | |||||
| if (ignoreCase) | |||||
| { | |||||
| #ifdef __USE_GNU | |||||
| return (strcasestr(fBuffer, strBuf) != nullptr); | |||||
| #else | |||||
| String tmp1(fBuffer), tmp2(strBuf); | |||||
| // memory allocation failed or empty string(s) | |||||
| if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null()) | |||||
| return false; | |||||
| tmp1.toLower(); | |||||
| tmp2.toLower(); | |||||
| return (std::strstr(tmp1, tmp2) != nullptr); | |||||
| #endif | |||||
| } | |||||
| return (std::strstr(fBuffer, strBuf) != nullptr); | |||||
| } | |||||
| /* | |||||
| * Check if character at 'pos' is a digit. | |||||
| */ | |||||
| bool isDigit(const std::size_t pos) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); | |||||
| return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); | |||||
| } | |||||
| /* | |||||
| * Check if the string starts with the character 'c'. | |||||
| */ | |||||
| bool startsWith(const char c) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); | |||||
| return (fBufferLen > 0 && fBuffer[0] == c); | |||||
| } | |||||
| /* | |||||
| * Check if the string starts with the string 'prefix'. | |||||
| */ | |||||
| bool startsWith(const char* const prefix) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); | |||||
| const std::size_t prefixLen(std::strlen(prefix)); | |||||
| if (fBufferLen < prefixLen) | |||||
| return false; | |||||
| return (std::strncmp(fBuffer, prefix, prefixLen) == 0); | |||||
| } | |||||
| /* | |||||
| * Check if the string ends with the character 'c'. | |||||
| */ | |||||
| bool endsWith(const char c) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); | |||||
| return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c); | |||||
| } | |||||
| /* | |||||
| * Check if the string ends with the string 'suffix'. | |||||
| */ | |||||
| bool endsWith(const char* const suffix) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); | |||||
| const std::size_t suffixLen(std::strlen(suffix)); | |||||
| if (fBufferLen < suffixLen) | |||||
| return false; | |||||
| return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); | |||||
| } | |||||
| /* | |||||
| * Find the first occurrence of character 'c' in the string. | |||||
| * Returns "length()" if the character is not found. | |||||
| */ | |||||
| std::size_t find(const char c, bool* const found = nullptr) const noexcept | |||||
| { | |||||
| if (fBufferLen == 0 || c == '\0') | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (fBuffer[i] == c) | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = true; | |||||
| return i; | |||||
| } | |||||
| } | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| /* | |||||
| * Find the first occurrence of string 'strBuf' in the string. | |||||
| * Returns "length()" if the string is not found. | |||||
| */ | |||||
| std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |||||
| { | |||||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| if (char* const subStrBuf = std::strstr(fBuffer, strBuf)) | |||||
| { | |||||
| const ssize_t ret(subStrBuf - fBuffer); | |||||
| if (ret < 0) | |||||
| { | |||||
| // should never happen! | |||||
| d_safe_assert("ret >= 0", __FILE__, __LINE__); | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| if (found != nullptr) | |||||
| *found = true; | |||||
| return static_cast<std::size_t>(ret); | |||||
| } | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| /* | |||||
| * Find the last occurrence of character 'c' in the string. | |||||
| * Returns "length()" if the character is not found. | |||||
| */ | |||||
| std::size_t rfind(const char c, bool* const found = nullptr) const noexcept | |||||
| { | |||||
| if (fBufferLen == 0 || c == '\0') | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| for (std::size_t i=fBufferLen; i > 0; --i) | |||||
| { | |||||
| if (fBuffer[i-1] == c) | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = true; | |||||
| return i-1; | |||||
| } | |||||
| } | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| return fBufferLen; | |||||
| } | |||||
| /* | |||||
| * Find the last occurrence of string 'strBuf' in the string. | |||||
| * Returns "length()" if the string is not found. | |||||
| */ | |||||
| std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = false; | |||||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |||||
| return fBufferLen; | |||||
| const std::size_t strBufLen(std::strlen(strBuf)); | |||||
| std::size_t ret = fBufferLen; | |||||
| const char* tmpBuf = fBuffer; | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) | |||||
| { | |||||
| if (found != nullptr) | |||||
| *found = true; | |||||
| break; | |||||
| } | |||||
| --ret; | |||||
| ++tmpBuf; | |||||
| } | |||||
| return fBufferLen-ret; | |||||
| } | |||||
| /* | |||||
| * Clear the string. | |||||
| */ | |||||
| void clear() noexcept | |||||
| { | |||||
| truncate(0); | |||||
| } | |||||
| /* | |||||
| * Replace all occurrences of character 'before' with character 'after'. | |||||
| */ | |||||
| String& replace(const char before, const char after) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this); | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (fBuffer[i] == before) | |||||
| fBuffer[i] = after; | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| /* | |||||
| * Truncate the string to size 'n'. | |||||
| */ | |||||
| String& truncate(const std::size_t n) noexcept | |||||
| { | |||||
| if (n >= fBufferLen) | |||||
| return *this; | |||||
| for (std::size_t i=n; i < fBufferLen; ++i) | |||||
| fBuffer[i] = '\0'; | |||||
| fBufferLen = n; | |||||
| return *this; | |||||
| } | |||||
| /* | |||||
| * Convert all non-basic characters to '_'. | |||||
| */ | |||||
| String& toBasic() noexcept | |||||
| { | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (fBuffer[i] >= '0' && fBuffer[i] <= '9') | |||||
| continue; | |||||
| if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |||||
| continue; | |||||
| if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |||||
| continue; | |||||
| if (fBuffer[i] == '_') | |||||
| continue; | |||||
| fBuffer[i] = '_'; | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| /* | |||||
| * Convert to all ascii characters to lowercase. | |||||
| */ | |||||
| String& toLower() noexcept | |||||
| { | |||||
| static const char kCharDiff('a' - 'A'); | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |||||
| fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| /* | |||||
| * Convert to all ascii characters to uppercase. | |||||
| */ | |||||
| String& toUpper() noexcept | |||||
| { | |||||
| static const char kCharDiff('a' - 'A'); | |||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| { | |||||
| if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |||||
| fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| /* | |||||
| * Direct access to the string buffer (read-only). | |||||
| */ | |||||
| const char* buffer() const noexcept | |||||
| { | |||||
| return fBuffer; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||||
| // Copyright (C) 2004-2008 René Nyffenegger | |||||
| static String asBase64(const void* const data, const std::size_t dataSize) | |||||
| { | |||||
| static const char* const kBase64Chars = | |||||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||||
| "abcdefghijklmnopqrstuvwxyz" | |||||
| "0123456789+/"; | |||||
| const std::size_t kTmpBufSize = d_nextPowerOf2(dataSize/3); | |||||
| const uchar* bytesToEncode((const uchar*)data); | |||||
| uint i=0, j=0; | |||||
| uint charArray3[3], charArray4[4]; | |||||
| char strBuf[kTmpBufSize+1]; | |||||
| strBuf[kTmpBufSize] = '\0'; | |||||
| std::size_t strBufIndex = 0; | |||||
| String ret; | |||||
| for (std::size_t s=0; s<dataSize; ++s) | |||||
| { | |||||
| charArray3[i++] = *(bytesToEncode++); | |||||
| if (i == 3) | |||||
| { | |||||
| charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |||||
| charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); | |||||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||||
| charArray4[3] = charArray3[2] & 0x3f; | |||||
| for (i=0; i<4; ++i) | |||||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | |||||
| if (strBufIndex >= kTmpBufSize-7) | |||||
| { | |||||
| strBuf[strBufIndex] = '\0'; | |||||
| strBufIndex = 0; | |||||
| ret += strBuf; | |||||
| } | |||||
| i = 0; | |||||
| } | |||||
| } | |||||
| if (i != 0) | |||||
| { | |||||
| for (j=i; j<3; ++j) | |||||
| charArray3[j] = '\0'; | |||||
| charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |||||
| charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); | |||||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||||
| charArray4[3] = charArray3[2] & 0x3f; | |||||
| for (j=0; j<4 && i<3 && j<i+1; ++j) | |||||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | |||||
| for (; i++ < 3;) | |||||
| strBuf[strBufIndex++] = '='; | |||||
| } | |||||
| if (strBufIndex != 0) | |||||
| { | |||||
| strBuf[strBufIndex] = '\0'; | |||||
| ret += strBuf; | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // public operators | |||||
| operator const char*() const noexcept | |||||
| { | |||||
| return fBuffer; | |||||
| } | |||||
| char operator[](const std::size_t pos) const noexcept | |||||
| { | |||||
| if (pos < fBufferLen) | |||||
| return fBuffer[pos]; | |||||
| d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | |||||
| static char fallback; | |||||
| fallback = '\0'; | |||||
| return fallback; | |||||
| } | |||||
| char& operator[](const std::size_t pos) noexcept | |||||
| { | |||||
| if (pos < fBufferLen) | |||||
| return fBuffer[pos]; | |||||
| d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | |||||
| static char fallback; | |||||
| fallback = '\0'; | |||||
| return fallback; | |||||
| } | |||||
| bool operator==(const char* const strBuf) const noexcept | |||||
| { | |||||
| return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); | |||||
| } | |||||
| bool operator==(const String& str) const noexcept | |||||
| { | |||||
| return operator==(str.fBuffer); | |||||
| } | |||||
| bool operator!=(const char* const strBuf) const noexcept | |||||
| { | |||||
| return !operator==(strBuf); | |||||
| } | |||||
| bool operator!=(const String& str) const noexcept | |||||
| { | |||||
| return !operator==(str.fBuffer); | |||||
| } | |||||
| String& operator=(const char* const strBuf) noexcept | |||||
| { | |||||
| _dup(strBuf); | |||||
| return *this; | |||||
| } | |||||
| String& operator=(const String& str) noexcept | |||||
| { | |||||
| _dup(str.fBuffer); | |||||
| return *this; | |||||
| } | |||||
| String& operator+=(const char* const strBuf) noexcept | |||||
| { | |||||
| if (strBuf == nullptr) | |||||
| return *this; | |||||
| const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| std::strcpy(newBuf, fBuffer); | |||||
| std::strcat(newBuf, strBuf); | |||||
| _dup(newBuf, newBufSize-1); | |||||
| return *this; | |||||
| } | |||||
| String& operator+=(const String& str) noexcept | |||||
| { | |||||
| return operator+=(str.fBuffer); | |||||
| } | |||||
| String operator+(const char* const strBuf) noexcept | |||||
| { | |||||
| const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| std::strcpy(newBuf, fBuffer); | |||||
| if (strBuf != nullptr) | |||||
| std::strcat(newBuf, strBuf); | |||||
| return String(newBuf); | |||||
| } | |||||
| String operator+(const String& str) noexcept | |||||
| { | |||||
| return operator+(str.fBuffer); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| char* fBuffer; // the actual string buffer | |||||
| std::size_t fBufferLen; // string length | |||||
| /* | |||||
| * Static null string. | |||||
| * Prevents allocation for new and/or empty strings. | |||||
| */ | |||||
| static char* _null() noexcept | |||||
| { | |||||
| static char sNull = '\0'; | |||||
| return &sNull; | |||||
| } | |||||
| /* | |||||
| * Helper function. | |||||
| * Called whenever the string needs to be allocated. | |||||
| * | |||||
| * Notes: | |||||
| * - Allocates string only if 'strBuf' is not null and new string contents are different | |||||
| * - If 'strBuf' is null, 'size' must be 0 | |||||
| */ | |||||
| void _dup(const char* const strBuf, const std::size_t size = 0) noexcept | |||||
| { | |||||
| if (strBuf != nullptr) | |||||
| { | |||||
| // don't recreate string if contents match | |||||
| if (std::strcmp(fBuffer, strBuf) == 0) | |||||
| return; | |||||
| if (fBuffer != _null()) | |||||
| std::free(fBuffer); | |||||
| fBufferLen = (size > 0) ? size : std::strlen(strBuf); | |||||
| fBuffer = (char*)std::malloc(fBufferLen+1); | |||||
| if (fBuffer == nullptr) | |||||
| { | |||||
| fBuffer = _null(); | |||||
| fBufferLen = 0; | |||||
| return; | |||||
| } | |||||
| std::strcpy(fBuffer, strBuf); | |||||
| fBuffer[fBufferLen] = '\0'; | |||||
| } | |||||
| else | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(size == 0); | |||||
| // don't recreate null string | |||||
| if (fBuffer == _null()) | |||||
| return; | |||||
| DISTRHO_SAFE_ASSERT(fBuffer != nullptr); | |||||
| std::free(fBuffer); | |||||
| fBuffer = _null(); | |||||
| fBufferLen = 0; | |||||
| } | |||||
| } | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| static inline | |||||
| String operator+(const String& strBefore, const char* const strBufAfter) noexcept | |||||
| { | |||||
| const char* const strBufBefore = strBefore.buffer(); | |||||
| const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; | |||||
| char newBuf[newBufSize]; | |||||
| std::strcpy(newBuf, strBufBefore); | |||||
| std::strcat(newBuf, strBufAfter); | |||||
| return String(newBuf); | |||||
| } | |||||
| static inline | |||||
| String operator+(const char* const strBufBefore, const String& strAfter) noexcept | |||||
| { | |||||
| const char* const strBufAfter = strAfter.buffer(); | |||||
| const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; | |||||
| char newBuf[newBufSize]; | |||||
| std::strcpy(newBuf, strBufBefore); | |||||
| std::strcat(newBuf, strBufAfter); | |||||
| return String(newBuf); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_STRING_HPP_INCLUDED | |||||
| @@ -0,0 +1,290 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_THREAD_HPP_INCLUDED | |||||
| #define DISTRHO_THREAD_HPP_INCLUDED | |||||
| #include "Mutex.hpp" | |||||
| #include "Sleep.hpp" | |||||
| #include "String.hpp" | |||||
| #ifdef DISTRHO_OS_LINUX | |||||
| # include <sys/prctl.h> | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // Thread class | |||||
| class Thread | |||||
| { | |||||
| protected: | |||||
| /* | |||||
| * Constructor. | |||||
| */ | |||||
| Thread(const char* const threadName = nullptr) noexcept | |||||
| : fLock(), | |||||
| fName(threadName), | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle({nullptr, 0}), | |||||
| #else | |||||
| fHandle(0), | |||||
| #endif | |||||
| fShouldExit(false) {} | |||||
| /* | |||||
| * Destructor. | |||||
| */ | |||||
| virtual ~Thread() /*noexcept*/ | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(! isThreadRunning()); | |||||
| stopThread(-1); | |||||
| } | |||||
| /* | |||||
| * Virtual function to be implemented by the subclass. | |||||
| */ | |||||
| virtual void run() = 0; | |||||
| // ------------------------------------------------------------------- | |||||
| public: | |||||
| /* | |||||
| * Check if the thread is running. | |||||
| */ | |||||
| bool isThreadRunning() const noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| return (fHandle.p != nullptr); | |||||
| #else | |||||
| return (fHandle != 0); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Check if the thread should exit. | |||||
| */ | |||||
| bool shouldThreadExit() const noexcept | |||||
| { | |||||
| return fShouldExit; | |||||
| } | |||||
| /* | |||||
| * Start the thread. | |||||
| */ | |||||
| bool startThread() noexcept | |||||
| { | |||||
| // check if already running | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); | |||||
| const MutexLocker cml(fLock); | |||||
| fShouldExit = false; | |||||
| pthread_t handle; | |||||
| if (pthread_create(&handle, nullptr, _entryPoint, this) == 0) | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); | |||||
| #else | |||||
| DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); | |||||
| #endif | |||||
| pthread_detach(handle); | |||||
| _copyFrom(handle); | |||||
| // wait for thread to start | |||||
| fLock.lock(); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /* | |||||
| * Stop the thread. | |||||
| * In the 'timeOutMilliseconds': | |||||
| * = 0 -> no wait | |||||
| * > 0 -> wait timeout value | |||||
| * < 0 -> wait forever | |||||
| */ | |||||
| bool stopThread(const int timeOutMilliseconds) noexcept | |||||
| { | |||||
| const MutexLocker cml(fLock); | |||||
| if (isThreadRunning()) | |||||
| { | |||||
| signalThreadShouldExit(); | |||||
| if (timeOutMilliseconds != 0) | |||||
| { | |||||
| // Wait for the thread to stop | |||||
| int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; | |||||
| for (; isThreadRunning();) | |||||
| { | |||||
| d_msleep(2); | |||||
| if (timeOutCheck < 0) | |||||
| continue; | |||||
| if (timeOutCheck > 0) | |||||
| timeOutCheck -= 1; | |||||
| else | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (isThreadRunning()) | |||||
| { | |||||
| // should never happen! | |||||
| d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__); | |||||
| // copy thread id so we can clear our one | |||||
| pthread_t threadId; | |||||
| _copyTo(threadId); | |||||
| _init(); | |||||
| try { | |||||
| pthread_cancel(threadId); | |||||
| } DISTRHO_SAFE_EXCEPTION("pthread_cancel"); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* | |||||
| * Tell the thread to stop as soon as possible. | |||||
| */ | |||||
| void signalThreadShouldExit() noexcept | |||||
| { | |||||
| fShouldExit = true; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| /* | |||||
| * Returns the name of the thread. | |||||
| * This is the name that gets set in the constructor. | |||||
| */ | |||||
| const String& getThreadName() const noexcept | |||||
| { | |||||
| return fName; | |||||
| } | |||||
| /* | |||||
| * Changes the name of the caller thread. | |||||
| */ | |||||
| static void setCurrentThreadName(const char* const name) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |||||
| #ifdef DISTRHO_OS_LINUX | |||||
| prctl(PR_SET_NAME, name, 0, 0, 0); | |||||
| #endif | |||||
| #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 | |||||
| pthread_setname_np(pthread_self(), name); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| Mutex fLock; // Thread lock | |||||
| const String fName; // Thread name | |||||
| volatile pthread_t fHandle; // Handle for this thread | |||||
| volatile bool fShouldExit; // true if thread should exit | |||||
| /* | |||||
| * Init pthread type. | |||||
| */ | |||||
| void _init() noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle.p = nullptr; | |||||
| fHandle.x = 0; | |||||
| #else | |||||
| fHandle = 0; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Copy our pthread type from another var. | |||||
| */ | |||||
| void _copyFrom(const pthread_t& handle) noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle.p = handle.p; | |||||
| fHandle.x = handle.x; | |||||
| #else | |||||
| fHandle = handle; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Copy our pthread type to another var. | |||||
| */ | |||||
| void _copyTo(volatile pthread_t& handle) const noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| handle.p = fHandle.p; | |||||
| handle.x = fHandle.x; | |||||
| #else | |||||
| handle = fHandle; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Thread entry point. | |||||
| */ | |||||
| void _runEntryPoint() noexcept | |||||
| { | |||||
| // report ready | |||||
| fLock.unlock(); | |||||
| setCurrentThreadName(fName); | |||||
| try { | |||||
| run(); | |||||
| } catch(...) {} | |||||
| // done | |||||
| _init(); | |||||
| } | |||||
| /* | |||||
| * Thread entry point. | |||||
| */ | |||||
| static void* _entryPoint(void* userData) noexcept | |||||
| { | |||||
| static_cast<Thread*>(userData)->_runEntryPoint(); | |||||
| return nullptr; | |||||
| } | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(Thread) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_THREAD_HPP_INCLUDED | |||||
| @@ -0,0 +1,134 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_DEFINES_H_INCLUDED | |||||
| #define DISTRHO_DEFINES_H_INCLUDED | |||||
| /* Compatibility with non-clang compilers */ | |||||
| #ifndef __has_feature | |||||
| # define __has_feature(x) 0 | |||||
| #endif | |||||
| #ifndef __has_extension | |||||
| # define __has_extension __has_feature | |||||
| #endif | |||||
| /* Check OS */ | |||||
| #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) | |||||
| # define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) | |||||
| # define DISTRHO_OS_WINDOWS 1 | |||||
| # define DISTRHO_DLL_EXTENSION "dll" | |||||
| #else | |||||
| # define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) | |||||
| # if defined(__APPLE__) | |||||
| # define DISTRHO_OS_MAC 1 | |||||
| # define DISTRHO_DLL_EXTENSION "dylib" | |||||
| # elif defined(__HAIKU__) | |||||
| # define DISTRHO_OS_HAIKU 1 | |||||
| # elif defined(__linux__) | |||||
| # define DISTRHO_OS_LINUX 1 | |||||
| # endif | |||||
| #endif | |||||
| #ifndef DISTRHO_DLL_EXTENSION | |||||
| # define DISTRHO_DLL_EXTENSION "so" | |||||
| #endif | |||||
| /* Check for C++11 support */ | |||||
| #if defined(HAVE_CPP11_SUPPORT) | |||||
| # define DISTRHO_PROPER_CPP11_SUPPORT | |||||
| #elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept) | |||||
| # define DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407 && ! defined(__clang__)) || (defined(__clang__) && ! __has_extension(cxx_override_control)) | |||||
| # define override // gcc4.7+ only | |||||
| # define final // gcc4.7+ only | |||||
| # endif | |||||
| #endif | |||||
| #ifndef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # define noexcept throw() | |||||
| # define override | |||||
| # define final | |||||
| # define nullptr NULL | |||||
| #endif | |||||
| /* Define DISTRHO_SAFE_ASSERT* */ | |||||
| #define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__); | |||||
| #define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; } | |||||
| #define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } | |||||
| #define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } | |||||
| /* Define DISTRHO_SAFE_EXCEPTION */ | |||||
| #define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } | |||||
| #define DISTRHO_SAFE_EXCEPTION_BREAK(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); break; } | |||||
| #define DISTRHO_SAFE_EXCEPTION_CONTINUE(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); continue; } | |||||
| #define DISTRHO_SAFE_EXCEPTION_RETURN(msg, ret) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); return ret; } | |||||
| /* Define DISTRHO_DECLARE_NON_COPY_CLASS */ | |||||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ | |||||
| private: \ | |||||
| ClassName(ClassName&) = delete; \ | |||||
| ClassName(const ClassName&) = delete; \ | |||||
| ClassName& operator=(ClassName&) = delete ; \ | |||||
| ClassName& operator=(const ClassName&) = delete; | |||||
| #else | |||||
| # define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ | |||||
| private: \ | |||||
| ClassName(ClassName&); \ | |||||
| ClassName(const ClassName&); \ | |||||
| ClassName& operator=(ClassName&); \ | |||||
| ClassName& operator=(const ClassName&); | |||||
| #endif | |||||
| /* Define DISTRHO_DECLARE_NON_COPY_STRUCT */ | |||||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \ | |||||
| StructName(StructName&) = delete; \ | |||||
| StructName(const StructName&) = delete; \ | |||||
| StructName& operator=(StructName&) = delete; \ | |||||
| StructName& operator=(const StructName&) = delete; | |||||
| #else | |||||
| # define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) | |||||
| #endif | |||||
| /* Define DISTRHO_PREVENT_HEAP_ALLOCATION */ | |||||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||||
| # define DISTRHO_PREVENT_HEAP_ALLOCATION \ | |||||
| private: \ | |||||
| static void* operator new(size_t) = delete; \ | |||||
| static void operator delete(void*) = delete; | |||||
| #else | |||||
| # define DISTRHO_PREVENT_HEAP_ALLOCATION \ | |||||
| private: \ | |||||
| static void* operator new(size_t); \ | |||||
| static void operator delete(void*); | |||||
| #endif | |||||
| /* Define namespace */ | |||||
| #ifndef DISTRHO_NAMESPACE | |||||
| # define DISTRHO_NAMESPACE DISTRHO | |||||
| #endif | |||||
| #define START_NAMESPACE_DISTRHO namespace DISTRHO_NAMESPACE { | |||||
| #define END_NAMESPACE_DISTRHO } | |||||
| #define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE; | |||||
| /* Useful typedefs */ | |||||
| typedef unsigned char uchar; | |||||
| typedef unsigned short int ushort; | |||||
| typedef unsigned int uint; | |||||
| typedef unsigned long int ulong; | |||||
| #endif // DISTRHO_DEFINES_H_INCLUDED | |||||
| @@ -0,0 +1,141 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "DistrhoPluginInternal.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Static data, see DistrhoPluginInternal.hpp */ | |||||
| uint32_t d_lastBufferSize = 0; | |||||
| double d_lastSampleRate = 0.0; | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Static fallback data, see DistrhoPluginInternal.hpp */ | |||||
| const String PluginExporter::sFallbackString; | |||||
| const AudioPort PluginExporter::sFallbackAudioPort; | |||||
| const ParameterRanges PluginExporter::sFallbackRanges; | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Plugin */ | |||||
| Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) | |||||
| : pData(new PrivateData()) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| #endif | |||||
| if (parameterCount > 0) | |||||
| { | |||||
| pData->parameterCount = parameterCount; | |||||
| pData->parameters = new Parameter[parameterCount]; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (programCount > 0) | |||||
| { | |||||
| pData->programCount = programCount; | |||||
| pData->programNames = new String[programCount]; | |||||
| } | |||||
| #else | |||||
| DISTRHO_SAFE_ASSERT(programCount == 0); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| if (stateCount > 0) | |||||
| { | |||||
| pData->stateCount = stateCount; | |||||
| pData->stateKeys = new String[stateCount]; | |||||
| pData->stateDefValues = new String[stateCount]; | |||||
| } | |||||
| #else | |||||
| DISTRHO_SAFE_ASSERT(stateCount == 0); | |||||
| #endif | |||||
| } | |||||
| Plugin::~Plugin() | |||||
| { | |||||
| delete pData; | |||||
| } | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Host state */ | |||||
| uint32_t Plugin::getBufferSize() const noexcept | |||||
| { | |||||
| return pData->bufferSize; | |||||
| } | |||||
| double Plugin::getSampleRate() const noexcept | |||||
| { | |||||
| return pData->sampleRate; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| const TimePosition& Plugin::getTimePosition() const noexcept | |||||
| { | |||||
| return pData->timePosition; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| void Plugin::setLatency(uint32_t frames) noexcept | |||||
| { | |||||
| pData->latency = frames; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| bool Plugin::writeMidiEvent(const MidiEvent& /*midiEvent*/) noexcept | |||||
| { | |||||
| // TODO | |||||
| return false; | |||||
| } | |||||
| #endif | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Init */ | |||||
| void Plugin::initAudioPort(bool input, uint32_t index, AudioPort& port) | |||||
| { | |||||
| if (port.hints & kAudioPortIsCV) | |||||
| { | |||||
| port.name = input ? "CV Input " : "CV Output "; | |||||
| port.name += String(index+1); | |||||
| port.symbol = input ? "cv_in_" : "cv_out_"; | |||||
| port.symbol += String(index+1); | |||||
| } | |||||
| else | |||||
| { | |||||
| port.name = input ? "Audio Input " : "Audio Output "; | |||||
| port.name += String(index+1); | |||||
| port.symbol = input ? "audio_in_" : "audio_out_"; | |||||
| port.symbol += String(index+1); | |||||
| } | |||||
| } | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Callbacks (optional) */ | |||||
| void Plugin::bufferSizeChanged(uint32_t) {} | |||||
| void Plugin::sampleRateChanged(double) {} | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,480 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU Lesser General Public | |||||
| * License as published by the Free Software Foundation. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU Lesser General Public License for more details. | |||||
| * | |||||
| * For a full copy of the license see the LGPL.txt file | |||||
| */ | |||||
| #include "DistrhoPluginInternal.hpp" | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| # include "DistrhoUIInternal.hpp" | |||||
| #endif | |||||
| #include "CarlaNative.hpp" | |||||
| // ----------------------------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| // ----------------------------------------------------------------------- | |||||
| // Carla UI | |||||
| #if ! DISTRHO_PLUGIN_WANT_STATE | |||||
| static const setStateFunc setStateCallback = nullptr; | |||||
| #endif | |||||
| #if ! DISTRHO_PLUGIN_IS_SYNTH | |||||
| static const sendNoteFunc sendNoteCallback = nullptr; | |||||
| #endif | |||||
| class UICarla | |||||
| { | |||||
| public: | |||||
| UICarla(const NativeHostDescriptor* const host, PluginExporter* const plugin) | |||||
| : fHost(host), | |||||
| fPlugin(plugin), | |||||
| fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer()) | |||||
| { | |||||
| fUI.setWindowTitle(host->uiName); | |||||
| if (host->uiParentId != 0) | |||||
| fUI.setWindowTransientWinId(host->uiParentId); | |||||
| } | |||||
| ~UICarla() | |||||
| { | |||||
| fUI.quit(); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| void carla_show(const bool yesNo) | |||||
| { | |||||
| fUI.setWindowVisible(yesNo); | |||||
| } | |||||
| bool carla_idle() | |||||
| { | |||||
| return fUI.idle(); | |||||
| } | |||||
| void carla_setParameterValue(const uint32_t index, const float value) | |||||
| { | |||||
| fUI.parameterChanged(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void carla_setMidiProgram(const uint32_t realProgram) | |||||
| { | |||||
| fUI.programLoaded(realProgram); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void carla_setCustomData(const char* const key, const char* const value) | |||||
| { | |||||
| fUI.stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| void carla_setUiTitle(const char* const uiTitle) | |||||
| { | |||||
| fUI.setWindowTitle(uiTitle); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| protected: | |||||
| void handleEditParameter(const uint32_t, const bool) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| void handleSetParameterValue(const uint32_t rindex, const float value) | |||||
| { | |||||
| fHost->ui_parameter_changed(fHost->handle, rindex, value); | |||||
| } | |||||
| void handleSetState(const char* const key, const char* const value) | |||||
| { | |||||
| fHost->ui_custom_data_changed(fHost->handle, key, value); | |||||
| } | |||||
| void handleSendNote(const uint8_t, const uint8_t, const uint8_t) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| void handleSetSize(const uint width, const uint height) | |||||
| { | |||||
| fUI.setWindowSize(width, height); | |||||
| } | |||||
| // --------------------------------------------- | |||||
| private: | |||||
| // Plugin stuff | |||||
| const NativeHostDescriptor* const fHost; | |||||
| PluginExporter* const fPlugin; | |||||
| // UI | |||||
| UIExporter fUI; | |||||
| // --------------------------------------------- | |||||
| // Callbacks | |||||
| #define handlePtr ((UICarla*)ptr) | |||||
| static void editParameterCallback(void* ptr, uint32_t index, bool started) | |||||
| { | |||||
| handlePtr->handleEditParameter(index, started); | |||||
| } | |||||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||||
| { | |||||
| handlePtr->handleSetParameterValue(rindex, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| handlePtr->handleSetState(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| handlePtr->handleSendNote(channel, note, velocity); | |||||
| } | |||||
| #endif | |||||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||||
| { | |||||
| handlePtr->handleSetSize(width, height); | |||||
| } | |||||
| #undef handlePtr | |||||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UICarla) | |||||
| }; | |||||
| #endif // DISTRHO_PLUGIN_HAS_UI | |||||
| // ----------------------------------------------------------------------- | |||||
| // Carla Plugin | |||||
| class PluginCarla : public NativePluginClass | |||||
| { | |||||
| public: | |||||
| PluginCarla(const NativeHostDescriptor* const host) | |||||
| : NativePluginClass(host) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| fUiPtr = nullptr; | |||||
| #endif | |||||
| } | |||||
| ~PluginCarla() override | |||||
| { | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| if (fUiPtr != nullptr) | |||||
| { | |||||
| delete fUiPtr; | |||||
| fUiPtr = nullptr; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| protected: | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin parameter calls | |||||
| uint32_t getParameterCount() const override | |||||
| { | |||||
| return fPlugin.getParameterCount(); | |||||
| } | |||||
| const NativeParameter* getParameterInfo(const uint32_t index) const override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), nullptr); | |||||
| static NativeParameter param; | |||||
| param.scalePointCount = 0; | |||||
| param.scalePoints = nullptr; | |||||
| { | |||||
| int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED; | |||||
| const uint32_t paramHints = fPlugin.getParameterHints(index); | |||||
| if (paramHints & kParameterIsAutomable) | |||||
| nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE; | |||||
| if (paramHints & kParameterIsBoolean) | |||||
| nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN; | |||||
| if (paramHints & kParameterIsInteger) | |||||
| nativeParamHints |= ::NATIVE_PARAMETER_IS_INTEGER; | |||||
| if (paramHints & kParameterIsLogarithmic) | |||||
| nativeParamHints |= ::NATIVE_PARAMETER_IS_LOGARITHMIC; | |||||
| if (paramHints & kParameterIsOutput) | |||||
| nativeParamHints |= ::NATIVE_PARAMETER_IS_OUTPUT; | |||||
| param.hints = static_cast<NativeParameterHints>(nativeParamHints); | |||||
| } | |||||
| param.name = fPlugin.getParameterName(index); | |||||
| param.unit = fPlugin.getParameterUnit(index); | |||||
| { | |||||
| const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | |||||
| param.ranges.def = ranges.def; | |||||
| param.ranges.min = ranges.min; | |||||
| param.ranges.max = ranges.max; | |||||
| } | |||||
| return ¶m; | |||||
| } | |||||
| float getParameterValue(const uint32_t index) const override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), 0.0f); | |||||
| return fPlugin.getParameterValue(index); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin midi-program calls | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| uint32_t getMidiProgramCount() const override | |||||
| { | |||||
| return fPlugin.getProgramCount(); | |||||
| } | |||||
| const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(index < getMidiProgramCount(), nullptr); | |||||
| static NativeMidiProgram midiProgram; | |||||
| midiProgram.bank = index / 128; | |||||
| midiProgram.program = index % 128; | |||||
| midiProgram.name = fPlugin.getProgramName(index); | |||||
| return &midiProgram; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin state calls | |||||
| void setParameterValue(const uint32_t index, const float value) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),); | |||||
| fPlugin.setParameterValue(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override | |||||
| { | |||||
| const uint32_t realProgram(bank * 128 + program); | |||||
| CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),); | |||||
| fPlugin.loadProgram(realProgram); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void setCustomData(const char* const key, const char* const value) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||||
| CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||||
| fPlugin.setState(key, value); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin process calls | |||||
| void activate() override | |||||
| { | |||||
| fPlugin.activate(); | |||||
| } | |||||
| void deactivate() override | |||||
| { | |||||
| fPlugin.deactivate(); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override | |||||
| { | |||||
| MidiEvent realMidiEvents[midiEventCount]; | |||||
| for (uint32_t i=0; i < midiEventCount; ++i) | |||||
| { | |||||
| const NativeMidiEvent& midiEvent(midiEvents[i]); | |||||
| MidiEvent& realMidiEvent(realMidiEvents[i]); | |||||
| realMidiEvent.frame = midiEvent.time; | |||||
| realMidiEvent.size = midiEvent.size; | |||||
| uint8_t j=0; | |||||
| for (; j<midiEvent.size; ++j) | |||||
| realMidiEvent.data[j] = midiEvent.data[j]; | |||||
| for (; j<midiEvent.size; ++j) | |||||
| realMidiEvent.data[j] = midiEvent.data[j]; | |||||
| realMidiEvent.dataExt = nullptr; | |||||
| } | |||||
| fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount); | |||||
| } | |||||
| #else | |||||
| void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override | |||||
| { | |||||
| fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin UI calls | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| void uiShow(const bool show) override | |||||
| { | |||||
| if (show) | |||||
| { | |||||
| createUiIfNeeded(); | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| fUiPtr->carla_show(show); | |||||
| } | |||||
| else if (fUiPtr != nullptr) | |||||
| { | |||||
| delete fUiPtr; | |||||
| fUiPtr = nullptr; | |||||
| } | |||||
| } | |||||
| void uiIdle() override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| if (! fUiPtr->carla_idle()) | |||||
| { | |||||
| uiClosed(); | |||||
| delete fUiPtr; | |||||
| fUiPtr = nullptr; | |||||
| } | |||||
| } | |||||
| void uiSetParameterValue(const uint32_t index, const float value) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),); | |||||
| fUiPtr->carla_setParameterValue(index, value); | |||||
| } | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void uiSetMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| const uint32_t realProgram(bank * 128 + program); | |||||
| CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),); | |||||
| fUiPtr->carla_setMidiProgram(realProgram); | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| void uiSetCustomData(const char* const key, const char* const value) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||||
| CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||||
| fUiPtr->carla_setCustomData(key, value); | |||||
| } | |||||
| # endif | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin dispatcher calls | |||||
| void bufferSizeChanged(const uint32_t bufferSize) override | |||||
| { | |||||
| fPlugin.setBufferSize(bufferSize, true); | |||||
| } | |||||
| void sampleRateChanged(const double sampleRate) override | |||||
| { | |||||
| fPlugin.setSampleRate(sampleRate, true); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| void uiNameChanged(const char* const uiName) override | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); | |||||
| fUiPtr->carla_setUiTitle(uiName); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| PluginExporter fPlugin; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| // UI | |||||
| UICarla* fUiPtr; | |||||
| void createUiIfNeeded() | |||||
| { | |||||
| if (fUiPtr == nullptr) | |||||
| { | |||||
| d_lastUiSampleRate = getSampleRate(); | |||||
| fUiPtr = new UICarla(getHostHandle(), &fPlugin); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginCarla) | |||||
| // ------------------------------------------------------------------- | |||||
| public: | |||||
| static NativePluginHandle _instantiate(const NativeHostDescriptor* host) | |||||
| { | |||||
| d_lastBufferSize = host->get_buffer_size(host->handle); | |||||
| d_lastSampleRate = host->get_sample_rate(host->handle); | |||||
| return new PluginCarla(host); | |||||
| } | |||||
| static void _cleanup(NativePluginHandle handle) | |||||
| { | |||||
| delete (PluginCarla*)handle; | |||||
| } | |||||
| }; | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,109 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_PLUGIN_CHECKS_H_INCLUDED | |||||
| #define DISTRHO_PLUGIN_CHECKS_H_INCLUDED | |||||
| #include "DistrhoPluginInfo.h" | |||||
| // ----------------------------------------------------------------------- | |||||
| // Check if all required macros are defined | |||||
| #ifndef DISTRHO_PLUGIN_NAME | |||||
| # error DISTRHO_PLUGIN_NAME undefined! | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_NUM_INPUTS | |||||
| # error DISTRHO_PLUGIN_NUM_INPUTS undefined! | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_NUM_OUTPUTS | |||||
| # error DISTRHO_PLUGIN_NUM_OUTPUTS undefined! | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_URI | |||||
| # error DISTRHO_PLUGIN_URI undefined! | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Define optional macros if not done yet | |||||
| #ifndef DISTRHO_PLUGIN_HAS_UI | |||||
| # define DISTRHO_PLUGIN_HAS_UI 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_IS_RT_SAFE | |||||
| # define DISTRHO_PLUGIN_IS_RT_SAFE 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_IS_SYNTH | |||||
| # define DISTRHO_PLUGIN_IS_SYNTH 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| # define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_LATENCY | |||||
| # define DISTRHO_PLUGIN_WANT_LATENCY 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| # define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| # define DISTRHO_PLUGIN_WANT_PROGRAMS 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_STATE | |||||
| # define DISTRHO_PLUGIN_WANT_STATE 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| # define DISTRHO_PLUGIN_WANT_TIMEPOS 0 | |||||
| #endif | |||||
| #ifndef DISTRHO_UI_USE_NANOVG | |||||
| # define DISTRHO_UI_USE_NANOVG 0 | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Define DISTRHO_UI_URI if needed | |||||
| #ifndef DISTRHO_UI_URI | |||||
| # define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Test if synth has audio outputs | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH && DISTRHO_PLUGIN_NUM_OUTPUTS == 0 | |||||
| # error Synths need audio output to work! | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| // Enable MIDI input if synth, test if midi-input disabled when synth | |||||
| #ifndef DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| # define DISTRHO_PLUGIN_WANT_MIDI_INPUT DISTRHO_PLUGIN_IS_SYNTH | |||||
| #elif DISTRHO_PLUGIN_IS_SYNTH && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| # error Synths need MIDI input to work! | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| #endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED | |||||
| @@ -0,0 +1,553 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_PLUGIN_INTERNAL_HPP_INCLUDED | |||||
| #define DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED | |||||
| #include "../DistrhoPlugin.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // Maxmimum values | |||||
| static const uint32_t kMaxMidiEvents = 512; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Static data, see DistrhoPlugin.cpp | |||||
| extern uint32_t d_lastBufferSize; | |||||
| extern double d_lastSampleRate; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Plugin private data | |||||
| struct Plugin::PrivateData { | |||||
| bool isProcessing; | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| AudioPort* audioPorts; | |||||
| #endif | |||||
| uint32_t parameterCount; | |||||
| Parameter* parameters; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| uint32_t programCount; | |||||
| String* programNames; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| uint32_t stateCount; | |||||
| String* stateKeys; | |||||
| String* stateDefValues; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| uint32_t latency; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| TimePosition timePosition; | |||||
| #endif | |||||
| uint32_t bufferSize; | |||||
| double sampleRate; | |||||
| PrivateData() noexcept | |||||
| : isProcessing(false), | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| audioPorts(nullptr), | |||||
| #endif | |||||
| parameterCount(0), | |||||
| parameters(nullptr), | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| programCount(0), | |||||
| programNames(nullptr), | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| stateCount(0), | |||||
| stateKeys(nullptr), | |||||
| stateDefValues(nullptr), | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| latency(0), | |||||
| #endif | |||||
| bufferSize(d_lastBufferSize), | |||||
| sampleRate(d_lastSampleRate) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(bufferSize != 0); | |||||
| DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | |||||
| } | |||||
| ~PrivateData() noexcept | |||||
| { | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| if (audioPorts != nullptr) | |||||
| { | |||||
| delete[] audioPorts; | |||||
| audioPorts = nullptr; | |||||
| } | |||||
| #endif | |||||
| if (parameters != nullptr) | |||||
| { | |||||
| delete[] parameters; | |||||
| parameters = nullptr; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (programNames != nullptr) | |||||
| { | |||||
| delete[] programNames; | |||||
| programNames = nullptr; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| if (stateKeys != nullptr) | |||||
| { | |||||
| delete[] stateKeys; | |||||
| stateKeys = nullptr; | |||||
| } | |||||
| if (stateDefValues != nullptr) | |||||
| { | |||||
| delete[] stateDefValues; | |||||
| stateDefValues = nullptr; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Plugin exporter class | |||||
| class PluginExporter | |||||
| { | |||||
| public: | |||||
| PluginExporter() | |||||
| : fPlugin(createPlugin()), | |||||
| fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | |||||
| fIsActive(false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| { | |||||
| uint32_t j=0; | |||||
| # if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++j) | |||||
| fPlugin->initAudioPort(true, i, fData->audioPorts[j]); | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++j) | |||||
| fPlugin->initAudioPort(false, i, fData->audioPorts[j]); | |||||
| # endif | |||||
| } | |||||
| #endif | |||||
| for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) | |||||
| fPlugin->initParameter(i, fData->parameters[i]); | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| for (uint32_t i=0, count=fData->programCount; i < count; ++i) | |||||
| fPlugin->initProgramName(i, fData->programNames[i]); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| for (uint32_t i=0, count=fData->stateCount; i < count; ++i) | |||||
| fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); | |||||
| #endif | |||||
| } | |||||
| ~PluginExporter() | |||||
| { | |||||
| delete fPlugin; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| const char* getName() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, ""); | |||||
| return fPlugin->getName(); | |||||
| } | |||||
| const char* getLabel() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, ""); | |||||
| return fPlugin->getLabel(); | |||||
| } | |||||
| const char* getMaker() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, ""); | |||||
| return fPlugin->getMaker(); | |||||
| } | |||||
| const char* getLicense() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, ""); | |||||
| return fPlugin->getLicense(); | |||||
| } | |||||
| uint32_t getVersion() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0); | |||||
| return fPlugin->getVersion(); | |||||
| } | |||||
| long getUniqueId() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0); | |||||
| return fPlugin->getUniqueId(); | |||||
| } | |||||
| void* getInstancePointer() const noexcept | |||||
| { | |||||
| return fPlugin; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| uint32_t getLatency() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->latency; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| const AudioPort& getAudioPort(const bool input, const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort); | |||||
| if (input) | |||||
| { | |||||
| # if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_INPUTS, sFallbackAudioPort); | |||||
| # endif | |||||
| } | |||||
| else | |||||
| { | |||||
| # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_OUTPUTS, sFallbackAudioPort); | |||||
| # endif | |||||
| } | |||||
| return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)]; | |||||
| } | |||||
| #endif | |||||
| uint32_t getParameterCount() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->parameterCount; | |||||
| } | |||||
| uint32_t getParameterHints(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0x0); | |||||
| return fData->parameters[index].hints; | |||||
| } | |||||
| bool isParameterOutput(const uint32_t index) const noexcept | |||||
| { | |||||
| return (getParameterHints(index) & kParameterIsOutput); | |||||
| } | |||||
| const String& getParameterName(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||||
| return fData->parameters[index].name; | |||||
| } | |||||
| const String& getParameterSymbol(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||||
| return fData->parameters[index].symbol; | |||||
| } | |||||
| const String& getParameterUnit(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||||
| return fData->parameters[index].unit; | |||||
| } | |||||
| const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackRanges); | |||||
| return fData->parameters[index].ranges; | |||||
| } | |||||
| float getParameterValue(const uint32_t index) const | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f); | |||||
| return fPlugin->getParameterValue(index); | |||||
| } | |||||
| void setParameterValue(const uint32_t index, const float value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount,); | |||||
| fPlugin->setParameterValue(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| uint32_t getProgramCount() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->programCount; | |||||
| } | |||||
| const String& getProgramName(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->programCount, sFallbackString); | |||||
| return fData->programNames[index]; | |||||
| } | |||||
| void loadProgram(const uint32_t index) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->programCount,); | |||||
| fPlugin->loadProgram(index); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| uint32_t getStateCount() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->stateCount; | |||||
| } | |||||
| const String& getStateKey(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||||
| return fData->stateKeys[index]; | |||||
| } | |||||
| const String& getStateDefaultValue(const uint32_t index) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||||
| return fData->stateDefValues[index]; | |||||
| } | |||||
| void setState(const char* const key, const char* const value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,); | |||||
| fPlugin->setState(key, value); | |||||
| } | |||||
| bool wantStateKey(const char* const key) const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, false); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', false); | |||||
| for (uint32_t i=0; i < fData->stateCount; ++i) | |||||
| { | |||||
| if (fData->stateKeys[i] == key) | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| void setTimePosition(const TimePosition& timePosition) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| std::memcpy(&fData->timePosition, &timePosition, sizeof(TimePosition)); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| bool isActive() const noexcept | |||||
| { | |||||
| return fIsActive; | |||||
| } | |||||
| void activate() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! fIsActive,); | |||||
| fIsActive = true; | |||||
| fPlugin->activate(); | |||||
| } | |||||
| void deactivate() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fIsActive,); | |||||
| fIsActive = false; | |||||
| fPlugin->deactivate(); | |||||
| } | |||||
| void deactivateIfNeeded() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| if (fIsActive) | |||||
| { | |||||
| fIsActive = false; | |||||
| fPlugin->deactivate(); | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void run(const float** const inputs, float** const outputs, const uint32_t frames, | |||||
| const MidiEvent* const midiEvents, const uint32_t midiEventCount) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| if (! fIsActive) | |||||
| { | |||||
| fIsActive = true; | |||||
| fPlugin->activate(); | |||||
| } | |||||
| fData->isProcessing = true; | |||||
| fPlugin->run(inputs, outputs, frames, midiEvents, midiEventCount); | |||||
| fData->isProcessing = false; | |||||
| } | |||||
| #else | |||||
| void run(const float** const inputs, float** const outputs, const uint32_t frames) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| if (! fIsActive) | |||||
| { | |||||
| fIsActive = true; | |||||
| fPlugin->activate(); | |||||
| } | |||||
| fData->isProcessing = true; | |||||
| fPlugin->run(inputs, outputs, frames); | |||||
| fData->isProcessing = false; | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| uint32_t getBufferSize() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->bufferSize; | |||||
| } | |||||
| double getSampleRate() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0.0); | |||||
| return fData->sampleRate; | |||||
| } | |||||
| void setBufferSize(const uint32_t bufferSize, const bool doCallback = false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT(bufferSize >= 2); | |||||
| if (fData->bufferSize == bufferSize) | |||||
| return; | |||||
| fData->bufferSize = bufferSize; | |||||
| if (doCallback) | |||||
| { | |||||
| if (fIsActive) fPlugin->deactivate(); | |||||
| fPlugin->bufferSizeChanged(bufferSize); | |||||
| if (fIsActive) fPlugin->activate(); | |||||
| } | |||||
| } | |||||
| void setSampleRate(const double sampleRate, const bool doCallback = false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT(sampleRate > 0.0); | |||||
| if (d_isEqual(fData->sampleRate, sampleRate)) | |||||
| return; | |||||
| fData->sampleRate = sampleRate; | |||||
| if (doCallback) | |||||
| { | |||||
| if (fIsActive) fPlugin->deactivate(); | |||||
| fPlugin->sampleRateChanged(sampleRate); | |||||
| if (fIsActive) fPlugin->activate(); | |||||
| } | |||||
| } | |||||
| private: | |||||
| // ------------------------------------------------------------------- | |||||
| // Plugin and DistrhoPlugin data | |||||
| Plugin* const fPlugin; | |||||
| Plugin::PrivateData* const fData; | |||||
| bool fIsActive; | |||||
| // ------------------------------------------------------------------- | |||||
| // Static fallback data, see DistrhoPlugin.cpp | |||||
| static const String sFallbackString; | |||||
| static const AudioPort sFallbackAudioPort; | |||||
| static const ParameterRanges sFallbackRanges; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) | |||||
| DISTRHO_PREVENT_HEAP_ALLOCATION | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED | |||||
| @@ -0,0 +1,523 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU Lesser General Public | |||||
| * License as published by the Free Software Foundation. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU Lesser General Public License for more details. | |||||
| * | |||||
| * For a full copy of the license see the LGPL.txt file | |||||
| */ | |||||
| #include "DistrhoPluginInternal.hpp" | |||||
| #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) | |||||
| # undef DISTRHO_PLUGIN_HAS_UI | |||||
| # define DISTRHO_PLUGIN_HAS_UI 0 | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| # include "DistrhoUIInternal.hpp" | |||||
| #else | |||||
| # include "../extra/Sleep.hpp" | |||||
| #endif | |||||
| #include "jack/jack.h" | |||||
| #include "jack/midiport.h" | |||||
| #include "jack/transport.h" | |||||
| #ifndef DISTRHO_OS_WINDOWS | |||||
| # include <signal.h> | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| START_NAMESPACE_DISTRHO | |||||
| #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE | |||||
| static const setStateFunc setStateCallback = nullptr; | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| static volatile bool gCloseSignalReceived = false; | |||||
| #ifdef DISTRHO_OS_WINDOWS | |||||
| static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept | |||||
| { | |||||
| if (dwCtrlType == CTRL_C_EVENT) | |||||
| { | |||||
| gCloseSignalReceived = true; | |||||
| return TRUE; | |||||
| } | |||||
| return FALSE; | |||||
| } | |||||
| static void initSignalHandler() | |||||
| { | |||||
| SetConsoleCtrlHandler(winSignalHandler, TRUE); | |||||
| } | |||||
| #else | |||||
| static void closeSignalHandler(int) noexcept | |||||
| { | |||||
| gCloseSignalReceived = true; | |||||
| } | |||||
| static void initSignalHandler() | |||||
| { | |||||
| struct sigaction sint; | |||||
| struct sigaction sterm; | |||||
| sint.sa_handler = closeSignalHandler; | |||||
| sint.sa_flags = SA_RESTART; | |||||
| sint.sa_restorer = nullptr; | |||||
| sigemptyset(&sint.sa_mask); | |||||
| sigaction(SIGINT, &sint, nullptr); | |||||
| sterm.sa_handler = closeSignalHandler; | |||||
| sterm.sa_flags = SA_RESTART; | |||||
| sterm.sa_restorer = nullptr; | |||||
| sigemptyset(&sterm.sa_mask); | |||||
| sigaction(SIGTERM, &sterm, nullptr); | |||||
| } | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| class PluginJack : public IdleCallback | |||||
| #else | |||||
| class PluginJack | |||||
| #endif | |||||
| { | |||||
| public: | |||||
| PluginJack(jack_client_t* const client) | |||||
| : fPlugin(), | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()), | |||||
| #endif | |||||
| fClient(client) | |||||
| { | |||||
| char strBuf[0xff+1]; | |||||
| strBuf[0xff] = '\0'; | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||||
| { | |||||
| std::snprintf(strBuf, 0xff, "in%i", i+1); | |||||
| fPortAudioIns[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| { | |||||
| std::snprintf(strBuf, 0xff, "out%i", i+1); | |||||
| fPortAudioOuts[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| fPortMidiIn = jack_port_register(fClient, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| if (fPlugin.getProgramCount() > 0) | |||||
| { | |||||
| fPlugin.loadProgram(0); | |||||
| # if DISTRHO_PLUGIN_HAS_UI | |||||
| fUI.programLoaded(0); | |||||
| # endif | |||||
| } | |||||
| #endif | |||||
| if (const uint32_t count = fPlugin.getParameterCount()) | |||||
| { | |||||
| fLastOutputValues = new float[count]; | |||||
| for (uint32_t i=0; i < count; ++i) | |||||
| { | |||||
| if (fPlugin.isParameterOutput(i)) | |||||
| { | |||||
| fLastOutputValues[i] = fPlugin.getParameterValue(i); | |||||
| } | |||||
| else | |||||
| { | |||||
| fLastOutputValues[i] = 0.0f; | |||||
| # if DISTRHO_PLUGIN_HAS_UI | |||||
| fUI.parameterChanged(i, fPlugin.getParameterValue(i)); | |||||
| # endif | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| fLastOutputValues = nullptr; | |||||
| } | |||||
| jack_set_buffer_size_callback(fClient, jackBufferSizeCallback, this); | |||||
| jack_set_sample_rate_callback(fClient, jackSampleRateCallback, this); | |||||
| jack_set_process_callback(fClient, jackProcessCallback, this); | |||||
| jack_on_shutdown(fClient, jackShutdownCallback, this); | |||||
| fPlugin.activate(); | |||||
| jack_activate(fClient); | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| if (const char* const name = jack_get_client_name(fClient)) | |||||
| fUI.setWindowTitle(name); | |||||
| else | |||||
| fUI.setWindowTitle(fPlugin.getName()); | |||||
| fUI.exec(this); | |||||
| #else | |||||
| while (! gCloseSignalReceived) | |||||
| d_sleep(1); | |||||
| #endif | |||||
| } | |||||
| ~PluginJack() | |||||
| { | |||||
| if (fClient == nullptr) | |||||
| return; | |||||
| jack_deactivate(fClient); | |||||
| fPlugin.deactivate(); | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| jack_port_unregister(fClient, fPortMidiIn); | |||||
| fPortMidiIn = nullptr; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||||
| { | |||||
| jack_port_unregister(fClient, fPortAudioIns[i]); | |||||
| fPortAudioIns[i] = nullptr; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| { | |||||
| jack_port_unregister(fClient, fPortAudioOuts[i]); | |||||
| fPortAudioOuts[i] = nullptr; | |||||
| } | |||||
| #endif | |||||
| jack_client_close(fClient); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| protected: | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| void idleCallback() override | |||||
| { | |||||
| if (gCloseSignalReceived) | |||||
| return fUI.quit(); | |||||
| float value; | |||||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||||
| { | |||||
| if (! fPlugin.isParameterOutput(i)) | |||||
| continue; | |||||
| value = fPlugin.getParameterValue(i); | |||||
| if (fLastOutputValues[i] == value) | |||||
| continue; | |||||
| fLastOutputValues[i] = value; | |||||
| fUI.parameterChanged(i, value); | |||||
| } | |||||
| fUI.exec_idle(); | |||||
| } | |||||
| #endif | |||||
| void jackBufferSize(const jack_nframes_t nframes) | |||||
| { | |||||
| fPlugin.setBufferSize(nframes, true); | |||||
| } | |||||
| void jackSampleRate(const jack_nframes_t nframes) | |||||
| { | |||||
| fPlugin.setSampleRate(nframes, true); | |||||
| } | |||||
| void jackProcess(const jack_nframes_t nframes) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||||
| audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes); | |||||
| #else | |||||
| static const float** audioIns = nullptr; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes); | |||||
| #else | |||||
| static float** audioOuts = nullptr; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| jack_position_t pos; | |||||
| fTimePosition.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling); | |||||
| if (pos.unique_1 == pos.unique_2) | |||||
| { | |||||
| fTimePosition.frame = pos.frame; | |||||
| if (pos.valid & JackTransportBBT) | |||||
| { | |||||
| fTimePosition.bbt.valid = true; | |||||
| fTimePosition.bbt.bar = pos.bar; | |||||
| fTimePosition.bbt.beat = pos.beat; | |||||
| fTimePosition.bbt.tick = pos.tick; | |||||
| fTimePosition.bbt.barStartTick = pos.bar_start_tick; | |||||
| fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; | |||||
| fTimePosition.bbt.beatType = pos.beat_type; | |||||
| fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat; | |||||
| fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute; | |||||
| } | |||||
| else | |||||
| fTimePosition.bbt.valid = false; | |||||
| } | |||||
| else | |||||
| { | |||||
| fTimePosition.bbt.valid = false; | |||||
| fTimePosition.frame = 0; | |||||
| } | |||||
| fPlugin.setTimePosition(fTimePosition); | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void* const midiBuf = jack_port_get_buffer(fPortMidiIn, nframes); | |||||
| if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) | |||||
| { | |||||
| uint32_t midiEventCount = 0; | |||||
| MidiEvent midiEvents[eventCount]; | |||||
| jack_midi_event_t jevent; | |||||
| for (uint32_t i=0; i < eventCount; ++i) | |||||
| { | |||||
| if (jack_midi_event_get(&jevent, midiBuf, i) != 0) | |||||
| break; | |||||
| MidiEvent& midiEvent(midiEvents[midiEventCount++]); | |||||
| midiEvent.frame = jevent.time; | |||||
| midiEvent.size = jevent.size; | |||||
| if (midiEvent.size > MidiEvent::kDataSize) | |||||
| midiEvent.dataExt = jevent.buffer; | |||||
| else | |||||
| std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); | |||||
| } | |||||
| fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); | |||||
| } | |||||
| else | |||||
| { | |||||
| fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0); | |||||
| } | |||||
| #else | |||||
| fPlugin.run(audioIns, audioOuts, nframes); | |||||
| #endif | |||||
| } | |||||
| void jackShutdown() | |||||
| { | |||||
| d_stderr("jack has shutdown, quitting now..."); | |||||
| fClient = nullptr; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| fUI.quit(); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void setParameterValue(const uint32_t index, const float value) | |||||
| { | |||||
| fPlugin.setParameterValue(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void setState(const char* const key, const char* const value) | |||||
| { | |||||
| fPlugin.setState(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| void setSize(const uint width, const uint height) | |||||
| { | |||||
| fUI.setWindowSize(width, height); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| PluginExporter fPlugin; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| UIExporter fUI; | |||||
| #endif | |||||
| jack_client_t* fClient; | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| jack_port_t* fPortMidiIn; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| TimePosition fTimePosition; | |||||
| #endif | |||||
| // Temporary data | |||||
| float* fLastOutputValues; | |||||
| // ------------------------------------------------------------------- | |||||
| // Callbacks | |||||
| #define uiPtr ((PluginJack*)ptr) | |||||
| static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) | |||||
| { | |||||
| uiPtr->jackBufferSize(nframes); | |||||
| return 0; | |||||
| } | |||||
| static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) | |||||
| { | |||||
| uiPtr->jackSampleRate(nframes); | |||||
| return 0; | |||||
| } | |||||
| static int jackProcessCallback(jack_nframes_t nframes, void* ptr) | |||||
| { | |||||
| uiPtr->jackProcess(nframes); | |||||
| return 0; | |||||
| } | |||||
| static void jackShutdownCallback(void* ptr) | |||||
| { | |||||
| uiPtr->jackShutdown(); | |||||
| } | |||||
| static void setParameterValueCallback(void* ptr, uint32_t index, float value) | |||||
| { | |||||
| uiPtr->setParameterValue(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| uiPtr->setState(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||||
| { | |||||
| uiPtr->setSize(width, height); | |||||
| } | |||||
| #endif | |||||
| #undef uiPtr | |||||
| }; | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| int main() | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO; | |||||
| jack_status_t status = jack_status_t(0x0); | |||||
| jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); | |||||
| if (client == nullptr) | |||||
| { | |||||
| String errorString; | |||||
| if (status & JackFailure) | |||||
| errorString += "Overall operation failed;\n"; | |||||
| if (status & JackInvalidOption) | |||||
| errorString += "The operation contained an invalid or unsupported option;\n"; | |||||
| if (status & JackNameNotUnique) | |||||
| errorString += "The desired client name was not unique;\n"; | |||||
| if (status & JackServerStarted) | |||||
| errorString += "The JACK server was started as a result of this operation;\n"; | |||||
| if (status & JackServerFailed) | |||||
| errorString += "Unable to connect to the JACK server;\n"; | |||||
| if (status & JackServerError) | |||||
| errorString += "Communication error with the JACK server;\n"; | |||||
| if (status & JackNoSuchClient) | |||||
| errorString += "Requested client does not exist;\n"; | |||||
| if (status & JackLoadFailure) | |||||
| errorString += "Unable to load internal client;\n"; | |||||
| if (status & JackInitFailure) | |||||
| errorString += "Unable to initialize client;\n"; | |||||
| if (status & JackShmFailure) | |||||
| errorString += "Unable to access shared memory;\n"; | |||||
| if (status & JackVersionError) | |||||
| errorString += "Client's protocol version does not match;\n"; | |||||
| if (status & JackBackendError) | |||||
| errorString += "Backend Error;\n"; | |||||
| if (status & JackClientZombie) | |||||
| errorString += "Client is being shutdown against its will;\n"; | |||||
| if (errorString.isNotEmpty()) | |||||
| { | |||||
| errorString[errorString.length()-2] = '.'; | |||||
| d_stderr("Failed to create jack client, reason was:\n%s", errorString.buffer()); | |||||
| } | |||||
| else | |||||
| d_stderr("Failed to create jack client, cannot continue!"); | |||||
| return 1; | |||||
| } | |||||
| USE_NAMESPACE_DISTRHO; | |||||
| initSignalHandler(); | |||||
| d_lastBufferSize = jack_get_buffer_size(client); | |||||
| d_lastSampleRate = jack_get_sample_rate(client); | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| d_lastUiSampleRate = d_lastSampleRate; | |||||
| #endif | |||||
| const PluginJack p(client); | |||||
| return 0; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,711 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "DistrhoPluginInternal.hpp" | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| # error Cannot use MIDI Output with LADSPA or DSSI | |||||
| #endif | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # include "dssi/dssi.h" | |||||
| #else | |||||
| # include "ladspa/ladspa.h" | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| # error Cannot use MIDI with LADSPA | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| # warning LADSPA cannot handle states | |||||
| # endif | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| # warning LADSPA/DSSI does not support TimePos | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| class PluginLadspaDssi | |||||
| { | |||||
| public: | |||||
| PluginLadspaDssi() | |||||
| : fPortControls(nullptr), | |||||
| fLastControlValues(nullptr) | |||||
| { | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||||
| fPortAudioIns[i] = nullptr; | |||||
| #else | |||||
| fPortAudioIns = nullptr; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| fPortAudioOuts[i] = nullptr; | |||||
| #else | |||||
| fPortAudioOuts = nullptr; | |||||
| #endif | |||||
| if (const uint32_t count = fPlugin.getParameterCount()) | |||||
| { | |||||
| fPortControls = new LADSPA_Data*[count]; | |||||
| fLastControlValues = new LADSPA_Data[count]; | |||||
| for (uint32_t i=0; i < count; ++i) | |||||
| { | |||||
| fPortControls[i] = nullptr; | |||||
| fLastControlValues[i] = fPlugin.getParameterValue(i); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| fPortControls = nullptr; | |||||
| fLastControlValues = nullptr; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| fPortLatency = nullptr; | |||||
| #endif | |||||
| } | |||||
| ~PluginLadspaDssi() noexcept | |||||
| { | |||||
| if (fPortControls != nullptr) | |||||
| { | |||||
| delete[] fPortControls; | |||||
| fPortControls = nullptr; | |||||
| } | |||||
| if (fLastControlValues != nullptr) | |||||
| { | |||||
| delete[] fLastControlValues; | |||||
| fLastControlValues = nullptr; | |||||
| } | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void ladspa_activate() | |||||
| { | |||||
| fPlugin.activate(); | |||||
| } | |||||
| void ladspa_deactivate() | |||||
| { | |||||
| fPlugin.deactivate(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void ladspa_connect_port(const ulong port, LADSPA_Data* const dataLocation) noexcept | |||||
| { | |||||
| ulong index = 0; | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| fPortAudioIns[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| fPortAudioOuts[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| if (port == index++) | |||||
| { | |||||
| fPortLatency = dataLocation; | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| for (ulong i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||||
| { | |||||
| if (port == index++) | |||||
| { | |||||
| fPortControls[i] = dataLocation; | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| void ladspa_run(const ulong sampleCount) | |||||
| { | |||||
| dssi_run_synth(sampleCount, nullptr, 0); | |||||
| } | |||||
| void dssi_run_synth(const ulong sampleCount, snd_seq_event_t* const events, const ulong eventCount) | |||||
| #else | |||||
| void ladspa_run(const ulong sampleCount) | |||||
| #endif | |||||
| { | |||||
| // pre-roll | |||||
| if (sampleCount == 0) | |||||
| return updateParameterOutputs(); | |||||
| // Check for updated parameters | |||||
| float curValue; | |||||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||||
| { | |||||
| if (fPortControls[i] == nullptr) | |||||
| continue; | |||||
| curValue = *fPortControls[i]; | |||||
| if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) | |||||
| { | |||||
| fLastControlValues[i] = curValue; | |||||
| fPlugin.setParameterValue(i, curValue); | |||||
| } | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| // Get MIDI Events | |||||
| uint32_t midiEventCount = 0; | |||||
| MidiEvent midiEvents[eventCount]; | |||||
| for (uint32_t i=0, j; i < eventCount; ++i) | |||||
| { | |||||
| const snd_seq_event_t& seqEvent(events[i]); | |||||
| // FIXME | |||||
| if (seqEvent.data.note.channel > 0xF || seqEvent.data.control.channel > 0xF) | |||||
| continue; | |||||
| switch (seqEvent.type) | |||||
| { | |||||
| case SND_SEQ_EVENT_NOTEOFF: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 3; | |||||
| midiEvents[j].data[0] = 0x80 + seqEvent.data.note.channel; | |||||
| midiEvents[j].data[1] = seqEvent.data.note.note; | |||||
| midiEvents[j].data[2] = 0; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| case SND_SEQ_EVENT_NOTEON: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 3; | |||||
| midiEvents[j].data[0] = 0x90 + seqEvent.data.note.channel; | |||||
| midiEvents[j].data[1] = seqEvent.data.note.note; | |||||
| midiEvents[j].data[2] = seqEvent.data.note.velocity; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| case SND_SEQ_EVENT_KEYPRESS: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 3; | |||||
| midiEvents[j].data[0] = 0xA0 + seqEvent.data.note.channel; | |||||
| midiEvents[j].data[1] = seqEvent.data.note.note; | |||||
| midiEvents[j].data[2] = seqEvent.data.note.velocity; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| case SND_SEQ_EVENT_CONTROLLER: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 3; | |||||
| midiEvents[j].data[0] = 0xB0 + seqEvent.data.control.channel; | |||||
| midiEvents[j].data[1] = seqEvent.data.control.param; | |||||
| midiEvents[j].data[2] = seqEvent.data.control.value; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| case SND_SEQ_EVENT_CHANPRESS: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 2; | |||||
| midiEvents[j].data[0] = 0xD0 + seqEvent.data.control.channel; | |||||
| midiEvents[j].data[1] = seqEvent.data.control.value; | |||||
| midiEvents[j].data[2] = 0; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| #if 0 // TODO | |||||
| case SND_SEQ_EVENT_PITCHBEND: | |||||
| j = midiEventCount++; | |||||
| midiEvents[j].frame = seqEvent.time.tick; | |||||
| midiEvents[j].size = 3; | |||||
| midiEvents[j].data[0] = 0xE0 + seqEvent.data.control.channel; | |||||
| midiEvents[j].data[1] = 0; | |||||
| midiEvents[j].data[2] = 0; | |||||
| midiEvents[j].data[3] = 0; | |||||
| break; | |||||
| #endif | |||||
| } | |||||
| } | |||||
| fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, midiEvents, midiEventCount); | |||||
| #else | |||||
| fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |||||
| #endif | |||||
| updateParameterOutputs(); | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| return; // unused | |||||
| (void)events; (void)eventCount; | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| char* dssi_configure(const char* const key, const char* const value) | |||||
| { | |||||
| if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) | |||||
| return nullptr; | |||||
| if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) | |||||
| return nullptr; | |||||
| fPlugin.setState(key, value); | |||||
| return nullptr; | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| const DSSI_Program_Descriptor* dssi_get_program(const ulong index) | |||||
| { | |||||
| if (index >= fPlugin.getProgramCount()) | |||||
| return nullptr; | |||||
| static DSSI_Program_Descriptor desc; | |||||
| desc.Bank = index / 128; | |||||
| desc.Program = index % 128; | |||||
| desc.Name = fPlugin.getProgramName(index); | |||||
| return &desc; | |||||
| } | |||||
| void dssi_select_program(const ulong bank, const ulong program) | |||||
| { | |||||
| const ulong realProgram(bank * 128 + program); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(realProgram < fPlugin.getProgramCount(),); | |||||
| fPlugin.loadProgram(realProgram); | |||||
| // Update control inputs | |||||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||||
| { | |||||
| if (fPlugin.isParameterOutput(i)) | |||||
| continue; | |||||
| fLastControlValues[i] = fPlugin.getParameterValue(i); | |||||
| if (fPortControls[i] != nullptr) | |||||
| *fPortControls[i] = fLastControlValues[i]; | |||||
| } | |||||
| } | |||||
| # endif | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| PluginExporter fPlugin; | |||||
| // LADSPA ports | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| const LADSPA_Data* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||||
| #else | |||||
| const LADSPA_Data** fPortAudioIns; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| LADSPA_Data* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||||
| #else | |||||
| LADSPA_Data** fPortAudioOuts; | |||||
| #endif | |||||
| LADSPA_Data** fPortControls; | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| LADSPA_Data* fPortLatency; | |||||
| #endif | |||||
| // Temporary data | |||||
| LADSPA_Data* fLastControlValues; | |||||
| // ------------------------------------------------------------------- | |||||
| void updateParameterOutputs() | |||||
| { | |||||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||||
| { | |||||
| if (! fPlugin.isParameterOutput(i)) | |||||
| continue; | |||||
| fLastControlValues[i] = fPlugin.getParameterValue(i); | |||||
| if (fPortControls[i] != nullptr) | |||||
| *fPortControls[i] = fLastControlValues[i]; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| if (fPortLatency != nullptr) | |||||
| *fPortLatency = fPlugin.getLatency(); | |||||
| #endif | |||||
| } | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, ulong sampleRate) | |||||
| { | |||||
| if (d_lastBufferSize == 0) | |||||
| d_lastBufferSize = 2048; | |||||
| d_lastSampleRate = sampleRate; | |||||
| return new PluginLadspaDssi(); | |||||
| } | |||||
| #define instancePtr ((PluginLadspaDssi*)instance) | |||||
| static void ladspa_connect_port(LADSPA_Handle instance, ulong port, LADSPA_Data* dataLocation) | |||||
| { | |||||
| instancePtr->ladspa_connect_port(port, dataLocation); | |||||
| } | |||||
| static void ladspa_activate(LADSPA_Handle instance) | |||||
| { | |||||
| instancePtr->ladspa_activate(); | |||||
| } | |||||
| static void ladspa_run(LADSPA_Handle instance, ulong sampleCount) | |||||
| { | |||||
| instancePtr->ladspa_run(sampleCount); | |||||
| } | |||||
| static void ladspa_deactivate(LADSPA_Handle instance) | |||||
| { | |||||
| instancePtr->ladspa_deactivate(); | |||||
| } | |||||
| static void ladspa_cleanup(LADSPA_Handle instance) | |||||
| { | |||||
| delete instancePtr; | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) | |||||
| { | |||||
| return instancePtr->dssi_configure(key, value); | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, ulong index) | |||||
| { | |||||
| return instancePtr->dssi_get_program(index); | |||||
| } | |||||
| static void dssi_select_program(LADSPA_Handle instance, ulong bank, ulong program) | |||||
| { | |||||
| instancePtr->dssi_select_program(bank, program); | |||||
| } | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| static void dssi_run_synth(LADSPA_Handle instance, ulong sampleCount, snd_seq_event_t* events, ulong eventCount) | |||||
| { | |||||
| instancePtr->dssi_run_synth(sampleCount, events, eventCount); | |||||
| } | |||||
| # endif | |||||
| #endif | |||||
| #undef instancePtr | |||||
| // ----------------------------------------------------------------------- | |||||
| static LADSPA_Descriptor sLadspaDescriptor = { | |||||
| /* UniqueID */ 0, | |||||
| /* Label */ nullptr, | |||||
| #if DISTRHO_PLUGIN_IS_RT_SAFE | |||||
| /* Properties */ LADSPA_PROPERTY_HARD_RT_CAPABLE, | |||||
| #else | |||||
| /* Properties */ 0x0, | |||||
| #endif | |||||
| /* Name */ nullptr, | |||||
| /* Maker */ nullptr, | |||||
| /* Copyright */ nullptr, | |||||
| /* PortCount */ 0, | |||||
| /* PortDescriptors */ nullptr, | |||||
| /* PortNames */ nullptr, | |||||
| /* PortRangeHints */ nullptr, | |||||
| /* ImplementationData */ nullptr, | |||||
| ladspa_instantiate, | |||||
| ladspa_connect_port, | |||||
| ladspa_activate, | |||||
| ladspa_run, | |||||
| /* run_adding */ nullptr, | |||||
| /* set_run_adding_gain */ nullptr, | |||||
| ladspa_deactivate, | |||||
| ladspa_cleanup | |||||
| }; | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| static DSSI_Descriptor sDssiDescriptor = { | |||||
| 1, | |||||
| &sLadspaDescriptor, | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| dssi_configure, | |||||
| # else | |||||
| /* configure */ nullptr, | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| dssi_get_program, | |||||
| dssi_select_program, | |||||
| # else | |||||
| /* get_program */ nullptr, | |||||
| /* select_program */ nullptr, | |||||
| # endif | |||||
| /* get_midi_controller_for_port */ nullptr, | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| dssi_run_synth, | |||||
| # else | |||||
| /* run_synth */ nullptr, | |||||
| # endif | |||||
| /* run_synth_adding */ nullptr, | |||||
| /* run_multiple_synths */ nullptr, | |||||
| /* run_multiple_synths_adding */ nullptr, | |||||
| nullptr, nullptr | |||||
| }; | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| class DescriptorInitializer | |||||
| { | |||||
| public: | |||||
| DescriptorInitializer() | |||||
| { | |||||
| // Create dummy plugin to get data from | |||||
| d_lastBufferSize = 512; | |||||
| d_lastSampleRate = 44100.0; | |||||
| PluginExporter plugin; | |||||
| d_lastBufferSize = 0; | |||||
| d_lastSampleRate = 0.0; | |||||
| // Get port count, init | |||||
| ulong port = 0; | |||||
| ulong portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.getParameterCount(); | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| portCount += 1; | |||||
| #endif | |||||
| const char** const portNames = new const char*[portCount]; | |||||
| LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor[portCount]; | |||||
| LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; | |||||
| // Set ports | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port) | |||||
| { | |||||
| const AudioPort& aport(plugin.getAudioPort(true, i)); | |||||
| portNames[port] = strdup(aport.name); | |||||
| portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; | |||||
| portRangeHints[port].HintDescriptor = 0x0; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 1.0f; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port) | |||||
| { | |||||
| const AudioPort& aport(plugin.getAudioPort(false, i)); | |||||
| portNames[port] = strdup(aport.name); | |||||
| portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; | |||||
| portRangeHints[port].HintDescriptor = 0x0; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 1.0f; | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| // Set latency port | |||||
| portNames[port] = strdup("_latency"); | |||||
| portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; | |||||
| portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE|LADSPA_HINT_INTEGER; | |||||
| portRangeHints[port].LowerBound = 0.0f; | |||||
| portRangeHints[port].UpperBound = 1.0f; | |||||
| ++port; | |||||
| #endif | |||||
| for (ulong i=0, count=plugin.getParameterCount(); i < count; ++i, ++port) | |||||
| { | |||||
| portNames[port] = strdup((const char*)plugin.getParameterName(i)); | |||||
| portDescriptors[port] = LADSPA_PORT_CONTROL; | |||||
| if (plugin.isParameterOutput(i)) | |||||
| portDescriptors[port] |= LADSPA_PORT_OUTPUT; | |||||
| else | |||||
| portDescriptors[port] |= LADSPA_PORT_INPUT; | |||||
| { | |||||
| const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||||
| const float defValue(ranges.def); | |||||
| portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; | |||||
| portRangeHints[port].LowerBound = ranges.min; | |||||
| portRangeHints[port].UpperBound = ranges.max; | |||||
| if (defValue == 0.0f) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; | |||||
| else if (defValue == 1.0f) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; | |||||
| else if (defValue == 100.0f) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; | |||||
| else if (defValue == 440.0f) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; | |||||
| else if (ranges.min == defValue) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; | |||||
| else if (ranges.max == defValue) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; | |||||
| else | |||||
| { | |||||
| const float middleValue = ranges.min/2.0f + ranges.max/2.0f; | |||||
| const float middleLow = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | |||||
| const float middleHigh = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | |||||
| if (defValue < middleLow) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; | |||||
| else if (defValue > middleHigh) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; | |||||
| else | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE; | |||||
| } | |||||
| } | |||||
| { | |||||
| const uint32_t hints(plugin.getParameterHints(i)); | |||||
| if (hints & kParameterIsBoolean) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; | |||||
| if (hints & kParameterIsInteger) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER; | |||||
| if (hints & kParameterIsLogarithmic) | |||||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC; | |||||
| } | |||||
| } | |||||
| // Set data | |||||
| sLadspaDescriptor.UniqueID = plugin.getUniqueId(); | |||||
| sLadspaDescriptor.Label = strdup(plugin.getLabel()); | |||||
| sLadspaDescriptor.Name = strdup(plugin.getName()); | |||||
| sLadspaDescriptor.Maker = strdup(plugin.getMaker()); | |||||
| sLadspaDescriptor.Copyright = strdup(plugin.getLicense()); | |||||
| sLadspaDescriptor.PortCount = portCount; | |||||
| sLadspaDescriptor.PortNames = portNames; | |||||
| sLadspaDescriptor.PortDescriptors = portDescriptors; | |||||
| sLadspaDescriptor.PortRangeHints = portRangeHints; | |||||
| } | |||||
| ~DescriptorInitializer() | |||||
| { | |||||
| if (sLadspaDescriptor.Label != nullptr) | |||||
| { | |||||
| std::free((void*)sLadspaDescriptor.Label); | |||||
| sLadspaDescriptor.Label = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.Name != nullptr) | |||||
| { | |||||
| std::free((void*)sLadspaDescriptor.Name); | |||||
| sLadspaDescriptor.Name = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.Maker != nullptr) | |||||
| { | |||||
| std::free((void*)sLadspaDescriptor.Maker); | |||||
| sLadspaDescriptor.Maker = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.Copyright != nullptr) | |||||
| { | |||||
| std::free((void*)sLadspaDescriptor.Copyright); | |||||
| sLadspaDescriptor.Copyright = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.PortDescriptors != nullptr) | |||||
| { | |||||
| delete[] sLadspaDescriptor.PortDescriptors; | |||||
| sLadspaDescriptor.PortDescriptors = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.PortRangeHints != nullptr) | |||||
| { | |||||
| delete[] sLadspaDescriptor.PortRangeHints; | |||||
| sLadspaDescriptor.PortRangeHints = nullptr; | |||||
| } | |||||
| if (sLadspaDescriptor.PortNames != nullptr) | |||||
| { | |||||
| for (ulong i=0; i < sLadspaDescriptor.PortCount; ++i) | |||||
| { | |||||
| if (sLadspaDescriptor.PortNames[i] != nullptr) | |||||
| std::free((void*)sLadspaDescriptor.PortNames[i]); | |||||
| } | |||||
| delete[] sLadspaDescriptor.PortNames; | |||||
| sLadspaDescriptor.PortNames = nullptr; | |||||
| } | |||||
| } | |||||
| }; | |||||
| static DescriptorInitializer sDescInit; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const LADSPA_Descriptor* ladspa_descriptor(ulong index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &sLadspaDescriptor : nullptr; | |||||
| } | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const DSSI_Descriptor* dssi_descriptor(ulong index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &sDssiDescriptor : nullptr; | |||||
| } | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,431 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "DistrhoPluginInternal.hpp" | |||||
| #include "lv2/atom.h" | |||||
| #include "lv2/buf-size.h" | |||||
| #include "lv2/data-access.h" | |||||
| #include "lv2/instance-access.h" | |||||
| #include "lv2/midi.h" | |||||
| #include "lv2/options.h" | |||||
| #include "lv2/port-props.h" | |||||
| #include "lv2/resize-port.h" | |||||
| #include "lv2/state.h" | |||||
| #include "lv2/time.h" | |||||
| #include "lv2/ui.h" | |||||
| #include "lv2/units.h" | |||||
| #include "lv2/urid.h" | |||||
| #include "lv2/worker.h" | |||||
| #include "lv2/lv2_kxstudio_properties.h" | |||||
| #include "lv2/lv2_programs.h" | |||||
| #include <fstream> | |||||
| #include <iostream> | |||||
| #ifndef DISTRHO_PLUGIN_URI | |||||
| # error DISTRHO_PLUGIN_URI undefined! | |||||
| #endif | |||||
| #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | |||||
| # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) | |||||
| # undef DISTRHO_PLUGIN_HAS_UI | |||||
| # define DISTRHO_PLUGIN_HAS_UI 0 | |||||
| #endif | |||||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||||
| #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||||
| // ----------------------------------------------------------------------- | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| void lv2_generate_ttl(const char* const basename) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| // Dummy plugin to get data from | |||||
| d_lastBufferSize = 512; | |||||
| d_lastSampleRate = 44100.0; | |||||
| PluginExporter plugin; | |||||
| d_lastBufferSize = 0; | |||||
| d_lastSampleRate = 0.0; | |||||
| String pluginDLL(basename); | |||||
| String pluginTTL(pluginDLL + ".ttl"); | |||||
| // --------------------------------------------- | |||||
| { | |||||
| std::cout << "Writing manifest.ttl..."; std::cout.flush(); | |||||
| std::fstream manifestFile("manifest.ttl", std::ios::out); | |||||
| 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 | |||||
| manifestString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||||
| #endif | |||||
| manifestString += "\n"; | |||||
| manifestString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||||
| manifestString += " a lv2:Plugin ;\n"; | |||||
| manifestString += " lv2:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||||
| manifestString += " rdfs:seeAlso <" + pluginTTL + "> .\n"; | |||||
| manifestString += "\n"; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| manifestString += "<" DISTRHO_UI_URI ">\n"; | |||||
| # if DISTRHO_OS_HAIKU | |||||
| manifestString += " a ui:BeUI ;\n"; | |||||
| # elif DISTRHO_OS_MAC | |||||
| manifestString += " a ui:CocoaUI ;\n"; | |||||
| # elif DISTRHO_OS_WINDOWS | |||||
| manifestString += " a ui:WindowsUI ;\n"; | |||||
| # else | |||||
| manifestString += " a ui:X11UI ;\n"; | |||||
| # endif | |||||
| # if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| String pluginUI(pluginDLL); | |||||
| pluginUI.truncate(pluginDLL.rfind("_dsp")); | |||||
| pluginUI += "_ui"; | |||||
| manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||||
| # else | |||||
| manifestString += " ui:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||||
| #endif | |||||
| manifestString += "\n"; | |||||
| manifestString += " lv2:extensionData ui:idleInterface ,\n"; | |||||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| manifestString += " ui:showInterface ,\n"; | |||||
| manifestString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||||
| # else | |||||
| manifestString += " ui:showInterface ;\n"; | |||||
| # endif | |||||
| manifestString += "\n"; | |||||
| manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||||
| manifestString += " ui:resize ,\n"; | |||||
| manifestString += " ui:touch ;\n"; | |||||
| manifestString += "\n"; | |||||
| # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| manifestString += " lv2:requiredFeature <" LV2_DATA_ACCESS_URI "> ,\n"; | |||||
| manifestString += " <" LV2_INSTANCE_ACCESS_URI "> ,\n"; | |||||
| manifestString += " <" LV2_OPTIONS__options "> ,\n"; | |||||
| # else | |||||
| manifestString += " lv2:requiredFeature <" LV2_OPTIONS__options "> ,\n"; | |||||
| # endif | |||||
| manifestString += " <" LV2_URID__map "> .\n"; | |||||
| #endif | |||||
| manifestFile << manifestString << std::endl; | |||||
| manifestFile.close(); | |||||
| std::cout << " done!" << std::endl; | |||||
| } | |||||
| // --------------------------------------------- | |||||
| { | |||||
| std::cout << "Writing " << pluginTTL << "..."; std::cout.flush(); | |||||
| std::fstream pluginFile(pluginTTL, std::ios::out); | |||||
| String pluginString; | |||||
| // header | |||||
| #if DISTRHO_LV2_USE_EVENTS_IN | |||||
| pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||||
| #endif | |||||
| pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||||
| pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||||
| pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||||
| pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||||
| #endif | |||||
| pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||||
| pluginString += "\n"; | |||||
| // plugin | |||||
| pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; | |||||
| #else | |||||
| pluginString += " a lv2:Plugin ;\n"; | |||||
| #endif | |||||
| pluginString += "\n"; | |||||
| // extensionData | |||||
| pluginString += " lv2:extensionData <" LV2_STATE__interface "> "; | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| pluginString += ",\n <" LV2_OPTIONS__interface "> "; | |||||
| pluginString += ",\n <" LV2_WORKER__interface "> "; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| pluginString += ",\n <" LV2_PROGRAMS__Interface "> "; | |||||
| #endif | |||||
| pluginString += ";\n\n"; | |||||
| // optionalFeatures | |||||
| #if DISTRHO_PLUGIN_IS_RT_SAFE | |||||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||||
| pluginString += " <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||||
| #else | |||||
| pluginString += " lv2:optionalFeature <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||||
| #endif | |||||
| pluginString += "\n"; | |||||
| // requiredFeatures | |||||
| pluginString += " lv2:requiredFeature <" LV2_OPTIONS__options "> "; | |||||
| pluginString += ",\n <" LV2_URID__map "> "; | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| pluginString += ",\n <" LV2_WORKER__schedule "> "; | |||||
| #endif | |||||
| pluginString += ";\n\n"; | |||||
| // UI | |||||
| #if DISTRHO_PLUGIN_HAS_UI | |||||
| pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | |||||
| pluginString += "\n"; | |||||
| #endif | |||||
| { | |||||
| uint32_t portIndex = 0; | |||||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex) | |||||
| { | |||||
| const AudioPort& port(plugin.getAudioPort(true, i)); | |||||
| if (i == 0) | |||||
| pluginString += " lv2:port [\n"; | |||||
| else | |||||
| pluginString += " [\n"; | |||||
| if (port.hints & kAudioPortIsCV) | |||||
| pluginString += " a lv2:InputPort, lv2:CVPort ;\n"; | |||||
| else | |||||
| pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; | |||||
| pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |||||
| if (port.hints & kAudioPortIsSidechain) | |||||
| pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||||
| if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) | |||||
| pluginString += " ] ;\n"; | |||||
| else | |||||
| pluginString += " ] ,\n"; | |||||
| } | |||||
| pluginString += "\n"; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex) | |||||
| { | |||||
| const AudioPort& port(plugin.getAudioPort(false, i)); | |||||
| if (i == 0) | |||||
| pluginString += " lv2:port [\n"; | |||||
| else | |||||
| pluginString += " [\n"; | |||||
| if (port.hints & kAudioPortIsCV) | |||||
| pluginString += " a lv2:OutputPort, lv2:CVPort ;\n"; | |||||
| else | |||||
| pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; | |||||
| pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |||||
| if (port.hints & kAudioPortIsSidechain) | |||||
| pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||||
| if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) | |||||
| pluginString += " ] ;\n"; | |||||
| else | |||||
| pluginString += " ] ,\n"; | |||||
| } | |||||
| pluginString += "\n"; | |||||
| #endif | |||||
| #if DISTRHO_LV2_USE_EVENTS_IN | |||||
| pluginString += " lv2:port [\n"; | |||||
| pluginString += " a lv2:InputPort, atom:AtomPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:name \"Events Input\" ;\n"; | |||||
| pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; | |||||
| pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||||
| pluginString += " atom:bufferType atom:Sequence ;\n"; | |||||
| # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||||
| pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
| pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_TIMEPOS | |||||
| pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; | |||||
| # endif | |||||
| pluginString += " ] ;\n\n"; | |||||
| ++portIndex; | |||||
| #endif | |||||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||||
| pluginString += " lv2:port [\n"; | |||||
| pluginString += " a lv2:OutputPort, atom:AtomPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:name \"Events Output\" ;\n"; | |||||
| pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; | |||||
| pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||||
| pluginString += " atom:bufferType atom:Sequence ;\n"; | |||||
| # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||||
| pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||||
| # endif | |||||
| # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
| pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | |||||
| # endif | |||||
| pluginString += " ] ;\n\n"; | |||||
| ++portIndex; | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| pluginString += " lv2:port [\n"; | |||||
| pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:name \"Latency\" ;\n"; | |||||
| pluginString += " lv2:symbol \"lv2_latency\" ;\n"; | |||||
| pluginString += " lv2:designation lv2:latency ;\n"; | |||||
| pluginString += " lv2:portProperty lv2:reportsLatency, lv2:integer ;\n"; | |||||
| pluginString += " ] ;\n\n"; | |||||
| ++portIndex; | |||||
| #endif | |||||
| for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i, ++portIndex) | |||||
| { | |||||
| if (i == 0) | |||||
| pluginString += " lv2:port [\n"; | |||||
| else | |||||
| pluginString += " [\n"; | |||||
| if (plugin.isParameterOutput(i)) | |||||
| pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||||
| else | |||||
| pluginString += " a lv2:InputPort, lv2:ControlPort ;\n"; | |||||
| pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||||
| pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; | |||||
| // symbol | |||||
| { | |||||
| String symbol(plugin.getParameterSymbol(i)); | |||||
| if (symbol.isEmpty()) | |||||
| symbol = "lv2_port_" + String(portIndex-1); | |||||
| pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |||||
| } | |||||
| // ranges | |||||
| { | |||||
| const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||||
| if (plugin.getParameterHints(i) & kParameterIsInteger) | |||||
| { | |||||
| pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; | |||||
| pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | |||||
| pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | |||||
| } | |||||
| else | |||||
| { | |||||
| pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; | |||||
| pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | |||||
| pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | |||||
| } | |||||
| } | |||||
| // unit | |||||
| { | |||||
| const String& unit(plugin.getParameterUnit(i)); | |||||
| if (! unit.isEmpty()) | |||||
| { | |||||
| if (unit == "db" || unit == "dB") | |||||
| { | |||||
| pluginString += " unit:unit unit:db ;\n"; | |||||
| } | |||||
| else if (unit == "hz" || unit == "Hz") | |||||
| { | |||||
| pluginString += " unit:unit unit:hz ;\n"; | |||||
| } | |||||
| else if (unit == "khz" || unit == "kHz") | |||||
| { | |||||
| pluginString += " unit:unit unit:khz ;\n"; | |||||
| } | |||||
| else if (unit == "mhz" || unit == "mHz") | |||||
| { | |||||
| pluginString += " unit:unit unit:mhz ;\n"; | |||||
| } | |||||
| else if (unit == "%") | |||||
| { | |||||
| pluginString += " unit:unit unit:pc ;\n"; | |||||
| } | |||||
| else | |||||
| { | |||||
| pluginString += " unit:unit [\n"; | |||||
| pluginString += " a unit:Unit ;\n"; | |||||
| pluginString += " unit:name \"" + unit + "\" ;\n"; | |||||
| pluginString += " unit:symbol \"" + unit + "\" ;\n"; | |||||
| pluginString += " unit:render \"%f " + unit + "\" ;\n"; | |||||
| pluginString += " ] ;\n"; | |||||
| } | |||||
| } | |||||
| } | |||||
| // hints | |||||
| { | |||||
| const uint32_t hints(plugin.getParameterHints(i)); | |||||
| if (hints & kParameterIsBoolean) | |||||
| pluginString += " lv2:portProperty lv2:toggled ;\n"; | |||||
| if (hints & kParameterIsInteger) | |||||
| pluginString += " lv2:portProperty lv2:integer ;\n"; | |||||
| if (hints & kParameterIsLogarithmic) | |||||
| pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; | |||||
| if ((hints & kParameterIsAutomable) == 0 && ! plugin.isParameterOutput(i)) | |||||
| { | |||||
| pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; | |||||
| pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; | |||||
| } | |||||
| } | |||||
| if (i+1 == count) | |||||
| pluginString += " ] ;\n\n"; | |||||
| else | |||||
| pluginString += " ] ,\n"; | |||||
| } | |||||
| } | |||||
| pluginString += " doap:name \"" + String(plugin.getName()) + "\" ;\n"; | |||||
| pluginString += " doap:maintainer [ foaf:name \"" + String(plugin.getMaker()) + "\" ] .\n"; | |||||
| pluginFile << pluginString << std::endl; | |||||
| pluginFile.close(); | |||||
| std::cout << " done!" << std::endl; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,123 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "DistrhoUIInternal.hpp" | |||||
| #include "src/WidgetPrivateData.hpp" | |||||
| START_NAMESPACE_DISTRHO | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Static data, see DistrhoUIInternal.hpp */ | |||||
| double d_lastUiSampleRate = 0.0; | |||||
| void* d_lastUiDspPtr = nullptr; | |||||
| Window* d_lastUiWindow = nullptr; | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * UI */ | |||||
| UI::UI(uint width, uint height) | |||||
| : UIWidget(*d_lastUiWindow), | |||||
| pData(new PrivateData()) | |||||
| { | |||||
| ((UIWidget*)this)->pData->needsFullViewport = false; | |||||
| if (width > 0 && height > 0) | |||||
| setSize(width, height); | |||||
| } | |||||
| UI::~UI() | |||||
| { | |||||
| delete pData; | |||||
| } | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Host state */ | |||||
| double UI::getSampleRate() const noexcept | |||||
| { | |||||
| return pData->sampleRate; | |||||
| } | |||||
| void UI::editParameter(uint32_t index, bool started) | |||||
| { | |||||
| pData->editParamCallback(index + pData->parameterOffset, started); | |||||
| } | |||||
| void UI::setParameterValue(uint32_t index, float value) | |||||
| { | |||||
| pData->setParamCallback(index + pData->parameterOffset, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void UI::setState(const char* key, const char* value) | |||||
| { | |||||
| pData->setStateCallback(key, value); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||||
| void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| pData->sendNoteCallback(channel, note, velocity); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * Direct DSP access */ | |||||
| void* UI::getPluginInstancePointer() const noexcept | |||||
| { | |||||
| return pData->dspPtr; | |||||
| } | |||||
| #endif | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * DSP/Plugin Callbacks (optional) */ | |||||
| void UI::sampleRateChanged(double) {} | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * UI Callbacks (optional) */ | |||||
| void UI::uiFileBrowserSelected(const char*) | |||||
| { | |||||
| } | |||||
| void UI::uiReshape(uint width, uint height) | |||||
| { | |||||
| glEnable(GL_BLEND); | |||||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||||
| glMatrixMode(GL_PROJECTION); | |||||
| glLoadIdentity(); | |||||
| glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); | |||||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||||
| glMatrixMode(GL_MODELVIEW); | |||||
| glLoadIdentity(); | |||||
| } | |||||
| /* ------------------------------------------------------------------------------------------------------------ | |||||
| * UI Resize Handling, internal */ | |||||
| void UI::onResize(const ResizeEvent& ev) | |||||
| { | |||||
| pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,511 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "DistrhoUIInternal.hpp" | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| # error DSSI UIs do not support direct access! | |||||
| #endif | |||||
| #include "../extra/Sleep.hpp" | |||||
| #include <lo/lo.h> | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| struct OscData { | |||||
| lo_address addr; | |||||
| const char* path; | |||||
| lo_server server; | |||||
| OscData() | |||||
| : addr(nullptr), | |||||
| path(nullptr), | |||||
| server(nullptr) {} | |||||
| void idle() const | |||||
| { | |||||
| if (server == nullptr) | |||||
| return; | |||||
| while (lo_server_recv_noblock(server, 0) != 0) {} | |||||
| } | |||||
| void send_configure(const char* const key, const char* const value) const | |||||
| { | |||||
| char targetPath[std::strlen(path)+11]; | |||||
| std::strcpy(targetPath, path); | |||||
| std::strcat(targetPath, "/configure"); | |||||
| lo_send(addr, targetPath, "ss", key, value); | |||||
| } | |||||
| void send_control(const int32_t index, const float value) const | |||||
| { | |||||
| char targetPath[std::strlen(path)+9]; | |||||
| std::strcpy(targetPath, path); | |||||
| std::strcat(targetPath, "/control"); | |||||
| lo_send(addr, targetPath, "if", index, value); | |||||
| } | |||||
| void send_midi(uchar data[4]) const | |||||
| { | |||||
| char targetPath[std::strlen(path)+6]; | |||||
| std::strcpy(targetPath, path); | |||||
| std::strcat(targetPath, "/midi"); | |||||
| lo_send(addr, targetPath, "m", data); | |||||
| } | |||||
| void send_update(const char* const url) const | |||||
| { | |||||
| char targetPath[std::strlen(path)+8]; | |||||
| std::strcpy(targetPath, path); | |||||
| std::strcat(targetPath, "/update"); | |||||
| lo_send(addr, targetPath, "s", url); | |||||
| } | |||||
| void send_exiting() const | |||||
| { | |||||
| char targetPath[std::strlen(path)+9]; | |||||
| std::strcpy(targetPath, path); | |||||
| std::strcat(targetPath, "/exiting"); | |||||
| lo_send(addr, targetPath, ""); | |||||
| } | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| class UIDssi | |||||
| { | |||||
| public: | |||||
| UIDssi(const OscData& oscData, const char* const uiTitle) | |||||
| : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), | |||||
| fHostClosed(false), | |||||
| fOscData(oscData) | |||||
| { | |||||
| fUI.setWindowTitle(uiTitle); | |||||
| } | |||||
| ~UIDssi() | |||||
| { | |||||
| if (fOscData.server != nullptr && ! fHostClosed) | |||||
| fOscData.send_exiting(); | |||||
| } | |||||
| void exec() | |||||
| { | |||||
| for (;;) | |||||
| { | |||||
| fOscData.idle(); | |||||
| if (fHostClosed || ! fUI.idle()) | |||||
| break; | |||||
| d_msleep(30); | |||||
| } | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void dssiui_configure(const char* key, const char* value) | |||||
| { | |||||
| fUI.stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| void dssiui_control(ulong index, float value) | |||||
| { | |||||
| fUI.parameterChanged(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void dssiui_program(ulong bank, ulong program) | |||||
| { | |||||
| fUI.programLoaded(bank * 128 + program); | |||||
| } | |||||
| #endif | |||||
| void dssiui_samplerate(const double sampleRate) | |||||
| { | |||||
| fUI.setSampleRate(sampleRate, true); | |||||
| } | |||||
| void dssiui_show() | |||||
| { | |||||
| fUI.setWindowVisible(true); | |||||
| } | |||||
| void dssiui_hide() | |||||
| { | |||||
| fUI.setWindowVisible(false); | |||||
| } | |||||
| void dssiui_quit() | |||||
| { | |||||
| fHostClosed = true; | |||||
| fUI.quit(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| protected: | |||||
| void setParameterValue(const uint32_t rindex, const float value) | |||||
| { | |||||
| if (fOscData.server == nullptr) | |||||
| return; | |||||
| fOscData.send_control(rindex, value); | |||||
| } | |||||
| void setState(const char* const key, const char* const value) | |||||
| { | |||||
| if (fOscData.server == nullptr) | |||||
| return; | |||||
| fOscData.send_configure(key, value); | |||||
| } | |||||
| void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||||
| { | |||||
| if (fOscData.server == nullptr) | |||||
| return; | |||||
| if (channel > 0xF) | |||||
| return; | |||||
| uint8_t mdata[4] = { 0, channel, note, velocity }; | |||||
| mdata[1] += (velocity != 0) ? 0x90 : 0x80; | |||||
| fOscData.send_midi(mdata); | |||||
| } | |||||
| void setSize(const uint width, const uint height) | |||||
| { | |||||
| fUI.setWindowSize(width, height); | |||||
| } | |||||
| private: | |||||
| UIExporter fUI; | |||||
| bool fHostClosed; | |||||
| const OscData& fOscData; | |||||
| // ------------------------------------------------------------------- | |||||
| // Callbacks | |||||
| #define uiPtr ((UIDssi*)ptr) | |||||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||||
| { | |||||
| uiPtr->setParameterValue(rindex, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| uiPtr->setState(key, value); | |||||
| } | |||||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| uiPtr->sendNote(channel, note, velocity); | |||||
| } | |||||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||||
| { | |||||
| uiPtr->setSize(width, height); | |||||
| } | |||||
| #undef uiPtr | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| static OscData gOscData; | |||||
| static const char* gUiTitle = nullptr; | |||||
| static UIDssi* globalUI = nullptr; | |||||
| static void initUiIfNeeded() | |||||
| { | |||||
| if (globalUI != nullptr) | |||||
| return; | |||||
| if (d_lastUiSampleRate == 0.0) | |||||
| d_lastUiSampleRate = 44100.0; | |||||
| globalUI = new UIDssi(gOscData, gUiTitle); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| d_debug("osc_debug_handler(\"%s\")", path); | |||||
| return 0; | |||||
| #ifndef DEBUG | |||||
| // unused | |||||
| (void)path; | |||||
| #endif | |||||
| } | |||||
| void osc_error_handler(int num, const char* msg, const char* path) | |||||
| { | |||||
| d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||||
| { | |||||
| const char* const key = &argv[0]->s; | |||||
| const char* const value = &argv[1]->s; | |||||
| d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value); | |||||
| initUiIfNeeded(); | |||||
| globalUI->dssiui_configure(key, value); | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||||
| { | |||||
| const int32_t rindex = argv[0]->i; | |||||
| const float value = argv[1]->f; | |||||
| d_debug("osc_control_handler(%i, %f)", rindex, value); | |||||
| int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| // latency | |||||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| index -= 1; | |||||
| #endif | |||||
| if (index < 0) | |||||
| return 0; | |||||
| initUiIfNeeded(); | |||||
| globalUI->dssiui_control(index, value); | |||||
| return 0; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||||
| { | |||||
| const int32_t bank = argv[0]->i; | |||||
| const int32_t program = argv[1]->f; | |||||
| d_debug("osc_program_handler(%i, %i)", bank, program); | |||||
| initUiIfNeeded(); | |||||
| globalUI->dssiui_program(bank, program); | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||||
| { | |||||
| const int32_t sampleRate = argv[0]->i; | |||||
| d_debug("osc_sample_rate_handler(%i)", sampleRate); | |||||
| d_lastUiSampleRate = sampleRate; | |||||
| if (globalUI != nullptr) | |||||
| globalUI->dssiui_samplerate(sampleRate); | |||||
| return 0; | |||||
| } | |||||
| int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| d_debug("osc_show_handler()"); | |||||
| initUiIfNeeded(); | |||||
| globalUI->dssiui_show(); | |||||
| return 0; | |||||
| } | |||||
| int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| d_debug("osc_hide_handler()"); | |||||
| if (globalUI != nullptr) | |||||
| globalUI->dssiui_hide(); | |||||
| return 0; | |||||
| } | |||||
| int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||||
| { | |||||
| d_debug("osc_quit_handler()"); | |||||
| if (globalUI != nullptr) | |||||
| globalUI->dssiui_quit(); | |||||
| return 0; | |||||
| } | |||||
| END_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| int main(int argc, char* argv[]) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| // dummy test mode | |||||
| if (argc == 1) | |||||
| { | |||||
| gUiTitle = "DSSI UI Test"; | |||||
| initUiIfNeeded(); | |||||
| globalUI->dssiui_show(); | |||||
| globalUI->exec(); | |||||
| delete globalUI; | |||||
| globalUI = nullptr; | |||||
| return 0; | |||||
| } | |||||
| if (argc != 5) | |||||
| { | |||||
| fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]); | |||||
| return 1; | |||||
| } | |||||
| const char* oscUrl = argv[1]; | |||||
| const char* uiTitle = argv[4]; | |||||
| char* const oscHost = lo_url_get_hostname(oscUrl); | |||||
| char* const oscPort = lo_url_get_port(oscUrl); | |||||
| char* const oscPath = lo_url_get_path(oscUrl); | |||||
| size_t oscPathSize = strlen(oscPath); | |||||
| lo_address oscAddr = lo_address_new(oscHost, oscPort); | |||||
| lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler); | |||||
| char* const oscServerPath = lo_server_get_url(oscServer); | |||||
| char pluginPath[strlen(oscServerPath)+oscPathSize]; | |||||
| strcpy(pluginPath, oscServerPath); | |||||
| strcat(pluginPath, oscPath+1); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| char oscPathConfigure[oscPathSize+11]; | |||||
| strcpy(oscPathConfigure, oscPath); | |||||
| strcat(oscPathConfigure, "/configure"); | |||||
| lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr); | |||||
| #endif | |||||
| char oscPathControl[oscPathSize+9]; | |||||
| strcpy(oscPathControl, oscPath); | |||||
| strcat(oscPathControl, "/control"); | |||||
| lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr); | |||||
| d_stdout("oscServerPath: \"%s\"", oscServerPath); | |||||
| d_stdout("pluginPath: \"%s\"", pluginPath); | |||||
| d_stdout("oscPathControl: \"%s\"", oscPathControl); | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| char oscPathProgram[oscPathSize+9]; | |||||
| strcpy(oscPathProgram, oscPath); | |||||
| strcat(oscPathProgram, "/program"); | |||||
| lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr); | |||||
| #endif | |||||
| char oscPathSampleRate[oscPathSize+13]; | |||||
| strcpy(oscPathSampleRate, oscPath); | |||||
| strcat(oscPathSampleRate, "/sample-rate"); | |||||
| lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr); | |||||
| char oscPathShow[oscPathSize+6]; | |||||
| strcpy(oscPathShow, oscPath); | |||||
| strcat(oscPathShow, "/show"); | |||||
| lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr); | |||||
| char oscPathHide[oscPathSize+6]; | |||||
| strcpy(oscPathHide, oscPath); | |||||
| strcat(oscPathHide, "/hide"); | |||||
| lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr); | |||||
| char oscPathQuit[oscPathSize+6]; | |||||
| strcpy(oscPathQuit, oscPath); | |||||
| strcat(oscPathQuit, "/quit"); | |||||
| lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr); | |||||
| lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr); | |||||
| gUiTitle = uiTitle; | |||||
| gOscData.addr = oscAddr; | |||||
| gOscData.path = oscPath; | |||||
| gOscData.server = oscServer; | |||||
| gOscData.send_update(pluginPath); | |||||
| // wait for init | |||||
| for (int i=0; i < 100; ++i) | |||||
| { | |||||
| lo_server_recv(oscServer); | |||||
| if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) | |||||
| break; | |||||
| d_msleep(50); | |||||
| } | |||||
| int ret = 1; | |||||
| if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) | |||||
| { | |||||
| initUiIfNeeded(); | |||||
| globalUI->exec(); | |||||
| delete globalUI; | |||||
| globalUI = nullptr; | |||||
| ret = 0; | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| lo_server_del_method(oscServer, oscPathConfigure, "ss"); | |||||
| #endif | |||||
| lo_server_del_method(oscServer, oscPathControl, "if"); | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| lo_server_del_method(oscServer, oscPathProgram, "ii"); | |||||
| #endif | |||||
| lo_server_del_method(oscServer, oscPathSampleRate, "i"); | |||||
| lo_server_del_method(oscServer, oscPathShow, ""); | |||||
| lo_server_del_method(oscServer, oscPathHide, ""); | |||||
| lo_server_del_method(oscServer, oscPathQuit, ""); | |||||
| lo_server_del_method(oscServer, nullptr, nullptr); | |||||
| std::free(oscServerPath); | |||||
| std::free(oscHost); | |||||
| std::free(oscPort); | |||||
| std::free(oscPath); | |||||
| lo_address_free(oscAddr); | |||||
| lo_server_free(oscServer); | |||||
| return ret; | |||||
| } | |||||
| @@ -0,0 +1,393 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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_UI_INTERNAL_HPP_INCLUDED | |||||
| #define DISTRHO_UI_INTERNAL_HPP_INCLUDED | |||||
| #include "../DistrhoUI.hpp" | |||||
| #include "../../dgl/Application.hpp" | |||||
| #include "../../dgl/Window.hpp" | |||||
| using DGL::Application; | |||||
| using DGL::IdleCallback; | |||||
| using DGL::Window; | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // Static data, see DistrhoUI.cpp | |||||
| extern double d_lastUiSampleRate; | |||||
| extern void* d_lastUiDspPtr; | |||||
| extern Window* d_lastUiWindow; | |||||
| // ----------------------------------------------------------------------- | |||||
| // UI callbacks | |||||
| typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||||
| typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); | |||||
| typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||||
| typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); | |||||
| typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||||
| // ----------------------------------------------------------------------- | |||||
| // UI private data | |||||
| struct UI::PrivateData { | |||||
| // DSP | |||||
| double sampleRate; | |||||
| uint32_t parameterOffset; | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| void* dspPtr; | |||||
| #endif | |||||
| // Callbacks | |||||
| editParamFunc editParamCallbackFunc; | |||||
| setParamFunc setParamCallbackFunc; | |||||
| setStateFunc setStateCallbackFunc; | |||||
| sendNoteFunc sendNoteCallbackFunc; | |||||
| setSizeFunc setSizeCallbackFunc; | |||||
| void* ptr; | |||||
| PrivateData() noexcept | |||||
| : sampleRate(d_lastUiSampleRate), | |||||
| parameterOffset(0), | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| dspPtr(d_lastUiDspPtr), | |||||
| #endif | |||||
| editParamCallbackFunc(nullptr), | |||||
| setParamCallbackFunc(nullptr), | |||||
| setStateCallbackFunc(nullptr), | |||||
| sendNoteCallbackFunc(nullptr), | |||||
| setSizeCallbackFunc(nullptr), | |||||
| ptr(nullptr) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | |||||
| #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) | |||||
| parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||||
| # if DISTRHO_PLUGIN_WANT_LATENCY | |||||
| parameterOffset += 1; | |||||
| # endif | |||||
| #endif | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_LV2 | |||||
| # if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |||||
| parameterOffset += 1; | |||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| parameterOffset += 1; | |||||
| # endif | |||||
| # endif | |||||
| #endif | |||||
| } | |||||
| void editParamCallback(const uint32_t rindex, const bool started) | |||||
| { | |||||
| if (editParamCallbackFunc != nullptr) | |||||
| editParamCallbackFunc(ptr, rindex, started); | |||||
| } | |||||
| void setParamCallback(const uint32_t rindex, const float value) | |||||
| { | |||||
| if (setParamCallbackFunc != nullptr) | |||||
| setParamCallbackFunc(ptr, rindex, value); | |||||
| } | |||||
| void setStateCallback(const char* const key, const char* const value) | |||||
| { | |||||
| if (setStateCallbackFunc != nullptr) | |||||
| setStateCallbackFunc(ptr, key, value); | |||||
| } | |||||
| void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||||
| { | |||||
| if (sendNoteCallbackFunc != nullptr) | |||||
| sendNoteCallbackFunc(ptr, channel, note, velocity); | |||||
| } | |||||
| void setSizeCallback(const uint width, const uint height) | |||||
| { | |||||
| if (setSizeCallbackFunc != nullptr) | |||||
| setSizeCallbackFunc(ptr, width, height); | |||||
| } | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Plugin Window, needed to take care of resize properly | |||||
| static inline | |||||
| UI* createUiWrapper(void* const dspPtr, Window* const window) | |||||
| { | |||||
| d_lastUiDspPtr = dspPtr; | |||||
| d_lastUiWindow = window; | |||||
| UI* const ret = createUI(); | |||||
| d_lastUiDspPtr = nullptr; | |||||
| d_lastUiWindow = nullptr; | |||||
| return ret; | |||||
| } | |||||
| class UIExporterWindow : public Window | |||||
| { | |||||
| public: | |||||
| UIExporterWindow(Application& app, const intptr_t winId, void* const dspPtr) | |||||
| : Window(app, winId), | |||||
| fUI(createUiWrapper(dspPtr, this)), | |||||
| fIsReady(false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| // set window size | |||||
| setResizable(false); | |||||
| setSize(fUI->getWidth(), fUI->getHeight()); | |||||
| } | |||||
| ~UIExporterWindow() | |||||
| { | |||||
| delete fUI; | |||||
| } | |||||
| UI* getUI() const noexcept | |||||
| { | |||||
| return fUI; | |||||
| } | |||||
| bool isReady() const noexcept | |||||
| { | |||||
| return fIsReady; | |||||
| } | |||||
| protected: | |||||
| // custom window reshape | |||||
| void onReshape(uint width, uint height) override | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| fUI->uiReshape(width, height); | |||||
| fIsReady = true; | |||||
| } | |||||
| // custom file-browser selected | |||||
| void fileBrowserSelected(const char* filename) override | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| fUI->uiFileBrowserSelected(filename); | |||||
| } | |||||
| private: | |||||
| UI* const fUI; | |||||
| bool fIsReady; | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| // UI exporter class | |||||
| class UIExporter | |||||
| { | |||||
| public: | |||||
| UIExporter(void* const ptr, const intptr_t winId, | |||||
| const editParamFunc editParamCall, const setParamFunc setParamCall, const setStateFunc setStateCall, const sendNoteFunc sendNoteCall, const setSizeFunc setSizeCall, | |||||
| void* const dspPtr = nullptr) | |||||
| : glApp(), | |||||
| glWindow(glApp, winId, dspPtr), | |||||
| fChangingSize(false), | |||||
| fUI(glWindow.getUI()), | |||||
| fData((fUI != nullptr) ? fUI->pData : nullptr) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| fData->ptr = ptr; | |||||
| fData->editParamCallbackFunc = editParamCall; | |||||
| fData->setParamCallbackFunc = setParamCall; | |||||
| fData->setStateCallbackFunc = setStateCall; | |||||
| fData->sendNoteCallbackFunc = sendNoteCall; | |||||
| fData->setSizeCallbackFunc = setSizeCall; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| uint getWidth() const noexcept | |||||
| { | |||||
| return glWindow.getWidth(); | |||||
| } | |||||
| uint getHeight() const noexcept | |||||
| { | |||||
| return glWindow.getHeight(); | |||||
| } | |||||
| bool isVisible() const noexcept | |||||
| { | |||||
| return glWindow.isVisible(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| intptr_t getWindowId() const noexcept | |||||
| { | |||||
| return glWindow.getWindowId(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| uint32_t getParameterOffset() const noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||||
| return fData->parameterOffset; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void parameterChanged(const uint32_t index, const float value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| fUI->parameterChanged(index, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void programLoaded(const uint32_t index) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| fUI->programLoaded(index); | |||||
| } | |||||
| #endif | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| void stateChanged(const char* const key, const char* const value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,); | |||||
| fUI->stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| void exec(IdleCallback* const cb) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| glWindow.addIdleCallback(cb); | |||||
| glWindow.setVisible(true); | |||||
| glApp.exec(); | |||||
| } | |||||
| void exec_idle() | |||||
| { | |||||
| if (glWindow.isReady()) | |||||
| fUI->uiIdle(); | |||||
| } | |||||
| bool idle() | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||||
| glApp.idle(); | |||||
| if (glWindow.isReady()) | |||||
| fUI->uiIdle(); | |||||
| return ! glApp.isQuiting(); | |||||
| } | |||||
| void quit() | |||||
| { | |||||
| glWindow.close(); | |||||
| glApp.quit(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void setWindowSize(const uint width, const uint height, const bool updateUI = false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,); | |||||
| fChangingSize = true; | |||||
| if (updateUI) | |||||
| fUI->setSize(width, height); | |||||
| glWindow.setSize(width, height); | |||||
| fChangingSize = false; | |||||
| } | |||||
| void setWindowTitle(const char* const uiTitle) | |||||
| { | |||||
| glWindow.setTitle(uiTitle); | |||||
| } | |||||
| void setWindowTransientWinId(const uintptr_t winId) | |||||
| { | |||||
| glWindow.setTransientWinId(winId); | |||||
| } | |||||
| bool setWindowVisible(const bool yesNo) | |||||
| { | |||||
| glWindow.setVisible(yesNo); | |||||
| return ! glApp.isQuiting(); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void setSampleRate(const double sampleRate, const bool doCallback = false) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT(sampleRate > 0.0); | |||||
| if (d_isEqual(fData->sampleRate, sampleRate)) | |||||
| return; | |||||
| fData->sampleRate = sampleRate; | |||||
| if (doCallback) | |||||
| fUI->sampleRateChanged(sampleRate); | |||||
| } | |||||
| private: | |||||
| // ------------------------------------------------------------------- | |||||
| // DGL Application and Window for this widget | |||||
| Application glApp; | |||||
| UIExporterWindow glWindow; | |||||
| // prevent recursion | |||||
| bool fChangingSize; | |||||
| // ------------------------------------------------------------------- | |||||
| // Widget and DistrhoUI data | |||||
| UI* const fUI; | |||||
| UI::PrivateData* const fData; | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_UI_INTERNAL_HPP_INCLUDED | |||||
| @@ -0,0 +1,536 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2015 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 "DistrhoUIInternal.hpp" | |||||
| #include "../extra/String.hpp" | |||||
| #include "lv2/atom.h" | |||||
| #include "lv2/atom-util.h" | |||||
| #include "lv2/data-access.h" | |||||
| #include "lv2/instance-access.h" | |||||
| #include "lv2/options.h" | |||||
| #include "lv2/ui.h" | |||||
| #include "lv2/urid.h" | |||||
| #include "lv2/lv2_kxstudio_properties.h" | |||||
| #include "lv2/lv2_programs.h" | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| class UiLv2 | |||||
| { | |||||
| public: | |||||
| UiLv2(const intptr_t winId, | |||||
| const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, | |||||
| const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, | |||||
| LV2UI_Widget* const widget, void* const dspPtr) | |||||
| : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, dspPtr), | |||||
| fUridMap(uridMap), | |||||
| fUiResize(uiResz), | |||||
| fUiTouch(uiTouch), | |||||
| fController(controller), | |||||
| fWriteFunction(writeFunc), | |||||
| fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||||
| fKeyValueURID(uridMap->map(uridMap->handle, "urn:distrho:keyValueState")), | |||||
| fWinIdWasNull(winId == 0) | |||||
| { | |||||
| if (fUiResize != nullptr && winId != 0) | |||||
| fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); | |||||
| if (widget != nullptr) | |||||
| *widget = (LV2UI_Widget*)fUI.getWindowId(); | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| // tell the DSP we're ready to receive msgs | |||||
| setState("__dpf_ui_data__", ""); | |||||
| #endif | |||||
| if (winId != 0) | |||||
| return; | |||||
| // if winId == 0 then options must not be null | |||||
| DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | |||||
| const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle)); | |||||
| const LV2_URID uridTransientWinId(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId)); | |||||
| bool hasTitle = false; | |||||
| for (int i=0; options[i].key != 0; ++i) | |||||
| { | |||||
| if (options[i].key == uridTransientWinId) | |||||
| { | |||||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Long)) | |||||
| { | |||||
| if (const int64_t transientWinId = *(const int64_t*)options[i].value) | |||||
| fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); | |||||
| } | |||||
| else | |||||
| d_stderr("Host provides transientWinId but has wrong value type"); | |||||
| } | |||||
| else if (options[i].key == uridWindowTitle) | |||||
| { | |||||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__String)) | |||||
| { | |||||
| if (const char* const windowTitle = (const char*)options[i].value) | |||||
| { | |||||
| hasTitle = true; | |||||
| fUI.setWindowTitle(windowTitle); | |||||
| } | |||||
| } | |||||
| else | |||||
| d_stderr("Host provides windowTitle but has wrong value type"); | |||||
| } | |||||
| } | |||||
| if (! hasTitle) | |||||
| fUI.setWindowTitle(DISTRHO_PLUGIN_NAME); | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer) | |||||
| { | |||||
| if (format == 0) | |||||
| { | |||||
| const uint32_t parameterOffset(fUI.getParameterOffset()); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rindex >= parameterOffset,) | |||||
| DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | |||||
| const float value(*(const float*)buffer); | |||||
| fUI.parameterChanged(rindex-parameterOffset, value); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_STATE | |||||
| else if (format == fEventTransferURID) | |||||
| { | |||||
| const LV2_Atom* const atom((const LV2_Atom*)buffer); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(atom->type == fKeyValueURID,); | |||||
| const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||||
| const char* const value = key+(std::strlen(key)+1); | |||||
| fUI.stateChanged(key, value); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| int lv2ui_idle() | |||||
| { | |||||
| if (fWinIdWasNull) | |||||
| return (fUI.idle() && fUI.isVisible()) ? 0 : 1; | |||||
| return fUI.idle() ? 0 : 1; | |||||
| } | |||||
| int lv2ui_show() | |||||
| { | |||||
| return fUI.setWindowVisible(true) ? 0 : 1; | |||||
| } | |||||
| int lv2ui_hide() | |||||
| { | |||||
| return fUI.setWindowVisible(false) ? 0 : 1; | |||||
| } | |||||
| int lv2ui_resize(uint width, uint height) | |||||
| { | |||||
| fUI.setWindowSize(width, height, true); | |||||
| return 0; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) | |||||
| { | |||||
| // currently unused | |||||
| return LV2_OPTIONS_ERR_UNKNOWN; | |||||
| } | |||||
| uint32_t lv2_set_options(const LV2_Options_Option* const options) | |||||
| { | |||||
| for (int i=0; options[i].key != 0; ++i) | |||||
| { | |||||
| if (options[i].key == fUridMap->map(fUridMap->handle, LV2_CORE__sampleRate)) | |||||
| { | |||||
| if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Double)) | |||||
| { | |||||
| const double sampleRate(*(const double*)options[i].value); | |||||
| fUI.setSampleRate(sampleRate); | |||||
| continue; | |||||
| } | |||||
| else | |||||
| { | |||||
| d_stderr("Host changed sampleRate but with wrong value type"); | |||||
| continue; | |||||
| } | |||||
| } | |||||
| } | |||||
| return LV2_OPTIONS_SUCCESS; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| void lv2ui_select_program(const uint32_t bank, const uint32_t program) | |||||
| { | |||||
| const uint32_t realProgram(bank * 128 + program); | |||||
| fUI.programLoaded(realProgram); | |||||
| } | |||||
| #endif | |||||
| // ------------------------------------------------------------------- | |||||
| protected: | |||||
| void editParameterValue(const uint32_t rindex, const bool started) | |||||
| { | |||||
| if (fUiTouch != nullptr && fUiTouch->touch != nullptr) | |||||
| fUiTouch->touch(fUiTouch->handle, rindex, started); | |||||
| } | |||||
| void setParameterValue(const uint32_t rindex, const float value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||||
| fWriteFunction(fController, rindex, sizeof(float), 0, &value); | |||||
| } | |||||
| void setState(const char* const key, const char* const value) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||||
| const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||||
| // join key and value | |||||
| String tmpStr; | |||||
| tmpStr += key; | |||||
| tmpStr += "\xff"; | |||||
| tmpStr += value; | |||||
| tmpStr[std::strlen(key)] = '\0'; | |||||
| // set msg size (key + separator + value + null terminator) | |||||
| const size_t msgSize(tmpStr.length()+1); | |||||
| // reserve atom space | |||||
| const size_t atomSize(lv2_atom_pad_size(sizeof(LV2_Atom) + msgSize)); | |||||
| char atomBuf[atomSize]; | |||||
| std::memset(atomBuf, 0, atomSize); | |||||
| // set atom info | |||||
| LV2_Atom* const atom((LV2_Atom*)atomBuf); | |||||
| atom->size = msgSize; | |||||
| atom->type = fKeyValueURID; | |||||
| // set atom data | |||||
| std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); | |||||
| // send to DSP side | |||||
| fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||||
| } | |||||
| void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) | |||||
| { | |||||
| } | |||||
| void setSize(const uint width, const uint height) | |||||
| { | |||||
| fUI.setWindowSize(width, height); | |||||
| if (fUiResize != nullptr && ! fWinIdWasNull) | |||||
| fUiResize->ui_resize(fUiResize->handle, width, height); | |||||
| } | |||||
| private: | |||||
| UIExporter fUI; | |||||
| // LV2 features | |||||
| const LV2_URID_Map* const fUridMap; | |||||
| const LV2UI_Resize* const fUiResize; | |||||
| const LV2UI_Touch* const fUiTouch; | |||||
| // LV2 UI stuff | |||||
| const LV2UI_Controller fController; | |||||
| const LV2UI_Write_Function fWriteFunction; | |||||
| // Need to save this | |||||
| const LV2_URID fEventTransferURID; | |||||
| const LV2_URID fKeyValueURID; | |||||
| // using ui:showInterface if true | |||||
| bool fWinIdWasNull; | |||||
| // ------------------------------------------------------------------- | |||||
| // Callbacks | |||||
| #define uiPtr ((UiLv2*)ptr) | |||||
| static void editParameterCallback(void* ptr, uint32_t rindex, bool started) | |||||
| { | |||||
| uiPtr->editParameterValue(rindex, started); | |||||
| } | |||||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||||
| { | |||||
| uiPtr->setParameterValue(rindex, value); | |||||
| } | |||||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||||
| { | |||||
| uiPtr->setState(key, value); | |||||
| } | |||||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||||
| { | |||||
| uiPtr->sendNote(channel, note, velocity); | |||||
| } | |||||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||||
| { | |||||
| uiPtr->setSize(width, height); | |||||
| } | |||||
| #undef uiPtr | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char*, LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) | |||||
| { | |||||
| if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | |||||
| { | |||||
| d_stderr("Invalid plugin URI"); | |||||
| return nullptr; | |||||
| } | |||||
| const LV2_Options_Option* options = nullptr; | |||||
| const LV2_URID_Map* uridMap = nullptr; | |||||
| const LV2UI_Resize* uiResize = nullptr; | |||||
| const LV2UI_Touch* uiTouch = nullptr; | |||||
| void* parentId = nullptr; | |||||
| void* instance = nullptr; | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| # define DISTRHO_DIRECT_ACCESS_URI "urn:distrho:direct-access" | |||||
| struct LV2_DirectAccess_Interface { | |||||
| void* (*get_instance_pointer)(LV2_Handle handle); | |||||
| }; | |||||
| const LV2_Extension_Data_Feature* extData = nullptr; | |||||
| #endif | |||||
| for (int i=0; features[i] != nullptr; ++i) | |||||
| { | |||||
| if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||||
| options = (const LV2_Options_Option*)features[i]->data; | |||||
| else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | |||||
| uridMap = (const LV2_URID_Map*)features[i]->data; | |||||
| else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) | |||||
| uiResize = (const LV2UI_Resize*)features[i]->data; | |||||
| else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) | |||||
| parentId = features[i]->data; | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) | |||||
| extData = (const LV2_Extension_Data_Feature*)features[i]->data; | |||||
| else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0) | |||||
| instance = features[i]->data; | |||||
| #endif | |||||
| } | |||||
| if (options == nullptr && parentId == nullptr) | |||||
| { | |||||
| d_stderr("Options feature missing (needed for show-interface), cannot continue!"); | |||||
| return nullptr; | |||||
| } | |||||
| if (uridMap == nullptr) | |||||
| { | |||||
| d_stderr("URID Map feature missing, cannot continue!"); | |||||
| return nullptr; | |||||
| } | |||||
| if (parentId == nullptr) | |||||
| { | |||||
| d_stdout("Parent Window Id missing, host should be using ui:showInterface..."); | |||||
| } | |||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| if (extData == nullptr || instance == nullptr) | |||||
| { | |||||
| d_stderr("Data or instance access missing, cannot continue!"); | |||||
| return nullptr; | |||||
| } | |||||
| if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_DIRECT_ACCESS_URI)) | |||||
| instance = directAccess->get_instance_pointer(instance); | |||||
| else | |||||
| instance = nullptr; | |||||
| if (instance == nullptr) | |||||
| { | |||||
| d_stderr("Failed to get direct access, cannot continue!"); | |||||
| return nullptr; | |||||
| } | |||||
| #endif | |||||
| const intptr_t winId((intptr_t)parentId); | |||||
| if (options != nullptr) | |||||
| { | |||||
| const LV2_URID uridSampleRate(uridMap->map(uridMap->handle, LV2_CORE__sampleRate)); | |||||
| for (int i=0; options[i].key != 0; ++i) | |||||
| { | |||||
| if (options[i].key == uridSampleRate) | |||||
| { | |||||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Double)) | |||||
| d_lastUiSampleRate = *(const double*)options[i].value; | |||||
| else | |||||
| d_stderr("Host provides sampleRate but has wrong value type"); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (d_lastUiSampleRate == 0.0) | |||||
| { | |||||
| d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)"); | |||||
| d_lastUiSampleRate = 44100.0; | |||||
| } | |||||
| return new UiLv2(winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, widget, instance); | |||||
| } | |||||
| #define uiPtr ((UiLv2*)ui) | |||||
| static void lv2ui_cleanup(LV2UI_Handle ui) | |||||
| { | |||||
| delete uiPtr; | |||||
| } | |||||
| static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) | |||||
| { | |||||
| uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| static int lv2ui_idle(LV2UI_Handle ui) | |||||
| { | |||||
| return uiPtr->lv2ui_idle(); | |||||
| } | |||||
| static int lv2ui_show(LV2UI_Handle ui) | |||||
| { | |||||
| return uiPtr->lv2ui_show(); | |||||
| } | |||||
| static int lv2ui_hide(LV2UI_Handle ui) | |||||
| { | |||||
| return uiPtr->lv2ui_hide(); | |||||
| } | |||||
| static int lv2ui_resize(LV2UI_Handle ui, int width, int height) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 1); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(width > 0, 1); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(height > 0, 1); | |||||
| return 1; // This needs more testing | |||||
| //return uiPtr->lv2ui_resize(width, height); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options) | |||||
| { | |||||
| return uiPtr->lv2_get_options(options); | |||||
| } | |||||
| static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options) | |||||
| { | |||||
| return uiPtr->lv2_set_options(options); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) | |||||
| { | |||||
| uiPtr->lv2ui_select_program(bank, program); | |||||
| } | |||||
| #endif | |||||
| // ----------------------------------------------------------------------- | |||||
| static const void* lv2ui_extension_data(const char* uri) | |||||
| { | |||||
| static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; | |||||
| static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle }; | |||||
| static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide }; | |||||
| static const LV2UI_Resize uiResz = { nullptr, lv2ui_resize }; | |||||
| if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) | |||||
| return &options; | |||||
| if (std::strcmp(uri, LV2_UI__idleInterface) == 0) | |||||
| return &uiIdle; | |||||
| if (std::strcmp(uri, LV2_UI__showInterface) == 0) | |||||
| return &uiShow; | |||||
| if (std::strcmp(uri, LV2_UI__resize) == 0) | |||||
| return &uiResz; | |||||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||||
| static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program }; | |||||
| if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) | |||||
| return &uiPrograms; | |||||
| #endif | |||||
| return nullptr; | |||||
| } | |||||
| #undef instancePtr | |||||
| // ----------------------------------------------------------------------- | |||||
| static const LV2UI_Descriptor sLv2UiDescriptor = { | |||||
| DISTRHO_UI_URI, | |||||
| lv2ui_instantiate, | |||||
| lv2ui_cleanup, | |||||
| lv2ui_port_event, | |||||
| lv2ui_extension_data | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| DISTRHO_PLUGIN_EXPORT | |||||
| const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) | |||||
| { | |||||
| USE_NAMESPACE_DISTRHO | |||||
| return (index == 0) ? &sLv2UiDescriptor : nullptr; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| @@ -0,0 +1,441 @@ | |||||
| /* -*- c-basic-offset: 4 -*- */ | |||||
| /* dssi.h | |||||
| DSSI version 1.0 | |||||
| Copyright (c) 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton | |||||
| This library is free software; you can redistribute it and/or | |||||
| modify it under the terms of the GNU Lesser General Public License | |||||
| as published by the Free Software Foundation; either version 2.1 of | |||||
| the License, or (at your option) any later version. | |||||
| This library is distributed in the hope that it will be useful, but | |||||
| WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
| Lesser General Public License for more details. | |||||
| You should have received a copy of the GNU Lesser General Public | |||||
| License along with this library; if not, write to the Free Software | |||||
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||||
| MA 02110-1301 USA | |||||
| */ | |||||
| #ifndef DSSI_INCLUDED | |||||
| #define DSSI_INCLUDED | |||||
| #include "../ladspa/ladspa.h" | |||||
| #include "seq_event-compat.h" | |||||
| #define DSSI_VERSION "1.0" | |||||
| #define DSSI_VERSION_MAJOR 1 | |||||
| #define DSSI_VERSION_MINOR 0 | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /* | |||||
| There is a need for an API that supports hosted MIDI soft synths | |||||
| with GUIs in Linux audio applications. In time the GMPI initiative | |||||
| should comprehensively address this need, but the requirement for | |||||
| Linux applications to be able to support simple hosted synths is | |||||
| here now, and GMPI is not. This proposal (the "DSSI Soft Synth | |||||
| Interface" or DSSI, pronounced "dizzy") aims to provide a simple | |||||
| solution in a way that we hope will prove complete and compelling | |||||
| enough to support now, yet not so compelling as to supplant GMPI or | |||||
| any other comprehensive future proposal. | |||||
| For simplicity and familiarity, this API is based as far as | |||||
| possible on existing work -- the LADSPA plugin API for control | |||||
| values and audio processing, and the ALSA sequencer event types for | |||||
| MIDI event communication. The GUI part of the proposal is quite | |||||
| new, but may also be applicable retroactively to LADSPA plugins | |||||
| that do not otherwise support this synth interface. | |||||
| */ | |||||
| typedef struct _DSSI_Program_Descriptor { | |||||
| /** Bank number for this program. Note that DSSI does not support | |||||
| MIDI-style separation of bank LSB and MSB values. There is no | |||||
| restriction on the set of available banks: the numbers do not | |||||
| need to be contiguous, there does not need to be a bank 0, etc. */ | |||||
| unsigned long Bank; | |||||
| /** Program number (unique within its bank) for this program. | |||||
| There is no restriction on the set of available programs: the | |||||
| numbers do not need to be contiguous, there does not need to | |||||
| be a program 0, etc. */ | |||||
| unsigned long Program; | |||||
| /** Name of the program. */ | |||||
| const char * Name; | |||||
| } DSSI_Program_Descriptor; | |||||
| typedef struct _DSSI_Descriptor { | |||||
| /** | |||||
| * DSSI_API_Version | |||||
| * | |||||
| * This member indicates the DSSI API level used by this plugin. | |||||
| * If we're lucky, this will never be needed. For now all plugins | |||||
| * must set it to 1. | |||||
| */ | |||||
| int DSSI_API_Version; | |||||
| /** | |||||
| * LADSPA_Plugin | |||||
| * | |||||
| * A DSSI synth plugin consists of a LADSPA plugin plus an | |||||
| * additional framework for controlling program settings and | |||||
| * transmitting MIDI events. A plugin must fully implement the | |||||
| * LADSPA descriptor fields as well as the required LADSPA | |||||
| * functions including instantiate() and (de)activate(). It | |||||
| * should also implement run(), with the same behaviour as if | |||||
| * run_synth() (below) were called with no synth events. | |||||
| * | |||||
| * In order to instantiate a synth the host calls the LADSPA | |||||
| * instantiate function, passing in this LADSPA_Descriptor | |||||
| * pointer. The returned LADSPA_Handle is used as the argument | |||||
| * for the DSSI functions below as well as for the LADSPA ones. | |||||
| */ | |||||
| const LADSPA_Descriptor *LADSPA_Plugin; | |||||
| /** | |||||
| * configure() | |||||
| * | |||||
| * This member is a function pointer that sends a piece of | |||||
| * configuration data to the plugin. The key argument specifies | |||||
| * some aspect of the synth's configuration that is to be changed, | |||||
| * and the value argument specifies a new value for it. A plugin | |||||
| * that does not require this facility at all may set this member | |||||
| * to NULL. | |||||
| * | |||||
| * This call is intended to set some session-scoped aspect of a | |||||
| * plugin's behaviour, for example to tell the plugin to load | |||||
| * sample data from a particular file. The plugin should act | |||||
| * immediately on the request. The call should return NULL on | |||||
| * success, or an error string that may be shown to the user. The | |||||
| * host will free the returned value after use if it is non-NULL. | |||||
| * | |||||
| * Calls to configure() are not automated as timed events. | |||||
| * Instead, a host should remember the last value associated with | |||||
| * each key passed to configure() during a given session for a | |||||
| * given plugin instance, and should call configure() with the | |||||
| * correct value for each key the next time it instantiates the | |||||
| * "same" plugin instance, for example on reloading a project in | |||||
| * which the plugin was used before. Plugins should note that a | |||||
| * host may typically instantiate a plugin multiple times with the | |||||
| * same configuration values, and should share data between | |||||
| * instances where practical. | |||||
| * | |||||
| * Calling configure() completely invalidates the program and bank | |||||
| * information last obtained from the plugin. | |||||
| * | |||||
| * Reserved and special key prefixes | |||||
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
| * The DSSI: prefix | |||||
| * ---------------- | |||||
| * Configure keys starting with DSSI: are reserved for particular | |||||
| * purposes documented in the DSSI specification. At the moment, | |||||
| * there is one such key: DSSI:PROJECT_DIRECTORY. A host may call | |||||
| * configure() passing this key and a directory path value. This | |||||
| * indicates to the plugin and its UI that a directory at that | |||||
| * path exists and may be used for project-local data. Plugins | |||||
| * may wish to use the project directory as a fallback location | |||||
| * when looking for other file data, or as a base for relative | |||||
| * paths in other configuration values. | |||||
| * | |||||
| * The GLOBAL: prefix | |||||
| * ------------------ | |||||
| * Configure keys starting with GLOBAL: may be used by the plugin | |||||
| * and its UI for any purpose, but are treated specially by the | |||||
| * host. When one of these keys is used in a configure OSC call | |||||
| * from the plugin UI, the host makes the corresponding configure | |||||
| * call (preserving the GLOBAL: prefix) not only to the target | |||||
| * plugin but also to all other plugins in the same instance | |||||
| * group, as well as their UIs. Note that if any instance | |||||
| * returns non-NULL from configure to indicate error, the host | |||||
| * may stop there (and the set of plugins on which configure has | |||||
| * been called will thus depend on the host implementation). | |||||
| * See also the configure OSC call documentation in RFC.txt. | |||||
| */ | |||||
| char *(*configure)(LADSPA_Handle Instance, | |||||
| const char *Key, | |||||
| const char *Value); | |||||
| #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" | |||||
| #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" | |||||
| #define DSSI_PROJECT_DIRECTORY_KEY \ | |||||
| DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" | |||||
| /** | |||||
| * get_program() | |||||
| * | |||||
| * This member is a function pointer that provides a description | |||||
| * of a program (named preset sound) available on this synth. A | |||||
| * plugin that does not support programs at all should set this | |||||
| * member to NULL. | |||||
| * | |||||
| * The Index argument is an index into the plugin's list of | |||||
| * programs, not a program number as represented by the Program | |||||
| * field of the DSSI_Program_Descriptor. (This distinction is | |||||
| * needed to support synths that use non-contiguous program or | |||||
| * bank numbers.) | |||||
| * | |||||
| * This function returns a DSSI_Program_Descriptor pointer that is | |||||
| * guaranteed to be valid only until the next call to get_program, | |||||
| * deactivate, or configure, on the same plugin instance. This | |||||
| * function must return NULL if passed an Index argument out of | |||||
| * range, so that the host can use it to query the number of | |||||
| * programs as well as their properties. | |||||
| */ | |||||
| const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, | |||||
| unsigned long Index); | |||||
| /** | |||||
| * select_program() | |||||
| * | |||||
| * This member is a function pointer that selects a new program | |||||
| * for this synth. The program change should take effect | |||||
| * immediately at the start of the next run_synth() call. (This | |||||
| * means that a host providing the capability of changing programs | |||||
| * between any two notes on a track must vary the block size so as | |||||
| * to place the program change at the right place. A host that | |||||
| * wanted to avoid this would probably just instantiate a plugin | |||||
| * for each program.) | |||||
| * | |||||
| * A plugin that does not support programs at all should set this | |||||
| * member NULL. Plugins should ignore a select_program() call | |||||
| * with an invalid bank or program. | |||||
| * | |||||
| * A plugin is not required to select any particular default | |||||
| * program on activate(): it's the host's duty to set a program | |||||
| * explicitly. The current program is invalidated by any call to | |||||
| * configure(). | |||||
| * | |||||
| * A plugin is permitted to re-write the values of its input | |||||
| * control ports when select_program is called. The host should | |||||
| * re-read the input control port values and update its own | |||||
| * records appropriately. (This is the only circumstance in | |||||
| * which a DSSI plugin is allowed to modify its own input ports.) | |||||
| */ | |||||
| void (*select_program)(LADSPA_Handle Instance, | |||||
| unsigned long Bank, | |||||
| unsigned long Program); | |||||
| /** | |||||
| * get_midi_controller_for_port() | |||||
| * | |||||
| * This member is a function pointer that returns the MIDI | |||||
| * controller number or NRPN that should be mapped to the given | |||||
| * input control port. If the given port should not have any MIDI | |||||
| * controller mapped to it, the function should return DSSI_NONE. | |||||
| * The behaviour of this function is undefined if the given port | |||||
| * number does not correspond to an input control port. A plugin | |||||
| * that does not want MIDI controllers mapped to ports at all may | |||||
| * set this member NULL. | |||||
| * | |||||
| * Correct values can be got using the macros DSSI_CC(num) and | |||||
| * DSSI_NRPN(num) as appropriate, and values can be combined using | |||||
| * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port | |||||
| * should respond to CC #23 and NRPN #1069. | |||||
| * | |||||
| * The host is responsible for doing proper scaling from MIDI | |||||
| * controller and NRPN value ranges to port ranges according to | |||||
| * the plugin's LADSPA port hints. Hosts should not deliver | |||||
| * through run_synth any MIDI controller events that have already | |||||
| * been mapped to control port values. | |||||
| * | |||||
| * A plugin should not attempt to request mappings from | |||||
| * controllers 0 or 32 (MIDI Bank Select MSB and LSB). | |||||
| */ | |||||
| int (*get_midi_controller_for_port)(LADSPA_Handle Instance, | |||||
| unsigned long Port); | |||||
| /** | |||||
| * run_synth() | |||||
| * | |||||
| * This member is a function pointer that runs a synth for a | |||||
| * block. This is identical in function to the LADSPA run() | |||||
| * function, except that it also supplies events to the synth. | |||||
| * | |||||
| * A plugin may provide this function, run_multiple_synths() (see | |||||
| * below), both, or neither (if it is not in fact a synth). A | |||||
| * plugin that does not provide this function must set this member | |||||
| * to NULL. Authors of synth plugins are encouraged to provide | |||||
| * this function if at all possible. | |||||
| * | |||||
| * The Events pointer points to a block of EventCount ALSA | |||||
| * sequencer events, which is used to communicate MIDI and related | |||||
| * events to the synth. Each event is timestamped relative to the | |||||
| * start of the block, (mis)using the ALSA "tick time" field as a | |||||
| * frame count. The host is responsible for ensuring that events | |||||
| * with differing timestamps are already ordered by time. | |||||
| * | |||||
| * See also the notes on activation, port connection etc in | |||||
| * ladpsa.h, in the context of the LADSPA run() function. | |||||
| * | |||||
| * Note Events | |||||
| * ~~~~~~~~~~~ | |||||
| * There are two minor requirements aimed at making the plugin | |||||
| * writer's life as simple as possible: | |||||
| * | |||||
| * 1. A host must never send events of type SND_SEQ_EVENT_NOTE. | |||||
| * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON | |||||
| * and NOTE_OFF events. A plugin should discard any one-point | |||||
| * NOTE events it sees. | |||||
| * | |||||
| * 2. A host must not attempt to switch notes off by sending | |||||
| * zero-velocity NOTE_ON events. It should always send true | |||||
| * NOTE_OFFs. It is the host's responsibility to remap events in | |||||
| * cases where an external MIDI source has sent it zero-velocity | |||||
| * NOTE_ONs. | |||||
| * | |||||
| * Bank and Program Events | |||||
| * ~~~~~~~~~~~~~~~~~~~~~~~ | |||||
| * Hosts must map MIDI Bank Select MSB and LSB (0 and 32) | |||||
| * controllers and MIDI Program Change events onto the banks and | |||||
| * programs specified by the plugin, using the DSSI select_program | |||||
| * call. No host should ever deliver a program change or bank | |||||
| * select controller to a plugin via run_synth. | |||||
| */ | |||||
| void (*run_synth)(LADSPA_Handle Instance, | |||||
| unsigned long SampleCount, | |||||
| snd_seq_event_t *Events, | |||||
| unsigned long EventCount); | |||||
| /** | |||||
| * run_synth_adding() | |||||
| * | |||||
| * This member is a function pointer that runs an instance of a | |||||
| * synth for a block, adding its outputs to the values already | |||||
| * present at the output ports. This is provided for symmetry | |||||
| * with LADSPA run_adding(), and is equally optional. A plugin | |||||
| * that does not provide it must set this member to NULL. | |||||
| */ | |||||
| void (*run_synth_adding)(LADSPA_Handle Instance, | |||||
| unsigned long SampleCount, | |||||
| snd_seq_event_t *Events, | |||||
| unsigned long EventCount); | |||||
| /** | |||||
| * run_multiple_synths() | |||||
| * | |||||
| * This member is a function pointer that runs multiple synth | |||||
| * instances for a block. This is very similar to run_synth(), | |||||
| * except that Instances, Events, and EventCounts each point to | |||||
| * arrays that hold the LADSPA handles, event buffers, and | |||||
| * event counts for each of InstanceCount instances. That is, | |||||
| * Instances points to an array of InstanceCount pointers to | |||||
| * DSSI plugin instantiations, Events points to an array of | |||||
| * pointers to each instantiation's respective event list, and | |||||
| * EventCounts points to an array containing each instantiation's | |||||
| * respective event count. | |||||
| * | |||||
| * A host using this function must guarantee that ALL active | |||||
| * instances of the plugin are represented in each call to the | |||||
| * function -- that is, a host may not call run_multiple_synths() | |||||
| * for some instances of a given plugin and then call run_synth() | |||||
| * as well for others. 'All .. instances of the plugin' means | |||||
| * every instance sharing the same LADSPA label and shared object | |||||
| * (*.so) file (rather than every instance sharing the same *.so). | |||||
| * 'Active' means any instance for which activate() has been called | |||||
| * but deactivate() has not. | |||||
| * | |||||
| * A plugin may provide this function, run_synths() (see above), | |||||
| * both, or neither (if it not in fact a synth). A plugin that | |||||
| * does not provide this function must set this member to NULL. | |||||
| * Plugin authors implementing run_multiple_synths are strongly | |||||
| * encouraged to implement run_synth as well if at all possible, | |||||
| * to aid simplistic hosts, even where it would be less efficient | |||||
| * to use it. | |||||
| */ | |||||
| void (*run_multiple_synths)(unsigned long InstanceCount, | |||||
| LADSPA_Handle *Instances, | |||||
| unsigned long SampleCount, | |||||
| snd_seq_event_t **Events, | |||||
| unsigned long *EventCounts); | |||||
| /** | |||||
| * run_multiple_synths_adding() | |||||
| * | |||||
| * This member is a function pointer that runs multiple synth | |||||
| * instances for a block, adding each synth's outputs to the | |||||
| * values already present at the output ports. This is provided | |||||
| * for symmetry with both the DSSI run_multiple_synths() and LADSPA | |||||
| * run_adding() functions, and is equally optional. A plugin | |||||
| * that does not provide it must set this member to NULL. | |||||
| */ | |||||
| void (*run_multiple_synths_adding)(unsigned long InstanceCount, | |||||
| LADSPA_Handle *Instances, | |||||
| unsigned long SampleCount, | |||||
| snd_seq_event_t **Events, | |||||
| unsigned long *EventCounts); | |||||
| /** | |||||
| * set_custom_data() | |||||
| */ | |||||
| int (*set_custom_data)(LADSPA_Handle Instance, | |||||
| void *Data, | |||||
| unsigned long DataLength); | |||||
| /** | |||||
| * get_custom_data() | |||||
| */ | |||||
| int (*get_custom_data)(LADSPA_Handle Instance, | |||||
| void **Data, | |||||
| unsigned long *DataLength); | |||||
| } DSSI_Descriptor; | |||||
| /** | |||||
| * DSSI supports a plugin discovery method similar to that of LADSPA: | |||||
| * | |||||
| * - DSSI hosts may wish to locate DSSI plugin shared object files by | |||||
| * searching the paths contained in the DSSI_PATH and LADSPA_PATH | |||||
| * environment variables, if they are present. Both are expected | |||||
| * to be colon-separated lists of directories to be searched (in | |||||
| * order), and DSSI_PATH should be searched first if both variables | |||||
| * are set. | |||||
| * | |||||
| * - Each shared object file containing DSSI plugins must include a | |||||
| * function dssi_descriptor(), with the following function prototype | |||||
| * and C-style linkage. Hosts may enumerate the plugin types | |||||
| * available in the shared object file by repeatedly calling | |||||
| * this function with successive Index values (beginning from 0), | |||||
| * until a return value of NULL indicates no more plugin types are | |||||
| * available. Each non-NULL return is the DSSI_Descriptor | |||||
| * of a distinct plugin type. | |||||
| */ | |||||
| const DSSI_Descriptor *dssi_descriptor(unsigned long Index); | |||||
| typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index); | |||||
| /* | |||||
| * Macros to specify particular MIDI controllers in return values from | |||||
| * get_midi_controller_for_port() | |||||
| */ | |||||
| #define DSSI_CC_BITS 0x20000000 | |||||
| #define DSSI_NRPN_BITS 0x40000000 | |||||
| #define DSSI_NONE -1 | |||||
| #define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) | |||||
| #define DSSI_CC(n) (DSSI_CC_BITS | (n)) | |||||
| #define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) | |||||
| #define DSSI_CC_NUMBER(n) ((n) & 0x7f) | |||||
| #define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) | |||||
| #define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) | |||||
| #define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) | |||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #endif /* DSSI_INCLUDED */ | |||||
| @@ -0,0 +1,272 @@ | |||||
| /* | |||||
| * This library is free software; you can redistribute it and/or modify | |||||
| * it under the terms of the GNU Lesser General Public License as | |||||
| * published by the Free Software Foundation; either version 2.1 of | |||||
| * the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU Lesser General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU Lesser General Public | |||||
| * License along with this library; if not, write to the Free Software | |||||
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||||
| * | |||||
| */ | |||||
| #ifndef __ALSA_SEQ_EVENT_COMPAT_H | |||||
| #define __ALSA_SEQ_EVENT_COMPAT_H | |||||
| /** | |||||
| * Sequencer event data type | |||||
| */ | |||||
| typedef unsigned char snd_seq_event_type_t; | |||||
| /** Sequencer event type */ | |||||
| enum snd_seq_event_type { | |||||
| /** system status; event data type = #snd_seq_result_t */ | |||||
| SND_SEQ_EVENT_SYSTEM = 0, | |||||
| /** returned result status; event data type = #snd_seq_result_t */ | |||||
| SND_SEQ_EVENT_RESULT, | |||||
| /** note on and off with duration; event data type = #snd_seq_ev_note_t */ | |||||
| SND_SEQ_EVENT_NOTE = 5, | |||||
| /** note on; event data type = #snd_seq_ev_note_t */ | |||||
| SND_SEQ_EVENT_NOTEON, | |||||
| /** note off; event data type = #snd_seq_ev_note_t */ | |||||
| SND_SEQ_EVENT_NOTEOFF, | |||||
| /** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */ | |||||
| SND_SEQ_EVENT_KEYPRESS, | |||||
| /** controller; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_CONTROLLER = 10, | |||||
| /** program change; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_PGMCHANGE, | |||||
| /** channel pressure; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_CHANPRESS, | |||||
| /** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */ | |||||
| SND_SEQ_EVENT_PITCHBEND, | |||||
| /** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_CONTROL14, | |||||
| /** 14 bit NRPN; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_NONREGPARAM, | |||||
| /** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_REGPARAM, | |||||
| /** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_SONGPOS = 20, | |||||
| /** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_SONGSEL, | |||||
| /** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_QFRAME, | |||||
| /** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_TIMESIGN, | |||||
| /** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */ | |||||
| SND_SEQ_EVENT_KEYSIGN, | |||||
| /** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_START = 30, | |||||
| /** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_CONTINUE, | |||||
| /** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_STOP, | |||||
| /** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_SETPOS_TICK, | |||||
| /** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_SETPOS_TIME, | |||||
| /** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_TEMPO, | |||||
| /** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_CLOCK, | |||||
| /** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_TICK, | |||||
| /** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_QUEUE_SKEW, | |||||
| /** Sync position changed; event data type = #snd_seq_ev_queue_control_t */ | |||||
| SND_SEQ_EVENT_SYNC_POS, | |||||
| /** Tune request; event data type = none */ | |||||
| SND_SEQ_EVENT_TUNE_REQUEST = 40, | |||||
| /** Reset to power-on state; event data type = none */ | |||||
| SND_SEQ_EVENT_RESET, | |||||
| /** Active sensing event; event data type = none */ | |||||
| SND_SEQ_EVENT_SENSING, | |||||
| /** Echo-back event; event data type = any type */ | |||||
| SND_SEQ_EVENT_ECHO = 50, | |||||
| /** OSS emulation raw event; event data type = any type */ | |||||
| SND_SEQ_EVENT_OSS, | |||||
| /** New client has connected; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_CLIENT_START = 60, | |||||
| /** Client has left the system; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_CLIENT_EXIT, | |||||
| /** Client status/info has changed; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_CLIENT_CHANGE, | |||||
| /** New port was created; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_PORT_START, | |||||
| /** Port was deleted from system; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_PORT_EXIT, | |||||
| /** Port status/info has changed; event data type = #snd_seq_addr_t */ | |||||
| SND_SEQ_EVENT_PORT_CHANGE, | |||||
| /** Ports connected; event data type = #snd_seq_connect_t */ | |||||
| SND_SEQ_EVENT_PORT_SUBSCRIBED, | |||||
| /** Ports disconnected; event data type = #snd_seq_connect_t */ | |||||
| SND_SEQ_EVENT_PORT_UNSUBSCRIBED, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR0 = 90, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR1, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR2, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR3, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR4, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR5, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR6, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR7, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR8, | |||||
| /** user-defined event; event data type = any (fixed size) */ | |||||
| SND_SEQ_EVENT_USR9, | |||||
| /** system exclusive data (variable length); event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_SYSEX = 130, | |||||
| /** error event; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_BOUNCE, | |||||
| /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_USR_VAR0 = 135, | |||||
| /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_USR_VAR1, | |||||
| /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_USR_VAR2, | |||||
| /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_USR_VAR3, | |||||
| /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ | |||||
| SND_SEQ_EVENT_USR_VAR4, | |||||
| /** NOP; ignored in any case */ | |||||
| SND_SEQ_EVENT_NONE = 255 | |||||
| }; | |||||
| /** Sequencer event address */ | |||||
| typedef struct snd_seq_addr { | |||||
| unsigned char client; /**< Client id */ | |||||
| unsigned char port; /**< Port id */ | |||||
| } snd_seq_addr_t; | |||||
| /** Connection (subscription) between ports */ | |||||
| typedef struct snd_seq_connect { | |||||
| snd_seq_addr_t sender; /**< sender address */ | |||||
| snd_seq_addr_t dest; /**< destination address */ | |||||
| } snd_seq_connect_t; | |||||
| /** Real-time data record */ | |||||
| typedef struct snd_seq_real_time { | |||||
| unsigned int tv_sec; /**< seconds */ | |||||
| unsigned int tv_nsec; /**< nanoseconds */ | |||||
| } snd_seq_real_time_t; | |||||
| /** (MIDI) Tick-time data record */ | |||||
| typedef unsigned int snd_seq_tick_time_t; | |||||
| /** unioned time stamp */ | |||||
| typedef union snd_seq_timestamp { | |||||
| snd_seq_tick_time_t tick; /**< tick-time */ | |||||
| struct snd_seq_real_time time; /**< real-time */ | |||||
| } snd_seq_timestamp_t; | |||||
| /** Note event */ | |||||
| typedef struct snd_seq_ev_note { | |||||
| unsigned char channel; /**< channel number */ | |||||
| unsigned char note; /**< note */ | |||||
| unsigned char velocity; /**< velocity */ | |||||
| unsigned char off_velocity; /**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */ | |||||
| unsigned int duration; /**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */ | |||||
| } snd_seq_ev_note_t; | |||||
| /** Controller event */ | |||||
| typedef struct snd_seq_ev_ctrl { | |||||
| unsigned char channel; /**< channel number */ | |||||
| unsigned char unused[3]; /**< reserved */ | |||||
| unsigned int param; /**< control parameter */ | |||||
| signed int value; /**< control value */ | |||||
| } snd_seq_ev_ctrl_t; | |||||
| /** generic set of bytes (12x8 bit) */ | |||||
| typedef struct snd_seq_ev_raw8 { | |||||
| unsigned char d[12]; /**< 8 bit value */ | |||||
| } snd_seq_ev_raw8_t; | |||||
| /** generic set of integers (3x32 bit) */ | |||||
| typedef struct snd_seq_ev_raw32 { | |||||
| unsigned int d[3]; /**< 32 bit value */ | |||||
| } snd_seq_ev_raw32_t; | |||||
| /** external stored data */ | |||||
| typedef struct snd_seq_ev_ext { | |||||
| unsigned int len; /**< length of data */ | |||||
| void *ptr; /**< pointer to data (note: can be 64-bit) */ | |||||
| } __attribute__((packed)) snd_seq_ev_ext_t; | |||||
| /** Result events */ | |||||
| typedef struct snd_seq_result { | |||||
| int event; /**< processed event type */ | |||||
| int result; /**< status */ | |||||
| } snd_seq_result_t; | |||||
| /** Queue skew values */ | |||||
| typedef struct snd_seq_queue_skew { | |||||
| unsigned int value; /**< skew value */ | |||||
| unsigned int base; /**< skew base */ | |||||
| } snd_seq_queue_skew_t; | |||||
| /** queue timer control */ | |||||
| typedef struct snd_seq_ev_queue_control { | |||||
| unsigned char queue; /**< affected queue */ | |||||
| unsigned char unused[3]; /**< reserved */ | |||||
| union { | |||||
| signed int value; /**< affected value (e.g. tempo) */ | |||||
| snd_seq_timestamp_t time; /**< time */ | |||||
| unsigned int position; /**< sync position */ | |||||
| snd_seq_queue_skew_t skew; /**< queue skew */ | |||||
| unsigned int d32[2]; /**< any data */ | |||||
| unsigned char d8[8]; /**< any data */ | |||||
| } param; /**< data value union */ | |||||
| } snd_seq_ev_queue_control_t; | |||||
| /** Sequencer event */ | |||||
| typedef struct snd_seq_event { | |||||
| snd_seq_event_type_t type; /**< event type */ | |||||
| unsigned char flags; /**< event flags */ | |||||
| unsigned char tag; /**< tag */ | |||||
| unsigned char queue; /**< schedule queue */ | |||||
| snd_seq_timestamp_t time; /**< schedule time */ | |||||
| snd_seq_addr_t source; /**< source address */ | |||||
| snd_seq_addr_t dest; /**< destination address */ | |||||
| union { | |||||
| snd_seq_ev_note_t note; /**< note information */ | |||||
| snd_seq_ev_ctrl_t control; /**< MIDI control information */ | |||||
| snd_seq_ev_raw8_t raw8; /**< raw8 data */ | |||||
| snd_seq_ev_raw32_t raw32; /**< raw32 data */ | |||||
| snd_seq_ev_ext_t ext; /**< external data */ | |||||
| snd_seq_ev_queue_control_t queue; /**< queue control */ | |||||
| snd_seq_timestamp_t time; /**< timestamp */ | |||||
| snd_seq_addr_t addr; /**< address */ | |||||
| snd_seq_connect_t connect; /**< connect information */ | |||||
| snd_seq_result_t result; /**< operation result code */ | |||||
| } data; /**< event data... */ | |||||
| } snd_seq_event_t; | |||||
| #endif /* __ALSA_SEQ_EVENT_COMPAT_H */ | |||||
| @@ -0,0 +1,603 @@ | |||||
| /* ladspa.h | |||||
| Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. | |||||
| Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, | |||||
| Stefan Westerfeld. | |||||
| This library is free software; you can redistribute it and/or | |||||
| modify it under the terms of the GNU Lesser General Public License | |||||
| as published by the Free Software Foundation; either version 2.1 of | |||||
| the License, or (at your option) any later version. | |||||
| This library is distributed in the hope that it will be useful, but | |||||
| WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
| Lesser General Public License for more details. | |||||
| You should have received a copy of the GNU Lesser General Public | |||||
| License along with this library; if not, write to the Free Software | |||||
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |||||
| USA. */ | |||||
| #ifndef LADSPA_INCLUDED | |||||
| #define LADSPA_INCLUDED | |||||
| #define LADSPA_VERSION "1.1" | |||||
| #define LADSPA_VERSION_MAJOR 1 | |||||
| #define LADSPA_VERSION_MINOR 1 | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /*****************************************************************************/ | |||||
| /* Overview: | |||||
| There is a large number of synthesis packages in use or development | |||||
| on the Linux platform at this time. This API (`The Linux Audio | |||||
| Developer's Simple Plugin API') attempts to give programmers the | |||||
| ability to write simple `plugin' audio processors in C/C++ and link | |||||
| them dynamically (`plug') into a range of these packages (`hosts'). | |||||
| It should be possible for any host and any plugin to communicate | |||||
| completely through this interface. | |||||
| This API is deliberately short and simple. To achieve compatibility | |||||
| with a range of promising Linux sound synthesis packages it | |||||
| attempts to find the `greatest common divisor' in their logical | |||||
| behaviour. Having said this, certain limiting decisions are | |||||
| implicit, notably the use of a fixed type (LADSPA_Data) for all | |||||
| data transfer and absence of a parameterised `initialisation' | |||||
| phase. See below for the LADSPA_Data typedef. | |||||
| Plugins are expected to distinguish between control and audio | |||||
| data. Plugins have `ports' that are inputs or outputs for audio or | |||||
| control data and each plugin is `run' for a `block' corresponding | |||||
| to a short time interval measured in samples. Audio data is | |||||
| communicated using arrays of LADSPA_Data, allowing a block of audio | |||||
| to be processed by the plugin in a single pass. Control data is | |||||
| communicated using single LADSPA_Data values. Control data has a | |||||
| single value at the start of a call to the `run()' or `run_adding()' | |||||
| function, and may be considered to remain this value for its | |||||
| duration. The plugin may assume that all its input and output ports | |||||
| have been connected to the relevant data location (see the | |||||
| `connect_port()' function below) before it is asked to run. | |||||
| Plugins will reside in shared object files suitable for dynamic | |||||
| linking by dlopen() and family. The file will provide a number of | |||||
| `plugin types' that can be used to instantiate actual plugins | |||||
| (sometimes known as `plugin instances') that can be connected | |||||
| together to perform tasks. | |||||
| This API contains very limited error-handling. */ | |||||
| /*****************************************************************************/ | |||||
| /* Fundamental data type passed in and out of plugin. This data type | |||||
| is used to communicate audio samples and control values. It is | |||||
| assumed that the plugin will work sensibly given any numeric input | |||||
| value although it may have a preferred range (see hints below). | |||||
| For audio it is generally assumed that 1.0f is the `0dB' reference | |||||
| amplitude and is a `normal' signal level. */ | |||||
| typedef float LADSPA_Data; | |||||
| /*****************************************************************************/ | |||||
| /* Special Plugin Properties: | |||||
| Optional features of the plugin type are encapsulated in the | |||||
| LADSPA_Properties type. This is assembled by ORing individual | |||||
| properties together. */ | |||||
| typedef int LADSPA_Properties; | |||||
| /* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a | |||||
| real-time dependency (e.g. listens to a MIDI device) and so its | |||||
| output must not be cached or subject to significant latency. */ | |||||
| #define LADSPA_PROPERTY_REALTIME 0x1 | |||||
| /* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin | |||||
| may cease to work correctly if the host elects to use the same data | |||||
| location for both input and output (see connect_port()). This | |||||
| should be avoided as enabling this flag makes it impossible for | |||||
| hosts to use the plugin to process audio `in-place.' */ | |||||
| #define LADSPA_PROPERTY_INPLACE_BROKEN 0x2 | |||||
| /* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin | |||||
| is capable of running not only in a conventional host but also in a | |||||
| `hard real-time' environment. To qualify for this the plugin must | |||||
| satisfy all of the following: | |||||
| (1) The plugin must not use malloc(), free() or other heap memory | |||||
| management within its run() or run_adding() functions. All new | |||||
| memory used in run() must be managed via the stack. These | |||||
| restrictions only apply to the run() function. | |||||
| (2) The plugin will not attempt to make use of any library | |||||
| functions with the exceptions of functions in the ANSI standard C | |||||
| and C maths libraries, which the host is expected to provide. | |||||
| (3) The plugin will not access files, devices, pipes, sockets, IPC | |||||
| or any other mechanism that might result in process or thread | |||||
| blocking. | |||||
| (4) The plugin will take an amount of time to execute a run() or | |||||
| run_adding() call approximately of form (A+B*SampleCount) where A | |||||
| and B depend on the machine and host in use. This amount of time | |||||
| may not depend on input signals or plugin state. The host is left | |||||
| the responsibility to perform timings to estimate upper bounds for | |||||
| A and B. */ | |||||
| #define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 | |||||
| #define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) | |||||
| #define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) | |||||
| #define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) | |||||
| /*****************************************************************************/ | |||||
| /* Plugin Ports: | |||||
| Plugins have `ports' that are inputs or outputs for audio or | |||||
| data. Ports can communicate arrays of LADSPA_Data (for audio | |||||
| inputs/outputs) or single LADSPA_Data values (for control | |||||
| input/outputs). This information is encapsulated in the | |||||
| LADSPA_PortDescriptor type which is assembled by ORing individual | |||||
| properties together. | |||||
| Note that a port must be an input or an output port but not both | |||||
| and that a port must be a control or audio port but not both. */ | |||||
| typedef int LADSPA_PortDescriptor; | |||||
| /* Property LADSPA_PORT_INPUT indicates that the port is an input. */ | |||||
| #define LADSPA_PORT_INPUT 0x1 | |||||
| /* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ | |||||
| #define LADSPA_PORT_OUTPUT 0x2 | |||||
| /* Property LADSPA_PORT_CONTROL indicates that the port is a control | |||||
| port. */ | |||||
| #define LADSPA_PORT_CONTROL 0x4 | |||||
| /* Property LADSPA_PORT_AUDIO indicates that the port is a audio | |||||
| port. */ | |||||
| #define LADSPA_PORT_AUDIO 0x8 | |||||
| #define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) | |||||
| #define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) | |||||
| #define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) | |||||
| #define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) | |||||
| /*****************************************************************************/ | |||||
| /* Plugin Port Range Hints: | |||||
| The host may wish to provide a representation of data entering or | |||||
| leaving a plugin (e.g. to generate a GUI automatically). To make | |||||
| this more meaningful, the plugin should provide `hints' to the host | |||||
| describing the usual values taken by the data. | |||||
| Note that these are only hints. The host may ignore them and the | |||||
| plugin must not assume that data supplied to it is meaningful. If | |||||
| the plugin receives invalid input data it is expected to continue | |||||
| to run without failure and, where possible, produce a sensible | |||||
| output (e.g. a high-pass filter given a negative cutoff frequency | |||||
| might switch to an all-pass mode). | |||||
| Hints are meaningful for all input and output ports but hints for | |||||
| input control ports are expected to be particularly useful. | |||||
| More hint information is encapsulated in the | |||||
| LADSPA_PortRangeHintDescriptor type which is assembled by ORing | |||||
| individual hint types together. Hints may require further | |||||
| LowerBound and UpperBound information. | |||||
| All the hint information for a particular port is aggregated in the | |||||
| LADSPA_PortRangeHint structure. */ | |||||
| typedef int LADSPA_PortRangeHintDescriptor; | |||||
| /* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field | |||||
| of the LADSPA_PortRangeHint should be considered meaningful. The | |||||
| value in this field should be considered the (inclusive) lower | |||||
| bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also | |||||
| specified then the value of LowerBound should be multiplied by the | |||||
| sample rate. */ | |||||
| #define LADSPA_HINT_BOUNDED_BELOW 0x1 | |||||
| /* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field | |||||
| of the LADSPA_PortRangeHint should be considered meaningful. The | |||||
| value in this field should be considered the (inclusive) upper | |||||
| bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also | |||||
| specified then the value of UpperBound should be multiplied by the | |||||
| sample rate. */ | |||||
| #define LADSPA_HINT_BOUNDED_ABOVE 0x2 | |||||
| /* Hint LADSPA_HINT_TOGGLED indicates that the data item should be | |||||
| considered a Boolean toggle. Data less than or equal to zero should | |||||
| be considered `off' or `false,' and data above zero should be | |||||
| considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in | |||||
| conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or | |||||
| LADSPA_HINT_DEFAULT_1. */ | |||||
| #define LADSPA_HINT_TOGGLED 0x4 | |||||
| /* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified | |||||
| should be interpreted as multiples of the sample rate. For | |||||
| instance, a frequency range from 0Hz to the Nyquist frequency (half | |||||
| the sample rate) could be requested by this hint in conjunction | |||||
| with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds | |||||
| at all must support this hint to retain meaning. */ | |||||
| #define LADSPA_HINT_SAMPLE_RATE 0x8 | |||||
| /* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the | |||||
| user will find it more intuitive to view values using a logarithmic | |||||
| scale. This is particularly useful for frequencies and gains. */ | |||||
| #define LADSPA_HINT_LOGARITHMIC 0x10 | |||||
| /* Hint LADSPA_HINT_INTEGER indicates that a user interface would | |||||
| probably wish to provide a stepped control taking only integer | |||||
| values. Any bounds set should be slightly wider than the actual | |||||
| integer range required to avoid floating point rounding errors. For | |||||
| instance, the integer set {0,1,2,3} might be described as [-0.1, | |||||
| 3.1]. */ | |||||
| #define LADSPA_HINT_INTEGER 0x20 | |||||
| /* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' | |||||
| value for the port that is sensible as a default. For instance, | |||||
| this value is suitable for use as an initial value in a user | |||||
| interface or as a value the host might assign to a control port | |||||
| when the user has not provided one. Defaults are encoded using a | |||||
| mask so only one default may be specified for a port. Some of the | |||||
| hints make use of lower and upper bounds, in which case the | |||||
| relevant bound or bounds must be available and | |||||
| LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting | |||||
| default must be rounded if LADSPA_HINT_INTEGER is present. Default | |||||
| values were introduced in LADSPA v1.1. */ | |||||
| #define LADSPA_HINT_DEFAULT_MASK 0x3C0 | |||||
| /* This default values indicates that no default is provided. */ | |||||
| #define LADSPA_HINT_DEFAULT_NONE 0x0 | |||||
| /* This default hint indicates that the suggested lower bound for the | |||||
| port should be used. */ | |||||
| #define LADSPA_HINT_DEFAULT_MINIMUM 0x40 | |||||
| /* This default hint indicates that a low value between the suggested | |||||
| lower and upper bounds should be chosen. For ports with | |||||
| LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + | |||||
| log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper | |||||
| * 0.25). */ | |||||
| #define LADSPA_HINT_DEFAULT_LOW 0x80 | |||||
| /* This default hint indicates that a middle value between the | |||||
| suggested lower and upper bounds should be chosen. For ports with | |||||
| LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + | |||||
| log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * | |||||
| 0.5). */ | |||||
| #define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 | |||||
| /* This default hint indicates that a high value between the suggested | |||||
| lower and upper bounds should be chosen. For ports with | |||||
| LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + | |||||
| log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper | |||||
| * 0.75). */ | |||||
| #define LADSPA_HINT_DEFAULT_HIGH 0x100 | |||||
| /* This default hint indicates that the suggested upper bound for the | |||||
| port should be used. */ | |||||
| #define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 | |||||
| /* This default hint indicates that the number 0 should be used. Note | |||||
| that this default may be used in conjunction with | |||||
| LADSPA_HINT_TOGGLED. */ | |||||
| #define LADSPA_HINT_DEFAULT_0 0x200 | |||||
| /* This default hint indicates that the number 1 should be used. Note | |||||
| that this default may be used in conjunction with | |||||
| LADSPA_HINT_TOGGLED. */ | |||||
| #define LADSPA_HINT_DEFAULT_1 0x240 | |||||
| /* This default hint indicates that the number 100 should be used. */ | |||||
| #define LADSPA_HINT_DEFAULT_100 0x280 | |||||
| /* This default hint indicates that the Hz frequency of `concert A' | |||||
| should be used. This will be 440 unless the host uses an unusual | |||||
| tuning convention, in which case it may be within a few Hz. */ | |||||
| #define LADSPA_HINT_DEFAULT_440 0x2C0 | |||||
| #define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) | |||||
| #define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) | |||||
| #define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) | |||||
| #define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) | |||||
| #define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) | |||||
| #define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) | |||||
| #define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) | |||||
| #define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_MINIMUM) | |||||
| #define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_LOW) | |||||
| #define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_MIDDLE) | |||||
| #define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_HIGH) | |||||
| #define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_MAXIMUM) | |||||
| #define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_0) | |||||
| #define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_1) | |||||
| #define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_100) | |||||
| #define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ | |||||
| == LADSPA_HINT_DEFAULT_440) | |||||
| typedef struct _LADSPA_PortRangeHint { | |||||
| /* Hints about the port. */ | |||||
| LADSPA_PortRangeHintDescriptor HintDescriptor; | |||||
| /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When | |||||
| LADSPA_HINT_SAMPLE_RATE is also active then this value should be | |||||
| multiplied by the relevant sample rate. */ | |||||
| LADSPA_Data LowerBound; | |||||
| /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When | |||||
| LADSPA_HINT_SAMPLE_RATE is also active then this value should be | |||||
| multiplied by the relevant sample rate. */ | |||||
| LADSPA_Data UpperBound; | |||||
| } LADSPA_PortRangeHint; | |||||
| /*****************************************************************************/ | |||||
| /* Plugin Handles: | |||||
| This plugin handle indicates a particular instance of the plugin | |||||
| concerned. It is valid to compare this to NULL (0 for C++) but | |||||
| otherwise the host should not attempt to interpret it. The plugin | |||||
| may use it to reference internal instance data. */ | |||||
| typedef void * LADSPA_Handle; | |||||
| /*****************************************************************************/ | |||||
| /* Descriptor for a Type of Plugin: | |||||
| This structure is used to describe a plugin type. It provides a | |||||
| number of functions to examine the type, instantiate it, link it to | |||||
| buffers and workspaces and to run it. */ | |||||
| typedef struct _LADSPA_Descriptor { | |||||
| /* This numeric identifier indicates the plugin type | |||||
| uniquely. Plugin programmers may reserve ranges of IDs from a | |||||
| central body to avoid clashes. Hosts may assume that IDs are | |||||
| below 0x1000000. */ | |||||
| unsigned long UniqueID; | |||||
| /* This identifier can be used as a unique, case-sensitive | |||||
| identifier for the plugin type within the plugin file. Plugin | |||||
| types should be identified by file and label rather than by index | |||||
| or plugin name, which may be changed in new plugin | |||||
| versions. Labels must not contain white-space characters. */ | |||||
| const char * Label; | |||||
| /* This indicates a number of properties of the plugin. */ | |||||
| LADSPA_Properties Properties; | |||||
| /* This member points to the null-terminated name of the plugin | |||||
| (e.g. "Sine Oscillator"). */ | |||||
| const char * Name; | |||||
| /* This member points to the null-terminated string indicating the | |||||
| maker of the plugin. This can be an empty string but not NULL. */ | |||||
| const char * Maker; | |||||
| /* This member points to the null-terminated string indicating any | |||||
| copyright applying to the plugin. If no Copyright applies the | |||||
| string "None" should be used. */ | |||||
| const char * Copyright; | |||||
| /* This indicates the number of ports (input AND output) present on | |||||
| the plugin. */ | |||||
| unsigned long PortCount; | |||||
| /* This member indicates an array of port descriptors. Valid indices | |||||
| vary from 0 to PortCount-1. */ | |||||
| const LADSPA_PortDescriptor * PortDescriptors; | |||||
| /* This member indicates an array of null-terminated strings | |||||
| describing ports (e.g. "Frequency (Hz)"). Valid indices vary from | |||||
| 0 to PortCount-1. */ | |||||
| const char * const * PortNames; | |||||
| /* This member indicates an array of range hints for each port (see | |||||
| above). Valid indices vary from 0 to PortCount-1. */ | |||||
| const LADSPA_PortRangeHint * PortRangeHints; | |||||
| /* This may be used by the plugin developer to pass any custom | |||||
| implementation data into an instantiate call. It must not be used | |||||
| or interpreted by the host. It is expected that most plugin | |||||
| writers will not use this facility as LADSPA_Handle should be | |||||
| used to hold instance data. */ | |||||
| void * ImplementationData; | |||||
| /* This member is a function pointer that instantiates a plugin. A | |||||
| handle is returned indicating the new plugin instance. The | |||||
| instantiation function accepts a sample rate as a parameter. The | |||||
| plugin descriptor from which this instantiate function was found | |||||
| must also be passed. This function must return NULL if | |||||
| instantiation fails. | |||||
| Note that instance initialisation should generally occur in | |||||
| activate() rather than here. */ | |||||
| LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, | |||||
| unsigned long SampleRate); | |||||
| /* This member is a function pointer that connects a port on an | |||||
| instantiated plugin to a memory location at which a block of data | |||||
| for the port will be read/written. The data location is expected | |||||
| to be an array of LADSPA_Data for audio ports or a single | |||||
| LADSPA_Data value for control ports. Memory issues will be | |||||
| managed by the host. The plugin must read/write the data at these | |||||
| locations every time run() or run_adding() is called and the data | |||||
| present at the time of this connection call should not be | |||||
| considered meaningful. | |||||
| connect_port() may be called more than once for a plugin instance | |||||
| to allow the host to change the buffers that the plugin is | |||||
| reading or writing. These calls may be made before or after | |||||
| activate() or deactivate() calls. | |||||
| connect_port() must be called at least once for each port before | |||||
| run() or run_adding() is called. When working with blocks of | |||||
| LADSPA_Data the plugin should pay careful attention to the block | |||||
| size passed to the run function as the block allocated may only | |||||
| just be large enough to contain the block of samples. | |||||
| Plugin writers should be aware that the host may elect to use the | |||||
| same buffer for more than one port and even use the same buffer | |||||
| for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). | |||||
| However, overlapped buffers or use of a single buffer for both | |||||
| audio and control data may result in unexpected behaviour. */ | |||||
| void (*connect_port)(LADSPA_Handle Instance, | |||||
| unsigned long Port, | |||||
| LADSPA_Data * DataLocation); | |||||
| /* This member is a function pointer that initialises a plugin | |||||
| instance and activates it for use. This is separated from | |||||
| instantiate() to aid real-time support and so that hosts can | |||||
| reinitialise a plugin instance by calling deactivate() and then | |||||
| activate(). In this case the plugin instance must reset all state | |||||
| information dependent on the history of the plugin instance | |||||
| except for any data locations provided by connect_port() and any | |||||
| gain set by set_run_adding_gain(). If there is nothing for | |||||
| activate() to do then the plugin writer may provide a NULL rather | |||||
| than an empty function. | |||||
| When present, hosts must call this function once before run() (or | |||||
| run_adding()) is called for the first time. This call should be | |||||
| made as close to the run() call as possible and indicates to | |||||
| real-time plugins that they are now live. Plugins should not rely | |||||
| on a prompt call to run() after activate(). activate() may not be | |||||
| called again unless deactivate() is called first. Note that | |||||
| connect_port() may be called before or after a call to | |||||
| activate(). */ | |||||
| void (*activate)(LADSPA_Handle Instance); | |||||
| /* This method is a function pointer that runs an instance of a | |||||
| plugin for a block. Two parameters are required: the first is a | |||||
| handle to the particular instance to be run and the second | |||||
| indicates the block size (in samples) for which the plugin | |||||
| instance may run. | |||||
| Note that if an activate() function exists then it must be called | |||||
| before run() or run_adding(). If deactivate() is called for a | |||||
| plugin instance then the plugin instance may not be reused until | |||||
| activate() has been called again. | |||||
| If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE | |||||
| then there are various things that the plugin should not do | |||||
| within the run() or run_adding() functions (see above). */ | |||||
| void (*run)(LADSPA_Handle Instance, | |||||
| unsigned long SampleCount); | |||||
| /* This method is a function pointer that runs an instance of a | |||||
| plugin for a block. This has identical behaviour to run() except | |||||
| in the way data is output from the plugin. When run() is used, | |||||
| values are written directly to the memory areas associated with | |||||
| the output ports. However when run_adding() is called, values | |||||
| must be added to the values already present in the memory | |||||
| areas. Furthermore, output values written must be scaled by the | |||||
| current gain set by set_run_adding_gain() (see below) before | |||||
| addition. | |||||
| run_adding() is optional. When it is not provided by a plugin, | |||||
| this function pointer must be set to NULL. When it is provided, | |||||
| the function set_run_adding_gain() must be provided also. */ | |||||
| void (*run_adding)(LADSPA_Handle Instance, | |||||
| unsigned long SampleCount); | |||||
| /* This method is a function pointer that sets the output gain for | |||||
| use when run_adding() is called (see above). If this function is | |||||
| never called the gain is assumed to default to 1. Gain | |||||
| information should be retained when activate() or deactivate() | |||||
| are called. | |||||
| This function should be provided by the plugin if and only if the | |||||
| run_adding() function is provided. When it is absent this | |||||
| function pointer must be set to NULL. */ | |||||
| void (*set_run_adding_gain)(LADSPA_Handle Instance, | |||||
| LADSPA_Data Gain); | |||||
| /* This is the counterpart to activate() (see above). If there is | |||||
| nothing for deactivate() to do then the plugin writer may provide | |||||
| a NULL rather than an empty function. | |||||
| Hosts must deactivate all activated units after they have been | |||||
| run() (or run_adding()) for the last time. This call should be | |||||
| made as close to the last run() call as possible and indicates to | |||||
| real-time plugins that they are no longer live. Plugins should | |||||
| not rely on prompt deactivation. Note that connect_port() may be | |||||
| called before or after a call to deactivate(). | |||||
| Deactivation is not similar to pausing as the plugin instance | |||||
| will be reinitialised when activate() is called to reuse it. */ | |||||
| void (*deactivate)(LADSPA_Handle Instance); | |||||
| /* Once an instance of a plugin has been finished with it can be | |||||
| deleted using the following function. The instance handle passed | |||||
| ceases to be valid after this call. | |||||
| If activate() was called for a plugin instance then a | |||||
| corresponding call to deactivate() must be made before cleanup() | |||||
| is called. */ | |||||
| void (*cleanup)(LADSPA_Handle Instance); | |||||
| } LADSPA_Descriptor; | |||||
| /**********************************************************************/ | |||||
| /* Accessing a Plugin: */ | |||||
| /* The exact mechanism by which plugins are loaded is host-dependent, | |||||
| however all most hosts will need to know is the name of shared | |||||
| object file containing the plugin types. To allow multiple hosts to | |||||
| share plugin types, hosts may wish to check for environment | |||||
| variable LADSPA_PATH. If present, this should contain a | |||||
| colon-separated path indicating directories that should be searched | |||||
| (in order) when loading plugin types. | |||||
| A plugin programmer must include a function called | |||||
| "ladspa_descriptor" with the following function prototype within | |||||
| the shared object file. This function will have C-style linkage (if | |||||
| you are using C++ this is taken care of by the `extern "C"' clause | |||||
| at the top of the file). | |||||
| A host will find the plugin shared object file by one means or | |||||
| another, find the ladspa_descriptor() function, call it, and | |||||
| proceed from there. | |||||
| Plugin types are accessed by index (not ID) using values from 0 | |||||
| upwards. Out of range indexes must result in this function | |||||
| returning NULL, so the plugin count can be determined by checking | |||||
| for the least index that results in NULL being returned. */ | |||||
| const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); | |||||
| /* Datatype corresponding to the ladspa_descriptor() function. */ | |||||
| typedef const LADSPA_Descriptor * | |||||
| (*LADSPA_Descriptor_Function)(unsigned long Index); | |||||
| /**********************************************************************/ | |||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #endif /* LADSPA_INCLUDED */ | |||||
| /* EOF */ | |||||
| @@ -0,0 +1,747 @@ | |||||
| /* | |||||
| Copyright 2008-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file forge.h An API for constructing LV2 atoms. | |||||
| This file provides an API for constructing Atoms which makes it relatively | |||||
| simple to build nested atoms of arbitrary complexity without requiring | |||||
| dynamic memory allocation. | |||||
| The API is based on successively appending the appropriate pieces to build a | |||||
| complete Atom. The size of containers is automatically updated. Functions | |||||
| that begin a container return (via their frame argument) a stack frame which | |||||
| must be popped when the container is finished. | |||||
| All output is written to a user-provided buffer or sink function. This | |||||
| makes it popssible to create create atoms on the stack, on the heap, in LV2 | |||||
| port buffers, in a ringbuffer, or elsewhere, all using the same API. | |||||
| This entire API is realtime safe if used with a buffer or a realtime safe | |||||
| sink, except lv2_atom_forge_init() which is only realtime safe if the URI | |||||
| map function is. | |||||
| Note these functions are all static inline, do not take their address. | |||||
| This header is non-normative, it is provided for convenience. | |||||
| */ | |||||
| #ifndef LV2_ATOM_FORGE_H | |||||
| #define LV2_ATOM_FORGE_H | |||||
| #include <assert.h> | |||||
| #include "atom.h" | |||||
| #include "atom-util.h" | |||||
| #include "urid.h" | |||||
| #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) | |||||
| # define LV2_ATOM_FORGE_DEPRECATED __attribute__((__deprecated__)) | |||||
| #else | |||||
| # define LV2_ATOM_FORGE_DEPRECATED | |||||
| #endif | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** Handle for LV2_Atom_Forge_Sink. */ | |||||
| typedef void* LV2_Atom_Forge_Sink_Handle; | |||||
| /** A reference to a chunk of written output. */ | |||||
| typedef intptr_t LV2_Atom_Forge_Ref; | |||||
| /** Sink function for writing output. See lv2_atom_forge_set_sink(). */ | |||||
| typedef LV2_Atom_Forge_Ref | |||||
| (*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle, | |||||
| const void* buf, | |||||
| uint32_t size); | |||||
| /** Function for resolving a reference. See lv2_atom_forge_set_sink(). */ | |||||
| typedef LV2_Atom* | |||||
| (*LV2_Atom_Forge_Deref_Func)(LV2_Atom_Forge_Sink_Handle handle, | |||||
| LV2_Atom_Forge_Ref ref); | |||||
| /** A stack frame used for keeping track of nested Atom containers. */ | |||||
| typedef struct _LV2_Atom_Forge_Frame { | |||||
| struct _LV2_Atom_Forge_Frame* parent; | |||||
| LV2_Atom_Forge_Ref ref; | |||||
| } LV2_Atom_Forge_Frame; | |||||
| /** A "forge" for creating atoms by appending to a buffer. */ | |||||
| typedef struct { | |||||
| uint8_t* buf; | |||||
| uint32_t offset; | |||||
| uint32_t size; | |||||
| LV2_Atom_Forge_Sink sink; | |||||
| LV2_Atom_Forge_Deref_Func deref; | |||||
| LV2_Atom_Forge_Sink_Handle handle; | |||||
| LV2_Atom_Forge_Frame* stack; | |||||
| LV2_URID Blank LV2_ATOM_FORGE_DEPRECATED; | |||||
| LV2_URID Bool; | |||||
| LV2_URID Chunk; | |||||
| LV2_URID Double; | |||||
| LV2_URID Float; | |||||
| LV2_URID Int; | |||||
| LV2_URID Long; | |||||
| LV2_URID Literal; | |||||
| LV2_URID Object; | |||||
| LV2_URID Path; | |||||
| LV2_URID Property; | |||||
| LV2_URID Resource LV2_ATOM_FORGE_DEPRECATED; | |||||
| LV2_URID Sequence; | |||||
| LV2_URID String; | |||||
| LV2_URID Tuple; | |||||
| LV2_URID URI; | |||||
| LV2_URID URID; | |||||
| LV2_URID Vector; | |||||
| } LV2_Atom_Forge; | |||||
| static inline void | |||||
| lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); | |||||
| /** | |||||
| Initialise @p forge. | |||||
| URIs will be mapped using @p map and stored, a reference to @p map itself is | |||||
| not held. | |||||
| */ | |||||
| static inline void | |||||
| lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) | |||||
| { | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||||
| #endif | |||||
| lv2_atom_forge_set_buffer(forge, NULL, 0); | |||||
| forge->Blank = map->map(map->handle, LV2_ATOM__Blank); | |||||
| forge->Bool = map->map(map->handle, LV2_ATOM__Bool); | |||||
| forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk); | |||||
| forge->Double = map->map(map->handle, LV2_ATOM__Double); | |||||
| forge->Float = map->map(map->handle, LV2_ATOM__Float); | |||||
| forge->Int = map->map(map->handle, LV2_ATOM__Int); | |||||
| forge->Long = map->map(map->handle, LV2_ATOM__Long); | |||||
| forge->Literal = map->map(map->handle, LV2_ATOM__Literal); | |||||
| forge->Object = map->map(map->handle, LV2_ATOM__Object); | |||||
| forge->Path = map->map(map->handle, LV2_ATOM__Path); | |||||
| forge->Property = map->map(map->handle, LV2_ATOM__Property); | |||||
| forge->Resource = map->map(map->handle, LV2_ATOM__Resource); | |||||
| forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence); | |||||
| forge->String = map->map(map->handle, LV2_ATOM__String); | |||||
| forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple); | |||||
| forge->URI = map->map(map->handle, LV2_ATOM__URI); | |||||
| forge->URID = map->map(map->handle, LV2_ATOM__URID); | |||||
| forge->Vector = map->map(map->handle, LV2_ATOM__Vector); | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| } | |||||
| static inline LV2_Atom* | |||||
| lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) | |||||
| { | |||||
| if (forge->buf) { | |||||
| return (LV2_Atom*)ref; | |||||
| } else { | |||||
| return forge->deref(forge->handle, ref); | |||||
| } | |||||
| } | |||||
| /** | |||||
| @name Object Stack | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Push a stack frame. | |||||
| This is done automatically by container functions (which take a stack frame | |||||
| pointer), but may be called by the user to push the top level container when | |||||
| writing to an existing Atom. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_push(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| LV2_Atom_Forge_Ref ref) | |||||
| { | |||||
| frame->parent = forge->stack; | |||||
| frame->ref = ref; | |||||
| forge->stack = frame; | |||||
| return ref; | |||||
| } | |||||
| /** Pop a stack frame. This must be called when a container is finished. */ | |||||
| static inline void | |||||
| lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) | |||||
| { | |||||
| assert(frame == forge->stack); | |||||
| forge->stack = frame->parent; | |||||
| } | |||||
| /** Return true iff the top of the stack has the given type. */ | |||||
| static inline bool | |||||
| lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) | |||||
| { | |||||
| return forge->stack && forge->stack->ref && | |||||
| (lv2_atom_forge_deref(forge, forge->stack->ref)->type == type); | |||||
| } | |||||
| /** Return true iff @p type is an atom:Object. */ | |||||
| static inline bool | |||||
| lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type) | |||||
| { | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||||
| #endif | |||||
| return (type == forge->Object || | |||||
| type == forge->Blank || | |||||
| type == forge->Resource); | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| } | |||||
| /** Return true iff @p type is an atom:Object with a blank ID. */ | |||||
| static inline bool | |||||
| lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge, | |||||
| uint32_t type, | |||||
| const LV2_Atom_Object_Body* body) | |||||
| { | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||||
| #endif | |||||
| return (type == forge->Blank || | |||||
| (type == forge->Object && body->id == 0)); | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| } | |||||
| /** | |||||
| @} | |||||
| @name Output Configuration | |||||
| @{ | |||||
| */ | |||||
| /** Set the output buffer where @p forge will write atoms. */ | |||||
| static inline void | |||||
| lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) | |||||
| { | |||||
| forge->buf = buf; | |||||
| forge->size = (uint32_t)size; | |||||
| forge->offset = 0; | |||||
| forge->deref = NULL; | |||||
| forge->sink = NULL; | |||||
| forge->handle = NULL; | |||||
| forge->stack = NULL; | |||||
| } | |||||
| /** | |||||
| Set the sink function where @p forge will write output. | |||||
| The return value of forge functions is an LV2_Atom_Forge_Ref which is an | |||||
| integer type safe to use as a pointer but is otherwise opaque. The sink | |||||
| function must return a ref that can be dereferenced to access as least | |||||
| sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For | |||||
| ringbuffers, this should be possible as long as the size of the buffer is a | |||||
| multiple of sizeof(LV2_Atom), since atoms are always aligned. | |||||
| Note that 0 is an invalid reference, so if you are using a buffer offset be | |||||
| sure to offset it such that 0 is never a valid reference. You will get | |||||
| confusing errors otherwise. | |||||
| */ | |||||
| static inline void | |||||
| lv2_atom_forge_set_sink(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Sink sink, | |||||
| LV2_Atom_Forge_Deref_Func deref, | |||||
| LV2_Atom_Forge_Sink_Handle handle) | |||||
| { | |||||
| forge->buf = NULL; | |||||
| forge->size = forge->offset = 0; | |||||
| forge->deref = deref; | |||||
| forge->sink = sink; | |||||
| forge->handle = handle; | |||||
| forge->stack = NULL; | |||||
| } | |||||
| /** | |||||
| @} | |||||
| @name Low Level Output | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Write raw output. This is used internally, but is also useful for writing | |||||
| atom types not explicitly supported by the forge API. Note the caller is | |||||
| responsible for ensuring the output is approriately padded. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size) | |||||
| { | |||||
| LV2_Atom_Forge_Ref out = 0; | |||||
| if (forge->sink) { | |||||
| out = forge->sink(forge->handle, data, size); | |||||
| } else { | |||||
| out = (LV2_Atom_Forge_Ref)forge->buf + (LV2_Atom_Forge_Ref)forge->offset; | |||||
| uint8_t* mem = forge->buf + forge->offset; | |||||
| if (forge->offset + size > forge->size) { | |||||
| return 0; | |||||
| } | |||||
| forge->offset += size; | |||||
| memcpy(mem, data, size); | |||||
| } | |||||
| for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { | |||||
| lv2_atom_forge_deref(forge, f->ref)->size += size; | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** Pad output accordingly so next write is 64-bit aligned. */ | |||||
| static inline void | |||||
| lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) | |||||
| { | |||||
| const uint64_t pad = 0; | |||||
| const uint32_t pad_size = lv2_atom_pad_size(written) - written; | |||||
| lv2_atom_forge_raw(forge, &pad, pad_size); | |||||
| } | |||||
| /** Write raw output, padding to 64-bits as necessary. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) | |||||
| { | |||||
| LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); | |||||
| if (out) { | |||||
| lv2_atom_forge_pad(forge, size); | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** Write a null-terminated string body. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_string_body(LV2_Atom_Forge* forge, | |||||
| const char* str, | |||||
| uint32_t len) | |||||
| { | |||||
| LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); | |||||
| if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { | |||||
| lv2_atom_forge_pad(forge, len + 1); | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** | |||||
| @} | |||||
| @name Atom Output | |||||
| @{ | |||||
| */ | |||||
| /** Write an atom:Atom header. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type) | |||||
| { | |||||
| const LV2_Atom a = { size, type }; | |||||
| return lv2_atom_forge_raw(forge, &a, sizeof(a)); | |||||
| } | |||||
| /** Write a primitive (fixed-size) atom. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a) | |||||
| { | |||||
| if (lv2_atom_forge_top_is(forge, forge->Vector)) { | |||||
| return lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size); | |||||
| } else { | |||||
| return lv2_atom_forge_write( | |||||
| forge, a, (uint32_t)sizeof(LV2_Atom) + a->size); | |||||
| } | |||||
| } | |||||
| /** Write an atom:Int. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val) | |||||
| { | |||||
| const LV2_Atom_Int a = { { sizeof(val), forge->Int }, val }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom:Long. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val) | |||||
| { | |||||
| const LV2_Atom_Long a = { { sizeof(val), forge->Long }, val }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom:Float. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_float(LV2_Atom_Forge* forge, float val) | |||||
| { | |||||
| const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom:Double. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_double(LV2_Atom_Forge* forge, double val) | |||||
| { | |||||
| const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom:Bool. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val) | |||||
| { | |||||
| const LV2_Atom_Bool a = { { sizeof(int32_t), forge->Bool }, val ? 1 : 0 }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom:URID. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id) | |||||
| { | |||||
| const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id }; | |||||
| return lv2_atom_forge_primitive(forge, &a.atom); | |||||
| } | |||||
| /** Write an atom compatible with atom:String. Used internally. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, | |||||
| uint32_t type, | |||||
| const char* str, | |||||
| uint32_t len) | |||||
| { | |||||
| const LV2_Atom_String a = { { len + 1, type } }; | |||||
| LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); | |||||
| if (out) { | |||||
| if (!lv2_atom_forge_string_body(forge, str, len)) { | |||||
| LV2_Atom* atom = lv2_atom_forge_deref(forge, out); | |||||
| atom->size = atom->type = 0; | |||||
| out = 0; | |||||
| } | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** Write an atom:String. Note that @p str need not be NULL terminated. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) | |||||
| { | |||||
| return lv2_atom_forge_typed_string(forge, forge->String, str, len); | |||||
| } | |||||
| /** | |||||
| Write an atom:URI. Note that @p uri need not be NULL terminated. | |||||
| This does not map the URI, but writes the complete URI string. To write | |||||
| a mapped URI, use lv2_atom_forge_urid(). | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) | |||||
| { | |||||
| return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); | |||||
| } | |||||
| /** Write an atom:Path. Note that @p path need not be NULL terminated. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) | |||||
| { | |||||
| return lv2_atom_forge_typed_string(forge, forge->Path, path, len); | |||||
| } | |||||
| /** Write an atom:Literal. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_literal(LV2_Atom_Forge* forge, | |||||
| const char* str, | |||||
| uint32_t len, | |||||
| uint32_t datatype, | |||||
| uint32_t lang) | |||||
| { | |||||
| const LV2_Atom_Literal a = { | |||||
| { (uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1), | |||||
| forge->Literal }, | |||||
| { datatype, | |||||
| lang } | |||||
| }; | |||||
| LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); | |||||
| if (out) { | |||||
| if (!lv2_atom_forge_string_body(forge, str, len)) { | |||||
| LV2_Atom* atom = lv2_atom_forge_deref(forge, out); | |||||
| atom->size = atom->type = 0; | |||||
| out = 0; | |||||
| } | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** Start an atom:Vector. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_vector_head(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| uint32_t child_size, | |||||
| uint32_t child_type) | |||||
| { | |||||
| const LV2_Atom_Vector a = { | |||||
| { sizeof(LV2_Atom_Vector_Body), forge->Vector }, | |||||
| { child_size, child_type } | |||||
| }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| } | |||||
| /** Write a complete atom:Vector. */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_vector(LV2_Atom_Forge* forge, | |||||
| uint32_t child_size, | |||||
| uint32_t child_type, | |||||
| uint32_t n_elems, | |||||
| const void* elems) | |||||
| { | |||||
| const LV2_Atom_Vector a = { | |||||
| { (uint32_t)(sizeof(LV2_Atom_Vector_Body) + n_elems * child_size), | |||||
| forge->Vector }, | |||||
| { child_size, child_type } | |||||
| }; | |||||
| LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); | |||||
| if (out) { | |||||
| lv2_atom_forge_write(forge, elems, child_size * n_elems); | |||||
| } | |||||
| return out; | |||||
| } | |||||
| /** | |||||
| Write the header of an atom:Tuple. | |||||
| The passed frame will be initialised to represent this tuple. To complete | |||||
| the tuple, write a sequence of atoms, then pop the frame with | |||||
| lv2_atom_forge_pop(). | |||||
| For example: | |||||
| @code | |||||
| // Write tuple (1, 2.0) | |||||
| LV2_Atom_Forge_Frame frame; | |||||
| LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame); | |||||
| lv2_atom_forge_int32(forge, 1); | |||||
| lv2_atom_forge_float(forge, 2.0); | |||||
| lv2_atom_forge_pop(forge, &frame); | |||||
| @endcode | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) | |||||
| { | |||||
| const LV2_Atom_Tuple a = { { 0, forge->Tuple } }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| } | |||||
| /** | |||||
| Write the header of an atom:Object. | |||||
| The passed frame will be initialised to represent this object. To complete | |||||
| the object, write a sequence of properties, then pop the frame with | |||||
| lv2_atom_forge_pop(). | |||||
| For example: | |||||
| @code | |||||
| LV2_URID eg_Cat = map("http://example.org/Cat"); | |||||
| LV2_URID eg_name = map("http://example.org/name"); | |||||
| // Start object with type eg_Cat and blank ID | |||||
| LV2_Atom_Forge_Frame frame; | |||||
| lv2_atom_forge_object(forge, &frame, 0, eg_Cat); | |||||
| // Append property eg:name = "Hobbes" | |||||
| lv2_atom_forge_key(forge, eg_name); | |||||
| lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes")); | |||||
| // Finish object | |||||
| lv2_atom_forge_pop(forge, &frame); | |||||
| @endcode | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_object(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| LV2_URID id, | |||||
| LV2_URID otype) | |||||
| { | |||||
| const LV2_Atom_Object a = { | |||||
| { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object }, | |||||
| { id, otype } | |||||
| }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| } | |||||
| /** | |||||
| The same as lv2_atom_forge_object(), but for object:Resource. | |||||
| This function is deprecated and should not be used in new code. | |||||
| Use lv2_atom_forge_object() directly instead. | |||||
| */ | |||||
| LV2_ATOM_FORGE_DEPRECATED | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_resource(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| LV2_URID id, | |||||
| LV2_URID otype) | |||||
| { | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||||
| #endif | |||||
| const LV2_Atom_Object a = { | |||||
| { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource }, | |||||
| { id, otype } | |||||
| }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| } | |||||
| /** | |||||
| The same as lv2_atom_forge_object(), but for object:Blank. | |||||
| This function is deprecated and should not be used in new code. | |||||
| Use lv2_atom_forge_object() directly instead. | |||||
| */ | |||||
| LV2_ATOM_FORGE_DEPRECATED | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_blank(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| uint32_t id, | |||||
| LV2_URID otype) | |||||
| { | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||||
| #endif | |||||
| const LV2_Atom_Object a = { | |||||
| { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank }, | |||||
| { id, otype } | |||||
| }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| #if defined(__clang__) | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| } | |||||
| /** | |||||
| Write a property key in an Object, to be followed by the value. | |||||
| See lv2_atom_forge_object() documentation for an example. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_key(LV2_Atom_Forge* forge, | |||||
| LV2_URID key) | |||||
| { | |||||
| const LV2_Atom_Property_Body a = { key, 0, { 0, 0 } }; | |||||
| return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); | |||||
| } | |||||
| /** | |||||
| Write the header for a property body in an object, with context. | |||||
| If you do not need the context, which is almost certainly the case, | |||||
| use the simpler lv2_atom_forge_key() instead. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_property_head(LV2_Atom_Forge* forge, | |||||
| LV2_URID key, | |||||
| LV2_URID context) | |||||
| { | |||||
| const LV2_Atom_Property_Body a = { key, context, { 0, 0 } }; | |||||
| return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); | |||||
| } | |||||
| /** | |||||
| Write the header for a Sequence. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge, | |||||
| LV2_Atom_Forge_Frame* frame, | |||||
| uint32_t unit) | |||||
| { | |||||
| const LV2_Atom_Sequence a = { | |||||
| { (uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence }, | |||||
| { unit, 0 } | |||||
| }; | |||||
| return lv2_atom_forge_push( | |||||
| forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); | |||||
| } | |||||
| /** | |||||
| Write the time stamp header of an Event (in a Sequence) in audio frames. | |||||
| After this, call the appropriate forge method(s) to write the body. Note | |||||
| the returned reference is to an LV2_Event which is NOT an Atom. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) | |||||
| { | |||||
| return lv2_atom_forge_write(forge, &frames, sizeof(frames)); | |||||
| } | |||||
| /** | |||||
| Write the time stamp header of an Event (in a Sequence) in beats. After | |||||
| this, call the appropriate forge method(s) to write the body. Note the | |||||
| returned reference is to an LV2_Event which is NOT an Atom. | |||||
| */ | |||||
| static inline LV2_Atom_Forge_Ref | |||||
| lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) | |||||
| { | |||||
| return lv2_atom_forge_write(forge, &beats, sizeof(beats)); | |||||
| } | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_ATOM_FORGE_H */ | |||||
| @@ -0,0 +1,249 @@ | |||||
| // lv2_atom_helpers.h | |||||
| // | |||||
| /**************************************************************************** | |||||
| Copyright (C) 2005-2013, rncbc aka Rui Nuno Capela. All rights reserved. | |||||
| This program is free software; you can redistribute it and/or | |||||
| modify it under the terms of the GNU General Public License | |||||
| as published by the Free Software Foundation; either version 2 | |||||
| of the License, or (at your option) any later version. | |||||
| This program is distributed in the hope that it will be useful, | |||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| GNU General Public License for more details. | |||||
| You should have received a copy of the GNU General Public License along | |||||
| with this program; if not, write to the Free Software Foundation, Inc., | |||||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
| *****************************************************************************/ | |||||
| /* Helper functions for LV2 atom:Sequence event buffer. | |||||
| * | |||||
| * tentatively adapted from: | |||||
| * | |||||
| * - lv2_evbuf.h,c - An abstract/opaque LV2 event buffer implementation. | |||||
| * | |||||
| * - event-helpers.h - Helper functions for the LV2 Event extension. | |||||
| * <http://lv2plug.in/ns/ext/event> | |||||
| * | |||||
| * Copyright 2008-2012 David Robillard <http://drobilla.net> | |||||
| */ | |||||
| #ifndef LV2_ATOM_HELPERS_H | |||||
| #define LV2_ATOM_HELPERS_H | |||||
| #include <stdint.h> | |||||
| #include <stdbool.h> | |||||
| #include <string.h> | |||||
| #include <stdlib.h> | |||||
| #include <assert.h> | |||||
| #include "atom-util.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| // An abstract/opaque LV2 atom:Sequence buffer. | |||||
| // | |||||
| typedef | |||||
| struct _LV2_Atom_Buffer | |||||
| { | |||||
| uint32_t capacity; | |||||
| uint32_t chunk_type; | |||||
| uint32_t sequence_type; | |||||
| LV2_Atom_Sequence atoms; | |||||
| } LV2_Atom_Buffer; | |||||
| // Clear and initialize an existing LV2 atom:Sequenece buffer. | |||||
| // | |||||
| static inline | |||||
| void lv2_atom_buffer_reset ( LV2_Atom_Buffer *buf, bool input ) | |||||
| { | |||||
| if (input) { | |||||
| buf->atoms.atom.size = sizeof(LV2_Atom_Sequence_Body); | |||||
| buf->atoms.atom.type = buf->sequence_type; | |||||
| } else { | |||||
| buf->atoms.atom.size = buf->capacity; | |||||
| buf->atoms.atom.type = buf->chunk_type; | |||||
| } | |||||
| } | |||||
| // Allocate a new, empty LV2 atom:Sequence buffer. | |||||
| // | |||||
| static inline | |||||
| LV2_Atom_Buffer *lv2_atom_buffer_new ( | |||||
| uint32_t capacity, uint32_t chunk_type, uint32_t sequence_type, bool input ) | |||||
| { | |||||
| LV2_Atom_Buffer *buf = (LV2_Atom_Buffer *) | |||||
| malloc(sizeof(LV2_Atom_Buffer) + sizeof(LV2_Atom_Sequence) + capacity); | |||||
| buf->capacity = capacity; | |||||
| buf->chunk_type = chunk_type; | |||||
| buf->sequence_type = sequence_type; | |||||
| lv2_atom_buffer_reset(buf, input); | |||||
| return buf; | |||||
| } | |||||
| // Free an LV2 atom:Sequenece buffer allocated with lv2_atome_buffer_new. | |||||
| // | |||||
| static inline | |||||
| void lv2_atom_buffer_free ( LV2_Atom_Buffer *buf ) | |||||
| { | |||||
| free(buf); | |||||
| } | |||||
| // Return the total padded size of events stored in a LV2 atom:Sequence buffer. | |||||
| // | |||||
| static inline | |||||
| uint32_t lv2_atom_buffer_get_size ( LV2_Atom_Buffer *buf ) | |||||
| { | |||||
| if (buf->atoms.atom.type == buf->sequence_type) | |||||
| return buf->atoms.atom.size - uint32_t(sizeof(LV2_Atom_Sequence_Body)); | |||||
| else | |||||
| return 0; | |||||
| } | |||||
| // Return the actual LV2 atom:Sequence implementation. | |||||
| // | |||||
| static inline | |||||
| LV2_Atom_Sequence *lv2_atom_buffer_get_sequence ( LV2_Atom_Buffer *buf ) | |||||
| { | |||||
| return &buf->atoms; | |||||
| } | |||||
| // An iterator over an atom:Sequence buffer. | |||||
| // | |||||
| typedef | |||||
| struct _LV2_Atom_Buffer_Iterator | |||||
| { | |||||
| LV2_Atom_Buffer *buf; | |||||
| uint32_t offset; | |||||
| } LV2_Atom_Buffer_Iterator; | |||||
| // Reset an iterator to point to the start of an LV2 atom:Sequence buffer. | |||||
| // | |||||
| static inline | |||||
| bool lv2_atom_buffer_begin ( | |||||
| LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) | |||||
| { | |||||
| iter->buf = buf; | |||||
| iter->offset = 0; | |||||
| return (buf->atoms.atom.size > 0); | |||||
| } | |||||
| // Reset an iterator to point to the end of an LV2 atom:Sequence buffer. | |||||
| // | |||||
| static inline | |||||
| bool lv2_atom_buffer_end ( | |||||
| LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) | |||||
| { | |||||
| iter->buf = buf; | |||||
| iter->offset = lv2_atom_pad_size(lv2_atom_buffer_get_size(buf)); | |||||
| return (iter->offset < buf->capacity - sizeof(LV2_Atom_Event)); | |||||
| } | |||||
| // Check if a LV2 atom:Sequenece buffer iterator is valid. | |||||
| // | |||||
| static inline | |||||
| bool lv2_atom_buffer_is_valid ( LV2_Atom_Buffer_Iterator *iter ) | |||||
| { | |||||
| return iter->offset < lv2_atom_buffer_get_size(iter->buf); | |||||
| } | |||||
| // Advance a LV2 atom:Sequenece buffer iterator forward one event. | |||||
| // | |||||
| static inline | |||||
| bool lv2_atom_buffer_increment ( LV2_Atom_Buffer_Iterator *iter ) | |||||
| { | |||||
| if (!lv2_atom_buffer_is_valid(iter)) | |||||
| return false; | |||||
| LV2_Atom_Buffer *buf = iter->buf; | |||||
| LV2_Atom_Sequence *atoms = &buf->atoms; | |||||
| uint32_t size = ((LV2_Atom_Event *) ((char *) | |||||
| LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset))->body.size; | |||||
| iter->offset += lv2_atom_pad_size(uint32_t(sizeof(LV2_Atom_Event)) + size); | |||||
| return true; | |||||
| } | |||||
| // Get the event currently pointed at a LV2 atom:Sequence buffer iterator. | |||||
| // | |||||
| static inline | |||||
| LV2_Atom_Event *lv2_atom_buffer_get ( | |||||
| LV2_Atom_Buffer_Iterator *iter, uint8_t **data ) | |||||
| { | |||||
| if (!lv2_atom_buffer_is_valid(iter)) | |||||
| return NULL; | |||||
| LV2_Atom_Buffer *buf = iter->buf; | |||||
| LV2_Atom_Sequence *atoms = &buf->atoms; | |||||
| LV2_Atom_Event *ev = (LV2_Atom_Event *) ((char *) | |||||
| LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); | |||||
| *data = (uint8_t *) LV2_ATOM_BODY(&ev->body); | |||||
| return ev; | |||||
| } | |||||
| // Write an event at a LV2 atom:Sequence buffer iterator. | |||||
| static inline | |||||
| bool lv2_atom_buffer_write ( | |||||
| LV2_Atom_Buffer_Iterator *iter, | |||||
| uint32_t frames, | |||||
| uint32_t /*subframes*/, | |||||
| uint32_t type, | |||||
| uint32_t size, | |||||
| const uint8_t *data ) | |||||
| { | |||||
| LV2_Atom_Buffer *buf = iter->buf; | |||||
| LV2_Atom_Sequence *atoms = &buf->atoms; | |||||
| if (buf->capacity - sizeof(LV2_Atom) - atoms->atom.size | |||||
| < sizeof(LV2_Atom_Event) + size) | |||||
| return false; | |||||
| LV2_Atom_Event *ev = (LV2_Atom_Event*) ((char *) | |||||
| LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); | |||||
| ev->time.frames = frames; | |||||
| ev->body.type = type; | |||||
| ev->body.size = size; | |||||
| memcpy(LV2_ATOM_BODY(&ev->body), data, size); | |||||
| size = lv2_atom_pad_size(uint32_t(sizeof(LV2_Atom_Event)) + size); | |||||
| atoms->atom.size += size; | |||||
| iter->offset += size; | |||||
| return true; | |||||
| } | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif // LV2_ATOM_HELPERS_H | |||||
| // end of lv2_atom_helpers.h | |||||
| @@ -0,0 +1,446 @@ | |||||
| /* | |||||
| Copyright 2008-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file util.h Helper functions for the LV2 Atom extension. | |||||
| Note these functions are all static inline, do not take their address. | |||||
| This header is non-normative, it is provided for convenience. | |||||
| */ | |||||
| #ifndef LV2_ATOM_UTIL_H | |||||
| #define LV2_ATOM_UTIL_H | |||||
| #include <stdarg.h> | |||||
| #include <stdint.h> | |||||
| #include <string.h> | |||||
| #include "atom.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** Pad a size to 64 bits. */ | |||||
| static inline uint32_t | |||||
| lv2_atom_pad_size(uint32_t size) | |||||
| { | |||||
| return (size + 7U) & (~7U); | |||||
| } | |||||
| /** Return the total size of @p atom, including the header. */ | |||||
| static inline uint32_t | |||||
| lv2_atom_total_size(const LV2_Atom* atom) | |||||
| { | |||||
| return (uint32_t)sizeof(LV2_Atom) + atom->size; | |||||
| } | |||||
| /** Return true iff @p atom is null. */ | |||||
| static inline bool | |||||
| lv2_atom_is_null(const LV2_Atom* atom) | |||||
| { | |||||
| return !atom || (atom->type == 0 && atom->size == 0); | |||||
| } | |||||
| /** Return true iff @p a is equal to @p b. */ | |||||
| static inline bool | |||||
| lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) | |||||
| { | |||||
| return (a == b) || ((a->type == b->type) && | |||||
| (a->size == b->size) && | |||||
| !memcmp(a + 1, b + 1, a->size)); | |||||
| } | |||||
| /** | |||||
| @name Sequence Iterator | |||||
| @{ | |||||
| */ | |||||
| /** Get an iterator pointing to the first event in a Sequence body. */ | |||||
| static inline const LV2_Atom_Event* | |||||
| lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) | |||||
| { | |||||
| return (const LV2_Atom_Event*)(body + 1); | |||||
| } | |||||
| /** Get an iterator pointing to the end of a Sequence body. */ | |||||
| static inline LV2_Atom_Event* | |||||
| lv2_atom_sequence_end(LV2_Atom_Sequence_Body* body, uint32_t size) | |||||
| { | |||||
| return (LV2_Atom_Event*)((uint8_t*)body + lv2_atom_pad_size(size)); | |||||
| } | |||||
| /** Return true iff @p i has reached the end of @p body. */ | |||||
| static inline bool | |||||
| lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, | |||||
| uint32_t size, | |||||
| const LV2_Atom_Event* i) | |||||
| { | |||||
| return (const uint8_t*)i >= ((const uint8_t*)body + size); | |||||
| } | |||||
| /** Return an iterator to the element following @p i. */ | |||||
| static inline const LV2_Atom_Event* | |||||
| lv2_atom_sequence_next(const LV2_Atom_Event* i) | |||||
| { | |||||
| return (const LV2_Atom_Event*)((const uint8_t*)i | |||||
| + sizeof(LV2_Atom_Event) | |||||
| + lv2_atom_pad_size(i->body.size)); | |||||
| } | |||||
| /** | |||||
| A macro for iterating over all events in a Sequence. | |||||
| @param seq The sequence to iterate over | |||||
| @param iter The name of the iterator | |||||
| This macro is used similarly to a for loop (which it expands to), e.g.: | |||||
| @code | |||||
| LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) { | |||||
| // Do something with ev (an LV2_Atom_Event*) here... | |||||
| } | |||||
| @endcode | |||||
| */ | |||||
| #define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ | |||||
| for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \ | |||||
| !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ | |||||
| (iter) = lv2_atom_sequence_next(iter)) | |||||
| /** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ | |||||
| #define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ | |||||
| for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(body); \ | |||||
| !lv2_atom_sequence_is_end(body, size, (iter)); \ | |||||
| (iter) = lv2_atom_sequence_next(iter)) | |||||
| /** | |||||
| @} | |||||
| @name Sequence Utilities | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Clear all events from @p sequence. | |||||
| This simply resets the size field, the other fields are left untouched. | |||||
| */ | |||||
| static inline void | |||||
| lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) | |||||
| { | |||||
| seq->atom.size = sizeof(LV2_Atom_Sequence_Body); | |||||
| } | |||||
| /** | |||||
| Append an event at the end of @p sequence. | |||||
| @param seq Sequence to append to. | |||||
| @param capacity Total capacity of the sequence atom | |||||
| (e.g. as set by the host for sequence output ports). | |||||
| @param event Event to write. | |||||
| @return A pointer to the newly written event in @p seq, | |||||
| or NULL on failure (insufficient space). | |||||
| */ | |||||
| static inline LV2_Atom_Event* | |||||
| lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, | |||||
| uint32_t capacity, | |||||
| const LV2_Atom_Event* event) | |||||
| { | |||||
| const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; | |||||
| if (capacity - seq->atom.size < total_size) { | |||||
| return NULL; | |||||
| } | |||||
| LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); | |||||
| memcpy(e, event, total_size); | |||||
| seq->atom.size += lv2_atom_pad_size(total_size); | |||||
| return e; | |||||
| } | |||||
| /** | |||||
| @} | |||||
| @name Tuple Iterator | |||||
| @{ | |||||
| */ | |||||
| /** Get an iterator pointing to the first element in @p tup. */ | |||||
| static inline const LV2_Atom* | |||||
| lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) | |||||
| { | |||||
| return (const LV2_Atom*)(LV2_ATOM_BODY_CONST(tup)); | |||||
| } | |||||
| /** Return true iff @p i has reached the end of @p body. */ | |||||
| static inline bool | |||||
| lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i) | |||||
| { | |||||
| return (const uint8_t*)i >= ((const uint8_t*)body + size); | |||||
| } | |||||
| /** Return an iterator to the element following @p i. */ | |||||
| static inline const LV2_Atom* | |||||
| lv2_atom_tuple_next(const LV2_Atom* i) | |||||
| { | |||||
| return (const LV2_Atom*)( | |||||
| (const uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size)); | |||||
| } | |||||
| /** | |||||
| A macro for iterating over all properties of a Tuple. | |||||
| @param tuple The tuple to iterate over | |||||
| @param iter The name of the iterator | |||||
| This macro is used similarly to a for loop (which it expands to), e.g.: | |||||
| @code | |||||
| LV2_ATOMO_TUPLE_FOREACH(tuple, elem) { | |||||
| // Do something with elem (an LV2_Atom*) here... | |||||
| } | |||||
| @endcode | |||||
| */ | |||||
| #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ | |||||
| for (const LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \ | |||||
| !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->size, (iter)); \ | |||||
| (iter) = lv2_atom_tuple_next(iter)) | |||||
| /** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ | |||||
| #define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ | |||||
| for (const LV2_Atom* (iter) = (const LV2_Atom*)body; \ | |||||
| !lv2_atom_tuple_is_end(body, size, (iter)); \ | |||||
| (iter) = lv2_atom_tuple_next(iter)) | |||||
| /** | |||||
| @} | |||||
| @name Object Iterator | |||||
| @{ | |||||
| */ | |||||
| /** Return a pointer to the first property in @p body. */ | |||||
| static inline const LV2_Atom_Property_Body* | |||||
| lv2_atom_object_begin(const LV2_Atom_Object_Body* body) | |||||
| { | |||||
| return (const LV2_Atom_Property_Body*)(body + 1); | |||||
| } | |||||
| /** Return true iff @p i has reached the end of @p obj. */ | |||||
| static inline bool | |||||
| lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, | |||||
| uint32_t size, | |||||
| const LV2_Atom_Property_Body* i) | |||||
| { | |||||
| return (const uint8_t*)i >= ((const uint8_t*)body + size); | |||||
| } | |||||
| /** Return an iterator to the property following @p i. */ | |||||
| static inline const LV2_Atom_Property_Body* | |||||
| lv2_atom_object_next(const LV2_Atom_Property_Body* i) | |||||
| { | |||||
| const LV2_Atom* const value = (const LV2_Atom*)( | |||||
| (const uint8_t*)i + 2 * sizeof(uint32_t)); | |||||
| return (const LV2_Atom_Property_Body*)( | |||||
| (const uint8_t*)i + lv2_atom_pad_size( | |||||
| (uint32_t)sizeof(LV2_Atom_Property_Body) + value->size)); | |||||
| } | |||||
| /** | |||||
| A macro for iterating over all properties of an Object. | |||||
| @param obj The object to iterate over | |||||
| @param iter The name of the iterator | |||||
| This macro is used similarly to a for loop (which it expands to), e.g.: | |||||
| @code | |||||
| LV2_ATOM_OBJECT_FOREACH(object, i) { | |||||
| // Do something with prop (an LV2_Atom_Property_Body*) here... | |||||
| } | |||||
| @endcode | |||||
| */ | |||||
| #define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ | |||||
| for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \ | |||||
| !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ | |||||
| (iter) = lv2_atom_object_next(iter)) | |||||
| /** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ | |||||
| #define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ | |||||
| for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(body); \ | |||||
| !lv2_atom_object_is_end(body, size, (iter)); \ | |||||
| (iter) = lv2_atom_object_next(iter)) | |||||
| /** | |||||
| @} | |||||
| @name Object Query | |||||
| @{ | |||||
| */ | |||||
| /** A single entry in an Object query. */ | |||||
| typedef struct { | |||||
| uint32_t key; /**< Key to query (input set by user) */ | |||||
| const LV2_Atom** value; /**< Found value (output set by query function) */ | |||||
| } LV2_Atom_Object_Query; | |||||
| static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = { 0, NULL }; | |||||
| /** | |||||
| Get an object's values for various keys. | |||||
| The value pointer of each item in @p query will be set to the location of | |||||
| the corresponding value in @p object. Every value pointer in @p query MUST | |||||
| be initialised to NULL. This function reads @p object in a single linear | |||||
| sweep. By allocating @p query on the stack, objects can be "queried" | |||||
| quickly without allocating any memory. This function is realtime safe. | |||||
| This function can only do "flat" queries, it is not smart enough to match | |||||
| variables in nested objects. | |||||
| For example: | |||||
| @code | |||||
| const LV2_Atom* name = NULL; | |||||
| const LV2_Atom* age = NULL; | |||||
| LV2_Atom_Object_Query q[] = { | |||||
| { urids.eg_name, &name }, | |||||
| { urids.eg_age, &age }, | |||||
| LV2_ATOM_OBJECT_QUERY_END | |||||
| }; | |||||
| lv2_atom_object_query(obj, q); | |||||
| // name and age are now set to the appropriate values in obj, or NULL. | |||||
| @endcode | |||||
| */ | |||||
| static inline int | |||||
| lv2_atom_object_query(const LV2_Atom_Object* object, | |||||
| LV2_Atom_Object_Query* query) | |||||
| { | |||||
| int matches = 0; | |||||
| int n_queries = 0; | |||||
| /* Count number of query keys so we can short-circuit when done */ | |||||
| for (LV2_Atom_Object_Query* q = query; q->key; ++q) { | |||||
| ++n_queries; | |||||
| } | |||||
| LV2_ATOM_OBJECT_FOREACH(object, prop) { | |||||
| for (LV2_Atom_Object_Query* q = query; q->key; ++q) { | |||||
| if (q->key == prop->key && !*q->value) { | |||||
| *q->value = &prop->value; | |||||
| if (++matches == n_queries) { | |||||
| return matches; | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| return matches; | |||||
| } | |||||
| /** | |||||
| Body only version of lv2_atom_object_get(). | |||||
| */ | |||||
| static inline int | |||||
| lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) | |||||
| { | |||||
| int matches = 0; | |||||
| int n_queries = 0; | |||||
| /* Count number of keys so we can short-circuit when done */ | |||||
| va_list args; | |||||
| va_start(args, body); | |||||
| for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { | |||||
| if (!va_arg(args, const LV2_Atom**)) { | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| va_end(args); | |||||
| LV2_ATOM_OBJECT_BODY_FOREACH(body, size, prop) { | |||||
| va_start(args, body); | |||||
| for (int i = 0; i < n_queries; ++i) { | |||||
| uint32_t qkey = va_arg(args, uint32_t); | |||||
| const LV2_Atom** qval = va_arg(args, const LV2_Atom**); | |||||
| if (qkey == prop->key && !*qval) { | |||||
| *qval = &prop->value; | |||||
| if (++matches == n_queries) { | |||||
| return matches; | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| va_end(args); | |||||
| } | |||||
| return matches; | |||||
| } | |||||
| /** | |||||
| Variable argument version of lv2_atom_object_query(). | |||||
| This is nicer-looking in code, but a bit more error-prone since it is not | |||||
| type safe and the argument list must be terminated. | |||||
| The arguments should be a series of uint32_t key and const LV2_Atom** value | |||||
| pairs, terminated by a zero key. The value pointers MUST be initialized to | |||||
| NULL. For example: | |||||
| @code | |||||
| const LV2_Atom* name = NULL; | |||||
| const LV2_Atom* age = NULL; | |||||
| lv2_atom_object_get(obj, | |||||
| uris.name_key, &name, | |||||
| uris.age_key, &age, | |||||
| 0); | |||||
| @endcode | |||||
| */ | |||||
| static inline int | |||||
| lv2_atom_object_get(const LV2_Atom_Object* object, ...) | |||||
| { | |||||
| int matches = 0; | |||||
| int n_queries = 0; | |||||
| /* Count number of keys so we can short-circuit when done */ | |||||
| va_list args; | |||||
| va_start(args, object); | |||||
| for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { | |||||
| if (!va_arg(args, const LV2_Atom**)) { | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| va_end(args); | |||||
| LV2_ATOM_OBJECT_FOREACH(object, prop) { | |||||
| va_start(args, object); | |||||
| for (int i = 0; i < n_queries; ++i) { | |||||
| uint32_t qkey = va_arg(args, uint32_t); | |||||
| const LV2_Atom** qval = va_arg(args, const LV2_Atom**); | |||||
| if (qkey == prop->key && !*qval) { | |||||
| *qval = &prop->value; | |||||
| if (++matches == n_queries) { | |||||
| return matches; | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| va_end(args); | |||||
| } | |||||
| return matches; | |||||
| } | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_ATOM_UTIL_H */ | |||||
| @@ -0,0 +1,246 @@ | |||||
| /* | |||||
| Copyright 2008-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file atom.h C header for the LV2 Atom extension | |||||
| <http://lv2plug.in/ns/ext/atom>. | |||||
| */ | |||||
| #ifndef LV2_ATOM_H | |||||
| #define LV2_ATOM_H | |||||
| #include <stdint.h> | |||||
| #include <stddef.h> | |||||
| #define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" | |||||
| #define LV2_ATOM_PREFIX LV2_ATOM_URI "#" | |||||
| #define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" | |||||
| #define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" | |||||
| #define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" | |||||
| #define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" | |||||
| #define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" | |||||
| #define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" | |||||
| #define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" | |||||
| #define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" | |||||
| #define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" | |||||
| #define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" | |||||
| #define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" | |||||
| #define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" | |||||
| #define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" | |||||
| #define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" | |||||
| #define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" | |||||
| #define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" | |||||
| #define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" | |||||
| #define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" | |||||
| #define LV2_ATOM__String LV2_ATOM_PREFIX "String" | |||||
| #define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" | |||||
| #define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" | |||||
| #define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" | |||||
| #define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" | |||||
| #define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" | |||||
| #define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" | |||||
| #define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" | |||||
| #define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" | |||||
| #define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" | |||||
| #define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" | |||||
| #define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" | |||||
| #define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" | |||||
| #define LV2_ATOM_REFERENCE_TYPE 0 | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** This expression will fail to compile if double does not fit in 64 bits. */ | |||||
| typedef char lv2_atom_assert_double_fits_in_64_bits[ | |||||
| ((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; | |||||
| /** | |||||
| Return a pointer to the contents of an Atom. The "contents" of an atom | |||||
| is the data past the complete type-specific header. | |||||
| @param type The type of the atom, e.g. LV2_Atom_String. | |||||
| @param atom A variable-sized atom. | |||||
| */ | |||||
| #define LV2_ATOM_CONTENTS(type, atom) \ | |||||
| ((uint8_t*)(atom) + sizeof(type)) | |||||
| /** | |||||
| Const version of LV2_ATOM_CONTENTS. | |||||
| */ | |||||
| #define LV2_ATOM_CONTENTS_CONST(type, atom) \ | |||||
| ((const uint8_t*)(atom) + sizeof(type)) | |||||
| /** | |||||
| Return a pointer to the body of an Atom. The "body" of an atom is the | |||||
| data just past the LV2_Atom head (i.e. the same offset for all types). | |||||
| */ | |||||
| #define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom) | |||||
| /** | |||||
| Const version of LV2_ATOM_BODY. | |||||
| */ | |||||
| #define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom) | |||||
| /** The header of an atom:Atom. */ | |||||
| typedef struct { | |||||
| uint32_t size; /**< Size in bytes, not including type and size. */ | |||||
| uint32_t type; /**< Type of this atom (mapped URI). */ | |||||
| } LV2_Atom; | |||||
| /** An atom:Int or atom:Bool. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| int32_t body; /**< Integer value. */ | |||||
| } LV2_Atom_Int; | |||||
| /** An atom:Long. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| int64_t body; /**< Integer value. */ | |||||
| } LV2_Atom_Long; | |||||
| /** An atom:Float. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| float body; /**< Floating point value. */ | |||||
| } LV2_Atom_Float; | |||||
| /** An atom:Double. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| double body; /**< Floating point value. */ | |||||
| } LV2_Atom_Double; | |||||
| /** An atom:Bool. May be cast to LV2_Atom. */ | |||||
| typedef LV2_Atom_Int LV2_Atom_Bool; | |||||
| /** An atom:URID. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| uint32_t body; /**< URID. */ | |||||
| } LV2_Atom_URID; | |||||
| /** An atom:String. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| /* Contents (a null-terminated UTF-8 string) follow here. */ | |||||
| } LV2_Atom_String; | |||||
| /** The body of an atom:Literal. */ | |||||
| typedef struct { | |||||
| uint32_t datatype; /**< Datatype URID. */ | |||||
| uint32_t lang; /**< Language URID. */ | |||||
| /* Contents (a null-terminated UTF-8 string) follow here. */ | |||||
| } LV2_Atom_Literal_Body; | |||||
| /** An atom:Literal. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| LV2_Atom_Literal_Body body; /**< Body. */ | |||||
| } LV2_Atom_Literal; | |||||
| /** An atom:Tuple. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| /* Contents (a series of complete atoms) follow here. */ | |||||
| } LV2_Atom_Tuple; | |||||
| /** The body of an atom:Vector. */ | |||||
| typedef struct { | |||||
| uint32_t child_size; /**< The size of each element in the vector. */ | |||||
| uint32_t child_type; /**< The type of each element in the vector. */ | |||||
| /* Contents (a series of packed atom bodies) follow here. */ | |||||
| } LV2_Atom_Vector_Body; | |||||
| /** An atom:Vector. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| LV2_Atom_Vector_Body body; /**< Body. */ | |||||
| } LV2_Atom_Vector; | |||||
| /** The body of an atom:Property (e.g. in an atom:Object). */ | |||||
| typedef struct { | |||||
| uint32_t key; /**< Key (predicate) (mapped URI). */ | |||||
| uint32_t context; /**< Context URID (may be, and generally is, 0). */ | |||||
| LV2_Atom value; /**< Value atom header. */ | |||||
| /* Value atom body follows here. */ | |||||
| } LV2_Atom_Property_Body; | |||||
| /** An atom:Property. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| LV2_Atom_Property_Body body; /**< Body. */ | |||||
| } LV2_Atom_Property; | |||||
| /** The body of an atom:Object. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| uint32_t id; /**< URID, or 0 for blank. */ | |||||
| uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */ | |||||
| /* Contents (a series of property bodies) follow here. */ | |||||
| } LV2_Atom_Object_Body; | |||||
| /** An atom:Object. May be cast to LV2_Atom. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| LV2_Atom_Object_Body body; /**< Body. */ | |||||
| } LV2_Atom_Object; | |||||
| /** The header of an atom:Event. Note this type is NOT an LV2_Atom. */ | |||||
| typedef struct { | |||||
| /** Time stamp. Which type is valid is determined by context. */ | |||||
| union { | |||||
| int64_t frames; /**< Time in audio frames. */ | |||||
| double beats; /**< Time in beats. */ | |||||
| } time; | |||||
| LV2_Atom body; /**< Event body atom header. */ | |||||
| /* Body atom contents follow here. */ | |||||
| } LV2_Atom_Event; | |||||
| /** | |||||
| The body of an atom:Sequence (a sequence of events). | |||||
| The unit field is either a URID that described an appropriate time stamp | |||||
| type, or may be 0 where a default stamp type is known. For | |||||
| LV2_Descriptor::run(), the default stamp type is audio frames. | |||||
| The contents of a sequence is a series of LV2_Atom_Event, each aligned | |||||
| to 64-bits, e.g.: | |||||
| <pre> | |||||
| | Event 1 (size 6) | Event 2 | |||||
| | | | | | | | | | | |||||
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |||||
| |FRAMES |SUBFRMS|TYPE |SIZE |DATADATADATAPAD|FRAMES |SUBFRMS|... | |||||
| </pre> | |||||
| */ | |||||
| typedef struct { | |||||
| uint32_t unit; /**< URID of unit of event time stamps. */ | |||||
| uint32_t pad; /**< Currently unused. */ | |||||
| /* Contents (a series of events) follow here. */ | |||||
| } LV2_Atom_Sequence_Body; | |||||
| /** An atom:Sequence. */ | |||||
| typedef struct { | |||||
| LV2_Atom atom; /**< Atom header. */ | |||||
| LV2_Atom_Sequence_Body body; /**< Body. */ | |||||
| } LV2_Atom_Sequence; | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_ATOM_H */ | |||||
| @@ -0,0 +1,30 @@ | |||||
| /* | |||||
| Copyright 2007-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef LV2_BUF_SIZE_H | |||||
| #define LV2_BUF_SIZE_H | |||||
| #define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" | |||||
| #define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" | |||||
| #define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" | |||||
| #define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" | |||||
| #define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" | |||||
| #define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" | |||||
| #define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" | |||||
| #define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" | |||||
| #endif /* LV2_BUF_SIZE_H */ | |||||
| @@ -0,0 +1,63 @@ | |||||
| /* | |||||
| LV2 Data Access Extension | |||||
| Copyright 2008-2011 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file data-access.h | |||||
| C header for the LV2 Extension Data extension | |||||
| <http://lv2plug.in/ns/ext/data-access>. | |||||
| This extension defines a method for (e.g.) plugin UIs to have (possibly | |||||
| marshalled) access to the extension_data function on a plugin instance. | |||||
| */ | |||||
| #ifndef LV2_DATA_ACCESS_H | |||||
| #define LV2_DATA_ACCESS_H | |||||
| #define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** | |||||
| The data field of the LV2_Feature for this extension. | |||||
| To support this feature the host must pass an LV2_Feature struct to the | |||||
| instantiate method with URI "http://lv2plug.in/ns/ext/data-access" | |||||
| and data pointed to an instance of this struct. | |||||
| */ | |||||
| typedef struct { | |||||
| /** | |||||
| A pointer to a method the UI can call to get data (of a type specified | |||||
| by some other extension) from the plugin. | |||||
| This call never is never guaranteed to return anything, UIs should | |||||
| degrade gracefully if direct access to the plugin data is not possible | |||||
| (in which case this function will return NULL). | |||||
| This is for access to large data that can only possibly work if the UI | |||||
| and plugin are running in the same process. For all other things, use | |||||
| the normal LV2 UI communication system. | |||||
| */ | |||||
| const void* (*data_access)(const char* uri); | |||||
| } LV2_Extension_Data_Feature; | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_DATA_ACCESS_H */ | |||||
| @@ -0,0 +1,144 @@ | |||||
| /* | |||||
| Dynamic manifest specification for LV2 | |||||
| Copyright 2008-2011 Stefano D'Angelo <zanga.mail@gmail.com> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file dynmanifest.h | |||||
| C header for the LV2 Dynamic Manifest extension | |||||
| <http://lv2plug.in/ns/ext/dynmanifest>. | |||||
| Revision: 1.2 | |||||
| */ | |||||
| #ifndef LV2_DYN_MANIFEST_H_INCLUDED | |||||
| #define LV2_DYN_MANIFEST_H_INCLUDED | |||||
| #include <stdio.h> | |||||
| #include "lv2.h" | |||||
| #define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** | |||||
| Dynamic manifest generator handle. | |||||
| This handle indicates a particular status of a dynamic manifest generator. | |||||
| The host MUST NOT attempt to interpret it and, unlikely LV2_Handle, it is | |||||
| NOT even valid to compare this to NULL. The dynamic manifest generator MAY | |||||
| use it to reference internal data. | |||||
| */ | |||||
| typedef void * LV2_Dyn_Manifest_Handle; | |||||
| /** | |||||
| Generate the dynamic manifest. | |||||
| @param handle Pointer to an uninitialized dynamic manifest generator handle. | |||||
| @param features NULL terminated array of LV2_Feature structs which represent | |||||
| the features the host supports. The dynamic manifest generator may refuse to | |||||
| (re)generate the dynamic manifest if required features are not found here | |||||
| (however hosts SHOULD NOT use this as a discovery mechanism, instead of | |||||
| reading the static manifest file). This array must always exist; if a host | |||||
| has no features, it MUST pass a single element array containing NULL. | |||||
| @return 0 on success, otherwise a non-zero error code. The host SHOULD | |||||
| evaluate the result of the operation by examining the returned value and | |||||
| MUST NOT try to interpret the value of handle. | |||||
| */ | |||||
| int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle * handle, | |||||
| const LV2_Feature *const * features); | |||||
| /** | |||||
| Fetch a "list" of subject URIs described in the dynamic manifest. | |||||
| The dynamic manifest generator has to fill the resource only with the needed | |||||
| triples to make the host aware of the "objects" it wants to expose. For | |||||
| example, if the plugin library exposes a regular LV2 plugin, it should | |||||
| output only a triple like the following: | |||||
| <http://www.example.com/plugin/uri> a lv2:Plugin . | |||||
| The objects that are elegible for exposure are those that would need to be | |||||
| represented by a subject node in a static manifest. | |||||
| @param handle Dynamic manifest generator handle. | |||||
| @param fp FILE * identifying the resource the host has to set up for the | |||||
| dynamic manifest generator. The host MUST pass a writable, empty resource to | |||||
| this function, and the dynamic manifest generator MUST ONLY perform write | |||||
| operations on it at the end of the stream (e.g., using only fprintf(), | |||||
| fwrite() and similar). | |||||
| @return 0 on success, otherwise a non-zero error code. | |||||
| */ | |||||
| int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, | |||||
| FILE * fp); | |||||
| /** | |||||
| Function that fetches data related to a specific URI. | |||||
| The dynamic manifest generator has to fill the resource with data related to | |||||
| object represented by the given URI. For example, if the library exposes a | |||||
| regular LV2 plugin whose URI, as retrieved by the host using | |||||
| lv2_dyn_manifest_get_subjects() is http://www.example.com/plugin/uri, it | |||||
| should output something like: | |||||
| <pre> | |||||
| <http://www.example.com/plugin/uri> | |||||
| a lv2:Plugin ; | |||||
| doap:name "My Plugin" ; | |||||
| lv2:binary <mylib.so> ; | |||||
| etc:etc "..." . | |||||
| </pre> | |||||
| @param handle Dynamic manifest generator handle. | |||||
| @param fp FILE * identifying the resource the host has to set up for the | |||||
| dynamic manifest generator. The host MUST pass a writable resource to this | |||||
| function, and the dynamic manifest generator MUST ONLY perform write | |||||
| operations on it at the current position of the stream (e.g. using only | |||||
| fprintf(), fwrite() and similar). | |||||
| @param uri URI to get data about (in the "plain" form, i.e., absolute URI | |||||
| without Turtle prefixes). | |||||
| @return 0 on success, otherwise a non-zero error code. | |||||
| */ | |||||
| int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, | |||||
| FILE * fp, | |||||
| const char * uri); | |||||
| /** | |||||
| Function that ends the operations on the dynamic manifest generator. | |||||
| This function SHOULD be used by the dynamic manifest generator to perform | |||||
| cleanup operations, etc. | |||||
| Once this function is called, referring to handle will cause undefined | |||||
| behavior. | |||||
| @param handle Dynamic manifest generator handle. | |||||
| */ | |||||
| void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); | |||||
| #ifdef __cplusplus | |||||
| } | |||||
| #endif | |||||
| #endif /* LV2_DYN_MANIFEST_H_INCLUDED */ | |||||
| @@ -0,0 +1,266 @@ | |||||
| /* | |||||
| Copyright 2008-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file event-helpers.h Helper functions for the LV2 Event extension | |||||
| <http://lv2plug.in/ns/ext/event>. | |||||
| */ | |||||
| #ifndef LV2_EVENT_HELPERS_H | |||||
| #define LV2_EVENT_HELPERS_H | |||||
| #include <stdint.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "event.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** @file | |||||
| * Helper functions for the LV2 Event extension | |||||
| * <http://lv2plug.in/ns/ext/event>. | |||||
| * | |||||
| * These functions are provided for convenience only, use of them is not | |||||
| * required for supporting lv2ev (i.e. the events extension is defined by the | |||||
| * raw buffer format described in lv2_event.h and NOT by this API). | |||||
| * | |||||
| * Note that these functions are all static inline which basically means: | |||||
| * do not take the address of these functions. */ | |||||
| /** Pad a size to 64 bits (for event sizes) */ | |||||
| static inline uint16_t | |||||
| lv2_event_pad_size(uint16_t size) | |||||
| { | |||||
| return (uint16_t)(size + 7U) & (uint16_t)(~7U); | |||||
| } | |||||
| /** Initialize (empty, reset..) an existing event buffer. | |||||
| * The contents of buf are ignored entirely and overwritten, except capacity | |||||
| * which is unmodified. */ | |||||
| static inline void | |||||
| lv2_event_buffer_reset(LV2_Event_Buffer* buf, | |||||
| uint16_t stamp_type, | |||||
| uint8_t* data) | |||||
| { | |||||
| buf->data = data; | |||||
| buf->header_size = sizeof(LV2_Event_Buffer); | |||||
| buf->stamp_type = stamp_type; | |||||
| buf->event_count = 0; | |||||
| buf->size = 0; | |||||
| } | |||||
| /** Allocate a new, empty event buffer. */ | |||||
| static inline LV2_Event_Buffer* | |||||
| lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) | |||||
| { | |||||
| const size_t size = sizeof(LV2_Event_Buffer) + capacity; | |||||
| LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(size); | |||||
| if (buf != NULL) { | |||||
| buf->capacity = capacity; | |||||
| lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1)); | |||||
| return buf; | |||||
| } else { | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| /** An iterator over an LV2_Event_Buffer. | |||||
| * | |||||
| * Multiple simultaneous read iterators over a single buffer is fine, | |||||
| * but changing the buffer invalidates all iterators (e.g. RW Lock). */ | |||||
| typedef struct { | |||||
| LV2_Event_Buffer* buf; | |||||
| uint32_t offset; | |||||
| } LV2_Event_Iterator; | |||||
| /** Reset an iterator to point to the start of @a buf. | |||||
| * @return True if @a iter is valid, otherwise false (buffer is empty) */ | |||||
| static inline bool | |||||
| lv2_event_begin(LV2_Event_Iterator* iter, | |||||
| LV2_Event_Buffer* buf) | |||||
| { | |||||
| iter->buf = buf; | |||||
| iter->offset = 0; | |||||
| return (buf->size > 0); | |||||
| } | |||||
| /** Check if @a iter is valid. | |||||
| * @return True if @a iter is valid, otherwise false (past end of buffer) */ | |||||
| static inline bool | |||||
| lv2_event_is_valid(LV2_Event_Iterator* iter) | |||||
| { | |||||
| return (iter->buf && (iter->offset < iter->buf->size)); | |||||
| } | |||||
| /** Advance @a iter forward one event. | |||||
| * @a iter must be valid. | |||||
| * @return True if @a iter is valid, otherwise false (reached end of buffer) */ | |||||
| static inline bool | |||||
| lv2_event_increment(LV2_Event_Iterator* iter) | |||||
| { | |||||
| if (!lv2_event_is_valid(iter)) { | |||||
| return false; | |||||
| } | |||||
| LV2_Event* const ev = (LV2_Event*)( | |||||
| (uint8_t*)iter->buf->data + iter->offset); | |||||
| iter->offset += lv2_event_pad_size( | |||||
| (uint16_t)((uint16_t)sizeof(LV2_Event) + ev->size)); | |||||
| return true; | |||||
| } | |||||
| /** Dereference an event iterator (get the event currently pointed at). | |||||
| * @a iter must be valid. | |||||
| * @a data if non-NULL, will be set to point to the contents of the event | |||||
| * returned. | |||||
| * @return A Pointer to the event @a iter is currently pointing at, or NULL | |||||
| * if the end of the buffer is reached (in which case @a data is | |||||
| * also set to NULL). */ | |||||
| static inline LV2_Event* | |||||
| lv2_event_get(LV2_Event_Iterator* iter, | |||||
| uint8_t** data) | |||||
| { | |||||
| if (!lv2_event_is_valid(iter)) { | |||||
| return NULL; | |||||
| } | |||||
| LV2_Event* const ev = (LV2_Event*)( | |||||
| (uint8_t*)iter->buf->data + iter->offset); | |||||
| if (data) | |||||
| *data = (uint8_t*)ev + sizeof(LV2_Event); | |||||
| return ev; | |||||
| } | |||||
| /** Write an event at @a iter. | |||||
| * The event (if any) pointed to by @a iter will be overwritten, and @a iter | |||||
| * incremented to point to the following event (i.e. several calls to this | |||||
| * function can be done in sequence without twiddling iter in-between). | |||||
| * @return True if event was written, otherwise false (buffer is full). */ | |||||
| static inline bool | |||||
| lv2_event_write(LV2_Event_Iterator* iter, | |||||
| uint32_t frames, | |||||
| uint32_t subframes, | |||||
| uint16_t type, | |||||
| uint16_t size, | |||||
| const uint8_t* data) | |||||
| { | |||||
| if (!iter->buf) | |||||
| return false; | |||||
| if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) | |||||
| return false; | |||||
| LV2_Event* const ev = (LV2_Event*)( | |||||
| (uint8_t*)iter->buf->data + iter->offset); | |||||
| ev->frames = frames; | |||||
| ev->subframes = subframes; | |||||
| ev->type = type; | |||||
| ev->size = size; | |||||
| memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); | |||||
| ++iter->buf->event_count; | |||||
| size = lv2_event_pad_size((uint16_t)(sizeof(LV2_Event) + size)); | |||||
| iter->buf->size += size; | |||||
| iter->offset += size; | |||||
| return true; | |||||
| } | |||||
| /** Reserve space for an event in the buffer and return a pointer to | |||||
| the memory where the caller can write the event data, or NULL if there | |||||
| is not enough room in the buffer. */ | |||||
| static inline uint8_t* | |||||
| lv2_event_reserve(LV2_Event_Iterator* iter, | |||||
| uint32_t frames, | |||||
| uint32_t subframes, | |||||
| uint16_t type, | |||||
| uint16_t size) | |||||
| { | |||||
| const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + size); | |||||
| if (iter->buf->capacity - iter->buf->size < total_size) | |||||
| return NULL; | |||||
| LV2_Event* const ev = (LV2_Event*)( | |||||
| (uint8_t*)iter->buf->data + iter->offset); | |||||
| ev->frames = frames; | |||||
| ev->subframes = subframes; | |||||
| ev->type = type; | |||||
| ev->size = size; | |||||
| ++iter->buf->event_count; | |||||
| const uint16_t padded_size = lv2_event_pad_size(total_size); | |||||
| iter->buf->size += padded_size; | |||||
| iter->offset += padded_size; | |||||
| return (uint8_t*)ev + sizeof(LV2_Event); | |||||
| } | |||||
| /** Write an event at @a iter. | |||||
| * The event (if any) pointed to by @a iter will be overwritten, and @a iter | |||||
| * incremented to point to the following event (i.e. several calls to this | |||||
| * function can be done in sequence without twiddling iter in-between). | |||||
| * @return True if event was written, otherwise false (buffer is full). */ | |||||
| static inline bool | |||||
| lv2_event_write_event(LV2_Event_Iterator* iter, | |||||
| const LV2_Event* ev, | |||||
| const uint8_t* data) | |||||
| { | |||||
| const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + ev->size); | |||||
| if (iter->buf->capacity - iter->buf->size < total_size) | |||||
| return false; | |||||
| LV2_Event* const write_ev = (LV2_Event*)( | |||||
| (uint8_t*)iter->buf->data + iter->offset); | |||||
| *write_ev = *ev; | |||||
| memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); | |||||
| ++iter->buf->event_count; | |||||
| const uint16_t padded_size = lv2_event_pad_size(total_size); | |||||
| iter->buf->size += padded_size; | |||||
| iter->offset += padded_size; | |||||
| return true; | |||||
| } | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_EVENT_HELPERS_H */ | |||||
| @@ -0,0 +1,294 @@ | |||||
| /* | |||||
| Copyright 2008-2011 David Robillard <http://drobilla.net> | |||||
| Copyright 2006-2007 Lars Luthman <lars.luthman@gmail.com> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file event.h | |||||
| C API for the LV2 Event extension <http://lv2plug.in/ns/ext/event>. | |||||
| This extension is a generic transport mechanism for time stamped events | |||||
| of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed | |||||
| events of any type; the type of events and timestamps are defined by a URI | |||||
| which is mapped to an integer by the host for performance reasons. | |||||
| This extension requires the host to support the LV2 URI Map extension. | |||||
| Any host which supports this extension MUST guarantee that any call to | |||||
| the LV2 URI Map uri_to_id function with the URI of this extension as the | |||||
| 'map' argument returns a value within the range of uint16_t. | |||||
| */ | |||||
| #ifndef LV2_EVENT_H | |||||
| #define LV2_EVENT_H | |||||
| #define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" | |||||
| #define LV2_EVENT_PREFIX LV2_EVENT_URI "#" | |||||
| #define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" | |||||
| #define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" | |||||
| #define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" | |||||
| #define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" | |||||
| #define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" | |||||
| #define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" | |||||
| #define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" | |||||
| #define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" | |||||
| #define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" | |||||
| #define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" | |||||
| #define LV2_EVENT_AUDIO_STAMP 0 | |||||
| #include <stdint.h> | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** | |||||
| The best Pulses Per Quarter Note for tempo-based uint32_t timestamps. | |||||
| Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble | |||||
| by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. | |||||
| */ | |||||
| static const uint32_t LV2_EVENT_PPQN = 3136573440U; | |||||
| /** | |||||
| An LV2 event (header only). | |||||
| LV2 events are generic time-stamped containers for any type of event. | |||||
| The type field defines the format of a given event's contents. | |||||
| This struct defines the header of an LV2 event. An LV2 event is a single | |||||
| chunk of POD (plain old data), usually contained in a flat buffer (see | |||||
| LV2_EventBuffer below). Unless a required feature says otherwise, hosts may | |||||
| assume a deep copy of an LV2 event can be created safely using a simple: | |||||
| memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) | |||||
| */ | |||||
| typedef struct { | |||||
| /** | |||||
| The frames portion of timestamp. The units used here can optionally be | |||||
| set for a port (with the lv2ev:timeUnits property), otherwise this is | |||||
| audio frames, corresponding to the sample_count parameter of the LV2 run | |||||
| method (e.g. frame 0 is the first frame for that call to run). | |||||
| */ | |||||
| uint32_t frames; | |||||
| /** | |||||
| The sub-frames portion of timestamp. The units used here can optionally | |||||
| be set for a port (with the lv2ev:timeUnits property), otherwise this is | |||||
| 1/(2^32) of an audio frame. | |||||
| */ | |||||
| uint32_t subframes; | |||||
| /** | |||||
| The type of this event, as a number which represents some URI | |||||
| defining an event type. This value MUST be some value previously | |||||
| returned from a call to the uri_to_id function defined in the LV2 | |||||
| URI map extension (see lv2_uri_map.h). | |||||
| There are special rules which must be followed depending on the type | |||||
| of an event. If the plugin recognizes an event type, the definition | |||||
| of that event type will describe how to interpret the event, and | |||||
| any required behaviour. Otherwise, if the type is 0, this event is a | |||||
| non-POD event and lv2_event_unref MUST be called if the event is | |||||
| 'dropped' (see above). Even if the plugin does not understand an event, | |||||
| it may pass the event through to an output by simply copying (and NOT | |||||
| calling lv2_event_unref). These rules are designed to allow for generic | |||||
| event handling plugins and large non-POD events, but with minimal hassle | |||||
| on simple plugins that "don't care" about these more advanced features. | |||||
| */ | |||||
| uint16_t type; | |||||
| /** | |||||
| The size of the data portion of this event in bytes, which immediately | |||||
| follows. The header size (12 bytes) is not included in this value. | |||||
| */ | |||||
| uint16_t size; | |||||
| /* size bytes of data follow here */ | |||||
| } LV2_Event; | |||||
| /** | |||||
| A buffer of LV2 events (header only). | |||||
| Like events (which this contains) an event buffer is a single chunk of POD: | |||||
| the entire buffer (including contents) can be copied with a single memcpy. | |||||
| The first contained event begins sizeof(LV2_EventBuffer) bytes after the | |||||
| start of this struct. | |||||
| After this header, the buffer contains an event header (defined by struct | |||||
| LV2_Event), followed by that event's contents (padded to 64 bits), followed | |||||
| by another header, etc: | |||||
| | | | | | | | | |||||
| | | | | | | | | | | | | | | | | | | | | | | | | | | |||||
| |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... | |||||
| */ | |||||
| typedef struct { | |||||
| /** | |||||
| The contents of the event buffer. This may or may not reside in the | |||||
| same block of memory as this header, plugins must not assume either. | |||||
| The host guarantees this points to at least capacity bytes of allocated | |||||
| memory (though only size bytes of that are valid events). | |||||
| */ | |||||
| uint8_t* data; | |||||
| /** | |||||
| The size of this event header in bytes (including everything). | |||||
| This is to allow for extending this header in the future without | |||||
| breaking binary compatibility. Whenever this header is copied, | |||||
| it MUST be done using this field (and NOT the sizeof this struct). | |||||
| */ | |||||
| uint16_t header_size; | |||||
| /** | |||||
| The type of the time stamps for events in this buffer. | |||||
| As a special exception, '0' always means audio frames and subframes | |||||
| (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. | |||||
| INPUTS: The host must set this field to the numeric ID of some URI | |||||
| defining the meaning of the frames/subframes fields of contained events | |||||
| (obtained by the LV2 URI Map uri_to_id function with the URI of this | |||||
| extension as the 'map' argument, see lv2_uri_map.h). The host must | |||||
| never pass a plugin a buffer which uses a stamp type the plugin does not | |||||
| 'understand'. The value of this field must never change, except when | |||||
| connect_port is called on the input port, at which time the host MUST | |||||
| have set the stamp_type field to the value that will be used for all | |||||
| subsequent run calls. | |||||
| OUTPUTS: The plugin may set this to any value that has been returned | |||||
| from uri_to_id with the URI of this extension for a 'map' argument. | |||||
| When connected to a buffer with connect_port, output ports MUST set this | |||||
| field to the type of time stamp they will be writing. On any call to | |||||
| connect_port on an event input port, the plugin may change this field on | |||||
| any output port, it is the responsibility of the host to check if any of | |||||
| these values have changed and act accordingly. | |||||
| */ | |||||
| uint16_t stamp_type; | |||||
| /** | |||||
| The number of events in this buffer. | |||||
| INPUTS: The host must set this field to the number of events contained | |||||
| in the data buffer before calling run(). The plugin must not change | |||||
| this field. | |||||
| OUTPUTS: The plugin must set this field to the number of events it has | |||||
| written to the buffer before returning from run(). Any initial value | |||||
| should be ignored by the plugin. | |||||
| */ | |||||
| uint32_t event_count; | |||||
| /** | |||||
| The size of the data buffer in bytes. | |||||
| This is set by the host and must not be changed by the plugin. | |||||
| The host is allowed to change this between run() calls. | |||||
| */ | |||||
| uint32_t capacity; | |||||
| /** | |||||
| The size of the initial portion of the data buffer containing data. | |||||
| INPUTS: The host must set this field to the number of bytes used | |||||
| by all events it has written to the buffer (including headers) | |||||
| before calling the plugin's run(). | |||||
| The plugin must not change this field. | |||||
| OUTPUTS: The plugin must set this field to the number of bytes | |||||
| used by all events it has written to the buffer (including headers) | |||||
| before returning from run(). | |||||
| Any initial value should be ignored by the plugin. | |||||
| */ | |||||
| uint32_t size; | |||||
| } LV2_Event_Buffer; | |||||
| /** | |||||
| Opaque pointer to host data. | |||||
| */ | |||||
| typedef void* LV2_Event_Callback_Data; | |||||
| /** | |||||
| Non-POD events feature. | |||||
| To support this feature the host must pass an LV2_Feature struct to the | |||||
| plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" | |||||
| and data pointed to an instance of this struct. Note this feature | |||||
| is not mandatory to support the event extension. | |||||
| */ | |||||
| typedef struct { | |||||
| /** | |||||
| Opaque pointer to host data. | |||||
| The plugin MUST pass this to any call to functions in this struct. | |||||
| Otherwise, it must not be interpreted in any way. | |||||
| */ | |||||
| LV2_Event_Callback_Data callback_data; | |||||
| /** | |||||
| Take a reference to a non-POD event. | |||||
| If a plugin receives an event with type 0, it means the event is a | |||||
| pointer to some object in memory and not a flat sequence of bytes | |||||
| in the buffer. When receiving a non-POD event, the plugin already | |||||
| has an implicit reference to the event. If the event is stored AND | |||||
| passed to an output, lv2_event_ref MUST be called on that event. | |||||
| If the event is only stored OR passed through, this is not necessary | |||||
| (as the plugin already has 1 implicit reference). | |||||
| @param event An event received at an input that will not be copied to | |||||
| an output or stored in any way. | |||||
| @param context The calling context. Like event types, this is a mapped | |||||
| URI, see lv2_context.h. Simple plugin with just a run() method should | |||||
| pass 0 here (the ID of the 'standard' LV2 run context). The host | |||||
| guarantees that this function is realtime safe iff @a context is | |||||
| realtime safe. | |||||
| PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. | |||||
| */ | |||||
| uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, | |||||
| LV2_Event* event); | |||||
| /** | |||||
| Drop a reference to a non-POD event. | |||||
| If a plugin receives an event with type 0, it means the event is a | |||||
| pointer to some object in memory and not a flat sequence of bytes | |||||
| in the buffer. If the plugin does not pass the event through to | |||||
| an output or store it internally somehow, it MUST call this function | |||||
| on the event (more information on using non-POD events below). | |||||
| @param event An event received at an input that will not be copied to an | |||||
| output or stored in any way. | |||||
| @param context The calling context. Like event types, this is a mapped | |||||
| URI, see lv2_context.h. Simple plugin with just a run() method should | |||||
| pass 0 here (the ID of the 'standard' LV2 run context). The host | |||||
| guarantees that this function is realtime safe iff @a context is | |||||
| realtime safe. | |||||
| PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. | |||||
| */ | |||||
| uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, | |||||
| LV2_Event* event); | |||||
| } LV2_Event_Feature; | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* LV2_EVENT_H */ | |||||