| @@ -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 */ | |||