| @@ -0,0 +1,54 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| class Window; | |||
| // ----------------------------------------------------------------------- | |||
| class App | |||
| { | |||
| public: | |||
| App(); | |||
| ~App(); | |||
| void idle(); | |||
| void exec(); | |||
| void quit(); | |||
| bool isQuiting() const; | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| friend class Window; | |||
| void addWindow(Window* const window); | |||
| void removeWindow(Window* const window); | |||
| void oneShown(); | |||
| void oneHidden(); | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_APP_HPP_INCLUDED | |||
| @@ -0,0 +1,143 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) | |||
| # define DGL_OS_WINDOWS 1 | |||
| #elif defined(__APPLE__) | |||
| # define DGL_OS_MAC 1 | |||
| #elif defined(__HAIKU__) | |||
| # define DGL_OS_HAIKU 1 | |||
| #elif defined(__linux__) | |||
| # define DGL_OS_LINUX 1 | |||
| #endif | |||
| #if defined(HAVE_CPP11_SUPPORT) | |||
| # define PROPER_CPP11_SUPPORT | |||
| #elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) | |||
| # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 | |||
| # define PROPER_CPP11_SUPPORT | |||
| # if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 | |||
| # define override // gcc4.7+ only | |||
| # endif | |||
| # endif | |||
| #endif | |||
| #ifndef PROPER_CPP11_SUPPORT | |||
| # ifndef __clang__ | |||
| # define noexcept throw() | |||
| # endif | |||
| # define override | |||
| # define nullptr (0) | |||
| #endif | |||
| #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; | |||
| #if DGL_OS_MAC | |||
| # include <OpenGL/gl.h> | |||
| #else | |||
| # include <GL/gl.h> | |||
| #endif | |||
| #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 | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| /* | |||
| * Convenience symbols for ASCII control characters. | |||
| */ | |||
| enum Char { | |||
| CHAR_BACKSPACE = 0x08, | |||
| CHAR_ESCAPE = 0x1B, | |||
| CHAR_DELETE = 0x7F | |||
| }; | |||
| /* | |||
| * Special (non-Unicode) keyboard keys. | |||
| */ | |||
| enum Key { | |||
| KEY_F1 = 1, | |||
| KEY_F2, | |||
| KEY_F3, | |||
| KEY_F4, | |||
| KEY_F5, | |||
| KEY_F6, | |||
| KEY_F7, | |||
| KEY_F8, | |||
| KEY_F9, | |||
| KEY_F10, | |||
| KEY_F11, | |||
| KEY_F12, | |||
| KEY_LEFT, | |||
| KEY_UP, | |||
| KEY_RIGHT, | |||
| KEY_DOWN, | |||
| KEY_PAGE_UP, | |||
| KEY_PAGE_DOWN, | |||
| KEY_HOME, | |||
| KEY_END, | |||
| KEY_INSERT, | |||
| KEY_SHIFT, | |||
| KEY_CTRL, | |||
| KEY_ALT, | |||
| KEY_SUPER | |||
| }; | |||
| /* | |||
| * Keyboard modifier flags. | |||
| */ | |||
| enum Modifier { | |||
| MODIFIER_SHIFT = 1 << 0, /**< Shift key */ | |||
| MODIFIER_CTRL = 1 << 1, /**< Control key */ | |||
| MODIFIER_ALT = 1 << 2, /**< Alt/Option key */ | |||
| MODIFIER_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
| }; | |||
| /* | |||
| * Cross-platform sleep function. | |||
| */ | |||
| void sleep(unsigned int secs); | |||
| /* | |||
| * Cross-platform msleep function. | |||
| */ | |||
| void msleep(unsigned int msecs); | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_BASE_HPP_INCLUDED | |||
| @@ -0,0 +1,142 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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_CAIRO_WIDGET_HPP_INCLUDED | |||
| #define DGL_CAIRO_WIDGET_HPP_INCLUDED | |||
| #include "Widget.hpp" | |||
| #include <cairo.h> | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class CairoWidget : public Widget | |||
| { | |||
| public: | |||
| CairoWidget(Window& parent) | |||
| : Widget(parent), | |||
| fContext(nullptr), | |||
| fSurface(nullptr), | |||
| fTextureId(0) | |||
| { | |||
| } | |||
| protected: | |||
| virtual void cairoDisplay(cairo_t* const context) = 0; | |||
| private: | |||
| void onReshape(int width, int height) override | |||
| { | |||
| // handle resize | |||
| setSize(width, height); | |||
| Widget::onReshape(width, height); | |||
| // free previous if needed | |||
| onClose(); | |||
| // create new | |||
| fSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); | |||
| fContext = cairo_create(fSurface); | |||
| glGenTextures(1, &fTextureId); | |||
| } | |||
| void onClose() override | |||
| { | |||
| if (fContext != nullptr) | |||
| { | |||
| cairo_destroy(fContext); | |||
| fContext = nullptr; | |||
| } | |||
| if (fSurface != nullptr) | |||
| { | |||
| cairo_surface_destroy(fSurface); | |||
| fSurface = nullptr; | |||
| } | |||
| if (fTextureId != 0) | |||
| { | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| } | |||
| void onDisplay() override | |||
| { | |||
| // wait for first resize | |||
| if (fSurface == nullptr || fContext == nullptr) | |||
| { | |||
| glClear(GL_COLOR_BUFFER_BIT); | |||
| return; | |||
| } | |||
| const int width = getWidth(); | |||
| const int height = getHeight(); | |||
| // draw cairo stuff | |||
| cairoDisplay(fContext); | |||
| // get cairo surface data (RGB24) | |||
| unsigned char* const surfaceData = cairo_image_surface_get_data(fSurface); | |||
| // enable GL texture | |||
| glEnable(GL_TEXTURE_RECTANGLE_ARB); | |||
| // set texture params | |||
| glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |||
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| // bind texture to surface data | |||
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fTextureId); | |||
| glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, surfaceData); | |||
| // draw the texture | |||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||
| glBegin(GL_QUADS); | |||
| glTexCoord2i(0, height); | |||
| glVertex2i(0, height); | |||
| glTexCoord2i(width, height); | |||
| glVertex2i(width, height); | |||
| glTexCoord2i(width, 0); | |||
| glVertex2i(width, 0); | |||
| glTexCoord2i(0, 0); | |||
| glVertex2i(0, 0); | |||
| glEnd(); | |||
| // cleanup | |||
| glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | |||
| glDisable(GL_TEXTURE_RECTANGLE_ARB); | |||
| } | |||
| private: | |||
| cairo_t* fContext; | |||
| cairo_surface_t* fSurface; | |||
| GLuint fTextureId; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_CAIRO_WIDGET_HPP_INCLUDED | |||
| @@ -0,0 +1,133 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| // ----------------------------------------------------------------------- | |||
| template<typename T> | |||
| class Point | |||
| { | |||
| public: | |||
| Point() noexcept; | |||
| Point(T x, T y) noexcept; | |||
| Point(const Point<T>& pos) noexcept; | |||
| T getX() const noexcept; | |||
| T getY() const noexcept; | |||
| void setX(T x) noexcept; | |||
| void setY(T y) noexcept; | |||
| void move(T x, T y) noexcept; | |||
| void move(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 Rectangle; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| template<typename T> | |||
| class Size | |||
| { | |||
| public: | |||
| Size() noexcept; | |||
| Size(T width, T height) noexcept; | |||
| Size(const Size<T>& size) noexcept; | |||
| T getWidth() const noexcept; | |||
| T getHeight() const noexcept; | |||
| void setWidth(T width) noexcept; | |||
| void setHeight(T height) 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*=(T m) noexcept; | |||
| Size<T>& operator/=(T 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; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| template<typename T> | |||
| class Rectangle | |||
| { | |||
| public: | |||
| Rectangle() noexcept; | |||
| Rectangle(T x, T y, T width, T height) noexcept; | |||
| Rectangle(T x, T y, const Size<T>& size) noexcept; | |||
| Rectangle(const Point<T>& pos, T width, T height) noexcept; | |||
| Rectangle(const Point<T>& pos, const Size<T>& size) noexcept; | |||
| Rectangle(const Rectangle<T>& rect) noexcept; | |||
| T getX() const noexcept; | |||
| T getY() const noexcept; | |||
| T getWidth() const noexcept; | |||
| T getHeight() const noexcept; | |||
| const Point<T>& getPos() const noexcept; | |||
| const Size<T>& getSize() const noexcept; | |||
| bool contains(T x, T y) const noexcept; | |||
| bool contains(const Point<T>& pos) const noexcept; | |||
| bool containsX(T x) const noexcept; | |||
| bool containsY(T y) const noexcept; | |||
| void setX(T x) noexcept; | |||
| void setY(T y) noexcept; | |||
| void setPos(T x, T y) noexcept; | |||
| void setPos(const Point<T>& pos) noexcept; | |||
| void move(T x, T y) noexcept; | |||
| void move(const Point<T>& pos) noexcept; | |||
| void setWidth(T width) noexcept; | |||
| void setHeight(T height) noexcept; | |||
| void setSize(T width, T height) noexcept; | |||
| void setSize(const Size<T>& size) noexcept; | |||
| Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; | |||
| private: | |||
| Point<T> fPos; | |||
| Size<T> fSize; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_GEOMETRY_HPP_INCLUDED | |||
| @@ -0,0 +1,66 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| // ----------------------------------------------------------------------- | |||
| class Image | |||
| { | |||
| public: | |||
| Image() noexcept; | |||
| Image(const char* rawData, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||
| Image(const char* rawData, const Size<int>& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||
| Image(const Image& image) noexcept; | |||
| void loadFromMemory(const char* rawData, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||
| void loadFromMemory(const char* rawData, const Size<int>& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||
| bool isValid() const noexcept; | |||
| int getWidth() const noexcept; | |||
| int getHeight() const noexcept; | |||
| const Size<int>& getSize() const noexcept; | |||
| const char* getRawData() const noexcept; | |||
| GLenum getFormat() const noexcept; | |||
| GLenum getType() const noexcept; | |||
| void draw() const; | |||
| void draw(int x, int y) const; | |||
| void draw(const Point<int>& pos) const; | |||
| 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<int> fSize; | |||
| GLenum fFormat; | |||
| GLenum fType; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_HPP_INCLUDED | |||
| @@ -0,0 +1,56 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #include "Image.hpp" | |||
| #include "Widget.hpp" | |||
| #include "Window.hpp" | |||
| #ifdef PROPER_CPP11_SUPPORT | |||
| # include <cstdint> | |||
| #else | |||
| # include <stdint.h> | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class ImageAboutWindow : public Window, | |||
| public Widget | |||
| { | |||
| public: | |||
| ImageAboutWindow(App& app, Window& parent, const Image& image = Image()); | |||
| ImageAboutWindow(Widget* widget, const Image& image = Image()); | |||
| void setImage(const Image& image); | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(int button, bool press, int x, int y) override; | |||
| bool onKeyboard(bool press, uint32_t key) override; | |||
| private: | |||
| Image fImgBackground; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_ABOUT_WINDOW_HPP_INCLUDED | |||
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #include "Image.hpp" | |||
| #include "Widget.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class ImageButton : public Widget | |||
| { | |||
| public: | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; | |||
| }; | |||
| ImageButton(Window& parent, const Image& image); | |||
| ImageButton(Widget* widget, const Image& image); | |||
| ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||
| ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); | |||
| ImageButton(const ImageButton& imageButton); | |||
| void setCallback(Callback* callback); | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(int button, bool press, int x, int y) override; | |||
| bool onMotion(int x, int y) override; | |||
| private: | |||
| Image fImageNormal; | |||
| Image fImageHover; | |||
| Image fImageDown; | |||
| Image* fCurImage; | |||
| int fCurButton; | |||
| Callback* fCallback; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_BUTTON_HPP_INCLUDED | |||
| @@ -0,0 +1,89 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #include "Image.hpp" | |||
| #include "Widget.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| 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; | |||
| }; | |||
| ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical); | |||
| ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical); | |||
| ImageKnob(const ImageKnob& imageKnob); | |||
| float getValue() const; | |||
| void setOrientation(Orientation orientation); | |||
| void setRange(float min, float max); | |||
| void setValue(float value, bool sendCallback = false); | |||
| void setRotationAngle(int angle); | |||
| void setCallback(Callback* callback); | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(int button, bool press, int x, int y) override; | |||
| bool onMotion(int x, int y) override; | |||
| void onReshape(int width, int height) override; | |||
| void onClose() override; | |||
| private: | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fValue; | |||
| Orientation fOrientation; | |||
| int fRotationAngle; | |||
| bool fDragging; | |||
| int fLastX; | |||
| int fLastY; | |||
| Callback* fCallback; | |||
| bool fIsImgVertical; | |||
| int fImgLayerSize; | |||
| int fImgLayerCount; | |||
| Rectangle<int> fKnobArea; | |||
| GLuint fTextureId; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_KNOB_HPP_INCLUDED | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #include "Image.hpp" | |||
| #include "Widget.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| 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; | |||
| }; | |||
| ImageSlider(Window& parent, const Image& image); | |||
| ImageSlider(Widget* widget, const Image& image); | |||
| ImageSlider(const ImageSlider& imageSlider); | |||
| float getValue() const; | |||
| void setStartPos(const Point<int>& startPos); | |||
| void setStartPos(int x, int y); | |||
| void setEndPos(const Point<int>& endPos); | |||
| void setEndPos(int x, int y); | |||
| void setRange(float min, float max); | |||
| void setValue(float value, bool sendCallback = false); | |||
| void setIsSwitch(bool yesNo); | |||
| void setCallback(Callback* callback); | |||
| protected: | |||
| void onDisplay() override; | |||
| bool onMouse(int button, bool press, int x, int y) override; | |||
| bool onMotion(int x, int y) override; | |||
| private: | |||
| Image fImage; | |||
| float fMinimum; | |||
| float fMaximum; | |||
| float fValue; | |||
| bool fIsSwitch; | |||
| bool fDragging; | |||
| int fStartedX; | |||
| int fStartedY; | |||
| Callback* fCallback; | |||
| Point<int> fStartPos; | |||
| Point<int> fEndPos; | |||
| Rectangle<int> fSliderArea; | |||
| void _recheckArea(); | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_IMAGE_SLIDER_HPP_INCLUDED | |||
| @@ -0,0 +1,67 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for dgl # | |||
| # ---------------- # | |||
| # Created by falkTX | |||
| # | |||
| include Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| BUILD_C_FLAGS += $(DGL_FLAGS) -I. | |||
| BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. | |||
| LINK_FLAGS += $(DGL_LIBS) | |||
| # -------------------------------------------------------------- | |||
| OBJS = \ | |||
| src/App.cpp.o \ | |||
| src/Base.cpp.o \ | |||
| src/Image.cpp.o \ | |||
| src/ImageAboutWindow.cpp.o \ | |||
| src/ImageButton.cpp.o \ | |||
| src/ImageKnob.cpp.o \ | |||
| src/ImageSlider.cpp.o \ | |||
| src/Geometry.cpp.o \ | |||
| src/Widget.cpp.o \ | |||
| src/Window.cpp.o | |||
| ifeq ($(MACOS),true) | |||
| OBJS += src/pugl/pugl_osx_extended.m.o | |||
| endif | |||
| TARGET = ../libdgl.a | |||
| # -------------------------------------------------------------- | |||
| all: $(TARGET) | |||
| # -------------------------------------------------------------- | |||
| ../libdgl.a: $(OBJS) | |||
| $(RM) $@ | |||
| $(AR) crs $@ $^ | |||
| ../libdgl.dll: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| ../libdgl.dylib: $(OBJS) | |||
| $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ | |||
| ../libdgl.so: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| # -------------------------------------------------------------- | |||
| %.cpp.o: %.cpp | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| $(RM) src/*.o src/pugl/*.o ../libdgl.* | |||
| debug: | |||
| $(MAKE) DEBUG=true | |||
| # -------------------------------------------------------------- | |||
| @@ -0,0 +1,79 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for dgl # | |||
| # ---------------- # | |||
| # Created by falkTX | |||
| # | |||
| AR ?= ar | |||
| RM ?= rm -f | |||
| CC ?= gcc | |||
| CXX ?= g++ | |||
| # -------------------------------------------------------------- | |||
| # Fallback to Linux if no other OS defined | |||
| ifneq ($(MACOS),true) | |||
| ifneq ($(WIN32),true) | |||
| LINUX=true | |||
| endif | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # Common build and link flags | |||
| BASE_FLAGS = -Wall -Wextra -fPIC -DPIC -pipe -DREAL_BUILD | |||
| BASE_OPTS = -O3 -ffast-math -mtune=generic -msse -msse2 -mfpmath=sse | |||
| ifeq ($(RASPPI),true) | |||
| # Raspberry-Pi optimization flags | |||
| BASE_OPTS = -O3 -ffast-math -march=armv6 -mfpu=vfp -mfloat-abi=hard | |||
| endif | |||
| ifeq ($(DEBUG),true) | |||
| BASE_FLAGS += -DDEBUG -O0 -g | |||
| else | |||
| BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | |||
| CXXFLAGS += -fvisibility-inlines-hidden | |||
| LINK_OPTS += -Wl,--strip-all | |||
| endif | |||
| BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) | |||
| BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++0x $(CXXFLAGS) | |||
| LINK_FLAGS = $(LINK_OPTS) -Wl,--no-undefined $(LDFLAGS) | |||
| ifeq ($(MACOS),true) | |||
| # Get rid of most options for old gcc4.2 | |||
| BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) | |||
| LINK_FLAGS = $(LDFLAGS) | |||
| 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,79 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "App.hpp" | |||
| #include "Window.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class StandaloneWindow | |||
| { | |||
| public: | |||
| StandaloneWindow() | |||
| : fApp(), | |||
| fWindow(fApp) | |||
| { | |||
| } | |||
| App& getApp() noexcept | |||
| { | |||
| return fApp; | |||
| } | |||
| Window& getWindow() noexcept | |||
| { | |||
| return fWindow; | |||
| } | |||
| void exec() | |||
| { | |||
| fWindow.show(); | |||
| fApp.exec(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // helpers | |||
| void setResizable(bool yesNo) | |||
| { | |||
| fWindow.setResizable(yesNo); | |||
| } | |||
| void setSize(unsigned int width, unsigned int height) | |||
| { | |||
| fWindow.setSize(width, height); | |||
| } | |||
| void setTitle(const char* title) | |||
| { | |||
| fWindow.setTitle(title); | |||
| } | |||
| private: | |||
| App fApp; | |||
| Window fWindow; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_STANDALONE_WINDOW_HPP_INCLUDED | |||
| @@ -0,0 +1,100 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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" | |||
| #ifdef PROPER_CPP11_SUPPORT | |||
| # include <cstdint> | |||
| #else | |||
| # include <stdint.h> | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class App; | |||
| class Window; | |||
| class Widget | |||
| { | |||
| public: | |||
| Widget(Window& parent); | |||
| virtual ~Widget(); | |||
| bool isVisible() const noexcept; | |||
| void setVisible(bool yesNo); | |||
| void show(); | |||
| void hide(); | |||
| int getX() const noexcept; | |||
| int getY() const noexcept; | |||
| const Point<int>& getPos() const noexcept; | |||
| void setX(int x); | |||
| void setY(int y); | |||
| void setPos(int x, int y); | |||
| void setPos(const Point<int>& pos); | |||
| void move(int x, int y); | |||
| void move(const Point<int>& pos); | |||
| int getWidth() const noexcept; | |||
| int getHeight() const noexcept; | |||
| const Size<int>& getSize() const noexcept; | |||
| void setWidth(int width); | |||
| void setHeight(int height); | |||
| void setSize(int width, int height); | |||
| void setSize(const Size<int>& size); | |||
| const Rectangle<int>& getArea() const noexcept; | |||
| uint32_t getEventTimestamp(); | |||
| int getModifiers(); | |||
| App& getParentApp() const noexcept; | |||
| Window& getParentWindow() const noexcept; | |||
| void repaint(); | |||
| protected: | |||
| virtual void onDisplay() = 0; | |||
| virtual bool onKeyboard(bool press, uint32_t key); | |||
| virtual bool onMouse(int button, bool press, int x, int y); | |||
| virtual bool onMotion(int x, int y); | |||
| virtual bool onScroll(float dx, float dy); | |||
| virtual bool onSpecial(bool press, Key key); | |||
| virtual void onReshape(int width, int height); | |||
| virtual void onClose(); | |||
| private: | |||
| Window& fParent; | |||
| bool fVisible; | |||
| Rectangle<int> fArea; | |||
| friend class Window; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_WIDGET_HPP_INCLUDED | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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" | |||
| #ifdef PROPER_CPP11_SUPPORT | |||
| # include <cstdint> | |||
| #else | |||
| # include <stdint.h> | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| class App; | |||
| class Widget; | |||
| class Window | |||
| { | |||
| public: | |||
| Window(App& app); | |||
| Window(App& app, Window& parent); | |||
| Window(App& app, intptr_t parentId); | |||
| virtual ~Window(); | |||
| void show(); | |||
| void hide(); | |||
| void close(); | |||
| void exec(bool lockWait = false); | |||
| void focus(); | |||
| void idle(); | |||
| void repaint(); | |||
| bool isVisible() const noexcept; | |||
| void setVisible(bool yesNo); | |||
| bool isResizable() const noexcept; | |||
| void setResizable(bool yesNo); | |||
| #ifndef DGL_OS_MAC | |||
| int getWidth() const noexcept; | |||
| int getHeight() const noexcept; | |||
| Size<int> getSize() const noexcept; | |||
| #endif | |||
| void setSize(unsigned int width, unsigned int height); | |||
| void setTitle(const char* title); | |||
| App& getApp() const noexcept; | |||
| uint32_t getEventTimestamp() const; | |||
| int getModifiers() const; | |||
| intptr_t getWindowId() const; | |||
| private: | |||
| class PrivateData; | |||
| PrivateData* const pData; | |||
| friend class Widget; | |||
| void addWidget(Widget* const widget); | |||
| void removeWidget(Widget* const widget); | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| #endif // DGL_WINDOW_HPP_INCLUDED | |||
| @@ -0,0 +1,111 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../App.hpp" | |||
| #include "../Window.hpp" | |||
| #include <list> | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| struct App::PrivateData { | |||
| bool doLoop; | |||
| unsigned visibleWindows; | |||
| std::list<Window*> windows; | |||
| PrivateData() | |||
| : doLoop(false), | |||
| visibleWindows(0) {} | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| App::App() | |||
| : pData(new PrivateData()) | |||
| { | |||
| } | |||
| App::~App() | |||
| { | |||
| pData->windows.clear(); | |||
| delete pData; | |||
| } | |||
| void App::idle() | |||
| { | |||
| for (std::list<Window*>::iterator it = pData->windows.begin(); it != pData->windows.end(); ++it) | |||
| { | |||
| Window* const window(*it); | |||
| window->idle(); | |||
| } | |||
| } | |||
| void App::exec() | |||
| { | |||
| while (pData->doLoop) | |||
| { | |||
| idle(); | |||
| msleep(10); | |||
| } | |||
| } | |||
| void App::quit() | |||
| { | |||
| pData->doLoop = false; | |||
| for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(); rit != pData->windows.rend(); ++rit) | |||
| { | |||
| Window* const window(*rit); | |||
| window->close(); | |||
| } | |||
| } | |||
| bool App::isQuiting() const | |||
| { | |||
| return !pData->doLoop; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| void App::addWindow(Window* const window) | |||
| { | |||
| if (window != nullptr) | |||
| pData->windows.push_back(window); | |||
| } | |||
| void App::removeWindow(Window* const window) | |||
| { | |||
| if (window != nullptr) | |||
| pData->windows.remove(window); | |||
| } | |||
| void App::oneShown() | |||
| { | |||
| if (++pData->visibleWindows == 1) | |||
| pData->doLoop = true; | |||
| } | |||
| void App::oneHidden() | |||
| { | |||
| if (--pData->visibleWindows == 0) | |||
| pData->doLoop = false; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,49 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../Base.hpp" | |||
| #if DGL_OS_WINDOWS | |||
| # include <windows.h> | |||
| #else | |||
| # include <unistd.h> | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| void sleep(unsigned int secs) | |||
| { | |||
| #ifdef DGL_OS_WINDOWS | |||
| ::Sleep(secs * 1000); | |||
| #else | |||
| ::sleep(secs); | |||
| #endif | |||
| } | |||
| void msleep(unsigned int msecs) | |||
| { | |||
| #ifdef DGL_OS_WINDOWS | |||
| ::Sleep(msecs); | |||
| #else | |||
| ::usleep(msecs * 1000); | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,416 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../Geometry.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // Point | |||
| template<typename T> | |||
| Point<T>::Point() noexcept | |||
| : fX(0), | |||
| fY(0) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Point<T>::Point(T x, T y) noexcept | |||
| : fX(x), | |||
| fY(y) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Point<T>::Point(const Point& pos) noexcept | |||
| : fX(pos.fX), | |||
| fY(pos.fY) | |||
| { | |||
| } | |||
| template<typename T> | |||
| T Point<T>::getX() const noexcept | |||
| { | |||
| return fX; | |||
| } | |||
| template<typename T> | |||
| T Point<T>::getY() const noexcept | |||
| { | |||
| return fY; | |||
| } | |||
| template<typename T> | |||
| void Point<T>::setX(T x) noexcept | |||
| { | |||
| fX = x; | |||
| } | |||
| template<typename T> | |||
| void Point<T>::setY(T y) noexcept | |||
| { | |||
| fY = y; | |||
| } | |||
| template<typename T> | |||
| void Point<T>::move(T x, T y) noexcept | |||
| { | |||
| fX += x; | |||
| fY += y; | |||
| } | |||
| template<typename T> | |||
| void Point<T>::move(const Point& pos) noexcept | |||
| { | |||
| fX += pos.fX; | |||
| fY += pos.fY; | |||
| } | |||
| template<typename T> | |||
| Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept | |||
| { | |||
| fX = pos.fX; | |||
| fY = pos.fY; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept | |||
| { | |||
| fX += pos.fX; | |||
| fY += pos.fY; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept | |||
| { | |||
| fX -= pos.fX; | |||
| fY -= pos.fY; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| bool Point<T>::operator==(const Point<T>& pos) const noexcept | |||
| { | |||
| return (fX == pos.fX && fY== pos.fY); | |||
| } | |||
| template<typename T> | |||
| bool Point<T>::operator!=(const Point<T>& pos) const noexcept | |||
| { | |||
| return !operator==(pos); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Size | |||
| template<typename T> | |||
| Size<T>::Size() noexcept | |||
| : fWidth(0), | |||
| fHeight(0) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Size<T>::Size(T width, T height) noexcept | |||
| : fWidth(width), | |||
| fHeight(height) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Size<T>::Size(const Size<T>& size) noexcept | |||
| : fWidth(size.fWidth), | |||
| fHeight(size.fHeight) | |||
| { | |||
| } | |||
| template<typename T> | |||
| T Size<T>::getWidth() const noexcept | |||
| { | |||
| return fWidth; | |||
| } | |||
| template<typename T> | |||
| T Size<T>::getHeight() const noexcept | |||
| { | |||
| return fHeight; | |||
| } | |||
| template<typename T> | |||
| void Size<T>::setWidth(T width) noexcept | |||
| { | |||
| fWidth = width; | |||
| } | |||
| template<typename T> | |||
| void Size<T>::setHeight(T height) noexcept | |||
| { | |||
| fHeight = height; | |||
| } | |||
| template<typename T> | |||
| Size<T>& Size<T>::operator=(const Size<T>& size) noexcept | |||
| { | |||
| fWidth = size.fWidth; | |||
| fHeight = size.fHeight; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Size<T>& Size<T>::operator+=(const Size<T>& size) noexcept | |||
| { | |||
| fWidth += size.fWidth; | |||
| fHeight += size.fHeight; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Size<T>& Size<T>::operator-=(const Size<T>& size) noexcept | |||
| { | |||
| fWidth -= size.fWidth; | |||
| fHeight -= size.fHeight; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Size<T>& Size<T>::operator*=(T m) noexcept | |||
| { | |||
| fWidth *= m; | |||
| fHeight *= m; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| Size<T>& Size<T>::operator/=(T d) noexcept | |||
| { | |||
| fWidth /= d; | |||
| fHeight /= d; | |||
| return *this; | |||
| } | |||
| template<typename T> | |||
| bool Size<T>::operator==(const Size<T>& size) const noexcept | |||
| { | |||
| return (fWidth == size.fWidth && fHeight == size.fHeight); | |||
| } | |||
| template<typename T> | |||
| bool Size<T>::operator!=(const Size<T>& size) const noexcept | |||
| { | |||
| return !operator==(size); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Rectangle | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle() noexcept | |||
| : fPos(0, 0), | |||
| fSize(0, 0) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle(T x, T y, T width, T height) noexcept | |||
| : fPos(x, y), | |||
| fSize(width, height) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle(T x, T y, const Size<T>& size) noexcept | |||
| : fPos(x, y), | |||
| fSize(size) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle(const Point<T>& pos, T width, T height) noexcept | |||
| : fPos(pos), | |||
| fSize(width, height) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle(const Point<T>& pos, const Size<T>& size) noexcept | |||
| : fPos(pos), | |||
| fSize(size) | |||
| { | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept | |||
| : fPos(rect.fPos), | |||
| fSize(rect.fSize) | |||
| { | |||
| } | |||
| template<typename T> | |||
| T Rectangle<T>::getX() const noexcept | |||
| { | |||
| return fPos.fX; | |||
| } | |||
| template<typename T> | |||
| T Rectangle<T>::getY() const noexcept | |||
| { | |||
| return fPos.fY; | |||
| } | |||
| template<typename T> | |||
| T Rectangle<T>::getWidth() const noexcept | |||
| { | |||
| return fSize.fWidth; | |||
| } | |||
| template<typename T> | |||
| T Rectangle<T>::getHeight() const noexcept | |||
| { | |||
| return fSize.fHeight; | |||
| } | |||
| template<typename T> | |||
| const Point<T>& Rectangle<T>::getPos() const noexcept | |||
| { | |||
| return fPos; | |||
| } | |||
| template<typename T> | |||
| const Size<T>& Rectangle<T>::getSize() const noexcept | |||
| { | |||
| return fSize; | |||
| } | |||
| template<typename T> | |||
| bool Rectangle<T>::contains(T x, T y) const noexcept | |||
| { | |||
| return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight); | |||
| } | |||
| template<typename T> | |||
| bool Rectangle<T>::contains(const Point<T>& pos) const noexcept | |||
| { | |||
| return contains(pos.fX, pos.fY); | |||
| } | |||
| template<typename T> | |||
| bool Rectangle<T>::containsX(T x) const noexcept | |||
| { | |||
| return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth); | |||
| } | |||
| template<typename T> | |||
| bool Rectangle<T>::containsY(T y) const noexcept | |||
| { | |||
| return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight); | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setX(T x) noexcept | |||
| { | |||
| fPos.fX = x; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setY(T y) noexcept | |||
| { | |||
| fPos.fY = y; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setPos(T x, T y) noexcept | |||
| { | |||
| fPos.fX = x; | |||
| fPos.fY = y; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setPos(const Point<T>& pos) noexcept | |||
| { | |||
| fPos = pos; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::move(T x, T y) noexcept | |||
| { | |||
| fPos.fX += x; | |||
| fPos.fY += y; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::move(const Point<T>& pos) noexcept | |||
| { | |||
| fPos += pos; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setWidth(T width) noexcept | |||
| { | |||
| fSize.fWidth = width; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setHeight(T height) noexcept | |||
| { | |||
| fSize.fHeight = height; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setSize(T width, T height) noexcept | |||
| { | |||
| fSize.fWidth = width; | |||
| fSize.fHeight = height; | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::setSize(const Size<T>& size) noexcept | |||
| { | |||
| fSize = size; | |||
| } | |||
| template<typename T> | |||
| Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept | |||
| { | |||
| fPos = rect.fPos; | |||
| fSize = rect.fSize; | |||
| return *this; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Possible template data types | |||
| template class Point<int>; | |||
| template class Point<long>; | |||
| template class Point<float>; | |||
| template class Point<double>; | |||
| template class Size<int>; | |||
| template class Size<long>; | |||
| template class Size<float>; | |||
| template class Size<double>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<long>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<double>; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,145 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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() noexcept | |||
| : fRawData(nullptr), | |||
| fSize(0, 0), | |||
| fFormat(0), | |||
| fType(0) | |||
| { | |||
| } | |||
| Image::Image(const char* rawData, int width, int height, GLenum format, GLenum type) noexcept | |||
| : fRawData(rawData), | |||
| fSize(width, height), | |||
| fFormat(format), | |||
| fType(type) | |||
| { | |||
| } | |||
| Image::Image(const char* rawData, const Size<int>& size, GLenum format, GLenum type) noexcept | |||
| : fRawData(rawData), | |||
| fSize(size), | |||
| fFormat(format), | |||
| fType(type) | |||
| { | |||
| } | |||
| Image::Image(const Image& image) noexcept | |||
| : fRawData(image.fRawData), | |||
| fSize(image.fSize), | |||
| fFormat(image.fFormat), | |||
| fType(image.fType) | |||
| { | |||
| } | |||
| void Image::loadFromMemory(const char* rawData, int width, int height, GLenum format, GLenum type) noexcept | |||
| { | |||
| loadFromMemory(rawData, Size<int>(width, height), format, type); | |||
| } | |||
| void Image::loadFromMemory(const char* rawData, const Size<int>& size, GLenum format, GLenum type) noexcept | |||
| { | |||
| fRawData = rawData; | |||
| fSize = size; | |||
| fFormat = format; | |||
| fType = type; | |||
| } | |||
| bool Image::isValid() const noexcept | |||
| { | |||
| return (fRawData != nullptr && getWidth() > 0 && getHeight() > 0); | |||
| } | |||
| int Image::getWidth() const noexcept | |||
| { | |||
| return fSize.getWidth(); | |||
| } | |||
| int Image::getHeight() const noexcept | |||
| { | |||
| return fSize.getHeight(); | |||
| } | |||
| const Size<int>& 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() const | |||
| { | |||
| draw(0, 0); | |||
| } | |||
| void Image::draw(int x, int y) const | |||
| { | |||
| if (! isValid()) | |||
| return; | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glRasterPos2i(x, fSize.getHeight()+y); | |||
| glDrawPixels(fSize.getWidth(), fSize.getHeight(), fFormat, fType, fRawData); | |||
| } | |||
| void Image::draw(const Point<int>& pos) const | |||
| { | |||
| draw(pos.getX(), pos.getY()); | |||
| } | |||
| Image& Image::operator=(const Image& image) noexcept | |||
| { | |||
| fRawData = image.fRawData; | |||
| fSize = image.fSize; | |||
| fFormat = image.fFormat; | |||
| fType = image.fType; | |||
| return *this; | |||
| } | |||
| bool Image::operator==(const Image& image) const noexcept | |||
| { | |||
| return (fRawData == image.fRawData); | |||
| } | |||
| bool Image::operator!=(const Image& image) const noexcept | |||
| { | |||
| return (fRawData != image.fRawData); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,83 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../ImageAboutWindow.hpp" | |||
| // FIXME: 32bit hack | |||
| #if ! (defined (__LP64__) || defined (_LP64) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) | |||
| # define PAD_SIZE +1 | |||
| #else | |||
| # define PAD_SIZE | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| ImageAboutWindow::ImageAboutWindow(App& app, Window& parent, const Image& image) | |||
| : Window(app, parent), | |||
| Widget((Window&)*this), | |||
| fImgBackground(image) | |||
| { | |||
| Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); | |||
| Window::setTitle("About"); | |||
| } | |||
| ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) | |||
| : Window(widget->getParentApp(), widget->getParentWindow()), | |||
| Widget((Window&)*this), | |||
| fImgBackground(image) | |||
| { | |||
| Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); | |||
| Window::setTitle("About"); | |||
| } | |||
| void ImageAboutWindow::setImage(const Image& image) | |||
| { | |||
| fImgBackground = image; | |||
| Window::setSize(image.getWidth(), image.getHeight() PAD_SIZE); | |||
| } | |||
| void ImageAboutWindow::onDisplay() | |||
| { | |||
| fImgBackground.draw(); | |||
| } | |||
| bool ImageAboutWindow::onMouse(int, bool press, int, int) | |||
| { | |||
| if (press) | |||
| { | |||
| Window::close(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageAboutWindow::onKeyboard(bool press, uint32_t key) | |||
| { | |||
| if (press && key == CHAR_ESCAPE) | |||
| { | |||
| Window::close(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,173 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../ImageButton.hpp" | |||
| #include <cassert> | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| ImageButton::ImageButton(Window& parent, const Image& image) | |||
| : Widget(parent), | |||
| fImageNormal(image), | |||
| fImageHover(image), | |||
| fImageDown(image), | |||
| fCurImage(&fImageNormal), | |||
| fCurButton(-1), | |||
| fCallback(nullptr) | |||
| { | |||
| } | |||
| ImageButton::ImageButton(Widget* widget, const Image& image) | |||
| : Widget(widget->getParentWindow()), | |||
| fImageNormal(image), | |||
| fImageHover(image), | |||
| fImageDown(image), | |||
| fCurImage(&fImageNormal), | |||
| fCurButton(-1), | |||
| fCallback(nullptr) | |||
| { | |||
| } | |||
| ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) | |||
| : Widget(parent), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageHover), | |||
| fImageDown(imageDown), | |||
| fCurImage(&fImageNormal), | |||
| fCurButton(-1), | |||
| fCallback(nullptr) | |||
| { | |||
| assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); | |||
| setSize(fCurImage->getSize()); | |||
| } | |||
| ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) | |||
| : Widget(widget->getParentWindow()), | |||
| fImageNormal(imageNormal), | |||
| fImageHover(imageHover), | |||
| fImageDown(imageDown), | |||
| fCurImage(&fImageNormal), | |||
| fCurButton(-1), | |||
| fCallback(nullptr) | |||
| { | |||
| assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); | |||
| setSize(fCurImage->getSize()); | |||
| } | |||
| ImageButton::ImageButton(const ImageButton& imageButton) | |||
| : Widget(imageButton.getParentWindow()), | |||
| fImageNormal(imageButton.fImageNormal), | |||
| fImageHover(imageButton.fImageHover), | |||
| fImageDown(imageButton.fImageDown), | |||
| fCurImage(&fImageNormal), | |||
| fCurButton(-1), | |||
| fCallback(imageButton.fCallback) | |||
| { | |||
| assert(fImageNormal.getSize() == fImageHover.getSize() && fImageHover.getSize() == fImageDown.getSize()); | |||
| setSize(fCurImage->getSize()); | |||
| } | |||
| void ImageButton::setCallback(Callback* callback) | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageButton::onDisplay() | |||
| { | |||
| fCurImage->draw(getPos()); | |||
| } | |||
| bool ImageButton::onMouse(int button, bool press, int x, int y) | |||
| { | |||
| if (fCurButton != -1 && ! press) | |||
| { | |||
| if (fCurImage != &fImageNormal) | |||
| { | |||
| fCurImage = &fImageNormal; | |||
| repaint(); | |||
| } | |||
| if (! getArea().contains(x, y)) | |||
| { | |||
| fCurButton = -1; | |||
| return false; | |||
| } | |||
| if (fCallback != nullptr) | |||
| fCallback->imageButtonClicked(this, fCurButton); | |||
| //if (getArea().contains(x, y)) | |||
| //{ | |||
| // fCurImage = &fImageHover; | |||
| // repaint(); | |||
| //} | |||
| fCurButton = -1; | |||
| return true; | |||
| } | |||
| if (press && getArea().contains(x, y)) | |||
| { | |||
| if (fCurImage != &fImageDown) | |||
| { | |||
| fCurImage = &fImageDown; | |||
| repaint(); | |||
| } | |||
| fCurButton = button; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageButton::onMotion(int x, int y) | |||
| { | |||
| if (fCurButton != -1) | |||
| return true; | |||
| if (getArea().contains(x, y)) | |||
| { | |||
| if (fCurImage != &fImageHover) | |||
| { | |||
| fCurImage = &fImageHover; | |||
| repaint(); | |||
| } | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| if (fCurImage != &fImageNormal) | |||
| { | |||
| fCurImage = &fImageNormal; | |||
| repaint(); | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,340 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../ImageKnob.hpp" | |||
| #include <cassert> | |||
| #include <cstdio> | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) | |||
| : Widget(parent), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fValue(0.5f), | |||
| fOrientation(orientation), | |||
| fRotationAngle(0), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(nullptr), | |||
| fIsImgVertical(image.getHeight() > image.getWidth()), | |||
| fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()), | |||
| fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize), | |||
| fKnobArea(0, 0, fImgLayerSize, fImgLayerSize), | |||
| fTextureId(0) | |||
| { | |||
| setSize(fImgLayerSize, fImgLayerSize); | |||
| } | |||
| ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) | |||
| : Widget(widget->getParentWindow()), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fValue(0.5f), | |||
| fOrientation(orientation), | |||
| fRotationAngle(0), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(nullptr), | |||
| fIsImgVertical(image.getHeight() > image.getWidth()), | |||
| fImgLayerSize(fIsImgVertical ? image.getWidth() : image.getHeight()), | |||
| fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerSize : image.getWidth()/fImgLayerSize), | |||
| fKnobArea(0, 0, fImgLayerSize, fImgLayerSize), | |||
| fTextureId(0) | |||
| { | |||
| setSize(fImgLayerSize, fImgLayerSize); | |||
| } | |||
| ImageKnob::ImageKnob(const ImageKnob& imageKnob) | |||
| : Widget(imageKnob.getParentWindow()), | |||
| fImage(imageKnob.fImage), | |||
| fMinimum(imageKnob.fMinimum), | |||
| fMaximum(imageKnob.fMaximum), | |||
| fValue(imageKnob.fValue), | |||
| fOrientation(imageKnob.fOrientation), | |||
| fRotationAngle(imageKnob.fRotationAngle), | |||
| fDragging(false), | |||
| fLastX(0), | |||
| fLastY(0), | |||
| fCallback(imageKnob.fCallback), | |||
| fIsImgVertical(imageKnob.fIsImgVertical), | |||
| fImgLayerSize(imageKnob.fImgLayerSize), | |||
| fImgLayerCount(imageKnob.fImgLayerCount), | |||
| fKnobArea(imageKnob.fKnobArea), | |||
| fTextureId(0) | |||
| { | |||
| setSize(fImgLayerSize, fImgLayerSize); | |||
| if (fRotationAngle != 0) | |||
| { | |||
| // force new texture creation | |||
| fRotationAngle = 0; | |||
| setRotationAngle(imageKnob.fRotationAngle); | |||
| } | |||
| } | |||
| float ImageKnob::getValue() const | |||
| { | |||
| return fValue; | |||
| } | |||
| void ImageKnob::setOrientation(Orientation orientation) | |||
| { | |||
| if (fOrientation == orientation) | |||
| return; | |||
| fOrientation = orientation; | |||
| } | |||
| void ImageKnob::setRange(float min, float max) | |||
| { | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| } | |||
| void ImageKnob::setValue(float value, bool sendCallback) | |||
| { | |||
| if (fValue == value) | |||
| return; | |||
| fValue = value; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| fCallback->imageKnobValueChanged(this, fValue); | |||
| } | |||
| void ImageKnob::setRotationAngle(int angle) | |||
| { | |||
| if (fRotationAngle == angle) | |||
| return; | |||
| if (fRotationAngle != 0) | |||
| { | |||
| // delete old texture | |||
| glDeleteTextures(1, &fTextureId); | |||
| fTextureId = 0; | |||
| } | |||
| fRotationAngle = angle; | |||
| if (angle != 0) | |||
| { | |||
| glEnable(GL_TEXTURE_2D); | |||
| glGenTextures(1, &fTextureId); | |||
| glBindTexture(GL_TEXTURE_2D, fTextureId); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData()); | |||
| 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); | |||
| float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| } | |||
| void ImageKnob::setCallback(Callback* callback) | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageKnob::onDisplay() | |||
| { | |||
| const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); | |||
| if (fRotationAngle != 0) | |||
| { | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, fTextureId); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWidth(), getHeight(), 0, fImage.getFormat(), fImage.getType(), fImage.getRawData()); | |||
| 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); | |||
| float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPushMatrix(); | |||
| const GLint w2 = getWidth()/2; | |||
| const GLint h2 = getHeight()/2; | |||
| glTranslatef(getX()+w2, getY()+h2, 0.0f); | |||
| glRotatef(normValue*fRotationAngle, 0.0f, 0.0f, 1.0f); | |||
| glBegin(GL_QUADS); | |||
| glTexCoord2f(0.0f, 1.0f); | |||
| glVertex2i(-w2, -h2); | |||
| glTexCoord2f(1.0f, 1.0f); | |||
| glVertex2i(getWidth()-w2, -h2); | |||
| glTexCoord2f(1.0f, 0.0f); | |||
| glVertex2i(getWidth()-w2, getHeight()-h2); | |||
| glTexCoord2f(0.0f, 0.0f); | |||
| glVertex2i(-w2, getHeight()-h2); | |||
| glEnd(); | |||
| glPopMatrix(); | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| else | |||
| { | |||
| const int layerDataSize = fImgLayerSize * fImgLayerSize * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3); | |||
| const int imageDataSize = layerDataSize * fImgLayerCount; | |||
| const int imageDataOffset = imageDataSize - layerDataSize - (layerDataSize * int(normValue * float(fImgLayerCount-1))); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glRasterPos2i(getX(), getY()+getHeight()); | |||
| glDrawPixels(fImgLayerSize, fImgLayerSize, fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset); | |||
| } | |||
| } | |||
| bool ImageKnob::onMouse(int button, bool press, int x, int y) | |||
| { | |||
| if (button != 1) | |||
| return false; | |||
| if (press) | |||
| { | |||
| if (! getArea().contains(x, y)) | |||
| return false; | |||
| fDragging = true; | |||
| fLastX = x; | |||
| fLastY = y; | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragStarted(this); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageKnobDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageKnob::onMotion(int x, int y) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| if (fOrientation == ImageKnob::Horizontal) | |||
| { | |||
| int movX = x - fLastX; | |||
| if (movX != 0) | |||
| { | |||
| float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||
| float value = fValue + (float(fMaximum - fMinimum) / d * float(movX)); | |||
| if (value < fMinimum) | |||
| value = fMinimum; | |||
| else if (value > fMaximum) | |||
| value = fMaximum; | |||
| setValue(value, true); | |||
| } | |||
| } | |||
| else if (fOrientation == ImageKnob::Vertical) | |||
| { | |||
| int movY = fLastY - y; | |||
| if (movY != 0) | |||
| { | |||
| float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||
| float value = fValue + (float(fMaximum - fMinimum) / d * float(movY)); | |||
| if (value < fMinimum) | |||
| value = fMinimum; | |||
| else if (value > fMaximum) | |||
| value = fMaximum; | |||
| setValue(value, true); | |||
| } | |||
| } | |||
| fLastX = x; | |||
| fLastY = y; | |||
| return true; | |||
| } | |||
| void ImageKnob::onReshape(int width, int height) | |||
| { | |||
| // if (fRotationAngle != 0) | |||
| // glEnable(GL_TEXTURE_2D); | |||
| Widget::onReshape(width, height); | |||
| } | |||
| void ImageKnob::onClose() | |||
| { | |||
| // delete old texture | |||
| setRotationAngle(0); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,316 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../ImageSlider.hpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| ImageSlider::ImageSlider(Window& parent, const Image& image) | |||
| : Widget(parent), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fValue(0.5f), | |||
| fIsSwitch(false), | |||
| fDragging(false), | |||
| fStartedX(0), | |||
| fStartedY(0), | |||
| fCallback(nullptr) | |||
| { | |||
| setSize(fImage.getSize()); | |||
| } | |||
| ImageSlider::ImageSlider(Widget* widget, const Image& image) | |||
| : Widget(widget->getParentWindow()), | |||
| fImage(image), | |||
| fMinimum(0.0f), | |||
| fMaximum(1.0f), | |||
| fValue(0.5f), | |||
| fIsSwitch(false), | |||
| fDragging(false), | |||
| fStartedX(0), | |||
| fStartedY(0), | |||
| fCallback(nullptr) | |||
| { | |||
| setSize(fImage.getSize()); | |||
| } | |||
| ImageSlider::ImageSlider(const ImageSlider& imageSlider) | |||
| : Widget(imageSlider.getParentWindow()), | |||
| fImage(imageSlider.fImage), | |||
| fMinimum(imageSlider.fMinimum), | |||
| fMaximum(imageSlider.fMaximum), | |||
| fValue(imageSlider.fValue), | |||
| fIsSwitch(imageSlider.fIsSwitch), | |||
| fDragging(false), | |||
| fStartedX(0), | |||
| fStartedY(0), | |||
| fCallback(imageSlider.fCallback), | |||
| fStartPos(imageSlider.fStartPos), | |||
| fEndPos(imageSlider.fEndPos), | |||
| fSliderArea(imageSlider.fSliderArea) | |||
| { | |||
| setSize(fImage.getSize()); | |||
| } | |||
| float ImageSlider::getValue() const | |||
| { | |||
| return fValue; | |||
| } | |||
| void ImageSlider::setStartPos(const Point<int>& startPos) | |||
| { | |||
| fStartPos = startPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setStartPos(int x, int y) | |||
| { | |||
| setStartPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setEndPos(const Point<int>& endPos) | |||
| { | |||
| fEndPos = endPos; | |||
| _recheckArea(); | |||
| } | |||
| void ImageSlider::setEndPos(int x, int y) | |||
| { | |||
| setEndPos(Point<int>(x, y)); | |||
| } | |||
| void ImageSlider::setRange(float min, float max) | |||
| { | |||
| if (fValue < min) | |||
| { | |||
| fValue = min; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } | |||
| else if (fValue > max) | |||
| { | |||
| fValue = max; | |||
| repaint(); | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } | |||
| fMinimum = min; | |||
| fMaximum = max; | |||
| } | |||
| void ImageSlider::setValue(float value, bool sendCallback) | |||
| { | |||
| if (fValue == value) | |||
| return; | |||
| fValue = value; | |||
| repaint(); | |||
| if (sendCallback && fCallback != nullptr) | |||
| fCallback->imageSliderValueChanged(this, fValue); | |||
| } | |||
| void ImageSlider::setIsSwitch(bool yesNo) | |||
| { | |||
| if (fIsSwitch == yesNo) | |||
| return; | |||
| fIsSwitch = yesNo; | |||
| repaint(); | |||
| } | |||
| void ImageSlider::setCallback(Callback* callback) | |||
| { | |||
| fCallback = callback; | |||
| } | |||
| void ImageSlider::onDisplay() | |||
| { | |||
| #if 0 // DEBUG, paints slider area | |||
| glColor3f(0.4f, 0.5f, 0.1f); | |||
| glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); | |||
| #endif | |||
| float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); | |||
| int x, y; | |||
| if (fStartPos.getX() == fEndPos.getX()) | |||
| { | |||
| x = fStartPos.getX(); | |||
| y = fEndPos.getY() - normValue*(fEndPos.getY()-fStartPos.getY()); | |||
| } | |||
| else if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| x = fEndPos.getX() - normValue*(fEndPos.getX()-fStartPos.getX()); | |||
| y = fStartPos.getY(); | |||
| } | |||
| else | |||
| return; | |||
| fImage.draw(x, y); | |||
| } | |||
| bool ImageSlider::onMouse(int button, bool press, int x, int y) | |||
| { | |||
| if (button != 1) | |||
| return false; | |||
| if (press) | |||
| { | |||
| if (! fSliderArea.contains(x, y)) | |||
| return false; | |||
| float vper; | |||
| if (fStartPos.getX() == fEndPos.getX()) | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| else if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| return false; | |||
| float value; | |||
| if (fIsSwitch) | |||
| { | |||
| if (vper < 0.5f) | |||
| value = fMaximum; | |||
| else | |||
| value = fMinimum; | |||
| } | |||
| else | |||
| { | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| value = fMinimum; | |||
| else if (value > fMaximum) | |||
| value = fMaximum; | |||
| } | |||
| fDragging = true; | |||
| fStartedX = x; | |||
| fStartedY = y; | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragStarted(this); | |||
| setValue(value, true); | |||
| return true; | |||
| } | |||
| else if (fDragging) | |||
| { | |||
| if (fCallback != nullptr) | |||
| fCallback->imageSliderDragFinished(this); | |||
| fDragging = false; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool ImageSlider::onMotion(int x, int y) | |||
| { | |||
| if (! fDragging) | |||
| return false; | |||
| bool horizontal = fStartPos.getY() == fEndPos.getY(); | |||
| if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) | |||
| { | |||
| float vper; | |||
| if (horizontal) | |||
| { | |||
| // horizontal | |||
| vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); | |||
| } | |||
| else | |||
| { | |||
| // vertical | |||
| vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); | |||
| } | |||
| float value; | |||
| if (fIsSwitch) | |||
| { | |||
| if (vper < 0.5f) | |||
| value = fMaximum; | |||
| else | |||
| value = fMinimum; | |||
| } | |||
| else | |||
| { | |||
| value = fMaximum - vper * (fMaximum - fMinimum); | |||
| if (value < fMinimum) | |||
| value = fMinimum; | |||
| else if (value > fMaximum) | |||
| value = fMaximum; | |||
| } | |||
| setValue(value, true); | |||
| } | |||
| else if (y < fSliderArea.getY()) | |||
| { | |||
| setValue(fMaximum, true); | |||
| } | |||
| else | |||
| { | |||
| setValue(fMinimum, true); | |||
| } | |||
| return true; | |||
| } | |||
| void ImageSlider::_recheckArea() | |||
| { | |||
| if (fStartPos.getX() == fEndPos.getX()) | |||
| { | |||
| fSliderArea = Rectangle<int>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| fImage.getWidth(), | |||
| fEndPos.getY() + fImage.getHeight() - fStartPos.getY()); | |||
| } | |||
| else if (fStartPos.getY() == fEndPos.getY()) | |||
| { | |||
| fSliderArea = Rectangle<int>(fStartPos.getX(), | |||
| fStartPos.getY(), | |||
| fEndPos.getX() + fImage.getWidth() - fStartPos.getX(), | |||
| fImage.getHeight()); | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,243 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../App.hpp" | |||
| #include "../Widget.hpp" | |||
| #include "../Window.hpp" | |||
| #include <cassert> | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // Widget | |||
| Widget::Widget(Window& parent) | |||
| : fParent(parent), | |||
| fVisible(true) | |||
| { | |||
| fParent.addWidget(this); | |||
| } | |||
| Widget::~Widget() | |||
| { | |||
| fParent.removeWidget(this); | |||
| } | |||
| bool Widget::isVisible() const noexcept | |||
| { | |||
| return fVisible; | |||
| } | |||
| void Widget::setVisible(bool yesNo) | |||
| { | |||
| if (fVisible == yesNo) | |||
| return; | |||
| fVisible = yesNo; | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::show() | |||
| { | |||
| setVisible(true); | |||
| } | |||
| void Widget::hide() | |||
| { | |||
| setVisible(false); | |||
| } | |||
| int Widget::getX() const noexcept | |||
| { | |||
| return fArea.getX(); | |||
| } | |||
| int Widget::getY() const noexcept | |||
| { | |||
| return fArea.getY(); | |||
| } | |||
| const Point<int>& Widget::getPos() const noexcept | |||
| { | |||
| return fArea.getPos(); | |||
| } | |||
| void Widget::setX(int x) | |||
| { | |||
| if (fArea.getX() == x) | |||
| return; | |||
| fArea.setX(x); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::setY(int y) | |||
| { | |||
| if (fArea.getY() == y) | |||
| return; | |||
| fArea.setY(y); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::setPos(int x, int y) | |||
| { | |||
| setPos(Point<int>(x, y)); | |||
| } | |||
| void Widget::setPos(const Point<int>& pos) | |||
| { | |||
| if (fArea.getPos() == pos) | |||
| return; | |||
| fArea.setPos(pos); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::move(int x, int y) | |||
| { | |||
| fArea.move(x, y); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::move(const Point<int>& pos) | |||
| { | |||
| fArea.move(pos); | |||
| fParent.repaint(); | |||
| } | |||
| int Widget::getWidth() const noexcept | |||
| { | |||
| return fArea.getWidth(); | |||
| } | |||
| int Widget::getHeight() const noexcept | |||
| { | |||
| return fArea.getHeight(); | |||
| } | |||
| const Size<int>& Widget::getSize() const noexcept | |||
| { | |||
| return fArea.getSize(); | |||
| } | |||
| void Widget::setWidth(int width) | |||
| { | |||
| if (fArea.getWidth() == width) | |||
| return; | |||
| fArea.setWidth(width); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::setHeight(int height) | |||
| { | |||
| if (fArea.getHeight() == height) | |||
| return; | |||
| fArea.setHeight(height); | |||
| fParent.repaint(); | |||
| } | |||
| void Widget::setSize(int width, int height) | |||
| { | |||
| setSize(Size<int>(width, height)); | |||
| } | |||
| void Widget::setSize(const Size<int>& size) | |||
| { | |||
| if (fArea.getSize() == size) | |||
| return; | |||
| fArea.setSize(size); | |||
| fParent.repaint(); | |||
| } | |||
| const Rectangle<int>& Widget::getArea() const noexcept | |||
| { | |||
| return fArea; | |||
| } | |||
| uint32_t Widget::getEventTimestamp() | |||
| { | |||
| return fParent.getEventTimestamp(); | |||
| } | |||
| int Widget::getModifiers() | |||
| { | |||
| return fParent.getModifiers(); | |||
| } | |||
| App& Widget::getParentApp() const noexcept | |||
| { | |||
| return fParent.getApp(); | |||
| } | |||
| Window& Widget::getParentWindow() const noexcept | |||
| { | |||
| return fParent; | |||
| } | |||
| void Widget::repaint() | |||
| { | |||
| fParent.repaint(); | |||
| } | |||
| bool Widget::onKeyboard(bool, uint32_t) | |||
| { | |||
| return false; | |||
| } | |||
| bool Widget::onMouse(int, bool, int, int) | |||
| { | |||
| return false; | |||
| } | |||
| bool Widget::onMotion(int, int) | |||
| { | |||
| return false; | |||
| } | |||
| bool Widget::onScroll(float, float) | |||
| { | |||
| return false; | |||
| } | |||
| bool Widget::onSpecial(bool, Key) | |||
| { | |||
| return false; | |||
| } | |||
| void Widget::onReshape(int width, int height) | |||
| { | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| glMatrixMode(GL_PROJECTION); | |||
| glLoadIdentity(); | |||
| glOrtho(0, width, height, 0, 0.0f, 1.0f); | |||
| glViewport(0, 0, width, height); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| glLoadIdentity(); | |||
| } | |||
| void Widget::onClose() | |||
| { | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,805 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "../App.hpp" | |||
| #include "../Widget.hpp" | |||
| #include "../Window.hpp" | |||
| #include <cassert> | |||
| #include <list> | |||
| #include "pugl/pugl.h" | |||
| #if DGL_OS_WINDOWS | |||
| # include "pugl/pugl_win.cpp" | |||
| #elif DGL_OS_MAC | |||
| extern "C" { | |||
| # include "pugl/pugl_osx_extended.h" | |||
| } | |||
| #elif DGL_OS_LINUX | |||
| extern "C" { | |||
| # include "pugl/pugl_x11.c" | |||
| } | |||
| #else | |||
| # error Unsupported platform | |||
| #endif | |||
| #define FOR_EACH_WIDGET(it) \ | |||
| for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) | |||
| #define FOR_EACH_WIDGET_INV(rit) \ | |||
| for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) | |||
| START_NAMESPACE_DGL | |||
| Window* dgl_lastUiParent = nullptr; | |||
| // ----------------------------------------------------------------------- | |||
| // Window Private | |||
| class Window::PrivateData | |||
| { | |||
| public: | |||
| PrivateData(App& app, Window* const self) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglCreate(0, "Window", 100, 100, true, false)), | |||
| fFirstInit(true), | |||
| fVisible(false), | |||
| fResizable(true), | |||
| #if DGL_OS_WINDOWS | |||
| hwnd(0) | |||
| #elif DGL_OS_LINUX | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| #else | |||
| _dummy('\0') | |||
| #endif | |||
| { | |||
| init(); | |||
| } | |||
| PrivateData(App& app, Window* const self, Window& parent) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglCreate(0, "Window", 100, 100, true, false)), | |||
| fFirstInit(true), | |||
| fVisible(false), | |||
| fResizable(true), | |||
| fModal(parent.pData), | |||
| #if DGL_OS_WINDOWS | |||
| hwnd(0) | |||
| #elif DGL_OS_LINUX | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| #else | |||
| _dummy('\0') | |||
| #endif | |||
| { | |||
| init(); | |||
| #if DGL_OS_LINUX | |||
| PuglInternals* const parentImpl = parent.pData->fView->impl; | |||
| XSetTransientForHint(xDisplay, xWindow, parentImpl->win); | |||
| #endif | |||
| } | |||
| PrivateData(App& app, Window* const self, const intptr_t parentId) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglCreate(parentId, "Window", 100, 100, true, true)), | |||
| fFirstInit(true), | |||
| fVisible(true), | |||
| fResizable(false), | |||
| #if DGL_OS_WINDOWS | |||
| hwnd(0) | |||
| #elif DGL_OS_LINUX | |||
| xDisplay(nullptr), | |||
| xWindow(0) | |||
| #else | |||
| _dummy('\0') | |||
| #endif | |||
| { | |||
| init(); | |||
| // starts visible | |||
| fApp.oneShown(); | |||
| fFirstInit = false; | |||
| } | |||
| void init() | |||
| { | |||
| if (fView == nullptr) | |||
| return; | |||
| dgl_lastUiParent = fSelf; | |||
| puglSetHandle(fView, this); | |||
| puglSetDisplayFunc(fView, onDisplayCallback); | |||
| puglSetKeyboardFunc(fView, onKeyboardCallback); | |||
| puglSetMotionFunc(fView, onMotionCallback); | |||
| puglSetMouseFunc(fView, onMouseCallback); | |||
| puglSetScrollFunc(fView, onScrollCallback); | |||
| puglSetSpecialFunc(fView, onSpecialCallback); | |||
| puglSetReshapeFunc(fView, onReshapeCallback); | |||
| puglSetCloseFunc(fView, onCloseCallback); | |||
| #if DGL_OS_WINDOWS | |||
| PuglInternals* impl = fView->impl; | |||
| hwnd = impl->hwnd; | |||
| #elif DGL_OS_LINUX | |||
| PuglInternals* impl = fView->impl; | |||
| xDisplay = impl->display; | |||
| xWindow = impl->win; | |||
| #endif | |||
| fApp.addWindow(fSelf); | |||
| } | |||
| ~PrivateData() | |||
| { | |||
| //fOnModal = false; | |||
| fWidgets.clear(); | |||
| if (fView != nullptr) | |||
| { | |||
| fApp.removeWindow(fSelf); | |||
| puglDestroy(fView); | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void close() | |||
| { | |||
| setVisible(false); | |||
| if (! fFirstInit) | |||
| { | |||
| fApp.oneHidden(); | |||
| fFirstInit = true; | |||
| } | |||
| } | |||
| void exec(const bool lockWait) | |||
| { | |||
| exec_init(); | |||
| if (lockWait) | |||
| { | |||
| while (fVisible && fModal.enabled) | |||
| { | |||
| // idle() | |||
| puglProcessEvents(fView); | |||
| if (fModal.parent != nullptr) | |||
| fModal.parent->idle(); | |||
| msleep(10); | |||
| } | |||
| exec_fini(); | |||
| } | |||
| else | |||
| { | |||
| idle(); | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void focus() | |||
| { | |||
| #if DGL_OS_WINDOWS | |||
| SetForegroundWindow(hwnd); | |||
| SetActiveWindow(hwnd); | |||
| SetFocus(hwnd); | |||
| #elif DGL_OS_MAC | |||
| puglImplFocus(fView); | |||
| #elif DGL_OS_LINUX | |||
| XRaiseWindow(xDisplay, xWindow); | |||
| XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime); | |||
| #endif | |||
| } | |||
| void idle() | |||
| { | |||
| puglProcessEvents(fView); | |||
| if (fVisible && fModal.enabled && fModal.parent != nullptr) | |||
| fModal.parent->idle(); | |||
| } | |||
| void repaint() | |||
| { | |||
| puglPostRedisplay(fView); | |||
| } | |||
| void flush() | |||
| { | |||
| #if DGL_OS_WINDOWS | |||
| UpdateWindow(hwnd); | |||
| #elif DGL_OS_MAC | |||
| #elif DGL_OS_LINUX | |||
| XFlush(xDisplay); | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| bool isVisible() const noexcept | |||
| { | |||
| return fVisible; | |||
| } | |||
| void setVisible(const bool yesNo) | |||
| { | |||
| if (fVisible == yesNo) | |||
| return; | |||
| fVisible = yesNo; | |||
| #ifndef DGL_OS_MAC | |||
| if (yesNo && fFirstInit) | |||
| setSize(fView->width, fView->height, true); | |||
| #endif | |||
| #if DGL_OS_WINDOWS | |||
| if (yesNo) | |||
| { | |||
| ShowWindow(hwnd, WS_VISIBLE); | |||
| if (! fFirstInit) | |||
| ShowWindow(hwnd, SW_RESTORE); | |||
| } | |||
| else | |||
| { | |||
| ShowWindow(hwnd, SW_HIDE); | |||
| } | |||
| #elif DGL_OS_MAC | |||
| puglImplSetVisible(fView, yesNo); | |||
| #elif DGL_OS_LINUX | |||
| if (yesNo) | |||
| XMapRaised(xDisplay, xWindow); | |||
| else | |||
| XUnmapWindow(xDisplay, xWindow); | |||
| #endif | |||
| if (yesNo) | |||
| { | |||
| if (fFirstInit) | |||
| { | |||
| fApp.oneShown(); | |||
| fFirstInit = false; | |||
| } | |||
| } | |||
| else if (fModal.enabled) | |||
| exec_fini(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| bool isResizable() const noexcept | |||
| { | |||
| return fResizable; | |||
| } | |||
| void setResizable(const bool yesNo) | |||
| { | |||
| if (fResizable == yesNo) | |||
| return; | |||
| fResizable = yesNo; | |||
| #ifndef DGL_OS_MAC | |||
| setSize(fView->width, fView->height, true); | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| #ifndef DGL_OS_MAC | |||
| int getWidth() const noexcept | |||
| { | |||
| return fView->width; | |||
| } | |||
| int getHeight() const noexcept | |||
| { | |||
| return fView->height; | |||
| } | |||
| Size<int> getSize() const noexcept | |||
| { | |||
| return Size<int>(fView->width, fView->height); | |||
| } | |||
| #endif | |||
| void setSize(unsigned int width, unsigned int height, const bool forced = false) | |||
| { | |||
| if (width == 0) | |||
| width = 1; | |||
| if (height == 0) | |||
| height = 1; | |||
| #ifndef DGL_OS_MAC | |||
| if (fView->width == (int)width && fView->height == (int)height && ! forced) | |||
| return; | |||
| fView->width = width; | |||
| fView->height = height; | |||
| #endif | |||
| #if DGL_OS_WINDOWS | |||
| int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
| if (fResizable) | |||
| winFlags |= WS_SIZEBOX; | |||
| RECT wr = { 0, 0, (long)width, (long)height }; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); | |||
| SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); | |||
| #elif DGL_OS_MAC | |||
| puglImplSetSize(fView, width, height); | |||
| #elif DGL_OS_LINUX | |||
| XResizeWindow(xDisplay, xWindow, width, height); | |||
| if (! fResizable) | |||
| { | |||
| XSizeHints sizeHints; | |||
| memset(&sizeHints, 0, sizeof(sizeHints)); | |||
| sizeHints.flags = PMinSize|PMaxSize; | |||
| sizeHints.min_width = width; | |||
| sizeHints.min_height = height; | |||
| sizeHints.max_width = width; | |||
| sizeHints.max_height = height; | |||
| XSetNormalHints(xDisplay, xWindow, &sizeHints); | |||
| } | |||
| #endif | |||
| repaint(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void setTitle(const char* const title) | |||
| { | |||
| #if DGL_OS_WINDOWS | |||
| SetWindowTextA(hwnd, title); | |||
| #elif DGL_OS_MAC | |||
| puglImplSetTitle(fView, title); | |||
| #elif DGL_OS_LINUX | |||
| XStoreName(xDisplay, xWindow, title); | |||
| #endif | |||
| } | |||
| App& getApp() const noexcept | |||
| { | |||
| return fApp; | |||
| } | |||
| int getModifiers() const | |||
| { | |||
| return puglGetModifiers(fView); | |||
| } | |||
| uint32_t getEventTimestamp() const | |||
| { | |||
| return puglGetEventTimestamp(fView); | |||
| } | |||
| intptr_t getWindowId() const | |||
| { | |||
| return puglGetNativeWindow(fView); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void addWidget(Widget* const widget) | |||
| { | |||
| fWidgets.push_back(widget); | |||
| } | |||
| void removeWidget(Widget* const widget) | |||
| { | |||
| fWidgets.remove(widget); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void exec_init() | |||
| { | |||
| fModal.enabled = true; | |||
| assert(fModal.parent != nullptr); | |||
| if (fModal.parent == nullptr) | |||
| return setVisible(true); | |||
| fModal.parent->fModal.childFocus = this; | |||
| #if DGL_OS_WINDOWS | |||
| // Center this window | |||
| PuglInternals* const parentImpl = fParent->fView->impl; | |||
| RECT curRect; | |||
| RECT parentRect; | |||
| GetWindowRect(hwnd, &curRect); | |||
| GetWindowRect(parentImpl->hwnd, &parentRect); | |||
| int x = parentRect.left+(parentRect.right-curRect.right)/2; | |||
| int y = parentRect.top +(parentRect.bottom-curRect.bottom)/2; | |||
| SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER); | |||
| UpdateWindow(hwnd); | |||
| #endif | |||
| fModal.parent->setVisible(true); | |||
| setVisible(true); | |||
| } | |||
| void exec_fini() | |||
| { | |||
| fModal.enabled = false; | |||
| if (fModal.parent != nullptr) | |||
| fModal.parent->fModal.childFocus = nullptr; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| protected: | |||
| void onDisplay() | |||
| { | |||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||
| FOR_EACH_WIDGET(it) | |||
| { | |||
| Widget* const widget(*it); | |||
| if (widget->isVisible()) | |||
| widget->onDisplay(); | |||
| } | |||
| } | |||
| void onKeyboard(const bool press, const uint32_t key) | |||
| { | |||
| if (fModal.childFocus != nullptr) | |||
| return fModal.childFocus->focus(); | |||
| FOR_EACH_WIDGET_INV(rit) | |||
| { | |||
| Widget* const widget(*rit); | |||
| if (widget->isVisible() && widget->onKeyboard(press, key)) | |||
| break; | |||
| } | |||
| } | |||
| void onMouse(const int button, const bool press, const int x, const int y) | |||
| { | |||
| if (fModal.childFocus != nullptr) | |||
| return fModal.childFocus->focus(); | |||
| FOR_EACH_WIDGET_INV(rit) | |||
| { | |||
| Widget* const widget(*rit); | |||
| if (widget->isVisible() && widget->onMouse(button, press, x, y)) | |||
| break; | |||
| } | |||
| } | |||
| void onMotion(const int x, const int y) | |||
| { | |||
| if (fModal.childFocus != nullptr) | |||
| return; | |||
| FOR_EACH_WIDGET_INV(rit) | |||
| { | |||
| Widget* const widget(*rit); | |||
| if (widget->isVisible() && widget->onMotion(x, y)) | |||
| break; | |||
| } | |||
| } | |||
| void onScroll(const float dx, const float dy) | |||
| { | |||
| if (fModal.childFocus != nullptr) | |||
| return; | |||
| FOR_EACH_WIDGET_INV(rit) | |||
| { | |||
| Widget* const widget(*rit); | |||
| if (widget->isVisible() && widget->onScroll(dx, dy)) | |||
| break; | |||
| } | |||
| } | |||
| void onSpecial(const bool press, const Key key) | |||
| { | |||
| if (fModal.childFocus != nullptr) | |||
| return; | |||
| FOR_EACH_WIDGET_INV(rit) | |||
| { | |||
| Widget* const widget(*rit); | |||
| if (widget->isVisible() && widget->onSpecial(press, key)) | |||
| break; | |||
| } | |||
| } | |||
| void onReshape(const int width, const int height) | |||
| { | |||
| printf("resized: %i:%i\n", width, height); | |||
| FOR_EACH_WIDGET(it) | |||
| { | |||
| Widget* const widget(*it); | |||
| widget->onReshape(width, height); | |||
| } | |||
| } | |||
| void onClose() | |||
| { | |||
| fModal.enabled = false; | |||
| if (fModal.childFocus != nullptr) | |||
| fModal.childFocus->onClose(); | |||
| FOR_EACH_WIDGET(it) | |||
| { | |||
| Widget* const widget(*it); | |||
| widget->onClose(); | |||
| } | |||
| close(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| App& fApp; | |||
| Window* const fSelf; | |||
| PuglView* const fView; | |||
| bool fFirstInit; | |||
| bool fVisible; | |||
| bool fResizable; | |||
| std::list<Widget*> fWidgets; | |||
| struct Modal { | |||
| bool enabled; | |||
| PrivateData* parent; | |||
| PrivateData* childFocus; | |||
| Modal() | |||
| : enabled(false), | |||
| parent(nullptr), | |||
| childFocus(nullptr) {} | |||
| Modal(PrivateData* const p) | |||
| : enabled(false), | |||
| parent(p), | |||
| childFocus(nullptr) {} | |||
| ~Modal() | |||
| { | |||
| assert(! enabled); | |||
| assert(childFocus == nullptr); | |||
| } | |||
| } fModal; | |||
| #if DGL_OS_WINDOWS | |||
| HWND hwnd; | |||
| #elif DGL_OS_LINUX | |||
| Display* xDisplay; | |||
| ::Window xWindow; | |||
| #else | |||
| char _dummy; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Callbacks | |||
| #define handlePtr ((PrivateData*)puglGetHandle(view)) | |||
| static void onDisplayCallback(PuglView* view) | |||
| { | |||
| handlePtr->onDisplay(); | |||
| } | |||
| static void onKeyboardCallback(PuglView* view, bool press, uint32_t key) | |||
| { | |||
| handlePtr->onKeyboard(press, key); | |||
| } | |||
| static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) | |||
| { | |||
| handlePtr->onMouse(button, press, x, y); | |||
| } | |||
| static void onMotionCallback(PuglView* view, int x, int y) | |||
| { | |||
| handlePtr->onMotion(x, y); | |||
| } | |||
| static void onScrollCallback(PuglView* view, float dx, float dy) | |||
| { | |||
| handlePtr->onScroll(dx, dy); | |||
| } | |||
| static void onSpecialCallback(PuglView* view, bool press, PuglKey key) | |||
| { | |||
| handlePtr->onSpecial(press, static_cast<Key>(key)); | |||
| } | |||
| static void onReshapeCallback(PuglView* view, int width, int height) | |||
| { | |||
| handlePtr->onReshape(width, height); | |||
| } | |||
| static void onCloseCallback(PuglView* view) | |||
| { | |||
| handlePtr->onClose(); | |||
| } | |||
| #undef handlePtr | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Window | |||
| Window::Window(App& app) | |||
| : pData(new PrivateData(app, this)) | |||
| { | |||
| } | |||
| Window::Window(App& app, Window& parent) | |||
| : pData(new PrivateData(app, this, parent)) | |||
| { | |||
| } | |||
| Window::Window(App& app, intptr_t parentId) | |||
| : pData(new PrivateData(app, this, parentId)) | |||
| { | |||
| } | |||
| Window::~Window() | |||
| { | |||
| delete pData; | |||
| } | |||
| void Window::show() | |||
| { | |||
| pData->setVisible(true); | |||
| } | |||
| void Window::hide() | |||
| { | |||
| pData->setVisible(false); | |||
| } | |||
| void Window::close() | |||
| { | |||
| pData->close(); | |||
| } | |||
| void Window::exec(bool lockWait) | |||
| { | |||
| pData->exec(lockWait); | |||
| } | |||
| void Window::focus() | |||
| { | |||
| pData->focus(); | |||
| } | |||
| void Window::idle() | |||
| { | |||
| pData->idle(); | |||
| } | |||
| void Window::repaint() | |||
| { | |||
| pData->repaint(); | |||
| } | |||
| bool Window::isVisible() const noexcept | |||
| { | |||
| return pData->isVisible(); | |||
| } | |||
| void Window::setVisible(bool yesNo) | |||
| { | |||
| pData->setVisible(yesNo); | |||
| } | |||
| bool Window::isResizable() const noexcept | |||
| { | |||
| return pData->isResizable(); | |||
| } | |||
| void Window::setResizable(bool yesNo) | |||
| { | |||
| pData->setResizable(yesNo); | |||
| } | |||
| #ifndef DGL_OS_MAC | |||
| int Window::getWidth() const noexcept | |||
| { | |||
| return pData->getWidth(); | |||
| } | |||
| int Window::getHeight() const noexcept | |||
| { | |||
| return pData->getHeight(); | |||
| } | |||
| Size<int> Window::getSize() const noexcept | |||
| { | |||
| return pData->getSize(); | |||
| } | |||
| #endif | |||
| void Window::setSize(unsigned int width, unsigned int height) | |||
| { | |||
| pData->setSize(width, height); | |||
| } | |||
| void Window::setTitle(const char* title) | |||
| { | |||
| pData->setTitle(title); | |||
| } | |||
| App& Window::getApp() const noexcept | |||
| { | |||
| return pData->getApp(); | |||
| } | |||
| int Window::getModifiers() const | |||
| { | |||
| return pData->getModifiers(); | |||
| } | |||
| uint32_t Window::getEventTimestamp() const | |||
| { | |||
| return pData->getEventTimestamp(); | |||
| } | |||
| intptr_t Window::getWindowId() const | |||
| { | |||
| return pData->getWindowId(); | |||
| } | |||
| void Window::addWidget(Widget* const widget) | |||
| { | |||
| pData->addWidget(widget); | |||
| } | |||
| void Window::removeWidget(Widget* const widget) | |||
| { | |||
| pData->removeWidget(widget); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,353 @@ | |||
| /* | |||
| 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.h API for Pugl, a minimal portable API for OpenGL. | |||
| */ | |||
| #ifndef PUGL_H_INCLUDED | |||
| #define PUGL_H_INCLUDED | |||
| #include <stdint.h> | |||
| /* | |||
| This API is pure portable C and contains no platform specific elements, or | |||
| even a GL dependency. However, unfortunately GL includes vary across | |||
| platforms so they are included here to allow 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 | |||
| #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 | |||
| # define PUGL_API | |||
| #endif | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #else | |||
| # include <stdbool.h> | |||
| #endif | |||
| /** | |||
| @defgroup pugl Pugl | |||
| A minimal portable API for OpenGL. | |||
| @{ | |||
| */ | |||
| /** | |||
| An OpenGL 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; | |||
| /** | |||
| Return status code. | |||
| */ | |||
| typedef enum { | |||
| PUGL_SUCCESS = 0 | |||
| } PuglStatus; | |||
| /** | |||
| Convenience symbols for ASCII control characters. | |||
| */ | |||
| typedef enum { | |||
| PUGL_CHAR_BACKSPACE = 0x08, | |||
| PUGL_CHAR_ESCAPE = 0x1B, | |||
| PUGL_CHAR_DELETE = 0x7F | |||
| } PuglChar; | |||
| /** | |||
| 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; | |||
| /** | |||
| Keyboard modifier flags. | |||
| */ | |||
| typedef enum { | |||
| PUGL_MOD_SHIFT = 1, /**< 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; | |||
| /** | |||
| Handle for opaque user data. | |||
| */ | |||
| typedef void* PuglHandle; | |||
| /** | |||
| 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 dx The scroll x distance. | |||
| @param dx The scroll y distance. | |||
| */ | |||
| typedef void (*PuglScrollFunc)(PuglView* view, 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 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); | |||
| /** | |||
| Create a new GL window. | |||
| @param parent Parent window, or 0 for top level. | |||
| @param title Window title, or NULL. | |||
| @param width Window width in pixels. | |||
| @param height Window height in pixels. | |||
| @param resizable Whether window should be user resizable. | |||
| @param visible Whether window should be initially visible. | |||
| */ | |||
| PUGL_API PuglView* | |||
| puglCreate(PuglNativeWindow parent, | |||
| const char* title, | |||
| int width, | |||
| int height, | |||
| bool resizable, | |||
| bool visible); | |||
| /** | |||
| 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); | |||
| /** | |||
| 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); | |||
| /** | |||
| 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); | |||
| /** | |||
| Return the native window handle. | |||
| */ | |||
| PUGL_API PuglNativeWindow | |||
| puglGetNativeWindow(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,143 @@ | |||
| /* | |||
| 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_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. | |||
| */ | |||
| #include "pugl.h" | |||
| typedef struct PuglInternalsImpl PuglInternals; | |||
| struct PuglViewImpl { | |||
| PuglHandle handle; | |||
| PuglCloseFunc closeFunc; | |||
| PuglDisplayFunc displayFunc; | |||
| PuglKeyboardFunc keyboardFunc; | |||
| PuglMotionFunc motionFunc; | |||
| PuglMouseFunc mouseFunc; | |||
| PuglReshapeFunc reshapeFunc; | |||
| PuglScrollFunc scrollFunc; | |||
| PuglSpecialFunc specialFunc; | |||
| PuglInternals* impl; | |||
| int width; | |||
| int height; | |||
| int mods; | |||
| bool mouse_in_view; | |||
| bool ignoreKeyRepeat; | |||
| bool redisplay; | |||
| uint32_t event_timestamp_ms; | |||
| }; | |||
| 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 | |||
| 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; | |||
| } | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,418 @@ | |||
| /* | |||
| 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) 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 result; | |||
| } | |||
| - (void)setPuglview:(PuglView*)view | |||
| { | |||
| puglview = view; | |||
| [self setContentSize:NSMakeSize(view->width, view->height) ]; | |||
| } | |||
| - (BOOL)windowShouldClose:(id)sender | |||
| { | |||
| if (puglview->closeFunc) | |||
| puglview->closeFunc(puglview); | |||
| return YES; | |||
| } | |||
| @end | |||
| void | |||
| puglDisplay(PuglView* view) | |||
| { | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| } | |||
| @interface PuglOpenGLView : NSOpenGLView | |||
| { | |||
| int colorBits; | |||
| int depthBits; | |||
| @public | |||
| PuglView* puglview; | |||
| NSTrackingArea* trackingArea; | |||
| } | |||
| - (id) initWithFrame:(NSRect)frame | |||
| colorBits:(int)numColorBits | |||
| depthBits:(int)numDepthBits; | |||
| - (void) reshape; | |||
| - (void) drawRect:(NSRect)rect; | |||
| - (void) mouseMoved:(NSEvent*)event; | |||
| - (void) mouseDragged:(NSEvent*)event; | |||
| - (void) mouseDown:(NSEvent*)event; | |||
| - (void) mouseUp:(NSEvent*)event; | |||
| - (void) rightMouseDown:(NSEvent*)event; | |||
| - (void) rightMouseUp:(NSEvent*)event; | |||
| - (void) keyDown:(NSEvent*)event; | |||
| - (void) keyUp:(NSEvent*)event; | |||
| - (void) flagsChanged:(NSEvent*)event; | |||
| @end | |||
| @implementation PuglOpenGLView | |||
| - (id) initWithFrame:(NSRect)frame | |||
| colorBits:(int)numColorBits | |||
| depthBits:(int)numDepthBits | |||
| { | |||
| colorBits = numColorBits; | |||
| depthBits = numDepthBits; | |||
| NSOpenGLPixelFormatAttribute pixelAttribs[16] = { | |||
| NSOpenGLPFADoubleBuffer, | |||
| NSOpenGLPFAAccelerated, | |||
| NSOpenGLPFAColorSize, | |||
| colorBits, | |||
| NSOpenGLPFADepthSize, | |||
| depthBits, | |||
| 0 | |||
| }; | |||
| NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] | |||
| initWithAttributes:pixelAttribs]; | |||
| if (pixelFormat) { | |||
| self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||
| [pixelFormat release]; | |||
| if (self) { | |||
| [[self openGLContext] makeCurrentContext]; | |||
| [self reshape]; | |||
| } | |||
| } else { | |||
| self = nil; | |||
| } | |||
| return self; | |||
| } | |||
| - (void) reshape | |||
| { | |||
| [[self openGLContext] update]; | |||
| NSRect bounds = [self bounds]; | |||
| int width = bounds.size.width; | |||
| int height = bounds.size.height; | |||
| 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. | |||
| */ | |||
| if (puglview->reshapeFunc) { | |||
| puglview->reshapeFunc(puglview, width, height); | |||
| } else { | |||
| puglDefaultReshape(puglview, width, height); | |||
| } | |||
| puglview->width = width; | |||
| puglview->height = height; | |||
| } | |||
| } | |||
| - (void) drawRect:(NSRect)rect | |||
| { | |||
| puglDisplay(puglview); | |||
| glFlush(); | |||
| glSwapAPPLE(); | |||
| } | |||
| 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; | |||
| } | |||
| -(void)updateTrackingAreas | |||
| { | |||
| if (trackingArea != nil) { | |||
| [self removeTrackingArea:trackingArea]; | |||
| [trackingArea release]; | |||
| } | |||
| const int opts = (NSTrackingMouseEnteredAndExited | | |||
| NSTrackingMouseMoved | | |||
| NSTrackingActiveAlways); | |||
| trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] | |||
| options:opts | |||
| owner:self | |||
| userInfo:nil]; | |||
| [self addTrackingArea:trackingArea]; | |||
| } | |||
| - (void)mouseEntered:(NSEvent*)theEvent | |||
| { | |||
| [self updateTrackingAreas]; | |||
| } | |||
| - (void)mouseExited:(NSEvent*)theEvent | |||
| { | |||
| } | |||
| - (void) mouseMoved:(NSEvent*)event | |||
| { | |||
| if (puglview->motionFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); | |||
| } | |||
| } | |||
| - (void) mouseDragged:(NSEvent*)event | |||
| { | |||
| if (puglview->motionFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); | |||
| } | |||
| } | |||
| - (void) mouseDown:(NSEvent*)event | |||
| { | |||
| if (puglview->mouseFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y); | |||
| } | |||
| } | |||
| - (void) mouseUp:(NSEvent*)event | |||
| { | |||
| if (puglview->mouseFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y); | |||
| } | |||
| [self updateTrackingAreas]; | |||
| } | |||
| - (void) rightMouseDown:(NSEvent*)event | |||
| { | |||
| if (puglview->mouseFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y); | |||
| } | |||
| } | |||
| - (void) rightMouseUp:(NSEvent*)event | |||
| { | |||
| if (puglview->mouseFunc) { | |||
| NSPoint loc = [event locationInWindow]; | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y); | |||
| } | |||
| } | |||
| - (void) scrollWheel:(NSEvent*)event | |||
| { | |||
| if (puglview->scrollFunc) { | |||
| puglview->mods = getModifiers(puglview, event); | |||
| puglview->scrollFunc(puglview, [event deltaX], [event deltaY]); | |||
| } | |||
| [self updateTrackingAreas]; | |||
| } | |||
| - (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; | |||
| }; | |||
| PuglView* | |||
| puglCreate(PuglNativeWindow parent, | |||
| const char* title, | |||
| int width, | |||
| int height, | |||
| bool resizable, | |||
| bool visible) | |||
| { | |||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||
| PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| if (!view || !impl) { | |||
| return NULL; | |||
| } | |||
| view->impl = impl; | |||
| view->width = width; | |||
| view->height = height; | |||
| [NSAutoreleasePool new]; | |||
| [NSApplication sharedApplication]; | |||
| NSString* titleString = [[NSString alloc] | |||
| initWithBytes:title | |||
| length:strlen(title) | |||
| encoding:NSUTF8StringEncoding]; | |||
| id window = [[PuglWindow new]retain]; | |||
| [window setPuglview:view]; | |||
| [window setTitle:titleString]; | |||
| impl->glview = [PuglOpenGLView new]; | |||
| impl->window = window; | |||
| impl->glview->puglview = view; | |||
| [window setContentView:impl->glview]; | |||
| [NSApp activateIgnoringOtherApps:YES]; | |||
| [window makeFirstResponder:impl->glview]; | |||
| [window makeKeyAndOrderFront:window]; | |||
| if (! visible) { | |||
| [window setIsVisible:NO]; | |||
| } | |||
| return view; | |||
| } | |||
| void | |||
| puglDestroy(PuglView* view) | |||
| { | |||
| view->impl->glview->puglview = NULL; | |||
| [view->impl->window close]; | |||
| [view->impl->glview release]; | |||
| [view->impl->window release]; | |||
| free(view->impl); | |||
| free(view); | |||
| } | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| [view->impl->glview setNeedsDisplay: YES]; | |||
| NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | |||
| NSEvent* event; | |||
| for (;;) { | |||
| event = [view->impl->window | |||
| nextEventMatchingMask:NSAnyEventMask | |||
| untilDate:[NSDate distantPast] | |||
| inMode:NSDefaultRunLoopMode | |||
| dequeue:YES]; | |||
| if (event == nil) | |||
| break; | |||
| [view->impl->window sendEvent: event]; | |||
| } | |||
| [pool release]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| void | |||
| puglPostRedisplay(PuglView* view) | |||
| { | |||
| view->redisplay = true; | |||
| } | |||
| PuglNativeWindow | |||
| puglGetNativeWindow(PuglView* view) | |||
| { | |||
| return (PuglNativeWindow)view->impl->glview; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Copyright 2013 Filipe Coelho <falktx@falktx.com> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| /** | |||
| @file pugl_osx_extended.h Extended OSX/Cocoa Pugl Implementation. | |||
| */ | |||
| #include <stdbool.h> | |||
| #include "pugl.h" | |||
| void puglImplFocus(PuglView* view); | |||
| void puglImplSetSize(PuglView* view, unsigned int width, unsigned int height); | |||
| void puglImplSetTitle(PuglView* view, const char* title); | |||
| void puglImplSetVisible(PuglView* view, bool yesNo); | |||
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Copyright 2013 Filipe Coelho <falktx@falktx.com> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| /** | |||
| @file pugl_osx_extended.m Extended OSX/Cocoa Pugl Implementation. | |||
| */ | |||
| #import "pugl_osx.m" | |||
| #include "pugl_osx_extended.h" | |||
| void puglImplFocus(PuglView* view) | |||
| { | |||
| // TODO | |||
| } | |||
| void puglImplSetSize(PuglView* view, unsigned int width, unsigned int height) | |||
| { | |||
| //id window = view->impl->window; | |||
| // TODO | |||
| //NSRect frame = [window frame]; | |||
| //frame.size.width = width; | |||
| //frame.size.height = height; | |||
| // display:NO ? | |||
| //[window setFrame:frame display:YES animate:NO]; | |||
| } | |||
| void puglImplSetTitle(PuglView* view, const char* title) | |||
| { | |||
| id window = view->impl->window; | |||
| NSString* titleString = [[NSString alloc] | |||
| initWithBytes:title | |||
| length:strlen(title) | |||
| encoding:NSUTF8StringEncoding]; | |||
| [window setTitle:titleString]; | |||
| } | |||
| void puglImplSetVisible(PuglView* view, bool yesNo) | |||
| { | |||
| id window = view->impl->window; | |||
| if (yesNo) | |||
| [window setIsVisible:YES]; | |||
| else | |||
| [window setIsVisible:NO]; | |||
| } | |||
| @@ -0,0 +1,374 @@ | |||
| /* | |||
| 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_win.cpp Windows/WGL Pugl Implementation. | |||
| */ | |||
| #include <windows.h> | |||
| #include <windowsx.h> | |||
| #include <GL/gl.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include "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 | |||
| const int LOCAL_CLOSE_MSG = WM_USER + 50; | |||
| struct PuglInternalsImpl { | |||
| HWND hwnd; | |||
| HDC hdc; | |||
| HGLRC hglrc; | |||
| WNDCLASS wc; | |||
| }; | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| PuglView* | |||
| puglCreate(PuglNativeWindow parent, | |||
| const char* title, | |||
| int width, | |||
| int height, | |||
| bool resizable, | |||
| bool visible) | |||
| { | |||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||
| PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| if (!view || !impl) { | |||
| return NULL; | |||
| } | |||
| view->impl = impl; | |||
| view->width = width; | |||
| view->height = height; | |||
| // 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]; | |||
| _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); | |||
| impl->wc.style = CS_OWNDC; | |||
| impl->wc.lpfnWndProc = wndProc; | |||
| impl->wc.cbClsExtra = 0; | |||
| impl->wc.cbWndExtra = 0; | |||
| impl->wc.hInstance = 0; | |||
| impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |||
| impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |||
| impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||
| impl->wc.lpszMenuName = NULL; | |||
| impl->wc.lpszClassName = classNameBuf; | |||
| RegisterClass(&impl->wc); | |||
| int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
| if (resizable) { | |||
| winFlags |= WS_SIZEBOX; | |||
| } | |||
| // Adjust the overall window size to accomodate our requested client size | |||
| RECT wr = { 0, 0, width, height }; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); | |||
| impl->hwnd = CreateWindowEx( | |||
| WS_EX_TOPMOST, | |||
| classNameBuf, title, | |||
| (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), | |||
| 0, 0, wr.right-wr.left, wr.bottom-wr.top, | |||
| (HWND)parent, NULL, NULL, NULL); | |||
| if (!impl->hwnd) { | |||
| free(impl); | |||
| free(view); | |||
| return NULL; | |||
| } | |||
| SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)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); | |||
| wglMakeCurrent(impl->hdc, impl->hglrc); | |||
| view->width = width; | |||
| view->height = height; | |||
| return view; | |||
| } | |||
| 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(view->impl); | |||
| free(view); | |||
| } | |||
| static void | |||
| puglReshape(PuglView* view, int width, int height) | |||
| { | |||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
| if (view->reshapeFunc) { | |||
| view->reshapeFunc(view, width, height); | |||
| } else { | |||
| puglDefaultReshape(view, width, height); | |||
| } | |||
| view->width = width; | |||
| view->height = height; | |||
| } | |||
| void | |||
| puglDisplay(PuglView* view) | |||
| { | |||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||
| glLoadIdentity(); | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| glFlush(); | |||
| SwapBuffers(view->impl->hdc); | |||
| view->redisplay = false; | |||
| } | |||
| 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; | |||
| setModifiers(view); | |||
| switch (message) { | |||
| case WM_CREATE: | |||
| case WM_SHOWWINDOW: | |||
| case WM_SIZE: | |||
| RECT rect; | |||
| GetClientRect(view->impl->hwnd, &rect); | |||
| puglReshape(view, rect.right, rect.bottom); | |||
| view->width = rect.right; | |||
| view->height = rect.bottom; | |||
| break; | |||
| case WM_PAINT: | |||
| BeginPaint(view->impl->hwnd, &ps); | |||
| puglDisplay(view); | |||
| EndPaint(view->impl->hwnd, &ps); | |||
| break; | |||
| case WM_MOUSEMOVE: | |||
| if (view->motionFunc) { | |||
| 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->scrollFunc( | |||
| view, 0, (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); | |||
| } | |||
| break; | |||
| case WM_MOUSEHWHEEL: | |||
| if (view->scrollFunc) { | |||
| view->scrollFunc( | |||
| view, (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0); | |||
| } | |||
| break; | |||
| case WM_KEYDOWN: | |||
| view->event_timestamp_ms = (GetMessageTime()); | |||
| if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { | |||
| break; | |||
| } // else nobreak | |||
| case WM_KEYUP: | |||
| if ((key = keySymToSpecial(wParam))) { | |||
| if (view->specialFunc) { | |||
| view->specialFunc(view, message == WM_KEYDOWN, key); | |||
| } | |||
| } else if (view->keyboardFunc) { | |||
| view->keyboardFunc(view, message == WM_KEYDOWN, wParam); | |||
| } | |||
| break; | |||
| case WM_QUIT: | |||
| case LOCAL_CLOSE_MSG: | |||
| if (view->closeFunc) { | |||
| view->closeFunc(view); | |||
| } | |||
| break; | |||
| default: | |||
| return DefWindowProc( | |||
| view->impl->hwnd, message, wParam, lParam); | |||
| } | |||
| return 0; | |||
| } | |||
| 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, GWL_USERDATA); | |||
| switch (message) { | |||
| case WM_CREATE: | |||
| PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | |||
| return 0; | |||
| case WM_CLOSE: | |||
| PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); | |||
| return 0; | |||
| case WM_DESTROY: | |||
| return 0; | |||
| default: | |||
| if (view) { | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,414 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||
| Copyright 2013 Robin Gareus <robin@gareus.org> | |||
| 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 <GL/gl.h> | |||
| #include <GL/glx.h> | |||
| #include <X11/Xatom.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/keysym.h> | |||
| #include "pugl_internal.h" | |||
| /* work around buggy re-parent & focus issues on some systems | |||
| * where no keyboard events are passed through even if the | |||
| * app has mouse-focus and all other events are working. | |||
| */ | |||
| //#define XKEYFOCUSGRAB | |||
| /* show messages during initalization | |||
| */ | |||
| //#define VERBOSE_PUGL | |||
| struct PuglInternalsImpl { | |||
| Display* display; | |||
| int screen; | |||
| Window win; | |||
| GLXContext ctx; | |||
| Bool doubleBuffered; | |||
| }; | |||
| /** | |||
| Attributes for single-buffered RGBA with at least | |||
| 4 bits per color and a 16 bit depth buffer. | |||
| */ | |||
| static int attrListSgl[] = { | |||
| GLX_RGBA, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| None | |||
| }; | |||
| /** | |||
| Attributes for double-buffered RGBA with at least | |||
| 4 bits per color and a 16 bit depth buffer. | |||
| */ | |||
| static int attrListDbl[] = { | |||
| GLX_RGBA, GLX_DOUBLEBUFFER, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| None | |||
| }; | |||
| PuglView* | |||
| puglCreate(PuglNativeWindow parent, | |||
| const char* title, | |||
| int width, | |||
| int height, | |||
| bool resizable, | |||
| bool visible) | |||
| { | |||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||
| PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| if (!view || !impl) { | |||
| return NULL; | |||
| } | |||
| view->impl = impl; | |||
| view->width = width; | |||
| view->height = height; | |||
| impl->display = XOpenDisplay(0); | |||
| impl->screen = DefaultScreen(impl->display); | |||
| XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||
| if (!vi) { | |||
| vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||
| impl->doubleBuffered = False; | |||
| #ifdef VERBOSE_PUGL | |||
| printf("puGL: singlebuffered rendering will be used, no doublebuffering available\n"); | |||
| #endif | |||
| } else { | |||
| impl->doubleBuffered = True; | |||
| #ifdef VERBOSE_PUGL | |||
| printf("puGL: doublebuffered rendering available\n"); | |||
| #endif | |||
| } | |||
| int glxMajor, glxMinor; | |||
| glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||
| #ifdef VERBOSE_PUGL | |||
| printf("puGL: GLX-Version : %d.%d\n", glxMajor, glxMinor); | |||
| #endif | |||
| impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
| Window xParent = parent | |||
| ? (Window)parent | |||
| : RootWindow(impl->display, impl->screen); | |||
| Colormap cmap = XCreateColormap( | |||
| impl->display, xParent, vi->visual, AllocNone); | |||
| XSetWindowAttributes attr; | |||
| memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||
| attr.colormap = cmap; | |||
| attr.border_pixel = 0; | |||
| attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | |||
| | ButtonPressMask | ButtonReleaseMask | |||
| #ifdef XKEYFOCUSGRAB | |||
| | EnterWindowMask | |||
| #endif | |||
| | PointerMotionMask | StructureNotifyMask; | |||
| impl->win = XCreateWindow( | |||
| impl->display, xParent, | |||
| 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||
| CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
| XSizeHints sizeHints; | |||
| memset(&sizeHints, 0, sizeof(sizeHints)); | |||
| if (!resizable) { | |||
| sizeHints.flags = PMinSize|PMaxSize; | |||
| sizeHints.min_width = width; | |||
| sizeHints.min_height = height; | |||
| sizeHints.max_width = width; | |||
| sizeHints.max_height = height; | |||
| XSetNormalHints(impl->display, impl->win, &sizeHints); | |||
| } | |||
| if (title) { | |||
| XStoreName(impl->display, impl->win, title); | |||
| } | |||
| if (!parent) { | |||
| Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||
| XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||
| } | |||
| if (visible) { | |||
| XMapRaised(impl->display, impl->win); | |||
| } | |||
| if (glXIsDirect(impl->display, impl->ctx)) { | |||
| #ifdef VERBOSE_PUGL | |||
| printf("puGL: DRI enabled\n"); | |||
| #endif | |||
| } else { | |||
| #ifdef VERBOSE_PUGL | |||
| printf("puGL: No DRI available\n"); | |||
| #endif | |||
| } | |||
| XFree(vi); | |||
| return view; | |||
| } | |||
| void | |||
| puglDestroy(PuglView* view) | |||
| { | |||
| if (!view) { | |||
| return; | |||
| } | |||
| glXDestroyContext(view->impl->display, view->impl->ctx); | |||
| 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) | |||
| { | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| if (view->reshapeFunc) { | |||
| view->reshapeFunc(view, width, height); | |||
| } else { | |||
| puglDefaultReshape(view, width, height); | |||
| } | |||
| view->width = width; | |||
| view->height = height; | |||
| } | |||
| static void | |||
| puglDisplay(PuglView* view) | |||
| { | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||
| glLoadIdentity(); | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| glFlush(); | |||
| if (view->impl->doubleBuffered) { | |||
| glXSwapBuffers(view->impl->display, view->impl->win); | |||
| } | |||
| view->redisplay = false; | |||
| } | |||
| 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; | |||
| } | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| XEvent event; | |||
| while (XPending(view->impl->display) > 0) { | |||
| XNextEvent(view->impl->display, &event); | |||
| 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, 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); | |||
| KeySym sym; | |||
| char str[5]; | |||
| int n = XLookupString(&event.xkey, str, 4, &sym, NULL); | |||
| PuglKey key = keySymToSpecial(sym); | |||
| if (!key && view->keyboardFunc) { | |||
| if (n == 1) { | |||
| view->keyboardFunc(view, true, str[0]); | |||
| } else { | |||
| fprintf(stderr, "warning: Unknown key %X\n", (int)sym); | |||
| } | |||
| } else if (view->specialFunc) { | |||
| view->specialFunc(view, true, key); | |||
| } | |||
| } 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 && view->keyboardFunc) { | |||
| KeySym sym = XLookupKeysym(&event.xkey, 0); | |||
| PuglKey special = keySymToSpecial(sym); | |||
| if (!special) { | |||
| view->keyboardFunc(view, false, sym); | |||
| } else if (view->specialFunc) { | |||
| view->specialFunc(view, false, special); | |||
| } | |||
| } | |||
| } break; | |||
| case ClientMessage: | |||
| if (!strcmp(XGetAtomName(view->impl->display, | |||
| event.xclient.message_type), | |||
| "WM_PROTOCOLS")) { | |||
| if (view->closeFunc) { | |||
| view->closeFunc(view); | |||
| } | |||
| } | |||
| break; | |||
| #ifdef XKEYFOCUSGRAB | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,239 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "DistrhoUtils.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // Parameter Hints | |||
| const uint32_t PARAMETER_IS_AUTOMABLE = 1 << 0; | |||
| const uint32_t PARAMETER_IS_BOOLEAN = 1 << 1; | |||
| const uint32_t PARAMETER_IS_INTEGER = 1 << 2; | |||
| const uint32_t PARAMETER_IS_LOGARITHMIC = 1 << 3; | |||
| const uint32_t PARAMETER_IS_OUTPUT = 1 << 4; | |||
| // ----------------------------------------------------------------------- | |||
| // Parameter Ranges | |||
| struct ParameterRanges { | |||
| float def; | |||
| float min; | |||
| float max; | |||
| ParameterRanges() noexcept | |||
| : def(0.0f), | |||
| min(0.0f), | |||
| max(1.0f) {} | |||
| ParameterRanges(float def, float min, float max) noexcept | |||
| { | |||
| this->def = def; | |||
| this->min = min; | |||
| this->max = max; | |||
| } | |||
| void clear() noexcept | |||
| { | |||
| def = 0.0f; | |||
| min = 0.0f; | |||
| max = 1.0f; | |||
| } | |||
| void fixValue(float& value) const noexcept | |||
| { | |||
| if (value < min) | |||
| value = min; | |||
| else if (value > max) | |||
| value = max; | |||
| } | |||
| float getFixedValue(const float& value) const noexcept | |||
| { | |||
| if (value < min) | |||
| return min; | |||
| else if (value > max) | |||
| return max; | |||
| return value; | |||
| } | |||
| float getNormalizedValue(const float& value) const noexcept | |||
| { | |||
| const float newValue((value - min) / (max - min)); | |||
| if (newValue <= 0.0f) | |||
| return 0.0f; | |||
| if (newValue >= 1.0f) | |||
| return 1.0f; | |||
| return newValue; | |||
| } | |||
| float getUnnormalizedValue(const float& value) const noexcept | |||
| { | |||
| return value * (max - min) + min; | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Parameter | |||
| struct Parameter { | |||
| uint32_t hints; | |||
| d_string name; | |||
| d_string symbol; | |||
| d_string unit; | |||
| ParameterRanges ranges; | |||
| Parameter() | |||
| : hints(0x0) {} | |||
| void clear() noexcept | |||
| { | |||
| hints = 0x0; | |||
| name = ""; | |||
| symbol = ""; | |||
| unit = ""; | |||
| ranges.clear(); | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // MidiEvent | |||
| struct MidiEvent { | |||
| uint32_t frame; | |||
| uint8_t size; | |||
| uint8_t buf[4]; | |||
| void clear() noexcept | |||
| { | |||
| frame = 0; | |||
| size = 0; | |||
| buf[0] = 0; | |||
| buf[1] = 0; | |||
| buf[2] = 0; | |||
| buf[3] = 0; | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // TimePos | |||
| struct TimePos { | |||
| bool playing; | |||
| uint64_t frame; | |||
| double bpm; | |||
| TimePos() noexcept | |||
| : playing(false), | |||
| frame(0), | |||
| bpm(120.0) {} | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Plugin | |||
| class Plugin | |||
| { | |||
| public: | |||
| Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount); | |||
| virtual ~Plugin(); | |||
| // ------------------------------------------------------------------- | |||
| // Host state | |||
| uint32_t d_getBufferSize() const noexcept; | |||
| double d_getSampleRate() const noexcept; | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| const TimePos& d_getTimePos() const noexcept; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| void d_setLatency(uint32_t frames) noexcept; | |||
| #endif | |||
| protected: | |||
| // ------------------------------------------------------------------- | |||
| // Information | |||
| virtual const char* d_getName() const noexcept { return DISTRHO_PLUGIN_NAME; } | |||
| virtual const char* d_getLabel() const noexcept = 0; | |||
| virtual const char* d_getMaker() const noexcept = 0; | |||
| virtual const char* d_getLicense() const noexcept = 0; | |||
| virtual uint32_t d_getVersion() const noexcept = 0; | |||
| virtual long d_getUniqueId() const noexcept = 0; | |||
| // ------------------------------------------------------------------- | |||
| // Init | |||
| virtual void d_initParameter(uint32_t index, Parameter& parameter) = 0; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| virtual void d_initProgramName(uint32_t index, d_string& programName) = 0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| virtual void d_initStateKey(uint32_t index, d_string& stateKey) = 0; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Internal data | |||
| virtual float d_getParameterValue(uint32_t index) const = 0; | |||
| virtual void d_setParameterValue(uint32_t index, float value) = 0; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| virtual void d_setProgram(uint32_t index) = 0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| virtual void d_setState(const char* key, const char* value) = 0; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Process | |||
| virtual void d_activate() {} | |||
| virtual void d_deactivate() {} | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| virtual void d_run(float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) = 0; | |||
| #else | |||
| virtual void d_run(float** inputs, float** outputs, uint32_t frames) = 0; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Callbacks (optional) | |||
| virtual void d_bufferSizeChanged(uint32_t newBufferSize); | |||
| virtual void d_sampleRateChanged(double newSampleRate); | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| friend class PluginExporter; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Create plugin, entry point | |||
| extern Plugin* createPlugin(); | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_PLUGIN_HPP_INCLUDED | |||
| @@ -0,0 +1,26 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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_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,94 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 "DistrhoUtils.hpp" | |||
| #include "../dgl/Widget.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // UI | |||
| class UI : public DGL::Widget | |||
| { | |||
| public: | |||
| UI(); | |||
| virtual ~UI(); | |||
| // ------------------------------------------------------------------- | |||
| // Host DSP State | |||
| double d_getSampleRate() const noexcept; | |||
| void d_editParameter(uint32_t index, bool started); | |||
| void d_setParameterValue(uint32_t index, float value); | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void d_setState(const char* key, const char* value); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| void d_sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Host UI State | |||
| void d_uiResize(unsigned int width, unsigned int height); | |||
| protected: | |||
| // ------------------------------------------------------------------- | |||
| // Basic Information | |||
| virtual const char* d_getName() const noexcept { return DISTRHO_PLUGIN_NAME; } | |||
| virtual unsigned int d_getWidth() const noexcept = 0; | |||
| virtual unsigned int d_getHeight() const noexcept = 0; | |||
| // ------------------------------------------------------------------- | |||
| // DSP Callbacks | |||
| virtual void d_parameterChanged(uint32_t index, float value) = 0; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| virtual void d_programChanged(uint32_t index) = 0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| virtual void d_stateChanged(const char* key, const char* value) = 0; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // UI Callbacks (optional) | |||
| virtual void d_uiIdle() {} | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| struct PrivateData; | |||
| PrivateData* const pData; | |||
| friend class UIExporter; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Create UI, entry point | |||
| extern UI* createUI(); | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_UI_HPP_INCLUDED | |||
| @@ -0,0 +1,25 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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_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,684 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 <cassert> | |||
| #include <cstdarg> | |||
| #include <cstdio> | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| #ifdef PROPER_CPP11_SUPPORT | |||
| # include <cstdint> | |||
| #else | |||
| # include <stdint.h> | |||
| #endif | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| # include <windows.h> | |||
| #else | |||
| # include <unistd.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); } | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // misc functions | |||
| static inline | |||
| long d_cconst(int a, int b, int c, int d) noexcept | |||
| { | |||
| return (a << 24) | (b << 16) | (c << 8) | (d << 0); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // string print functions | |||
| #ifndef DEBUG | |||
| # define d_debug(...) | |||
| #else | |||
| static inline | |||
| void d_debug(const char* const fmt, ...) | |||
| { | |||
| 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); | |||
| } | |||
| #endif | |||
| static inline | |||
| void d_stdout(const char* const fmt, ...) | |||
| { | |||
| va_list args; | |||
| va_start(args, fmt); | |||
| std::vfprintf(stdout, fmt, args); | |||
| std::fprintf(stdout, "\n"); | |||
| va_end(args); | |||
| } | |||
| static inline | |||
| void d_stderr(const char* const fmt, ...) | |||
| { | |||
| va_list args; | |||
| va_start(args, fmt); | |||
| std::vfprintf(stderr, fmt, args); | |||
| std::fprintf(stderr, "\n"); | |||
| va_end(args); | |||
| } | |||
| static inline | |||
| void d_stderr2(const char* const fmt, ...) | |||
| { | |||
| 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); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // d_*sleep | |||
| static inline | |||
| void d_sleep(unsigned int secs) | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| Sleep(secs * 1000); | |||
| #else | |||
| sleep(secs); | |||
| #endif | |||
| } | |||
| static inline | |||
| void d_msleep(unsigned int msecs) | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| Sleep(msecs); | |||
| #else | |||
| usleep(msecs * 1000); | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // d_string class | |||
| class d_string | |||
| { | |||
| public: | |||
| // ------------------------------------------------------------------- | |||
| // constructors (no explicit conversions allowed) | |||
| /* | |||
| * Empty string. | |||
| */ | |||
| explicit d_string() | |||
| { | |||
| _init(); | |||
| _dup(nullptr); | |||
| } | |||
| /* | |||
| * Simple character. | |||
| */ | |||
| explicit d_string(const char c) | |||
| { | |||
| char ch[2]; | |||
| ch[0] = c; | |||
| ch[1] = '\0'; | |||
| _init(); | |||
| _dup(ch); | |||
| } | |||
| /* | |||
| * Simple char string. | |||
| */ | |||
| explicit d_string(char* const strBuf) | |||
| { | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Simple const char string. | |||
| */ | |||
| explicit d_string(const char* const strBuf) | |||
| { | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Integer. | |||
| */ | |||
| explicit d_string(const int value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%d", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Unsigned integer, possibly in hexadecimal. | |||
| */ | |||
| explicit d_string(const unsigned int value, const bool hexadecimal = false) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Long integer. | |||
| */ | |||
| explicit d_string(const long int value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%ld", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Long unsigned integer, possibly hexadecimal. | |||
| */ | |||
| explicit d_string(const unsigned long int value, const bool hexadecimal = false) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Single-precision floating point number. | |||
| */ | |||
| explicit d_string(const float value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%f", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Double-precision floating point number. | |||
| */ | |||
| explicit d_string(const double value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%g", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // non-explicit constructor | |||
| /* | |||
| * Create string from another string. | |||
| */ | |||
| d_string(const d_string& str) | |||
| { | |||
| _init(); | |||
| _dup(str.fBuffer); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // destructor | |||
| /* | |||
| * Destructor. | |||
| */ | |||
| ~d_string() | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| fBuffer = nullptr; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // public methods | |||
| /* | |||
| * Get length of the string. | |||
| */ | |||
| 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 | |||
| { | |||
| if (strBuf == nullptr) | |||
| return false; | |||
| if (ignoreCase) | |||
| { | |||
| #ifdef __USE_GNU | |||
| return (strcasestr(fBuffer, strBuf) != nullptr); | |||
| #else | |||
| d_string tmp1(fBuffer), tmp2(strBuf); | |||
| tmp1.toLower(); | |||
| tmp2.toLower(); | |||
| return (std::strstr((const char*)tmp1, (const char*)tmp2) != nullptr); | |||
| #endif | |||
| } | |||
| return (std::strstr(fBuffer, strBuf) != nullptr); | |||
| } | |||
| /* | |||
| * Overloaded function. | |||
| */ | |||
| bool contains(const d_string& str, const bool ignoreCase = false) const | |||
| { | |||
| return contains(str.fBuffer, ignoreCase); | |||
| } | |||
| /* | |||
| * Check if character at 'pos' is a digit. | |||
| */ | |||
| bool isDigit(const size_t pos) const noexcept | |||
| { | |||
| if (pos >= fBufferLen) | |||
| return false; | |||
| return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); | |||
| } | |||
| /* | |||
| * Check if the string starts with the character 'c'. | |||
| */ | |||
| bool startsWith(const char c) const | |||
| { | |||
| if (c == '\0') | |||
| return false; | |||
| return (fBufferLen > 0 && fBuffer[0] == c); | |||
| } | |||
| /* | |||
| * Check if the string starts with the string 'prefix'. | |||
| */ | |||
| bool startsWith(const char* const prefix) const | |||
| { | |||
| if (prefix == nullptr) | |||
| return false; | |||
| const size_t prefixLen(std::strlen(prefix)); | |||
| if (fBufferLen < prefixLen) | |||
| return false; | |||
| return (std::strncmp(fBuffer + (fBufferLen-prefixLen), prefix, prefixLen) == 0); | |||
| } | |||
| /* | |||
| * Check if the string ends with the character 'c'. | |||
| */ | |||
| bool endsWith(const char c) const | |||
| { | |||
| if (c == '\0') | |||
| return false; | |||
| return (fBufferLen > 0 && fBuffer[fBufferLen] == c); | |||
| } | |||
| /* | |||
| * Check if the string ends with the string 'suffix'. | |||
| */ | |||
| bool endsWith(const char* const suffix) const | |||
| { | |||
| if (suffix == nullptr) | |||
| return false; | |||
| const size_t suffixLen(std::strlen(suffix)); | |||
| if (fBufferLen < suffixLen) | |||
| return false; | |||
| return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); | |||
| } | |||
| /* | |||
| * Clear the string. | |||
| */ | |||
| void clear() noexcept | |||
| { | |||
| truncate(0); | |||
| } | |||
| /* | |||
| * Replace all occurrences of character 'before' with character 'after'. | |||
| */ | |||
| void replace(const char before, const char after) noexcept | |||
| { | |||
| if (before == '\0' || after == '\0') | |||
| return; | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == before) | |||
| fBuffer[i] = after; | |||
| else if (fBuffer[i] == '\0') | |||
| break; | |||
| } | |||
| } | |||
| /* | |||
| * Truncate the string to size 'n'. | |||
| */ | |||
| void truncate(const size_t n) noexcept | |||
| { | |||
| if (n >= fBufferLen) | |||
| return; | |||
| for (size_t i=n; i < fBufferLen; ++i) | |||
| fBuffer[i] = '\0'; | |||
| fBufferLen = n; | |||
| } | |||
| /* | |||
| * Convert all non-basic characters to '_'. | |||
| */ | |||
| void toBasic() noexcept | |||
| { | |||
| for (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] = '_'; | |||
| } | |||
| } | |||
| /* | |||
| * Convert to all ascii characters to lowercase. | |||
| */ | |||
| void toLower() noexcept | |||
| { | |||
| static const char kCharDiff('a' - 'A'); | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |||
| fBuffer[i] += kCharDiff; | |||
| } | |||
| } | |||
| /* | |||
| * Convert to all ascii characters to uppercase. | |||
| */ | |||
| void toUpper() noexcept | |||
| { | |||
| static const char kCharDiff('a' - 'A'); | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |||
| fBuffer[i] -= kCharDiff; | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // public operators | |||
| operator const char*() const noexcept | |||
| { | |||
| return fBuffer; | |||
| } | |||
| char& operator[](const size_t pos) const noexcept | |||
| { | |||
| return fBuffer[pos]; | |||
| } | |||
| bool operator==(const char* const strBuf) const | |||
| { | |||
| return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); | |||
| } | |||
| bool operator==(const d_string& str) const | |||
| { | |||
| return operator==(str.fBuffer); | |||
| } | |||
| bool operator!=(const char* const strBuf) const | |||
| { | |||
| return !operator==(strBuf); | |||
| } | |||
| bool operator!=(const d_string& str) const | |||
| { | |||
| return !operator==(str.fBuffer); | |||
| } | |||
| d_string& operator=(const char* const strBuf) | |||
| { | |||
| _dup(strBuf); | |||
| return *this; | |||
| } | |||
| d_string& operator=(const d_string& str) | |||
| { | |||
| return operator=(str.fBuffer); | |||
| } | |||
| d_string& operator+=(const char* const strBuf) | |||
| { | |||
| if (strBuf == nullptr) | |||
| return *this; | |||
| const 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; | |||
| } | |||
| d_string& operator+=(const d_string& str) | |||
| { | |||
| return operator+=(str.fBuffer); | |||
| } | |||
| d_string operator+(const char* const strBuf) | |||
| { | |||
| const 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 d_string(newBuf); | |||
| } | |||
| d_string operator+(const d_string& str) | |||
| { | |||
| return operator+(str.fBuffer); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| char* fBuffer; // the actual string buffer | |||
| size_t fBufferLen; // string length | |||
| bool fFirstInit; // true when first initiated | |||
| /* | |||
| * Shared init function. | |||
| * Called on all constructors. | |||
| */ | |||
| void _init() noexcept | |||
| { | |||
| fBuffer = nullptr; | |||
| fBufferLen = 0; | |||
| fFirstInit = true; | |||
| } | |||
| /* | |||
| * Helper function. | |||
| * Called whenever the string needs to be allocated. | |||
| * | |||
| * Notes: | |||
| * - Allocates string only if first initiated, or 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 size_t size = 0) | |||
| { | |||
| if (strBuf != nullptr) | |||
| { | |||
| // don't recreate string if contents match | |||
| if (fFirstInit || std::strcmp(fBuffer, strBuf) != 0) | |||
| { | |||
| if (! fFirstInit) | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| } | |||
| fBufferLen = (size > 0) ? size : std::strlen(strBuf); | |||
| fBuffer = new char[fBufferLen+1]; | |||
| std::strcpy(fBuffer, strBuf); | |||
| fBuffer[fBufferLen] = '\0'; | |||
| fFirstInit = false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| assert(size == 0); | |||
| // don't recreate null string | |||
| if (fFirstInit || fBufferLen != 0) | |||
| { | |||
| if (! fFirstInit) | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| } | |||
| fBufferLen = 0; | |||
| fBuffer = new char[1]; | |||
| fBuffer[0] = '\0'; | |||
| fFirstInit = false; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| static inline | |||
| d_string operator+(const d_string& strBefore, const char* const strBufAfter) | |||
| { | |||
| const char* const strBufBefore = (const char*)strBefore; | |||
| const size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; | |||
| char newBuf[newBufSize]; | |||
| std::strcpy(newBuf, strBufBefore); | |||
| std::strcat(newBuf, strBufAfter); | |||
| return d_string(newBuf); | |||
| } | |||
| static inline | |||
| d_string operator+(const char* const strBufBefore, const d_string& strAfter) | |||
| { | |||
| const char* const strBufAfter = (const char*)strAfter; | |||
| const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; | |||
| char newBuf[newBufSize]; | |||
| std::strcpy(newBuf, strBufBefore); | |||
| std::strcat(newBuf, strBufAfter); | |||
| return d_string(newBuf); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| #endif // DISTRHO_UTILS_HPP_INCLUDED | |||
| @@ -0,0 +1,114 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||
| #include "DistrhoPluginInfo.h" | |||
| #ifndef DISTRHO_PLUGIN_NAME | |||
| # error DISTRHO_PLUGIN_NAME undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_HAS_UI | |||
| # error DISTRHO_PLUGIN_HAS_UI undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_IS_SYNTH | |||
| # error DISTRHO_PLUGIN_IS_SYNTH 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_WANT_LATENCY | |||
| # error DISTRHO_PLUGIN_WANT_LATENCY undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| # error DISTRHO_PLUGIN_WANT_PROGRAMS undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_WANT_STATE | |||
| # error DISTRHO_PLUGIN_WANT_STATE undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| # error DISTRHO_PLUGIN_WANT_TIMEPOS undefined! | |||
| #endif | |||
| #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 | |||
| # define DISTRHO_DLL_EXTENSION "so" | |||
| # elif defined(__linux__) | |||
| # define DISTRHO_OS_LINUX 1 | |||
| # define DISTRHO_DLL_EXTENSION "so" | |||
| # endif | |||
| #endif | |||
| #ifndef DISTRHO_DLL_EXTENSION | |||
| # define DISTRHO_DLL_EXTENSION "so" | |||
| #endif | |||
| #if defined(HAVE_CPP11_SUPPORT) | |||
| # define PROPER_CPP11_SUPPORT | |||
| #elif defined(__GNUC__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) | |||
| # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 | |||
| # define PROPER_CPP11_SUPPORT | |||
| # if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 | |||
| # define override // gcc4.7+ only | |||
| # endif | |||
| # endif | |||
| #endif | |||
| #ifndef PROPER_CPP11_SUPPORT | |||
| # ifndef __clang__ | |||
| # define noexcept throw() | |||
| # endif | |||
| # define override | |||
| # define nullptr (0) | |||
| #endif | |||
| #ifndef DISTRHO_NO_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; | |||
| #else | |||
| # define START_NAMESPACE_DISTRHO | |||
| # define END_NAMESPACE_DISTRHO | |||
| # define USE_NAMESPACE_DISTRHO | |||
| #endif | |||
| #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||
| #endif // DISTRHO_DEFINES_H_INCLUDED | |||
| @@ -0,0 +1,107 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 d_string PluginExporter::sFallbackString; | |||
| const ParameterRanges PluginExporter::sFallbackRanges; | |||
| // ----------------------------------------------------------------------- | |||
| // Plugin | |||
| Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) | |||
| : pData(new PrivateData()) | |||
| { | |||
| if (parameterCount > 0) | |||
| { | |||
| pData->parameterCount = parameterCount; | |||
| pData->parameters = new Parameter[parameterCount]; | |||
| } | |||
| if (programCount > 0) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| pData->programCount = programCount; | |||
| pData->programNames = new d_string[programCount]; | |||
| #endif | |||
| } | |||
| if (stateCount > 0) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| pData->stateCount = stateCount; | |||
| pData->stateKeys = new d_string[stateCount]; | |||
| #endif | |||
| } | |||
| } | |||
| Plugin::~Plugin() | |||
| { | |||
| delete pData; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Host state | |||
| uint32_t Plugin::d_getBufferSize() const noexcept | |||
| { | |||
| return pData->bufferSize; | |||
| } | |||
| double Plugin::d_getSampleRate() const noexcept | |||
| { | |||
| return pData->sampleRate; | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| const TimePos& Plugin::d_getTimePos() const noexcept | |||
| { | |||
| return pData->timePos; | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| void Plugin::d_setLatency(uint32_t frames) noexcept | |||
| { | |||
| pData->latency = frames; | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Callbacks (optional) | |||
| void Plugin::d_bufferSizeChanged(uint32_t) | |||
| { | |||
| } | |||
| void Plugin::d_sampleRateChanged(double) | |||
| { | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -0,0 +1,380 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 { | |||
| uint32_t parameterCount; | |||
| Parameter* parameters; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| uint32_t programCount; | |||
| d_string* programNames; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| uint32_t stateCount; | |||
| d_string* stateKeys; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| uint32_t latency; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| TimePos timePos; | |||
| #endif | |||
| uint32_t bufferSize; | |||
| double sampleRate; | |||
| PrivateData() noexcept | |||
| : parameterCount(0), | |||
| parameters(nullptr), | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| programCount(0), | |||
| programNames(nullptr), | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| stateCount(0), | |||
| stateKeys(nullptr), | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| latency(0), | |||
| #endif | |||
| bufferSize(d_lastBufferSize), | |||
| sampleRate(d_lastSampleRate) | |||
| { | |||
| assert(bufferSize != 0); | |||
| assert(sampleRate != 0.0); | |||
| } | |||
| ~PrivateData() | |||
| { | |||
| 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; | |||
| } | |||
| #endif | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // Plugin exporter class | |||
| class PluginExporter | |||
| { | |||
| public: | |||
| PluginExporter() | |||
| : fPlugin(createPlugin()), | |||
| fData((fPlugin != nullptr) ? fPlugin->pData : nullptr) | |||
| { | |||
| assert(fPlugin != nullptr); | |||
| if (fPlugin == nullptr) | |||
| return; | |||
| for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) | |||
| fPlugin->d_initParameter(i, fData->parameters[i]); | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| for (uint32_t i=0, count=fData->programCount; i < count; ++i) | |||
| fPlugin->d_initProgramName(i, fData->programNames[i]); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| for (uint32_t i=0, count=fData->stateCount; i < count; ++i) | |||
| fPlugin->d_initStateKey(i, fData->stateKeys[i]); | |||
| #endif | |||
| } | |||
| ~PluginExporter() | |||
| { | |||
| delete fPlugin; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| const char* getName() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getName() : ""; | |||
| } | |||
| const char* getLabel() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getLabel() : ""; | |||
| } | |||
| const char* getMaker() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getMaker() : ""; | |||
| } | |||
| const char* getLicense() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getLicense() : ""; | |||
| } | |||
| uint32_t getVersion() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getVersion() : 1000; | |||
| } | |||
| long getUniqueId() const noexcept | |||
| { | |||
| return (fPlugin != nullptr) ? fPlugin->d_getUniqueId() : 0; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| uint32_t getLatency() const noexcept | |||
| { | |||
| return (fData != nullptr) ? fData->latency : 0; | |||
| } | |||
| #endif | |||
| uint32_t getParameterCount() const noexcept | |||
| { | |||
| return (fData != nullptr) ? fData->parameterCount : 0; | |||
| } | |||
| uint32_t getParameterHints(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].hints : 0x0; | |||
| } | |||
| bool isParameterOutput(const uint32_t index) const noexcept | |||
| { | |||
| return (getParameterHints(index) & PARAMETER_IS_OUTPUT); | |||
| } | |||
| const d_string& getParameterName(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].name : sFallbackString; | |||
| } | |||
| const d_string& getParameterSymbol(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].symbol : sFallbackString; | |||
| } | |||
| const d_string& getParameterUnit(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].unit : sFallbackString; | |||
| } | |||
| const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fData != nullptr && index < fData->parameterCount) ? fData->parameters[index].ranges : sFallbackRanges; | |||
| } | |||
| float getParameterValue(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| return (fPlugin != nullptr && index < fData->parameterCount) ? fPlugin->d_getParameterValue(index) : 0.0f; | |||
| } | |||
| void setParameterValue(const uint32_t index, const float value) | |||
| { | |||
| assert(index < fData->parameterCount); | |||
| if (fPlugin != nullptr && index < fData->parameterCount) | |||
| fPlugin->d_setParameterValue(index, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| uint32_t getProgramCount() const noexcept | |||
| { | |||
| return (fData != nullptr) ? fData->programCount : 0; | |||
| } | |||
| const d_string& getProgramName(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->programCount); | |||
| return (fData != nullptr && index < fData->programCount) ? fData->programNames[index] : sFallbackString; | |||
| } | |||
| void setProgram(const uint32_t index) | |||
| { | |||
| assert(index < fData->programCount); | |||
| if (fPlugin != nullptr && index < fData->programCount) | |||
| fPlugin->d_setProgram(index); | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| uint32_t getStateCount() const noexcept | |||
| { | |||
| return fData != nullptr ? fData->stateCount : 0; | |||
| } | |||
| const d_string& getStateKey(const uint32_t index) const noexcept | |||
| { | |||
| assert(index < fData->stateCount); | |||
| return (fData != nullptr && index < fData->stateCount) ? fData->stateKeys[index] : sFallbackString; | |||
| } | |||
| void setState(const char* const key, const char* const value) | |||
| { | |||
| assert(key != nullptr && value != nullptr); | |||
| if (fPlugin != nullptr && key != nullptr && value != nullptr) | |||
| fPlugin->d_setState(key, value); | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| void setTimePos(const bool playing, const uint64_t frame, const double bpm) | |||
| { | |||
| if (fData != nullptr) | |||
| { | |||
| fData->timePos.playing = playing; | |||
| fData->timePos.frame = frame; | |||
| fData->timePos.bpm = bpm; | |||
| } | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| void activate() | |||
| { | |||
| if (fPlugin != nullptr) | |||
| fPlugin->d_activate(); | |||
| } | |||
| void deactivate() | |||
| { | |||
| if (fPlugin != nullptr) | |||
| fPlugin->d_deactivate(); | |||
| } | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| void run(float** const inputs, float** const outputs, const uint32_t frames, const MidiEvent* const midiEvents, const uint32_t midiEventCount) | |||
| { | |||
| if (fPlugin != nullptr) | |||
| fPlugin->d_run(inputs, outputs, frames, midiEvents, midiEventCount); | |||
| } | |||
| #else | |||
| void run(float** const inputs, float** const outputs, const uint32_t frames) | |||
| { | |||
| if (fPlugin != nullptr) | |||
| fPlugin->d_run(inputs, outputs, frames); | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| void setBufferSize(const uint32_t bufferSize, bool doCallback = false) | |||
| { | |||
| assert(bufferSize >= 2); | |||
| if (fData != nullptr) | |||
| { | |||
| if (doCallback && fData->bufferSize == bufferSize) | |||
| doCallback = false; | |||
| fData->bufferSize = bufferSize; | |||
| } | |||
| if (fPlugin != nullptr && doCallback) | |||
| { | |||
| fPlugin->d_deactivate(); | |||
| fPlugin->d_bufferSizeChanged(bufferSize); | |||
| fPlugin->d_activate(); | |||
| } | |||
| } | |||
| void setSampleRate(const double sampleRate, bool doCallback = false) | |||
| { | |||
| assert(sampleRate > 0.0); | |||
| if (fData != nullptr) | |||
| { | |||
| if (doCallback && fData->sampleRate == sampleRate) | |||
| doCallback = false; | |||
| fData->sampleRate = sampleRate; | |||
| } | |||
| if (fPlugin != nullptr && doCallback) | |||
| { | |||
| fPlugin->d_deactivate(); | |||
| fPlugin->d_sampleRateChanged(sampleRate); | |||
| fPlugin->d_activate(); | |||
| } | |||
| } | |||
| private: | |||
| // ------------------------------------------------------------------- | |||
| // private members accessed by DistrhoPlugin class | |||
| Plugin* const fPlugin; | |||
| Plugin::PrivateData* const fData; | |||
| // ------------------------------------------------------------------- | |||
| // Static fallback data, see DistrhoPlugin.cpp | |||
| static const d_string sFallbackString; | |||
| static const ParameterRanges sFallbackRanges; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED | |||
| @@ -0,0 +1,702 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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" | |||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
| # include "dssi/dssi.h" | |||
| #else | |||
| # include "ladspa/ladspa.h" | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| # error Cannot build synth plugin 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 | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| fPortLatency = nullptr; | |||
| #endif | |||
| } | |||
| ~PluginLadspaDssi() | |||
| { | |||
| if (fPortControls != nullptr) | |||
| { | |||
| delete[] fPortControls; | |||
| fPortControls = nullptr; | |||
| } | |||
| if (fLastControlValues) | |||
| { | |||
| delete[] fLastControlValues; | |||
| fLastControlValues = nullptr; | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void ladspa_activate() | |||
| { | |||
| fPlugin.activate(); | |||
| } | |||
| void ladspa_deactivate() | |||
| { | |||
| fPlugin.deactivate(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void ladspa_connect_port(const unsigned long port, LADSPA_Data* const dataLocation) | |||
| { | |||
| unsigned long index = 0; | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
| for (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
| { | |||
| if (port == index++) | |||
| { | |||
| fPortAudioIns[i] = dataLocation; | |||
| return; | |||
| } | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| for (unsigned long 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 (unsigned long i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
| { | |||
| if (port == index++) | |||
| { | |||
| fPortControls[i] = dataLocation; | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
| void ladspa_run(const unsigned long sampleCount) | |||
| { | |||
| dssi_run_synth(sampleCount, nullptr, 0); | |||
| } | |||
| void dssi_run_synth(const unsigned long sampleCount, snd_seq_event_t* const events, const unsigned long eventCount) | |||
| #else | |||
| void ladspa_run(const unsigned long 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_IS_SYNTH | |||
| // 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]); | |||
| 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].buf[0] = 0x80 + seqEvent.data.note.channel; | |||
| midiEvents[j].buf[1] = seqEvent.data.note.note; | |||
| midiEvents[j].buf[2] = 0; | |||
| midiEvents[j].buf[3] = 0; | |||
| break; | |||
| case SND_SEQ_EVENT_NOTEON: | |||
| j = midiEventCount++; | |||
| midiEvents[j].frame = seqEvent.time.tick; | |||
| midiEvents[j].size = 3; | |||
| midiEvents[j].buf[0] = 0x90 + seqEvent.data.note.channel; | |||
| midiEvents[j].buf[1] = seqEvent.data.note.note; | |||
| midiEvents[j].buf[2] = seqEvent.data.note.velocity; | |||
| midiEvents[j].buf[3] = 0; | |||
| break; | |||
| case SND_SEQ_EVENT_KEYPRESS: | |||
| j = midiEventCount++; | |||
| midiEvents[j].frame = seqEvent.time.tick; | |||
| midiEvents[j].size = 3; | |||
| midiEvents[j].buf[0] = 0xA0 + seqEvent.data.note.channel; | |||
| midiEvents[j].buf[1] = seqEvent.data.note.note; | |||
| midiEvents[j].buf[2] = seqEvent.data.note.velocity; | |||
| midiEvents[j].buf[3] = 0; | |||
| break; | |||
| case SND_SEQ_EVENT_CONTROLLER: | |||
| j = midiEventCount++; | |||
| midiEvents[j].frame = seqEvent.time.tick; | |||
| midiEvents[j].size = 3; | |||
| midiEvents[j].buf[0] = 0xB0 + seqEvent.data.control.channel; | |||
| midiEvents[j].buf[1] = seqEvent.data.control.param; | |||
| midiEvents[j].buf[2] = seqEvent.data.control.value; | |||
| midiEvents[j].buf[3] = 0; | |||
| break; | |||
| case SND_SEQ_EVENT_CHANPRESS: | |||
| j = midiEventCount++; | |||
| midiEvents[j].frame = seqEvent.time.tick; | |||
| midiEvents[j].size = 2; | |||
| midiEvents[j].buf[0] = 0xD0 + seqEvent.data.control.channel; | |||
| midiEvents[j].buf[1] = seqEvent.data.control.value; | |||
| midiEvents[j].buf[2] = 0; | |||
| midiEvents[j].buf[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].buf[0] = 0xE0 + seqEvent.data.control.channel; | |||
| midiEvents[j].buf[1] = 0; | |||
| midiEvents[j].buf[2] = 0; | |||
| midiEvents[j].buf[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_IS_SYNTH | |||
| 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 unsigned long 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 unsigned long bank, const unsigned long program) | |||
| { | |||
| const unsigned long realProgram(bank * 128 + program); | |||
| if (realProgram >= fPlugin.getProgramCount()) | |||
| return; | |||
| fPlugin.setProgram(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 | |||
| LADSPA_Data* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
| #else | |||
| 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*, unsigned long 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, unsigned long 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, unsigned long 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, unsigned long index) | |||
| { | |||
| return instancePtr->dssi_get_program(index); | |||
| } | |||
| static void dssi_select_program(LADSPA_Handle instance, unsigned long bank, unsigned long program) | |||
| { | |||
| instancePtr->dssi_select_program(bank, program); | |||
| } | |||
| # endif | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| static void dssi_run_synth(LADSPA_Handle instance, unsigned long sampleCount, snd_seq_event_t* events, unsigned long eventCount) | |||
| { | |||
| instancePtr->dssi_run_synth(sampleCount, events, eventCount); | |||
| } | |||
| # endif | |||
| #endif | |||
| #undef instancePtr | |||
| // ----------------------------------------------------------------------- | |||
| static LADSPA_Descriptor sLadspaDescriptor = { | |||
| /* UniqueID */ 0, | |||
| /* Label */ nullptr, | |||
| /* Properties */ LADSPA_PROPERTY_REALTIME | LADSPA_PROPERTY_HARD_RT_CAPABLE, | |||
| /* 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_IS_SYNTH | |||
| 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 | |||
| unsigned long port = 0; | |||
| unsigned long 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 (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port) | |||
| { | |||
| char portName[24] = { '\0' }; | |||
| std::sprintf(portName, "Audio Input %lu", i+1); | |||
| portNames[port] = strdup(portName); | |||
| 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 (unsigned long i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port) | |||
| { | |||
| char portName[24] = { '\0' }; | |||
| std::sprintf(portName, "Audio Output %lu", i+1); | |||
| portNames[port] = strdup(portName); | |||
| 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; | |||
| portRangeHints[port].LowerBound = 0.0f; | |||
| portRangeHints[port].UpperBound = 1.0f; | |||
| ++port; | |||
| #endif | |||
| for (unsigned long 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 & PARAMETER_IS_BOOLEAN) | |||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; | |||
| if (hints & PARAMETER_IS_INTEGER) | |||
| portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER; | |||
| if (hints & PARAMETER_IS_LOGARITHMIC) | |||
| 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 (unsigned long 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(unsigned long index) | |||
| { | |||
| USE_NAMESPACE_DISTRHO | |||
| return (index == 0) ? &sLadspaDescriptor : nullptr; | |||
| } | |||
| #ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
| DISTRHO_PLUGIN_EXPORT | |||
| const DSSI_Descriptor* dssi_descriptor(unsigned long index) | |||
| { | |||
| USE_NAMESPACE_DISTRHO | |||
| return (index == 0) ? &sDssiDescriptor : nullptr; | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| @@ -0,0 +1,714 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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/atom-util.h" | |||
| #include "lv2/buf-size.h" | |||
| #include "lv2/midi.h" | |||
| #include "lv2/options.h" | |||
| #include "lv2/state.h" | |||
| #include "lv2/time.h" | |||
| #include "lv2/urid.h" | |||
| #include "lv2/worker.h" | |||
| #include "lv2/lv2_programs.h" | |||
| #ifdef noexcept | |||
| # undef noexcept | |||
| #endif | |||
| #include <map> | |||
| #ifndef DISTRHO_PLUGIN_URI | |||
| # error DISTRHO_PLUGIN_URI undefined! | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| # warning LV2 State still TODO | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| # warning LV2 TimePos still TODO | |||
| #endif | |||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
| #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
| typedef std::map<d_string,d_string> StringMap; | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| class PluginLv2 | |||
| { | |||
| public: | |||
| PluginLv2(const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker) | |||
| : fPortControls(nullptr), | |||
| fLastControlValues(nullptr), | |||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
| fURIDs(uridMap), | |||
| #endif | |||
| fUridMap(uridMap), | |||
| fWorker(worker) | |||
| { | |||
| #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 | |||
| { | |||
| const uint32_t count(fPlugin.getParameterCount()); | |||
| fPortControls = new float*[count]; | |||
| fLastControlValues = new float[count]; | |||
| for (uint32_t i=0; i < count; ++i) | |||
| { | |||
| fPortControls[i] = nullptr; | |||
| fLastControlValues[i] = fPlugin.getParameterValue(i); | |||
| } | |||
| } | |||
| #if DISTRHO_LV2_USE_EVENTS_IN | |||
| fPortEventsIn = nullptr; | |||
| #endif | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| fPortEventsOut = nullptr; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| fPortLatency = nullptr; | |||
| #endif | |||
| } | |||
| ~PluginLv2() | |||
| { | |||
| if (fPortControls != nullptr) | |||
| { | |||
| delete[] fPortControls; | |||
| fPortControls = nullptr; | |||
| } | |||
| if (fLastControlValues) | |||
| { | |||
| delete[] fLastControlValues; | |||
| fLastControlValues = nullptr; | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| fStateMap.clear(); | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void lv2_activate() | |||
| { | |||
| fPlugin.activate(); | |||
| } | |||
| void lv2_deactivate() | |||
| { | |||
| fPlugin.deactivate(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void lv2_connect_port(const uint32_t port, void* const dataLocation) | |||
| { | |||
| uint32_t index = 0; | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
| { | |||
| if (port == index++) | |||
| { | |||
| fPortAudioIns[i] = (float*)dataLocation; | |||
| return; | |||
| } | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| { | |||
| if (port == index++) | |||
| { | |||
| fPortAudioOuts[i] = (float*)dataLocation; | |||
| return; | |||
| } | |||
| } | |||
| #endif | |||
| #if DISTRHO_LV2_USE_EVENTS_IN | |||
| if (port == index++) | |||
| { | |||
| fPortEventsIn = (LV2_Atom_Sequence*)dataLocation; | |||
| return; | |||
| } | |||
| #endif | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| if (port == index++) | |||
| { | |||
| fPortEventsOut = (LV2_Atom_Sequence*)dataLocation; | |||
| return; | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| if (port == index++) | |||
| { | |||
| fPortLatency = (float*)dataLocation; | |||
| return; | |||
| } | |||
| #endif | |||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
| { | |||
| if (port == index++) | |||
| { | |||
| fPortControls[i] = (float*)dataLocation; | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void lv2_run(const uint32_t sampleCount) | |||
| { | |||
| // 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_LV2_USE_EVENTS_IN | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| uint32_t midiEventCount = 0; | |||
| # endif | |||
| LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | |||
| { | |||
| if (event == nullptr) | |||
| break; | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| if (event->body.type == fURIDs.midiEvent) | |||
| { | |||
| if (event->body.size > 4 || midiEventCount >= kMaxMidiEvents) | |||
| continue; | |||
| const uint8_t* const data((const uint8_t*)(event + 1)); | |||
| MidiEvent& midiEvent(fMidiEvents[midiEventCount]); | |||
| midiEvent.frame = event->time.frames; | |||
| midiEvent.size = event->body.size; | |||
| uint8_t i; | |||
| for (i=0; i < midiEvent.size; ++i) | |||
| midiEvent.buf[i] = data[i]; | |||
| for (; i < 4; ++i) | |||
| midiEvent.buf[i] = 0; | |||
| ++midiEventCount; | |||
| continue; | |||
| } | |||
| # endif | |||
| # if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| if (event->body.type == fURIDs.timePosition) | |||
| { | |||
| // TODO | |||
| } | |||
| # endif | |||
| # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
| if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) | |||
| { | |||
| const void* const data((const void*)(event + 1)); | |||
| fWorker->schedule_work(fWorker->handle, event->body.size, data); | |||
| } | |||
| # endif | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); | |||
| #else | |||
| fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |||
| #endif | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| #endif | |||
| updateParameterOutputs(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| 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_BUF_SIZE__maxBlockLength)) | |||
| { | |||
| if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Int)) | |||
| { | |||
| const int bufferSize(*(const int*)options[i].value); | |||
| fPlugin.setBufferSize(bufferSize); | |||
| return LV2_OPTIONS_SUCCESS; | |||
| } | |||
| else | |||
| { | |||
| d_stderr("Host changed maxBlockLength but with wrong value type"); | |||
| return LV2_OPTIONS_ERR_BAD_VALUE; | |||
| } | |||
| } | |||
| else 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); | |||
| fPlugin.setSampleRate(sampleRate); | |||
| return LV2_OPTIONS_SUCCESS; | |||
| } | |||
| else | |||
| { | |||
| d_stderr("Host changed sampleRate but with wrong value type"); | |||
| return LV2_OPTIONS_ERR_BAD_VALUE; | |||
| } | |||
| } | |||
| } | |||
| return LV2_OPTIONS_ERR_BAD_KEY; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) | |||
| { | |||
| if (index >= fPlugin.getProgramCount()) | |||
| return nullptr; | |||
| static LV2_Program_Descriptor desc; | |||
| desc.bank = index / 128; | |||
| desc.program = index % 128; | |||
| desc.name = fPlugin.getProgramName(index); | |||
| return &desc; | |||
| } | |||
| void lv2_select_program(const uint32_t bank, const uint32_t program) | |||
| { | |||
| const uint32_t realProgram(bank * 128 + program); | |||
| if (realProgram >= fPlugin.getProgramCount()) | |||
| return; | |||
| fPlugin.setProgram(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 | |||
| // ------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t flags) | |||
| { | |||
| //flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& key = it->first; | |||
| const d_string& value = it->second; | |||
| store(handle, fUridMap->map(fUridMap->handle, (const char*)key), (const char*)value, value.length()+1, fURIDs.atomString, flags); | |||
| } | |||
| return LV2_STATE_SUCCESS; | |||
| } | |||
| LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle) | |||
| { | |||
| size_t size = 0; | |||
| uint32_t type = 0; | |||
| uint32_t flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; | |||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
| { | |||
| const d_string& key = fPlugin.getStateKey(i); | |||
| const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, (const char*)key), &size, &type, &flags); | |||
| if (size == 0) | |||
| continue; | |||
| if (data == nullptr) | |||
| continue; | |||
| if (type != fURIDs.atomString) | |||
| continue; | |||
| const char* const value((const char*)data); | |||
| if (std::strlen(value) != size) | |||
| continue; | |||
| setState(key, value); | |||
| } | |||
| return LV2_STATE_SUCCESS; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| LV2_Worker_Status lv2_work(const void* const data) | |||
| { | |||
| const char* const stateKey((const char*)data); | |||
| const char* const stateValue(stateKey+std::strlen(stateKey)+1); | |||
| setState(stateKey, stateValue); | |||
| return LV2_WORKER_SUCCESS; | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| PluginExporter fPlugin; | |||
| // LV2 ports | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
| float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
| #else | |||
| float** fPortAudioIns; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
| #else | |||
| float** fPortAudioOuts; | |||
| #endif | |||
| float** fPortControls; | |||
| #if DISTRHO_LV2_USE_EVENTS_IN | |||
| LV2_Atom_Sequence* fPortEventsIn; | |||
| #endif | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| LV2_Atom_Sequence* fPortEventsOut; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| float* fPortLatency; | |||
| #endif | |||
| // Temporary data | |||
| float* fLastControlValues; | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| MidiEvent fMidiEvents[kMaxMidiEvents]; | |||
| #endif | |||
| // LV2 URIDs | |||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
| struct URIDs { | |||
| LV2_URID atomString; | |||
| LV2_URID distrhoState; | |||
| LV2_URID midiEvent; | |||
| LV2_URID timePosition; | |||
| URIDs(const LV2_URID_Map* const uridMap) | |||
| : atomString(uridMap->map(uridMap->handle, LV2_ATOM__String)), | |||
| distrhoState(uridMap->map(uridMap->handle, "urn:distrho:keyValueState")), | |||
| midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||
| timePosition(uridMap->map(uridMap->handle, LV2_TIME__Position)) {} | |||
| } fURIDs; | |||
| #endif | |||
| // LV2 features | |||
| const LV2_URID_Map* const fUridMap; | |||
| const LV2_Worker_Schedule* const fWorker; | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| StringMap fStateMap; | |||
| void setState(const char* const key, const char* const newValue) | |||
| { | |||
| fPlugin.setState(key, newValue); | |||
| // check if key already exists | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& d_key = it->first; | |||
| if (d_key == key) | |||
| { | |||
| it->second = newValue; | |||
| return; | |||
| } | |||
| } | |||
| // add a new one then | |||
| d_string d_key(key); | |||
| fStateMap[d_key] = newValue; | |||
| } | |||
| #endif | |||
| 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 LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) | |||
| { | |||
| const LV2_Options_Option* options = nullptr; | |||
| const LV2_URID_Map* uridMap = nullptr; | |||
| const LV2_Worker_Schedule* worker = nullptr; | |||
| 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_WORKER__schedule) == 0) | |||
| worker = (const LV2_Worker_Schedule*)features[i]->data; | |||
| } | |||
| if (options == nullptr) | |||
| { | |||
| d_stderr("Options feature missing, cannot continue!"); | |||
| return nullptr; | |||
| } | |||
| if (uridMap == nullptr) | |||
| { | |||
| d_stderr("URID Map feature missing, cannot continue!"); | |||
| return nullptr; | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| if (worker == nullptr) | |||
| { | |||
| d_stderr("Worker feature missing, cannot continue!"); | |||
| return nullptr; | |||
| } | |||
| #endif | |||
| for (int i=0; options[i].key != 0; ++i) | |||
| { | |||
| if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) | |||
| { | |||
| if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) | |||
| d_lastBufferSize = *(const int*)options[i].value; | |||
| else | |||
| d_stderr("Host provides maxBlockLength but has wrong value type"); | |||
| break; | |||
| } | |||
| } | |||
| if (d_lastBufferSize == 0) | |||
| d_lastBufferSize = 2048; | |||
| d_lastSampleRate = sampleRate; | |||
| return new PluginLv2(uridMap, worker); | |||
| } | |||
| #define instancePtr ((PluginLv2*)instance) | |||
| static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) | |||
| { | |||
| instancePtr->lv2_connect_port(port, dataLocation); | |||
| } | |||
| static void lv2_activate(LV2_Handle instance) | |||
| { | |||
| instancePtr->lv2_activate(); | |||
| } | |||
| static void lv2_run(LV2_Handle instance, uint32_t sampleCount) | |||
| { | |||
| instancePtr->lv2_run(sampleCount); | |||
| } | |||
| static void lv2_deactivate(LV2_Handle instance) | |||
| { | |||
| instancePtr->lv2_deactivate(); | |||
| } | |||
| static void lv2_cleanup(LV2_Handle instance) | |||
| { | |||
| delete instancePtr; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| static uint32_t lv2_get_options(LV2_Handle instance, LV2_Options_Option* options) | |||
| { | |||
| return instancePtr->lv2_get_options(options); | |||
| } | |||
| static uint32_t lv2_set_options(LV2_Handle instance, const LV2_Options_Option* options) | |||
| { | |||
| return instancePtr->lv2_set_options(options); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) | |||
| { | |||
| return instancePtr->lv2_get_program(index); | |||
| } | |||
| static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) | |||
| { | |||
| instancePtr->lv2_select_program(bank, program); | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| static LV2_State_Status lv2_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const*) | |||
| { | |||
| return instancePtr->lv2_save(store, handle, flags); | |||
| } | |||
| static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t, const LV2_Feature* const*) | |||
| { | |||
| return instancePtr->lv2_restore(retrieve, handle); | |||
| } | |||
| LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function, LV2_Worker_Respond_Handle, uint32_t, const void* data) | |||
| { | |||
| return instancePtr->lv2_work(data); | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| static const void* lv2_extension_data(const char* uri) | |||
| { | |||
| static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; | |||
| if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) | |||
| return &options; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; | |||
| if (std::strcmp(uri, LV2_PROGRAMS__Interface) == 0) | |||
| return &programs; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| static const LV2_State_Interface state = { lv2_save, lv2_restore }; | |||
| static const LV2_Worker_Interface worker = { lv2_work, nullptr, nullptr }; | |||
| if (std::strcmp(uri, LV2_STATE__interface) == 0) | |||
| return &state; | |||
| if (std::strcmp(uri, LV2_WORKER__interface) == 0) | |||
| return &worker; | |||
| #endif | |||
| return nullptr; | |||
| } | |||
| #undef instancePtr | |||
| // ----------------------------------------------------------------------- | |||
| static const LV2_Descriptor sLv2Descriptor = { | |||
| DISTRHO_PLUGIN_URI, | |||
| lv2_instantiate, | |||
| lv2_connect_port, | |||
| lv2_activate, | |||
| lv2_run, | |||
| lv2_deactivate, | |||
| lv2_cleanup, | |||
| lv2_extension_data | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| DISTRHO_PLUGIN_EXPORT | |||
| const LV2_Descriptor* lv2_descriptor(uint32_t index) | |||
| { | |||
| USE_NAMESPACE_DISTRHO | |||
| return (index == 0) ? &sLv2Descriptor : nullptr; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -0,0 +1,367 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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/midi.h" | |||
| #include "lv2/options.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_programs.h" | |||
| #include <fstream> | |||
| #include <iostream> | |||
| #ifndef DISTRHO_PLUGIN_URI | |||
| # error DISTRHO_PLUGIN_URI undefined! | |||
| #endif | |||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
| #define DISTRHO_LV2_USE_EVENTS_OUT (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; | |||
| d_string pluginLabel(basename); | |||
| d_string pluginTTL(pluginLabel + ".ttl"); | |||
| // --------------------------------------------- | |||
| { | |||
| std::cout << "Writing manifest.ttl..."; std::cout.flush(); | |||
| std::fstream manifestFile("manifest.ttl", std::ios::out); | |||
| d_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 <" + pluginLabel + "." 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_MACOS | |||
| manifestString += " a ui:CocoaUI ;\n"; | |||
| # elif DISTRHO_OS_WINDOWS | |||
| manifestString += " a ui:WindowsUI ;\n"; | |||
| # else | |||
| manifestString += " a ui:X11UI ;\n"; | |||
| # endif | |||
| manifestString += " ui:binary <" + pluginLabel + "_ui." DISTRHO_DLL_EXTENSION "> ;\n"; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| manifestString += " lv2:extensionData ui:idleInterface ,\n"; | |||
| manifestString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||
| #else | |||
| manifestString += " lv2:extensionData ui:idleInterface ;\n"; | |||
| #endif | |||
| manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
| manifestString += " ui:touch ;\n"; | |||
| manifestString += " lv2:requiredFeature ui:resize ,\n"; | |||
| manifestString += " <" LV2_OPTIONS__options "> ,\n"; | |||
| 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); | |||
| d_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"; | |||
| #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 | |||
| pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||
| pluginString += " <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||
| 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) | |||
| { | |||
| if (i == 0) | |||
| pluginString += " lv2:port [\n"; | |||
| else | |||
| pluginString += " [\n"; | |||
| pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; | |||
| pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_audio_in_" + d_string(i+1) + "\" ;\n"; | |||
| pluginString += " lv2:name \"Audio Input " + d_string(i+1) + "\" ;\n"; | |||
| if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) | |||
| pluginString += " ] ;\n\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) | |||
| { | |||
| if (i == 0) | |||
| pluginString += " lv2:port [\n"; | |||
| else | |||
| pluginString += " [\n"; | |||
| pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; | |||
| pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_audio_out_" + d_string(i+1) + "\" ;\n"; | |||
| pluginString += " lv2:name \"Audio Output " + d_string(i+1) + "\" ;\n"; | |||
| if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) | |||
| pluginString += " ] ;\n\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 " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:name \"Events Input\" ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_events_in\" ;\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_TIMEPOS | |||
| pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; | |||
| # endif | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\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 " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:name \"Events Output\" ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; | |||
| pluginString += " atom:bufferType atom:Sequence ;\n"; | |||
| pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||
| pluginString += " ] ;\n\n"; | |||
| ++portIndex; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| pluginString += " lv2:port [\n"; | |||
| pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||
| pluginString += " lv2:index " + d_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 ;\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 " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; | |||
| // symbol | |||
| { | |||
| d_string symbol(plugin.getParameterSymbol(i)); | |||
| if (symbol.isEmpty()) | |||
| symbol = "lv2_port_" + d_string(portIndex-1); | |||
| pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |||
| } | |||
| // ranges | |||
| { | |||
| const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||
| if (plugin.getParameterHints(i) & PARAMETER_IS_INTEGER) | |||
| { | |||
| pluginString += " lv2:default " + d_string(int(plugin.getParameterValue(i))) + " ;\n"; | |||
| pluginString += " lv2:minimum " + d_string(int(ranges.min)) + " ;\n"; | |||
| pluginString += " lv2:maximum " + d_string(int(ranges.max)) + " ;\n"; | |||
| } | |||
| else | |||
| { | |||
| pluginString += " lv2:default " + d_string(plugin.getParameterValue(i)) + " ;\n"; | |||
| pluginString += " lv2:minimum " + d_string(ranges.min) + " ;\n"; | |||
| pluginString += " lv2:maximum " + d_string(ranges.max) + " ;\n"; | |||
| } | |||
| } | |||
| // unit | |||
| { | |||
| const d_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 & PARAMETER_IS_BOOLEAN) | |||
| pluginString += " lv2:portProperty lv2:toggled ;\n"; | |||
| if (hints & PARAMETER_IS_INTEGER) | |||
| pluginString += " lv2:portProperty lv2:integer ;\n"; | |||
| if (hints & PARAMETER_IS_LOGARITHMIC) | |||
| pluginString += " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ;\n"; | |||
| if ((hints & PARAMETER_IS_AUTOMABLE) == 0 && ! plugin.isParameterOutput(i)) | |||
| pluginString += " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#expensive> ;\n"; | |||
| } | |||
| if (i+1 == count) | |||
| pluginString += " ] ;\n\n"; | |||
| else | |||
| pluginString += " ] ,\n"; | |||
| } | |||
| } | |||
| pluginString += " doap:name \"" + d_string(plugin.getName()) + "\" ;\n"; | |||
| pluginString += " doap:maintainer [ foaf:name \"" + d_string(plugin.getMaker()) + "\" ] .\n"; | |||
| pluginFile << pluginString << std::endl; | |||
| pluginFile.close(); | |||
| std::cout << " done!" << std::endl; | |||
| } | |||
| } | |||
| @@ -0,0 +1,963 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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_HAS_UI | |||
| # include "DistrhoUIInternal.hpp" | |||
| #endif | |||
| #ifndef __cdecl | |||
| # define __cdecl | |||
| #endif | |||
| // has some conflicts | |||
| #ifdef noexcept | |||
| # undef noexcept | |||
| #endif | |||
| #define VESTIGE_HEADER | |||
| #define VST_FORCE_DEPRECATED 0 | |||
| #include <map> | |||
| #include <string> | |||
| #ifdef VESTIGE_HEADER | |||
| # include "vestige/aeffectx.h" | |||
| #define effFlagsProgramChunks (1 << 5) | |||
| #define effGetParamLabel 6 | |||
| #define effGetParamDisplay 7 | |||
| #define effGetChunk 23 | |||
| #define effSetChunk 24 | |||
| #define effCanBeAutomated 26 | |||
| #define effGetProgramNameIndexed 29 | |||
| #define effGetPlugCategory 35 | |||
| #define kPlugCategEffect 1 | |||
| #define kPlugCategSynth 2 | |||
| #define kVstVersion 2400 | |||
| struct ERect { | |||
| int16_t top, left, bottom, right; | |||
| }; | |||
| #else | |||
| # include "vst/aeffectx.h" | |||
| #endif | |||
| typedef std::map<d_string,d_string> StringMap; | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| void strncpy(char* const dst, const char* const src, const size_t size) | |||
| { | |||
| std::strncpy(dst, src, size); | |||
| dst[size] = '\0'; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| class StateHelper | |||
| { | |||
| public: | |||
| virtual ~StateHelper() {} | |||
| virtual void setSharedState(const char* const newKey, const char* const newValue) = 0; | |||
| }; | |||
| #else | |||
| typedef void StateHelper; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| class UIVst | |||
| { | |||
| public: | |||
| UIVst(const audioMasterCallback audioMaster, AEffect* const effect, PluginExporter* const plugin, StateHelper* const stateHelper, const intptr_t winId) | |||
| : fAudioMaster(audioMaster), | |||
| fEffect(effect), | |||
| fPlugin(plugin), | |||
| fStateHelper(stateHelper), | |||
| fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), | |||
| fParameterChecks(nullptr), | |||
| fParameterValues(nullptr) | |||
| { | |||
| const uint32_t paramCount(plugin->getParameterCount()); | |||
| if (paramCount > 0) | |||
| { | |||
| fParameterChecks = new bool[paramCount]; | |||
| fParameterValues = new float[paramCount]; | |||
| for (uint32_t i=0; i < paramCount; ++i) | |||
| { | |||
| fParameterChecks[i] = false; | |||
| fParameterValues[i] = 0.0f; | |||
| } | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| fNextProgram = -1; | |||
| #endif | |||
| } | |||
| ~UIVst() | |||
| { | |||
| if (fParameterChecks != nullptr) | |||
| { | |||
| delete[] fParameterChecks; | |||
| fParameterChecks = nullptr; | |||
| } | |||
| if (fParameterValues != nullptr) | |||
| { | |||
| delete[] fParameterValues; | |||
| fParameterValues = nullptr; | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void idle() | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| if (fNextProgram != -1) | |||
| { | |||
| fUI.programChanged(fNextProgram); | |||
| fNextProgram = -1; | |||
| } | |||
| #endif | |||
| for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) | |||
| { | |||
| if (fParameterChecks[i]) | |||
| { | |||
| fParameterChecks[i] = false; | |||
| fUI.parameterChanged(i, fParameterValues[i]); | |||
| } | |||
| } | |||
| fUI.idle(); | |||
| } | |||
| int16_t getWidth() const | |||
| { | |||
| return fUI.getWidth(); | |||
| } | |||
| int16_t getHeight() const | |||
| { | |||
| return fUI.getHeight(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // functions called from the plugin side, RT no block | |||
| void setParameterValueFromPlugin(const uint32_t index, const float perValue) | |||
| { | |||
| fParameterChecks[index] = true; | |||
| fParameterValues[index] = perValue; | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| void setProgramFromPlugin(const uint32_t index) | |||
| { | |||
| fNextProgram = index; | |||
| // set previous parameters invalid | |||
| for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) | |||
| fParameterChecks[i] = false; | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // functions called from the plugin side, block | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void setStateFromPlugin(const char* const key, const char* const value) | |||
| { | |||
| fUI.stateChanged(key, value); | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| protected: | |||
| intptr_t hostCallback(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) | |||
| { | |||
| return fAudioMaster(fEffect, opcode, index, value, ptr, opt); | |||
| } | |||
| void editParameter(const uint32_t index, const bool started) | |||
| { | |||
| if (started) | |||
| hostCallback(audioMasterBeginEdit, index, 0, nullptr, 0.0f); | |||
| else | |||
| hostCallback(audioMasterEndEdit, index, 0, nullptr, 0.0f); | |||
| } | |||
| void setParameterValue(const uint32_t index, const float realValue) | |||
| { | |||
| const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); | |||
| const float perValue(ranges.getNormalizedValue(realValue)); | |||
| fPlugin->setParameterValue(index, realValue); | |||
| hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); | |||
| } | |||
| void setState(const char* const key, const char* const value) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| fStateHelper->setSharedState(key, value); | |||
| #else | |||
| return; // unused | |||
| (void)key; | |||
| (void)value; | |||
| #endif | |||
| } | |||
| void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
| { | |||
| #if 0 //DISTRHO_PLUGIN_IS_SYNTH | |||
| // TODO | |||
| #else | |||
| return; // unused | |||
| (void)channel; | |||
| (void)note; | |||
| (void)velocity; | |||
| #endif | |||
| } | |||
| void uiResize(const unsigned int width, const unsigned int height) | |||
| { | |||
| fUI.setSize(width, height); | |||
| hostCallback(audioMasterSizeWindow, width, height, nullptr, 0.0f); | |||
| } | |||
| private: | |||
| // Vst stuff | |||
| const audioMasterCallback fAudioMaster; | |||
| AEffect* const fEffect; | |||
| PluginExporter* const fPlugin; | |||
| StateHelper* const fStateHelper; | |||
| // Plugin UI | |||
| UIExporter fUI; | |||
| // Temporary data | |||
| bool* fParameterChecks; | |||
| float* fParameterValues; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| int32_t fNextProgram; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Callbacks | |||
| #define handlePtr ((UIVst*)ptr) | |||
| static void editParameterCallback(void* ptr, uint32_t index, bool started) | |||
| { | |||
| handlePtr->editParameter(index, started); | |||
| } | |||
| static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||
| { | |||
| handlePtr->setParameterValue(rindex, value); | |||
| } | |||
| static void setStateCallback(void* ptr, const char* key, const char* value) | |||
| { | |||
| handlePtr->setState(key, value); | |||
| } | |||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
| { | |||
| handlePtr->sendNote(channel, note, velocity); | |||
| } | |||
| static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||
| { | |||
| handlePtr->uiResize(width, height); | |||
| } | |||
| #undef handlePtr | |||
| }; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| class PluginVst : public StateHelper | |||
| #else | |||
| class PluginVst | |||
| #endif | |||
| { | |||
| public: | |||
| PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) | |||
| : fAudioMaster(audioMaster), | |||
| fEffect(effect) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| fCurProgram = -1; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| fMidiEventCount = 0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| fVstUi = nullptr; | |||
| fVstRect.top = 0; | |||
| fVstRect.left = 0; | |||
| fVstRect.bottom = 0; | |||
| fVstRect.right = 0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| fStateChunk = nullptr; | |||
| #endif | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| ~PluginVst() | |||
| { | |||
| if (fStateChunk != nullptr) | |||
| { | |||
| delete[] fStateChunk; | |||
| fStateChunk = nullptr; | |||
| } | |||
| fStateMap.clear(); | |||
| } | |||
| #endif | |||
| intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) | |||
| { | |||
| int32_t ret = 0; | |||
| switch (opcode) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| case effSetProgram: | |||
| if (value >= 0 && value < static_cast<intptr_t>(fPlugin.getProgramCount())) | |||
| { | |||
| fCurProgram = value; | |||
| fPlugin.setProgram(fCurProgram); | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUi != nullptr) | |||
| fVstUi->setProgramFromPlugin(fCurProgram); | |||
| #endif | |||
| ret = 1; | |||
| } | |||
| break; | |||
| case effGetProgram: | |||
| ret = fCurProgram; | |||
| break; | |||
| //case effSetProgramName: | |||
| // unsupported | |||
| // break; | |||
| case effGetProgramName: | |||
| if (ptr != nullptr && fCurProgram >= 0 && fCurProgram < static_cast<int32_t>(fPlugin.getProgramCount())) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, fPlugin.getProgramName(fCurProgram), 24); | |||
| ret = 1; | |||
| } | |||
| break; | |||
| #endif | |||
| case effGetParamDisplay: | |||
| if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount())) | |||
| { | |||
| char* buf = (char*)ptr; | |||
| std::snprintf((char*)ptr, 8, "%f", fPlugin.getParameterValue(index)); | |||
| buf[8] = '\0'; | |||
| ret = 1; | |||
| } | |||
| break; | |||
| case effSetSampleRate: | |||
| fPlugin.setSampleRate(opt, true); | |||
| break; | |||
| case effSetBlockSize: | |||
| fPlugin.setBufferSize(value, true); | |||
| break; | |||
| case effMainsChanged: | |||
| if (value != 0) | |||
| { | |||
| fPlugin.activate(); | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| fMidiEventCount = 0; | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| fPlugin.deactivate(); | |||
| } | |||
| break; | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| case effEditGetRect: | |||
| if (fVstUi != nullptr) | |||
| { | |||
| fVstRect.right = fVstUi->getWidth(); | |||
| fVstRect.bottom = fVstUi->getHeight(); | |||
| } | |||
| else | |||
| { | |||
| d_lastUiSampleRate = fAudioMaster(fEffect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); | |||
| UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
| fVstRect.right = tmpUI.getWidth(); | |||
| fVstRect.bottom = tmpUI.getHeight(); | |||
| } | |||
| *(ERect**)ptr = &fVstRect; | |||
| ret = 1; | |||
| break; | |||
| case effEditOpen: | |||
| if (fVstUi == nullptr) | |||
| { | |||
| d_lastUiSampleRate = fAudioMaster(fEffect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); | |||
| fVstUi = new UIVst(fAudioMaster, fEffect, &fPlugin, this, (intptr_t)ptr); | |||
| # if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| if (fCurProgram >= 0) | |||
| fVstUi->setProgramFromPlugin(fCurProgram); | |||
| # endif | |||
| for (uint32_t i=0, count = fPlugin.getParameterCount(); i < count; ++i) | |||
| fVstUi->setParameterValueFromPlugin(i, fPlugin.getParameterValue(i)); | |||
| fVstUi->idle(); | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| if (fStateMap.size() > 0) | |||
| { | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& key = it->first; | |||
| const d_string& value = it->second; | |||
| fVstUi->setStateFromPlugin((const char*)key, (const char*)value); | |||
| } | |||
| fVstUi->idle(); | |||
| } | |||
| #endif | |||
| ret = 1; | |||
| } | |||
| break; | |||
| case effEditClose: | |||
| if (fVstUi != nullptr) | |||
| { | |||
| delete fVstUi; | |||
| fVstUi = nullptr; | |||
| ret = 1; | |||
| } | |||
| break; | |||
| case effEditIdle: | |||
| if (fVstUi != nullptr) | |||
| fVstUi->idle(); | |||
| break; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| case effGetChunk: | |||
| if (ptr == nullptr) | |||
| return 0; | |||
| if (fStateChunk != nullptr) | |||
| delete[] fStateChunk; | |||
| if (fStateMap.size() == 0) | |||
| { | |||
| fStateChunk = new char[1]; | |||
| fStateChunk[0] = '\0'; | |||
| ret = 1; | |||
| } | |||
| else | |||
| { | |||
| std::string tmpStr; | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& key = it->first; | |||
| const d_string& value = it->second; | |||
| tmpStr += (const char*)key; | |||
| tmpStr += "\xff"; | |||
| tmpStr += (const char*)value; | |||
| tmpStr += "\xff"; | |||
| } | |||
| const size_t size(tmpStr.size()); | |||
| fStateChunk = new char[size]; | |||
| std::memcpy(fStateChunk, tmpStr.c_str(), size*sizeof(char)); | |||
| for (size_t i=0; i < size; ++i) | |||
| { | |||
| if (fStateChunk[i] == '\xff') | |||
| fStateChunk[i] = '\0'; | |||
| } | |||
| ret = size; | |||
| } | |||
| *(void**)ptr = fStateChunk; | |||
| break; | |||
| case effSetChunk: | |||
| if (value <= 0) | |||
| return 0; | |||
| if (value == 1) | |||
| return 1; | |||
| if (const char* const state = (const char*)ptr) | |||
| { | |||
| const size_t stateSize = value; | |||
| const char* stateKey = state; | |||
| const char* stateValue = nullptr; | |||
| for (size_t i=0; i < stateSize; ++i) | |||
| { | |||
| // find next null char | |||
| if (state[i] != '\0') | |||
| continue; | |||
| // found, set value | |||
| stateValue = &state[i+1]; | |||
| setSharedState(stateKey, stateValue); | |||
| if (fVstUi != nullptr) | |||
| fVstUi->setStateFromPlugin(stateKey, stateValue); | |||
| // increment text position | |||
| i += std::strlen(stateValue) + 2; | |||
| // check if end of data | |||
| if (i >= stateSize) | |||
| break; | |||
| // get next key | |||
| stateKey = &state[i]; | |||
| } | |||
| ret = 1; | |||
| } | |||
| break; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| case effProcessEvents: | |||
| if (const VstEvents* const events = (const VstEvents*)ptr) | |||
| { | |||
| if (events->numEvents == 0) | |||
| break; | |||
| for (int i=0, count=events->numEvents; i < count; ++i) | |||
| { | |||
| const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]); | |||
| if (vstMidiEvent == nullptr) | |||
| break; | |||
| if (vstMidiEvent->type != kVstMidiType) | |||
| continue; | |||
| if (fMidiEventCount >= kMaxMidiEvents) | |||
| break; | |||
| MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); | |||
| midiEvent.frame = vstMidiEvent->deltaFrames; | |||
| midiEvent.size = 3; | |||
| std::memcpy(midiEvent.buf, vstMidiEvent->midiData, 3*sizeof(uint8_t)); | |||
| } | |||
| } | |||
| break; | |||
| #endif | |||
| case effCanBeAutomated: | |||
| if (index < static_cast<int32_t>(fPlugin.getParameterCount())) | |||
| { | |||
| const uint32_t hints(fPlugin.getParameterHints(index)); | |||
| // must be automable, and not output | |||
| if ((hints & PARAMETER_IS_AUTOMABLE) != 0 && (hints & PARAMETER_IS_OUTPUT) == 0) | |||
| ret = 1; | |||
| } | |||
| break; | |||
| #if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS) | |||
| case effCanDo: | |||
| if (const char* const canDo = (const char*)ptr) | |||
| { | |||
| # if DISTRHO_PLUGIN_IS_SYNTH | |||
| if (std::strcmp(canDo, "receiveVstEvents") == 0) | |||
| return 1; | |||
| if (std::strcmp(canDo, "receiveVstMidiEvent") == 0) | |||
| return 1; | |||
| # endif | |||
| # if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| if (std::strcmp(canDo, "receiveVstTimeInfo") == 0) | |||
| return 1; | |||
| # endif | |||
| } | |||
| break; | |||
| #endif | |||
| //case effStartProcess: | |||
| //case effStopProcess: | |||
| // unused | |||
| // break; | |||
| } | |||
| return ret; | |||
| } | |||
| float vst_getParameter(const int32_t index) | |||
| { | |||
| const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | |||
| return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); | |||
| } | |||
| void vst_setParameter(const int32_t index, const float value) | |||
| { | |||
| const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | |||
| const float realValue(ranges.getUnnormalizedValue(value)); | |||
| fPlugin.setParameterValue(index, realValue); | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUi != nullptr) | |||
| fVstUi->setParameterValueFromPlugin(index, realValue); | |||
| #endif | |||
| } | |||
| void vst_processReplacing(float** const inputs, float** const outputs, const int32_t sampleFrames) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| if (const VstTimeInfo* const timeInfo = (const VstTimeInfo*)fEffect->dispatcher(fEffect, audioMasterGetTime, 0, kVstTempoValid, nullptr, 0.0f)) | |||
| fPlugin.setTimePos((timeInfo->flags & kVstTransportPlaying) != 0, timeInfo->samplePos, timeInfo->tempo); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount); | |||
| fMidiEventCount = 0; | |||
| #else | |||
| fPlugin.run(inputs, outputs, sampleFrames); | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| friend class UIVst; | |||
| private: | |||
| // VST stuff | |||
| const audioMasterCallback fAudioMaster; | |||
| AEffect* const fEffect; | |||
| // Plugin | |||
| PluginExporter fPlugin; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| int32_t fCurProgram; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| uint32_t fMidiEventCount; | |||
| MidiEvent fMidiEvents[kMaxMidiEvents]; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| UIVst* fVstUi; | |||
| ERect fVstRect; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| char* fStateChunk; | |||
| StringMap fStateMap; | |||
| void setSharedState(const char* const newKey, const char* const newValue) override | |||
| { | |||
| fPlugin.setState(newKey, newValue); | |||
| // check if key already exists | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& key = it->first; | |||
| if (key == newKey) | |||
| { | |||
| it->second = newValue; | |||
| return; | |||
| } | |||
| } | |||
| // add a new one then | |||
| d_string d_key(newKey); | |||
| fStateMap[d_key] = newValue; | |||
| } | |||
| #endif | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| #ifdef VESTIGE_HEADER | |||
| # define handlePtr ((PluginVst*)effect->ptr2) | |||
| # define validEffect effect != nullptr && effect->ptr2 != nullptr | |||
| #else | |||
| # define handlePtr ((PluginVst*)effect->resvd2) | |||
| # define validEffect effect != nullptr && effect->resvd2 != 0 | |||
| #endif | |||
| static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) | |||
| { | |||
| // first internal init | |||
| bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d); | |||
| if (doInternalInit) | |||
| { | |||
| // set valid but dummy values | |||
| d_lastBufferSize = 512; | |||
| d_lastSampleRate = 44100.0; | |||
| } | |||
| // Create dummy plugin to get data from | |||
| static PluginExporter plugin; | |||
| if (doInternalInit) | |||
| { | |||
| // unset | |||
| d_lastBufferSize = 0; | |||
| d_lastSampleRate = 0.0; | |||
| *(PluginExporter**)ptr = &plugin; | |||
| return 0; | |||
| } | |||
| // handle base opcodes | |||
| switch (opcode) | |||
| { | |||
| case effOpen: | |||
| #ifdef VESTIGE_HEADER | |||
| if (effect != nullptr && effect->ptr3 != nullptr) | |||
| { | |||
| audioMasterCallback audioMaster = (audioMasterCallback)effect->ptr3; | |||
| #else | |||
| if (effect != nullptr && effect->object != nullptr) | |||
| { | |||
| audioMasterCallback audioMaster = (audioMasterCallback)effect->object; | |||
| #endif | |||
| d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); | |||
| d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); | |||
| PluginVst* const plugin(new PluginVst(audioMaster, effect)); | |||
| #ifdef VESTIGE_HEADER | |||
| effect->ptr2 = plugin; | |||
| #else | |||
| effect->resvd2 = (intptr_t)plugin; | |||
| #endif | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effClose: | |||
| if (validEffect) | |||
| { | |||
| #ifdef VESTIGE_HEADER | |||
| delete (PluginVst*)effect->ptr2; | |||
| effect->ptr2 = nullptr; | |||
| effect->ptr3 = nullptr; | |||
| #else | |||
| delete (PluginVst*)effect->resvd2; | |||
| effect->resvd2 = 0; | |||
| effect->object = nullptr; | |||
| #endif | |||
| delete effect; | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effGetParamLabel: | |||
| if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getParameterUnit(index), 8); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effGetParamName: | |||
| if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getParameterName(index), 8); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| case effGetProgramNameIndexed: | |||
| if (ptr != nullptr && index < static_cast<int32_t>(plugin.getProgramCount())) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getProgramName(index), 24); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| #endif | |||
| case effGetPlugCategory: | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| return kPlugCategSynth; | |||
| #else | |||
| return kPlugCategEffect; | |||
| #endif | |||
| case effGetEffectName: | |||
| if (ptr != nullptr) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getName(), 64); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effGetVendorString: | |||
| if (ptr != nullptr) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getMaker(), 64); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effGetProductString: | |||
| if (ptr != nullptr) | |||
| { | |||
| DISTRHO::strncpy((char*)ptr, plugin.getLabel(), 32); | |||
| return 1; | |||
| } | |||
| return 0; | |||
| case effGetVendorVersion: | |||
| return plugin.getVersion(); | |||
| case effGetVstVersion: | |||
| return kVstVersion; | |||
| }; | |||
| // handle advanced opcodes | |||
| if (validEffect) | |||
| return handlePtr->vst_dispatcher(opcode, index, value, ptr, opt); | |||
| return 0; | |||
| } | |||
| static float vst_getParameterCallback(AEffect* effect, int32_t index) | |||
| { | |||
| if (validEffect) | |||
| return handlePtr->vst_getParameter(index); | |||
| return 0.0f; | |||
| } | |||
| static void vst_setParameterCallback(AEffect* effect, int32_t index, float value) | |||
| { | |||
| if (validEffect) | |||
| handlePtr->vst_setParameter(index, value); | |||
| } | |||
| static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) | |||
| { | |||
| if (validEffect) | |||
| handlePtr->vst_processReplacing(inputs, outputs, sampleFrames); | |||
| } | |||
| static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) | |||
| { | |||
| if (validEffect) | |||
| handlePtr->vst_processReplacing(inputs, outputs, sampleFrames); | |||
| } | |||
| #undef handlePtr | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| DISTRHO_PLUGIN_EXPORT | |||
| const AEffect* VSTPluginMain(audioMasterCallback audioMaster) | |||
| { | |||
| USE_NAMESPACE_DISTRHO | |||
| // old version | |||
| if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0) | |||
| return nullptr; | |||
| // first internal init | |||
| PluginExporter* plugin = nullptr; | |||
| vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f); | |||
| AEffect* const effect(new AEffect); | |||
| std::memset(effect, 0, sizeof(AEffect)); | |||
| // vst fields | |||
| effect->magic = kEffectMagic; | |||
| effect->uniqueID = plugin->getUniqueId(); | |||
| #ifdef VESTIGE_HEADER | |||
| *(int32_t*)&effect->unknown1 = plugin->getVersion(); | |||
| #else | |||
| effect->version = plugin->getVersion(); | |||
| #endif | |||
| // plugin fields | |||
| effect->numParams = plugin->getParameterCount(); | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| effect->numPrograms = plugin->getProgramCount(); | |||
| #endif | |||
| effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS; | |||
| effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; | |||
| // plugin flags | |||
| effect->flags |= effFlagsCanReplacing; | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| effect->flags |= effFlagsIsSynth; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| effect->flags |= effFlagsHasEditor; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| effect->flags |= effFlagsProgramChunks; | |||
| #endif | |||
| // static calls | |||
| effect->dispatcher = vst_dispatcherCallback; | |||
| effect->process = vst_processCallback; | |||
| effect->getParameter = vst_getParameterCallback; | |||
| effect->setParameter = vst_setParameterCallback; | |||
| effect->processReplacing = vst_processReplacingCallback; | |||
| // pointers | |||
| #ifdef VESTIGE_HEADER | |||
| effect->ptr3 = (void*)audioMaster; | |||
| #else | |||
| effect->object = (void*)audioMaster; | |||
| #endif | |||
| return effect; | |||
| } | |||
| @@ -0,0 +1,89 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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" | |||
| START_NAMESPACE_DGL | |||
| extern Window* dgl_lastUiParent; | |||
| END_NAMESPACE_DGL | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // Static data | |||
| double d_lastUiSampleRate = 0.0; | |||
| // ----------------------------------------------------------------------- | |||
| // UI | |||
| UI::UI() | |||
| : DGL::Widget(*DGL::dgl_lastUiParent), | |||
| pData(new PrivateData()) | |||
| { | |||
| assert(DGL::dgl_lastUiParent != nullptr); | |||
| DGL::dgl_lastUiParent = nullptr; | |||
| } | |||
| UI::~UI() | |||
| { | |||
| delete pData; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Host DSP State | |||
| double UI::d_getSampleRate() const noexcept | |||
| { | |||
| return pData->sampleRate; | |||
| } | |||
| void UI::d_editParameter(uint32_t index, bool started) | |||
| { | |||
| pData->editParamCallback(index + pData->parameterOffset, started); | |||
| } | |||
| void UI::d_setParameterValue(uint32_t index, float value) | |||
| { | |||
| pData->setParamCallback(index + pData->parameterOffset, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void UI::d_setState(const char* key, const char* value) | |||
| { | |||
| pData->setStateCallback(key, value); | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| void UI::d_sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | |||
| { | |||
| pData->sendNoteCallback(channel, note, velocity); | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Host UI State | |||
| void UI::d_uiResize(unsigned int width, unsigned int height) | |||
| { | |||
| pData->uiResizeCallback(width, height); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -0,0 +1,489 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 <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(unsigned char 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, uiResizeCallback), | |||
| fHostClosed(false), | |||
| fOscData(oscData) | |||
| { | |||
| fUI.setTitle(uiTitle); | |||
| } | |||
| ~UIDssi() | |||
| { | |||
| if (fOscData.server != nullptr && ! fHostClosed) | |||
| fOscData.send_exiting(); | |||
| } | |||
| void exec() | |||
| { | |||
| for (;;) | |||
| { | |||
| fOscData.idle(); | |||
| if (! fUI.idle()) | |||
| break; | |||
| d_msleep(50); | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void dssiui_configure(const char* key, const char* value) | |||
| { | |||
| fUI.stateChanged(key, value); | |||
| } | |||
| #endif | |||
| void dssiui_control(unsigned long index, float value) | |||
| { | |||
| fUI.parameterChanged(index, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| void dssiui_program(unsigned long bank, unsigned long program) | |||
| { | |||
| fUI.programChanged(bank * 128 + program); | |||
| } | |||
| #endif | |||
| void dssiui_show() | |||
| { | |||
| fUI.setVisible(true); | |||
| } | |||
| void dssiui_hide() | |||
| { | |||
| fUI.setVisible(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 uiResize(const unsigned int width, const unsigned int height) | |||
| { | |||
| fUI.setSize(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 uiResizeCallback(void* ptr, unsigned int width, unsigned int height) | |||
| { | |||
| uiPtr->uiResize(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; | |||
| } | |||
| 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; | |||
| 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(); | |||
| 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,249 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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/App.hpp" | |||
| #include "../../dgl/Window.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // Static data, see DistrhoUI.cpp | |||
| extern double d_lastUiSampleRate; | |||
| // ----------------------------------------------------------------------- | |||
| // 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 (*uiResizeFunc) (void* ptr, unsigned int width, unsigned int height); | |||
| // ----------------------------------------------------------------------- | |||
| // UI private data | |||
| struct UI::PrivateData { | |||
| // DSP | |||
| double sampleRate; | |||
| uint32_t parameterOffset; | |||
| // Callbacks | |||
| editParamFunc editParamCallbackFunc; | |||
| setParamFunc setParamCallbackFunc; | |||
| setStateFunc setStateCallbackFunc; | |||
| sendNoteFunc sendNoteCallbackFunc; | |||
| uiResizeFunc uiResizeCallbackFunc; | |||
| void* ptr; | |||
| PrivateData() noexcept | |||
| : sampleRate(d_lastUiSampleRate), | |||
| parameterOffset(0), | |||
| editParamCallbackFunc(nullptr), | |||
| setParamCallbackFunc(nullptr), | |||
| setStateCallbackFunc(nullptr), | |||
| sendNoteCallbackFunc(nullptr), | |||
| uiResizeCallbackFunc(nullptr), | |||
| ptr(nullptr) | |||
| { | |||
| assert(sampleRate != 0.0); | |||
| #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 uiResizeCallback(const unsigned int width, const unsigned int height) | |||
| { | |||
| if (uiResizeCallbackFunc != nullptr) | |||
| uiResizeCallbackFunc(ptr, width, height); | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // 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 uiResizeFunc uiResizeCall) | |||
| : glApp(), | |||
| glWindow(glApp, winId), | |||
| fUi(createUI()), | |||
| fData((fUi != nullptr) ? fUi->pData : nullptr) | |||
| { | |||
| assert(fUi != nullptr); | |||
| if (fUi == nullptr) | |||
| return; | |||
| fData->ptr = ptr; | |||
| fData->editParamCallbackFunc = editParamCall; | |||
| fData->setParamCallbackFunc = setParamCall; | |||
| fData->setStateCallbackFunc = setStateCall; | |||
| fData->sendNoteCallbackFunc = sendNoteCall; | |||
| fData->uiResizeCallbackFunc = uiResizeCall; | |||
| glWindow.setSize(fUi->d_getWidth(), fUi->d_getHeight()); | |||
| glWindow.setResizable(false); | |||
| } | |||
| ~UIExporter() | |||
| { | |||
| delete fUi; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| const char* getName() const noexcept | |||
| { | |||
| return (fUi != nullptr) ? fUi->d_getName() : ""; | |||
| } | |||
| unsigned int getWidth() const noexcept | |||
| { | |||
| return (fUi != nullptr) ? fUi->d_getWidth() : 0; | |||
| } | |||
| unsigned int getHeight() const noexcept | |||
| { | |||
| return (fUi != nullptr) ? fUi->d_getHeight() : 0; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| uint32_t getParameterOffset() const noexcept | |||
| { | |||
| return (fData != nullptr) ? fData->parameterOffset : 0; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| void parameterChanged(const uint32_t index, const float value) | |||
| { | |||
| if (fUi != nullptr) | |||
| fUi->d_parameterChanged(index, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| void programChanged(const uint32_t index) | |||
| { | |||
| if (fUi != nullptr) | |||
| fUi->d_programChanged(index); | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void stateChanged(const char* const key, const char* const value) | |||
| { | |||
| if (fUi != nullptr) | |||
| fUi->d_stateChanged(key, value); | |||
| } | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| bool idle() | |||
| { | |||
| if (fUi != nullptr) | |||
| fUi->d_uiIdle(); | |||
| glApp.idle(); | |||
| return ! glApp.isQuiting(); | |||
| } | |||
| void quit() | |||
| { | |||
| glWindow.close(); | |||
| glApp.quit(); | |||
| } | |||
| void setSize(const unsigned int width, const unsigned int height) | |||
| { | |||
| glWindow.setSize(width, height); | |||
| } | |||
| void setTitle(const char* const uiTitle) | |||
| { | |||
| glWindow.setTitle(uiTitle); | |||
| } | |||
| void setVisible(const bool yesNo) | |||
| { | |||
| glWindow.setVisible(yesNo); | |||
| } | |||
| private: | |||
| // ------------------------------------------------------------------- | |||
| // DGL Application and Window for this plugin | |||
| DGL::App glApp; | |||
| DGL::Window glWindow; | |||
| // ------------------------------------------------------------------- | |||
| // private members accessed by DistrhoPlugin class | |||
| UI* const fUi; | |||
| UI::PrivateData* const fData; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_UI_INTERNAL_HPP_INCLUDED | |||
| @@ -0,0 +1,333 @@ | |||
| /* | |||
| * DISTRHO Plugin Toolkit (DPT) | |||
| * Copyright (C) 2012-2013 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 | |||