Browse Source

Initial commit

gh-pages
falkTX 11 years ago
commit
f8670208e8
87 changed files with 18657 additions and 0 deletions
  1. +54
    -0
      dgl/App.hpp
  2. +143
    -0
      dgl/Base.hpp
  3. +142
    -0
      dgl/CairoWidget.hpp
  4. +133
    -0
      dgl/Geometry.hpp
  5. +66
    -0
      dgl/Image.hpp
  6. +56
    -0
      dgl/ImageAboutWindow.hpp
  7. +64
    -0
      dgl/ImageButton.hpp
  8. +89
    -0
      dgl/ImageKnob.hpp
  9. +85
    -0
      dgl/ImageSlider.hpp
  10. +67
    -0
      dgl/Makefile
  11. +79
    -0
      dgl/Makefile.mk
  12. +79
    -0
      dgl/StandaloneWindow.hpp
  13. +100
    -0
      dgl/Widget.hpp
  14. +85
    -0
      dgl/Window.hpp
  15. +111
    -0
      dgl/src/App.cpp
  16. +49
    -0
      dgl/src/Base.cpp
  17. +416
    -0
      dgl/src/Geometry.cpp
  18. +145
    -0
      dgl/src/Image.cpp
  19. +83
    -0
      dgl/src/ImageAboutWindow.cpp
  20. +173
    -0
      dgl/src/ImageButton.cpp
  21. +340
    -0
      dgl/src/ImageKnob.cpp
  22. +316
    -0
      dgl/src/ImageSlider.cpp
  23. +243
    -0
      dgl/src/Widget.cpp
  24. +805
    -0
      dgl/src/Window.cpp
  25. +353
    -0
      dgl/src/pugl/pugl.h
  26. +143
    -0
      dgl/src/pugl/pugl_internal.h
  27. +418
    -0
      dgl/src/pugl/pugl_osx.m
  28. +29
    -0
      dgl/src/pugl/pugl_osx_extended.h
  29. +64
    -0
      dgl/src/pugl/pugl_osx_extended.m
  30. +374
    -0
      dgl/src/pugl/pugl_win.cpp
  31. +414
    -0
      dgl/src/pugl/pugl_x11.c
  32. +239
    -0
      distrho/DistrhoPlugin.hpp
  33. +26
    -0
      distrho/DistrhoPluginMain.cpp
  34. +94
    -0
      distrho/DistrhoUI.hpp
  35. +25
    -0
      distrho/DistrhoUIMain.cpp
  36. +684
    -0
      distrho/DistrhoUtils.hpp
  37. +114
    -0
      distrho/src/DistrhoDefines.h
  38. +107
    -0
      distrho/src/DistrhoPlugin.cpp
  39. +380
    -0
      distrho/src/DistrhoPluginInternal.hpp
  40. +702
    -0
      distrho/src/DistrhoPluginLADSPA+DSSI.cpp
  41. +714
    -0
      distrho/src/DistrhoPluginLV2.cpp
  42. +367
    -0
      distrho/src/DistrhoPluginLV2export.cpp
  43. +963
    -0
      distrho/src/DistrhoPluginVST.cpp
  44. +89
    -0
      distrho/src/DistrhoUI.cpp
  45. +489
    -0
      distrho/src/DistrhoUIDSSI.cpp
  46. +249
    -0
      distrho/src/DistrhoUIInternal.hpp
  47. +333
    -0
      distrho/src/DistrhoUILV2.cpp
  48. +441
    -0
      distrho/src/dssi/dssi.h
  49. +272
    -0
      distrho/src/dssi/seq_event-compat.h
  50. +603
    -0
      distrho/src/ladspa/ladspa.h
  51. +619
    -0
      distrho/src/lv2/atom-forge.h
  52. +255
    -0
      distrho/src/lv2/atom-helpers.h
  53. +401
    -0
      distrho/src/lv2/atom-util.h
  54. +246
    -0
      distrho/src/lv2/atom.h
  55. +30
    -0
      distrho/src/lv2/buf-size.h
  56. +63
    -0
      distrho/src/lv2/data-access.h
  57. +144
    -0
      distrho/src/lv2/dynmanifest.h
  58. +263
    -0
      distrho/src/lv2/event-helpers.h
  59. +294
    -0
      distrho/src/lv2/event.h
  60. +37
    -0
      distrho/src/lv2/instance-access.h
  61. +99
    -0
      distrho/src/lv2/log.h
  62. +145
    -0
      distrho/src/lv2/logger.h
  63. +98
    -0
      distrho/src/lv2/lv2-midifunctions.h
  64. +175
    -0
      distrho/src/lv2/lv2-miditype.h
  65. +454
    -0
      distrho/src/lv2/lv2.h
  66. +107
    -0
      distrho/src/lv2/lv2_external_ui.h
  67. +174
    -0
      distrho/src/lv2/lv2_programs.h
  68. +105
    -0
      distrho/src/lv2/lv2_rtmempool.h
  69. +226
    -0
      distrho/src/lv2/midi.h
  70. +34
    -0
      distrho/src/lv2/morph.h
  71. +132
    -0
      distrho/src/lv2/options.h
  72. +49
    -0
      distrho/src/lv2/parameters.h
  73. +55
    -0
      distrho/src/lv2/patch.h
  74. +64
    -0
      distrho/src/lv2/port-groups.h
  75. +42
    -0
      distrho/src/lv2/port-props.h
  76. +34
    -0
      distrho/src/lv2/presets.h
  77. +72
    -0
      distrho/src/lv2/resize-port.h
  78. +352
    -0
      distrho/src/lv2/state.h
  79. +49
    -0
      distrho/src/lv2/time.h
  80. +407
    -0
      distrho/src/lv2/ui.h
  81. +62
    -0
      distrho/src/lv2/units.h
  82. +98
    -0
      distrho/src/lv2/uri-map.h
  83. +129
    -0
      distrho/src/lv2/urid.h
  84. +158
    -0
      distrho/src/lv2/worker.h
  85. +279
    -0
      distrho/src/vestige/aeffectx.h
  86. +16
    -0
      lv2-ttl-generator/GNUmakefile
  87. +81
    -0
      lv2-ttl-generator/lv2_ttl_generator.c

+ 54
- 0
dgl/App.hpp View File

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

+ 143
- 0
dgl/Base.hpp View File

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

+ 142
- 0
dgl/CairoWidget.hpp View File

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

+ 133
- 0
dgl/Geometry.hpp View File

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

+ 66
- 0
dgl/Image.hpp View File

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

+ 56
- 0
dgl/ImageAboutWindow.hpp View File

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

+ 64
- 0
dgl/ImageButton.hpp View File

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

+ 89
- 0
dgl/ImageKnob.hpp View File

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

+ 85
- 0
dgl/ImageSlider.hpp View File

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

+ 67
- 0
dgl/Makefile View File

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

# --------------------------------------------------------------

+ 79
- 0
dgl/Makefile.mk View File

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

# --------------------------------------------------------------

+ 79
- 0
dgl/StandaloneWindow.hpp View File

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

+ 100
- 0
dgl/Widget.hpp View File

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

+ 85
- 0
dgl/Window.hpp View File

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

+ 111
- 0
dgl/src/App.cpp View File

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

+ 49
- 0
dgl/src/Base.cpp View File

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

+ 416
- 0
dgl/src/Geometry.cpp View File

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


+ 145
- 0
dgl/src/Image.cpp View File

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

+ 83
- 0
dgl/src/ImageAboutWindow.cpp View File

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

+ 173
- 0
dgl/src/ImageButton.cpp View File

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

+ 340
- 0
dgl/src/ImageKnob.cpp View File

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

+ 316
- 0
dgl/src/ImageSlider.cpp View File

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

+ 243
- 0
dgl/src/Widget.cpp View File

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

+ 805
- 0
dgl/src/Window.cpp View File

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

+ 353
- 0
dgl/src/pugl/pugl.h View File

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

+ 143
- 0
dgl/src/pugl/pugl_internal.h View File

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

+ 418
- 0
dgl/src/pugl/pugl_osx.m View File

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

+ 29
- 0
dgl/src/pugl/pugl_osx_extended.h View File

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

+ 64
- 0
dgl/src/pugl/pugl_osx_extended.m View File

@@ -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];
}

+ 374
- 0
dgl/src/pugl/pugl_win.cpp View File

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

+ 414
- 0
dgl/src/pugl/pugl_x11.c View File

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

+ 239
- 0
distrho/DistrhoPlugin.hpp View File

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

+ 26
- 0
distrho/DistrhoPluginMain.cpp View File

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

+ 94
- 0
distrho/DistrhoUI.hpp View File

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

+ 25
- 0
distrho/DistrhoUIMain.cpp View File

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

+ 684
- 0
distrho/DistrhoUtils.hpp View File

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

+ 114
- 0
distrho/src/DistrhoDefines.h View File

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

+ 107
- 0
distrho/src/DistrhoPlugin.cpp View File

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

+ 380
- 0
distrho/src/DistrhoPluginInternal.hpp View File

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

+ 702
- 0
distrho/src/DistrhoPluginLADSPA+DSSI.cpp View File

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

// -----------------------------------------------------------------------

+ 714
- 0
distrho/src/DistrhoPluginLV2.cpp View File

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

// -----------------------------------------------------------------------

+ 367
- 0
distrho/src/DistrhoPluginLV2export.cpp View File

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

+ 963
- 0
distrho/src/DistrhoPluginVST.cpp View File

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

+ 89
- 0
distrho/src/DistrhoUI.cpp View File

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

+ 489
- 0
distrho/src/DistrhoUIDSSI.cpp View File

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

+ 249
- 0
distrho/src/DistrhoUIInternal.hpp View File

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

+ 333
- 0
distrho/src/DistrhoUILV2.cpp View File

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