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