From d144aae73090969fa0116291182d165e4272003c Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 2 Nov 2012 11:22:37 +0000 Subject: [PATCH] Import DISTRHO-Plugin-Toolkit code, will be used for Carla plugin --- c++/carla-backend/DistrhoPluginInfo.h | 35 + .../distrho-plugin-toolkit/DistrhoPlugin.h | 188 +++ .../DistrhoPluginMain.cpp | 28 + .../distrho-plugin-toolkit/DistrhoUI.h | 92 + .../distrho-plugin-toolkit/DistrhoUIMain.cpp | 35 + .../distrho-plugin-toolkit/DistrhoUIOpenGL.h | 131 ++ .../DistrhoUIOpenGLExt.h | 306 ++++ .../distrho-plugin-toolkit/DistrhoUIQt4.h | 74 + .../distrho-plugin-toolkit/DistrhoUtils.h | 256 +++ .../distrho-plugin-toolkit/distrho.doxygen | 289 ++++ .../src/DistrhoDefines.h | 91 + .../src/DistrhoPlugin.cpp | 95 ++ .../src/DistrhoPluginInternal.h | 337 ++++ .../src/DistrhoPluginJACK.cpp | 366 ++++ .../src/DistrhoPluginLADSPA+DSSI.cpp | 690 ++++++++ .../src/DistrhoPluginLV2.cpp | 597 +++++++ .../src/DistrhoPluginLV2export.cpp | 347 ++++ .../src/DistrhoPluginVST.cpp | 724 ++++++++ .../distrho-plugin-toolkit/src/DistrhoUI.cpp | 104 ++ .../src/DistrhoUIDSSI.cpp | 612 +++++++ .../src/DistrhoUIInternal.h | 548 ++++++ .../src/DistrhoUILV2.cpp | 372 +++++ .../src/DistrhoUIOpenGL.cpp | 80 + .../src/DistrhoUIOpenGLExt.cpp | 1486 +++++++++++++++++ .../src/DistrhoUIQt4.cpp | 75 + .../distrho-plugin-toolkit/src/dssi | 1 + .../distrho-plugin-toolkit/src/ladspa | 1 + .../distrho-plugin-toolkit/src/lv2-sdk | 1 + .../distrho-plugin-toolkit/src/pugl/pugl.h | 346 ++++ .../src/pugl/pugl_internal.h | 132 ++ .../src/pugl/pugl_osx.m | 325 ++++ .../src/pugl/pugl_win.cpp | 339 ++++ .../src/pugl/pugl_x11.c | 384 +++++ 33 files changed, 9487 insertions(+) create mode 100644 c++/carla-backend/DistrhoPluginInfo.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp create mode 120000 c++/carla-backend/distrho-plugin-toolkit/src/dssi create mode 120000 c++/carla-backend/distrho-plugin-toolkit/src/ladspa create mode 120000 c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp create mode 100644 c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c diff --git a/c++/carla-backend/DistrhoPluginInfo.h b/c++/carla-backend/DistrhoPluginInfo.h new file mode 100644 index 0000000..0c5d9b6 --- /dev/null +++ b/c++/carla-backend/DistrhoPluginInfo.h @@ -0,0 +1,35 @@ +/* + * Carla Backend + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the COPYING file + */ + +#ifndef __DISTRHO_PLUGIN_INFO_H__ +#define __DISTRHO_PLUGIN_INFO_H__ + +#define DISTRHO_PLUGIN_NAME "Carla" + +#define DISTRHO_PLUGIN_HAS_UI 0 +#define DISTRHO_PLUGIN_IS_SYNTH 1 + +#define DISTRHO_PLUGIN_NUM_INPUTS 2 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 + +#define DISTRHO_PLUGIN_WANT_LATENCY 1 +#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 +#define DISTRHO_PLUGIN_WANT_STATE 1 + +#define DISTRHO_PLUGIN_URI "http://kxstudio.sf.net/carla" + +#endif // __DISTRHO_PLUGIN_INFO_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h new file mode 100644 index 0000000..3e5be6a --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoPlugin.h @@ -0,0 +1,188 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_PLUGIN_H__ +#define __DISTRHO_PLUGIN_H__ + +#include "DistrhoUtils.h" + +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; + float step; + float stepSmall; + float stepLarge; + + ParameterRanges() + : def(0.0f), + min(0.0f), + max(1.0f), + step(0.001f), + stepSmall(0.00001f), + stepLarge(0.01f) {} + + ParameterRanges(float def, float min, float max) + : step(0.001f), + stepSmall(0.00001f), + stepLarge(0.01f) + { + this->def = def; + this->min = min; + this->max = max; + } + + ParameterRanges(float def, float min, float max, float step, float stepSmall, float stepLarge) + { + this->def = def; + this->min = min; + this->max = max; + this->step = step; + this->stepSmall = stepSmall; + this->stepLarge = stepLarge; + } + + void fixRange(float& value) const + { + if (value < min) + value = min; + else if (value > max) + value = max; + } +}; + +// Parameter +struct Parameter { + uint32_t hints; + d_string name; + d_string symbol; + d_string unit; + ParameterRanges ranges; + + Parameter() + : hints(0x0), + name(nullptr), + symbol(nullptr), + unit(nullptr) {} +}; + +// MidiEvent +struct MidiEvent { + uint32_t frame; + uint8_t buffer[3]; + + MidiEvent() +#if 0 // FIXME - code below is valid C++11 + : frame(0), + buffer{0} {} +#else + : frame(0) { buffer[0] = buffer[1] = buffer[2] = 0; } +#endif +}; + +// TimePos +struct TimePos { + double bpm; + + TimePos() + : bpm(120.0) {} +}; + +// ------------------------------------------------- + +struct PluginPrivateData; + +class Plugin +{ +public: + Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount); + virtual ~Plugin(); + + // --------------------------------------------- + + // Host state + uint32_t d_bufferSize() const; + double d_sampleRate() const; + const TimePos* d_timePos() const; +#if DISTRHO_PLUGIN_WANT_LATENCY + void d_setLatency(uint32_t samples); +#endif + + // --------------------------------------------- + +protected: + // Information + virtual const char* d_name() { return DISTRHO_PLUGIN_NAME; } + virtual const char* d_label() = 0; + virtual const char* d_maker() = 0; + virtual const char* d_license() = 0; + virtual uint32_t d_version() = 0; + virtual long d_uniqueId() = 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_parameterValue(uint32_t index) = 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() = 0; + virtual void d_deactivate() = 0; + virtual void d_run(const float** inputs, float** outputs, uint32_t frames, uint32_t midiEventCount, const MidiEvent* midiEvents) = 0; + + // Callbacks + virtual void d_bufferSizeChanged(uint32_t newBufferSize); + + // --------------------------------------------- + +private: + PluginPrivateData* data; + friend class PluginInternal; +}; + +Plugin* createPlugin(); + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // __DISTRHO_PLUGIN_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp b/c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp new file mode 100644 index 0000000..056553f --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoPluginMain.cpp @@ -0,0 +1,28 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "src/DistrhoPlugin.cpp" + +#if defined(DISTRHO_PLUGIN_TARGET_JACK) +# include "src/DistrhoPluginJACK.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI) +# include "src/DistrhoPluginLADSPA+DSSI.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) +# include "src/DistrhoPluginLV2.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_VST) +# include "src/DistrhoPluginVST.cpp" +#endif diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h new file mode 100644 index 0000000..4774990 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUI.h @@ -0,0 +1,92 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UI_H__ +#define __DISTRHO_UI_H__ + +#include "DistrhoUtils.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +struct UIPrivateData; + +class UI +{ +public: + UI(); + virtual ~UI(); + + // --------------------------------------------- + + // Host DSP State + double d_sampleRate() const; + void d_setParameterValue(uint32_t index, float value); +#if DISTRHO_PLUGIN_WANT_STATE + void d_setState(const char* key, const char* value); +#endif + + // Host UI State + void d_uiEditParameter(uint32_t index, bool started); +#if DISTRHO_PLUGIN_IS_SYNTH + void d_uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity); +#endif + void d_uiResize(unsigned int width, unsigned int height); + + // --------------------------------------------- + +protected: + // Information + virtual unsigned int d_width() = 0; + virtual unsigned int d_height() = 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 +#if DISTRHO_PLUGIN_IS_SYNTH + virtual void d_uiNoteReceived(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity); +#endif + + // UI Callbacks + virtual void d_uiIdle() = 0; + + // --------------------------------------------- + +private: + UIPrivateData* data; + friend class UIInternal; +#ifdef DISTRHO_UI_QT4 + friend class Qt4UI; +#else + friend class OpenGLUI; + friend class OpenGLExtUI; +#endif +}; + +UI* createUI(); + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // __DISTRHO_UI_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp new file mode 100644 index 0000000..37040a6 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIMain.cpp @@ -0,0 +1,35 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "src/DistrhoUI.cpp" + +#if defined(DISTRHO_PLUGIN_TARGET_JACK) +// nothing +#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) +# include "src/DistrhoUIDSSI.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) +# include "src/DistrhoUILV2.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_VST) +// nothing +#endif + +#ifdef DISTRHO_UI_QT4 +# include "src/DistrhoUIQt4.cpp" +#else +# include "src/DistrhoUIOpenGL.cpp" +# include "src/DistrhoUIOpenGLExt.cpp" +#endif diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h new file mode 100644 index 0000000..50168d4 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGL.h @@ -0,0 +1,131 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UI_OPENGL_H__ +#define __DISTRHO_UI_OPENGL_H__ + +#include "src/DistrhoDefines.h" + +#ifdef DISTRHO_UI_OPENGL + +#include "DistrhoUI.h" + +#if DISTRHO_OS_MAC +# include +#else +# include +#endif + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +enum Char { + CHAR_BACKSPACE = 0x08, + CHAR_ESCAPE = 0x1B, + CHAR_DELETE = 0x7F +}; + +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 +}; + +enum Mod { + MOD_SHIFT = 1 << 0, /**< Shift key */ + MOD_CTRL = 1 << 1, /**< Control key */ + MOD_ALT = 1 << 2, /**< Alt/Option key */ + MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ +}; + +// ------------------------------------------------- + +class OpenGLUI : public UI +{ +public: + OpenGLUI(); + virtual ~OpenGLUI(); + + // --------------------------------------------- + + // Host UI State (OpenGL) + int d_uiGetModifiers(); + void d_uiIgnoreKeyRepeat(bool ignore); + void d_uiRepaint(); + + // --------------------------------------------- + +protected: + // Information + virtual unsigned int d_width() = 0; + virtual unsigned int d_height() = 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 + virtual void d_uiIdle(); + virtual void d_onInit() = 0; + virtual void d_onDisplay() = 0; + virtual void d_onKeyboard(bool press, uint32_t key) = 0; + virtual void d_onMotion(int x, int y) = 0; + virtual void d_onMouse(int button, bool press, int x, int y) = 0; + virtual void d_onReshape(int width, int height) = 0; + virtual void d_onScroll(float dx, float dy) = 0; + virtual void d_onSpecial(bool press, Key key) = 0; + virtual void d_onClose() = 0; + +private: + friend class UIInternal; +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_OPENGL + +#endif // __DISTRHO_UI_OPENGL_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h new file mode 100644 index 0000000..b710acb --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIOpenGLExt.h @@ -0,0 +1,306 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UI_OPENGL_EXT_H__ +#define __DISTRHO_UI_OPENGL_EXT_H__ + +#include "src/DistrhoDefines.h" + +#ifdef DISTRHO_UI_OPENGL + +#include "DistrhoUIOpenGL.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +class Point +{ +public: + Point(int x, int y); + Point(const Point& pos); + + int getX() const; + int getY() const; + + void setX(int x); + void setY(int y); + + Point& operator=(const Point& pos); + Point& operator+=(const Point& pos); + Point& operator-=(const Point& pos); + bool operator==(const Point& pos) const; + bool operator!=(const Point& pos) const; + +private: + int _x, _y; + friend class Rectangle; +}; + +class Size +{ +public: + Size(int width, int height); + Size(const Size& size); + + int getWidth() const; + int getHeight() const; + + void setWidth(int width); + void setHeight(int height); + + Size& operator=(const Size& size); + Size& operator+=(const Size& size); + Size& operator-=(const Size& size); + Size& operator*=(int m); + Size& operator/=(int d); + Size& operator*=(float m); + Size& operator/=(float d); + +private: + int _width, _height; + friend class Rectangle; +}; + +class Rectangle +{ +public: + Rectangle(int x, int y, int width, int height); + Rectangle(int x, int y, const Size& size); + Rectangle(const Point& pos, int width, int height); + Rectangle(const Point& pos, const Size& size); + Rectangle(const Rectangle& rect); + + int getX() const; + int getY() const; + int getWidth() const; + int getHeight() const; + + const Point& getPos() const; + const Size& getSize() const; + + bool contains(int x, int y) const; + bool contains(const Point& pos) const; + bool containsX(int x) const; + bool containsY(int y) const; + + void setX(int x); + void setY(int y); + void setPos(int x, int y); + void setPos(const Point& pos); + + void move(int x, int y); + void move(const Point& pos); + + void setWidth(int width); + void setHeight(int height); + void setSize(int width, int height); + void setSize(const Size& size); + + void grow(int m); + void grow(float m); + void grow(int width, int height); + void grow(const Size& size); + + void shrink(int m); + void shrink(float m); + void shrink(int width, int height); + void shrink(const Size& size); + + Rectangle& operator=(const Rectangle& rect); + Rectangle& operator+=(const Point& pos); + Rectangle& operator-=(const Point& pos); + Rectangle& operator+=(const Size& size); + Rectangle& operator-=(const Size& size); + +private: + Point _pos; + Size _size; +}; + +// ------------------------------------------------- + +class Image +{ +public: + Image(const char* data, int width, int height, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE); + Image(const char* data, const Size& size, GLenum format = GL_BGRA, GLenum type = GL_UNSIGNED_BYTE); + Image(const Image& image); + + bool isValid() const; + + int getWidth() const; + int getHeight() const; + const Size& getSize() const; + + const char* getData() const; + GLenum getFormat() const; + GLenum getType() const; + + Image& operator=(const Image& image); + +private: + const char* _data; + Size _size; + GLenum _format; + GLenum _type; + friend class OpenGLExtUI; +}; + +class ImageButton +{ +public: + ImageButton(const Image& imageNormal, const Image& imageHover, const Image& imageDown, const Point& pos); + ImageButton(const ImageButton& imageButton); + + int getWidth() const; + int getHeight() const; + const Size& getSize() const; + + ImageButton& operator=(const ImageButton& imageButton); + +private: + Image _imageNormal; + Image _imageHover; + Image _imageDown; + Image* _curImage; + Point _pos; + Rectangle _area; + friend class OpenGLExtUI; +}; + +class ImageKnob +{ +public: + enum Orientation { + Horizontal, + Vertical + }; + + ImageKnob(const Image& image, const Point& pos, Orientation orientation = Vertical); + ImageKnob(const ImageKnob& imageKnob); + + void setOrientation(Orientation orientation); + void setRange(float min, float max); + void setValue(float value); + + ImageKnob& operator=(const ImageKnob& slider); + +private: + Image _image; + Point _pos; + Orientation _orientation; + bool _isVertical; + int _layerSize; + int _layerCount; + Rectangle _area; + float _min, _max, _value; + friend class OpenGLExtUI; +}; + +class ImageSlider +{ +public: + ImageSlider(const Image& image, const Point& startPos, const Point& endPos); + ImageSlider(const ImageSlider& imageSlider); + + int getWidth() const; + int getHeight() const; + + void setRange(float min, float max); + void setValue(float value); + + ImageSlider& operator=(const ImageSlider& slider); + +private: + Image _image; + Point _startPos; + Point _endPos; + Rectangle _area; + float _min, _max, _value; + friend class OpenGLExtUI; +}; + +// ------------------------------------------------- + +struct OpenGLExtUIPrivateData; + +class OpenGLExtUI : public OpenGLUI +{ +public: + OpenGLExtUI(); + virtual ~OpenGLExtUI(); + + // --------------------------------------------- + +protected: + // Information + virtual unsigned int d_width() = 0; + virtual unsigned int d_height() = 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 + virtual void d_uiIdle(); + + // Extended Calls + void setBackgroundImage(const Image& image); + + void addImageButton(ImageButton* button); + void addImageKnob(ImageKnob* knob); + void addImageSlider(ImageSlider* slider); + + void showImageModalDialog(const Image& image, const char* title); + + // Extended Callbacks + virtual void imageButtonClicked(ImageButton* button); + virtual void imageKnobDragStarted(ImageKnob* knob); + virtual void imageKnobDragFinished(ImageKnob* knob); + virtual void imageKnobValueChanged(ImageKnob* knob, float value); + virtual void imageSliderDragStarted(ImageSlider* slider); + virtual void imageSliderDragFinished(ImageSlider* slider); + virtual void imageSliderValueChanged(ImageSlider* slider, float value); + +private: + OpenGLExtUIPrivateData* data; + + // Implemented internally + void d_onInit(); + void d_onDisplay(); + void d_onKeyboard(bool press, uint32_t key); + void d_onMotion(int x, int y); + void d_onMouse(int button, bool press, int x, int y); + void d_onReshape(int width, int height); + void d_onScroll(float dx, float dy); + void d_onSpecial(bool press, Key key); + void d_onClose(); +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_OPENGL + +#endif // __DISTRHO_UI_OPENGL_EXT_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h new file mode 100644 index 0000000..1a9f20d --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUIQt4.h @@ -0,0 +1,74 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UI_QT4_H__ +#define __DISTRHO_UI_QT4_H__ + +#include "src/DistrhoDefines.h" + +#ifdef DISTRHO_UI_QT4 + +#include "DistrhoUI.h" + +#include + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +class Qt4UI : public UI, + public QWidget +{ +public: + Qt4UI(); + virtual ~Qt4UI(); + + // --------------------------------------------- + +protected: + // Information + virtual bool d_resizable() { return false; } + virtual int d_minimumWidth() { return 100; } + virtual int d_minimumHeight() { return 100; } + + // 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 + virtual void d_uiIdle(); + + // Implement resize internally + unsigned int d_width() { return width(); } + unsigned int d_height() { return height(); } + +private: + friend class UIInternal; +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_QT4 + +#endif // __DISTRHO_UI_QT4_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h new file mode 100644 index 0000000..5d082c6 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/DistrhoUtils.h @@ -0,0 +1,256 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UTILS_H__ +#define __DISTRHO_UTILS_H__ + +#include "src/DistrhoDefines.h" + +#if DISTRHO_OS_WINDOWS +# include +#else +# include +#endif + +#include +#include +#include +#include + +// ------------------------------------------------- + +inline +float d_absf(float value) +{ + return (value < 0.0f) ? -value : value; +} + +inline +float d_minf(float x, float y) +{ + return (x < y ? x : y); +} + +inline +float d_maxf(float x, float y) +{ + return (x > y ? x : y); +} + +inline +long d_cconst(int a, int b, int c, int d) +{ + return (a << 24) | (b << 16) | (c << 8) | (d << 0); +} + +inline +void d_sleep(unsigned int seconds) +{ +#if DISTRHO_OS_WINDOWS + Sleep(seconds * 1000); +#else + sleep(seconds); +#endif +} + +inline +void d_msleep(unsigned int mseconds) +{ +#if DISTRHO_OS_WINDOWS + Sleep(mseconds); +#else + usleep(mseconds * 1000); +#endif +} + +inline +void d_usleep(unsigned int useconds) +{ +#if DISTRHO_OS_WINDOWS + Sleep(useconds / 1000); +#else + usleep(useconds); +#endif +} + +inline +void d_setenv(const char* key, const char* value) +{ +#if DISTRHO_OS_WINDOWS + SetEnvironmentVariableA(key, value); +#else + setenv(key, value, 1); +#endif +} + +// ------------------------------------------------- + +class d_string +{ +public: + d_string() + { + buffer = strdup(""); + } + + d_string(const char* strBuf) + { + buffer = strdup(strBuf ? strBuf : ""); + } + + d_string(const d_string& str) + { + buffer = strdup(str.buffer); + } + + d_string(int value) + { + size_t strBufSize = abs(value/10) + 3; + char strBuf[strBufSize]; + snprintf(strBuf, strBufSize, "%d", value); + + buffer = strdup(strBuf); + } + + d_string(unsigned int value) + { + size_t strBufSize = value/10 + 2; + char strBuf[strBufSize]; + snprintf(strBuf, strBufSize, "%u", value); + + buffer = strdup(strBuf); + } + + d_string(float value) + { + char strBuf[255]; + snprintf(strBuf, 255, "%f", value); + + buffer = strdup(strBuf); + } + + ~d_string() + { + free(buffer); + } + + size_t length() const + { + return strlen(buffer); + } + + bool isEmpty() const + { + return (*buffer == 0); + } + + // --------------------------------------------- + + operator const char*() const + { + return buffer; + } + + bool operator==(const char* strBuf) const + { + return (strcmp(buffer, strBuf) == 0); + } + + bool operator==(const d_string& str) const + { + return operator==(str.buffer); + } + + bool operator!=(const char* strBuf) const + { + return !operator==(strBuf); + } + + bool operator!=(const d_string& str) const + { + return !operator==(str.buffer); + } + + d_string& operator=(const char* strBuf) + { + free(buffer); + + buffer = strdup(strBuf); + + return *this; + } + + d_string& operator=(const d_string& str) + { + return operator=(str.buffer); + } + + d_string& operator+=(const char* strBuf) + { + size_t newBufSize = strlen(buffer) + strlen(strBuf) + 1; + char newBuf[newBufSize]; + + strcpy(newBuf, buffer); + strcat(newBuf, strBuf); + free(buffer); + + buffer = strdup(newBuf); + + return *this; + } + + d_string& operator+=(const d_string& str) + { + return operator+=(str.buffer); + } + + d_string operator+(const char* strBuf) + { + size_t newBufSize = strlen(buffer) + strlen(strBuf) + 1; + char newBuf[newBufSize]; + + strcpy(newBuf, buffer); + strcat(newBuf, strBuf); + + return d_string(newBuf); + } + + d_string operator+(const d_string& str) + { + return operator+(str.buffer); + } + +private: + char* buffer; +}; + +static inline +d_string operator+(const char* strBufBefore, const d_string& strAfter) +{ + const char* strBufAfter = (const char*)strAfter; + size_t newBufSize = strlen(strBufBefore) + strlen(strBufAfter) + 1; + char newBuf[newBufSize]; + + strcpy(newBuf, strBufBefore); + strcat(newBuf, strBufAfter); + + return d_string(newBuf); +} + +// ------------------------------------------------- + +#endif // __DISTRHO_UTILS_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen b/c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen new file mode 100644 index 0000000..f59a49a --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/distrho.doxygen @@ -0,0 +1,289 @@ +# Doxyfile 1.7.6.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "DISTRHO Plugin Toolkit" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = doxygen +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +USE_INLINE_TREES = NO +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_RELPATH = http://www.mathjax.org/mathjax +MATHJAX_EXTENSIONS = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN \ + DISTRHO_PLUGIN_TARGET_LADSPA DISTRHO_PLUGIN_TARGET_DSSI DISTRHO_PLUGIN_TARGET_LV2 DISTRHO_PLUGIN_TARGET_VST \ + DISTRHO_PLUGIN_HAS_UI DISTRHO_PLUGIN_IS_SYNTH DISTRHO_PLUGIN_WANT_PROGRAMS DISTRHO_PLUGIN_WANT_STATE +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h new file mode 100644 index 0000000..6b71ca9 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoDefines.h @@ -0,0 +1,91 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_DEFINES_H__ +#define __DISTRHO_DEFINES_H__ + +#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 + +#if defined(__WIN32__) || defined(__WIN64__) +# define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) +# define DISTRHO_OS_WINDOWS 1 +# define DISTRHO_DLL_EXT "dll" +#else +# define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) +# if defined(__APPLE__) +# define DISTRHO_OS_MAC 1 +# define DISTRHO_DLL_EXT "dylib" +# elif defined(__HAIKU__) +# define DISTRHO_OS_HAIKU 1 +# define DISTRHO_DLL_EXT "so" +# elif defined(__linux__) +# define DISTRHO_OS_LINUX 1 +# define DISTRHO_DLL_EXT "so" +# else +# define DISTRHO_DLL_EXT "so" +# endif +#endif + +#ifndef DISTRHO_NO_NAMESPACE +# define START_NAMESPACE_DISTRHO namespace DISTRHO { +# define END_NAMESPACE_DISTRHO } +# define USE_NAMESPACE_DISTRHO using namespace DISTRHO; +#else +# define START_NAMESPACE_DISTRHO +# define END_NAMESPACE_DISTRHO +# define USE_NAMESPACE_DISTRHO +#endif + +#ifndef DISTRHO_UI_QT4 +# define DISTRHO_UI_OPENGL +#endif + +#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" + +#endif // __DISTRHO_DEFINES_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp new file mode 100644 index 0000000..32443af --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPlugin.cpp @@ -0,0 +1,95 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoPluginInternal.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +const d_string PluginInternal::fallbackString; +const ParameterRanges PluginInternal::fallbackRanges; + +// ------------------------------------------------- + +Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) +{ + data = new PluginPrivateData; + + if (parameterCount > 0) + { + data->parameterCount = parameterCount; + data->parameters = new Parameter [parameterCount]; + } + + if (programCount > 0) + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + data->programCount = programCount; + data->programNames = new d_string [programCount]; +#endif + } + + if (stateCount > 0) + { +#if DISTRHO_PLUGIN_WANT_STATE + data->stateCount = stateCount; + data->stateKeys = new d_string [stateCount]; +#endif + } +} + +Plugin::~Plugin() +{ + delete data; +} + +// ------------------------------------------------- +// Host state + +uint32_t Plugin::d_bufferSize() const +{ + return data->bufferSize; +} + +double Plugin::d_sampleRate() const +{ + return data->sampleRate; +} + +const TimePos* Plugin::d_timePos() const +{ + return &data->timePos; +} + +#if DISTRHO_PLUGIN_WANT_LATENCY +void Plugin::d_setLatency(uint32_t samples) +{ + data->latency = samples; +} +#endif + +// ------------------------------------------------- +// Callbacks + +void Plugin::d_bufferSizeChanged(uint32_t) +{ +} + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h new file mode 100644 index 0000000..660b0fb --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginInternal.h @@ -0,0 +1,337 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_PLUGIN_INTERNAL_H__ +#define __DISTRHO_PLUGIN_INTERNAL_H__ + +#include "DistrhoPlugin.h" + +#include + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +#define MAX_MIDI_EVENTS 512 + +static uint32_t d_lastBufferSize = 0; +static double d_lastSampleRate = 0.0; + +struct PluginPrivateData { + uint32_t bufferSize; + double sampleRate; + + 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 + TimePos timePos; + + PluginPrivateData() + : bufferSize(d_lastBufferSize), + sampleRate(d_lastSampleRate), + 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 + timePos() + { + assert(d_lastSampleRate != 0.0); + } + + ~PluginPrivateData() + { + if (parameterCount > 0 && parameters) + delete[] parameters; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (programCount > 0 && programNames) + delete[] programNames; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + if (stateCount > 0 && stateKeys) + delete[] stateKeys; +#endif + } +}; + +// ------------------------------------------------- + +class PluginInternal +{ +public: + PluginInternal() + : plugin(createPlugin()), + data(nullptr) + { + assert(plugin); + + if (! plugin) + return; + + data = plugin->data; + + for (uint32_t i=0; i < data->parameterCount; i++) + plugin->d_initParameter(i, data->parameters[i]); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + for (uint32_t i=0; i < data->programCount; i++) + plugin->d_initProgramName(i, data->programNames[i]); +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0; i < data->stateCount; i++) + plugin->d_initStateKey(i, data->stateKeys[i]); +#endif + } + + ~PluginInternal() + { + if (plugin) + delete plugin; + } + + // --------------------------------------------- + + const char* name() + { + assert(plugin); + return plugin ? plugin->d_name() : ""; + } + + const char* label() + { + assert(plugin); + return plugin ? plugin->d_label() : ""; + } + + const char* maker() + { + assert(plugin); + return plugin ? plugin->d_maker() : ""; + } + + const char* license() + { + assert(plugin); + return plugin ? plugin->d_license() : ""; + } + + uint32_t version() + { + assert(plugin); + return plugin ? plugin->d_version() : 1000; + } + + long uniqueId() + { + assert(plugin); + return plugin ? plugin->d_uniqueId() : 0; + } + + // --------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_LATENCY + uint32_t latency() const + { + assert(data); + return data ? data->latency : 0; + } +#endif + + uint32_t parameterCount() const + { + assert(data); + return data ? data->parameterCount : 0; + } + + uint32_t parameterHints(uint32_t index) const + { + assert(data && index < data->parameterCount); + return (data && index < data->parameterCount) ? data->parameters[index].hints : 0x0; + } + + bool parameterIsOutput(uint32_t index) const + { + uint32_t hints = parameterHints(index); + return bool(hints & PARAMETER_IS_OUTPUT); + } + + const d_string& parameterName(uint32_t index) const + { + assert(data && index < data->parameterCount); + return (data && index < data->parameterCount) ? data->parameters[index].name : fallbackString; + } + + const d_string& parameterSymbol(uint32_t index) const + { + assert(data && index < data->parameterCount); + return (data && index < data->parameterCount) ? data->parameters[index].symbol : fallbackString; + } + + const d_string& parameterUnit(uint32_t index) const + { + assert(data && index < data->parameterCount); + return (data && index < data->parameterCount) ? data->parameters[index].unit : fallbackString; + } + + const ParameterRanges* parameterRanges(uint32_t index) const + { + assert(data && index < data->parameterCount); + return (data && index < data->parameterCount) ? &data->parameters[index].ranges : &fallbackRanges; + } + + float parameterValue(uint32_t index) + { + assert(plugin && index < data->parameterCount); + return (plugin && index < data->parameterCount) ? plugin->d_parameterValue(index) : 0.0f; + } + + void setParameterValue(uint32_t index, float value) + { + assert(plugin && index < data->parameterCount); + + if (plugin && index < data->parameterCount) + plugin->d_setParameterValue(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + uint32_t programCount() const + { + assert(data); + return data ? data->programCount : 0; + } + + const d_string& programName(uint32_t index) const + { + assert(data && index < data->programCount); + return (data && index < data->programCount) ? data->programNames[index] : fallbackString; + } + + void setProgram(uint32_t index) + { + assert(plugin && index < data->programCount); + + if (plugin && index < data->programCount) + plugin->d_setProgram(index); + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + uint32_t stateCount() const + { + assert(data); + return data ? data->stateCount : 0; + } + + const d_string& stateKey(uint32_t index) const + { + assert(data && index < data->stateCount); + return (data && index < data->stateCount) ? data->stateKeys[index] : fallbackString; + } + + void setState(const char* key, const char* value) + { + assert(plugin && key && value); + + if (plugin && key && value) + plugin->d_setState(key, value); + } +#endif + + // --------------------------------------------- + + void activate() + { + assert(plugin); + + if (plugin) + plugin->d_activate(); + } + + void deactivate() + { + assert(plugin); + + if (plugin) + plugin->d_deactivate(); + } + + void run(const float** inputs, float** outputs, uint32_t frames, uint32_t midiEventCount, const MidiEvent* midiEvents) + { + assert(plugin && frames >= 2); + + if (plugin) + plugin->d_run(inputs, outputs, frames, midiEventCount, midiEvents); + } + + void setBufferSize(uint32_t bufferSize, bool callback = false) + { + assert(data && plugin && bufferSize >= 2); + + if (callback && data->bufferSize == bufferSize) + callback = false; + + if (data) + data->bufferSize = bufferSize; + + if (plugin && callback) + { + plugin->d_deactivate(); + plugin->d_bufferSizeChanged(bufferSize); + plugin->d_activate(); + } + } + + // --------------------------------------------- + +protected: + Plugin* const plugin; + PluginPrivateData* data; + +private: + static const d_string fallbackString; + static const ParameterRanges fallbackRanges; +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // __DISTRHO_PLUGIN_INTERNAL_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp new file mode 100644 index 0000000..a9947c0 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginJACK.cpp @@ -0,0 +1,366 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifdef DISTRHO_PLUGIN_TARGET_JACK + +#include "DistrhoDefines.h" + +#if ! DISTRHO_PLUGIN_HAS_UI +# error Standalone JACK mode requires UI +#endif + +#include "DistrhoPluginInternal.h" +#include "DistrhoUIInternal.h" + +#include +#include +#include + +#include +#include +#include +#include + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +class PluginJack : public QMainWindow +{ +public: + PluginJack(jack_client_t* client_) + : QMainWindow(nullptr), + widget(this), + settings("DISTRHO", DISTRHO_PLUGIN_NAME), + ui(this, widget.winId(), setParameterCallback, setStateCallback, nullptr, uiNoteCallback, uiResizeCallback), + client(client_) + { + setCentralWidget(&widget); + setFixedSize(ui.getWidth(), ui.getHeight()); + setWindowTitle(DISTRHO_PLUGIN_NAME); + + if (DISTRHO_PLUGIN_NUM_INPUTS > 0) + { + portsIn = new jack_port_t* [DISTRHO_PLUGIN_NUM_INPUTS]; + + for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + portsIn[i] = jack_port_register(client, d_string("Audio Input ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + } + else + portsIn = nullptr; + + if (DISTRHO_PLUGIN_NUM_OUTPUTS > 0) + { + portsOut = new jack_port_t* [DISTRHO_PLUGIN_NUM_OUTPUTS]; + + for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + portsOut[i] = jack_port_register(client, d_string("Audio Output ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + } + else + portsOut = nullptr; + +#if DISTRHO_PLUGIN_IS_SYNTH + portMidi = jack_port_register(client, "Midi Input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); +#endif + + jack_set_process_callback(client, jackProcessCallback, this); + + uiTimer = startTimer(30); + + // load settings + restoreGeometry(settings.value("Global/Geometry", QByteArray()).toByteArray()); + + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + bool ok; + float value = settings.value(QString("Parameters/%1").arg(i)).toFloat(&ok); + + if (ok) + { + plugin.setParameterValue(i, value); + ui.parameterChanged(i, value); + } + } + +#if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0; i < plugin.stateCount(); i++) + { + const char* key = plugin.stateKey(i); + QString stringValue(settings.value(key).toString()); + + if (! stringValue.isEmpty()) + ui.stateChanged(key, stringValue.toUtf8().constData()); + } +#endif + } + + ~PluginJack() + { + // save settings + settings.setValue("Global/Geometry", saveGeometry()); + + if (uiTimer) + killTimer(uiTimer); + + if (portsIn) + delete[] portsIn; + + if (portsOut) + delete[] portsOut; + } + + // --------------------------------------------- + +protected: + void setParameterValue(uint32_t index, float value) + { + plugin.setParameterValue(index, value); + settings.setValue(QString("Parameters/%1").arg(index), value); + } + +#if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* key, const char* value) + { + plugin.setState(key, value); + settings.setValue(key, value); + } +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + void uiNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { + // TODO + } +#endif + + void uiResize(unsigned int width, unsigned int height) + { + widget.setFixedSize(width, height); + setFixedSize(width, height); + } + + int jackProcess(jack_nframes_t nframes) + { + if (nframes <= 1) + return 1; + + // Check for updated bufferSize + if (nframes != d_lastBufferSize) + { + d_lastBufferSize = nframes; + plugin.setBufferSize(nframes, true); + } + + const float* ins[DISTRHO_PLUGIN_NUM_INPUTS]; + float* outs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + + for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + ins[i] = (float*)jack_port_get_buffer(portsIn[i], nframes); + + for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + outs[i] = (float*)jack_port_get_buffer(portsOut[i], nframes); + +#if DISTRHO_PLUGIN_IS_SYNTH + uint32_t midiEventCount = 0; + + void* mIn = jack_port_get_buffer(portMidi, nframes); + // TODO + + plugin.run(ins, outs, nframes, midiEventCount, midiEvents); +#else + plugin.run(ins, outs, nframes, 0, nullptr); +#endif + + return 0; + } + + // --------------------------------------------- + + void closeEvent(QCloseEvent* event) + { + QMainWindow::closeEvent(event); + + qApp->quit(); + } + + void timerEvent(QTimerEvent* event) + { + if (event->timerId() == uiTimer) + ui.idle(); + + QMainWindow::timerEvent(event); + } + + // --------------------------------------------- + +private: + // Qt4 stuff + int uiTimer; + QWidget widget; + QSettings settings; + + PluginInternal plugin; + UIInternal ui; + + jack_client_t* const client; + jack_port_t** portsIn; + jack_port_t** portsOut; + +#if DISTRHO_PLUGIN_IS_SYNTH + jack_port_t* portMidi; + MidiEvent midiEvents[MAX_MIDI_EVENTS]; +#endif + + // --------------------------------------------- + // Callbacks + + static void setParameterCallback(void* ptr, uint32_t index, float value) + { + PluginJack* _this_ = (PluginJack*)ptr; + assert(_this_); + + _this_->setParameterValue(index, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { +#if DISTRHO_PLUGIN_WANT_STATE + PluginJack* _this_ = (PluginJack*)ptr; + assert(_this_); + + _this_->setState(key, value); +#else + Q_UNUSED(ptr); + Q_UNUSED(key); + Q_UNUSED(value); +#endif + } + + static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { +#if DISTRHO_PLUGIN_IS_SYNTH + PluginJack* _this_ = (PluginJack*)ptr; + assert(_this_); + + _this_->uiNote(onOff, channel, note, velocity); +#else + Q_UNUSED(ptr); + Q_UNUSED(onOff); + Q_UNUSED(channel); + Q_UNUSED(note); + Q_UNUSED(velocity); +#endif + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + PluginJack* _this_ = (PluginJack*)ptr; + assert(_this_); + + _this_->uiResize(width, height); + } + + static int jackProcessCallback(jack_nframes_t nframes, void* arg) + { + PluginJack* _this_ = (PluginJack*)arg; + assert(_this_); + + return _this_->jackProcess(nframes); + } +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +std::string jack_status_get_error_string(const jack_status_t& status) +{ + std::string errorString; + + if (status & JackFailure) + errorString += "Overall operation failed;\n"; + if (status & JackInvalidOption) + errorString += "The operation contained an invalid or unsupported option;\n"; + if (status & JackNameNotUnique) + errorString += "The desired client name was not unique;\n"; + if (status & JackServerStarted) + errorString += "The JACK server was started as a result of this operation;\n"; + if (status & JackServerFailed) + errorString += "Unable to connect to the JACK server;\n"; + if (status & JackServerError) + errorString += "Communication error with the JACK server;\n"; + if (status & JackNoSuchClient) + errorString += "Requested client does not exist;\n"; + if (status & JackLoadFailure) + errorString += "Unable to load internal client;\n"; + if (status & JackInitFailure) + errorString += "Unable to initialize client;\n"; + if (status & JackShmFailure) + errorString += "Unable to access shared memory;\n"; + if (status & JackVersionError) + errorString += "Client's protocol version does not match;\n"; + if (status & JackBackendError) + errorString += "Backend Error;\n"; + if (status & JackClientZombie) + errorString += "Client is being shutdown against its will;\n"; + + if (errorString.size() > 0) + errorString.replace(errorString.size()-2, 2, "."); + + return errorString; +} + +int main(int argc, char* argv[]) +{ + USE_NAMESPACE_DISTRHO + QApplication app(argc, argv, true); + + jack_status_t status; + jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNullOption, &status); + + if (! client) + { + std::string errorString(jack_status_get_error_string(status)); + QMessageBox::critical(nullptr, app.translate(DISTRHO_PLUGIN_NAME, "Error"), + app.translate(DISTRHO_PLUGIN_NAME, + "Could not connect to JACK, possible reasons:\n" + "%1").arg(QString::fromStdString(errorString))); + return 1; + } + + d_lastBufferSize = jack_get_buffer_size(client); + d_lastSampleRate = jack_get_sample_rate(client); + setLastUiSampleRate(d_lastSampleRate); + + PluginJack plugin(client); + plugin.show(); + + jack_activate(client); + + int ret = app.exec(); + + jack_deactivate(client); + jack_client_close(client); + + return ret; +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_JACK diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp new file mode 100644 index 0000000..c8612d0 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLADSPA+DSSI.cpp @@ -0,0 +1,690 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#if defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI) + +#include "DistrhoPluginInternal.h" + +#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 + +#include + +typedef LADSPA_Data* LADSPA_DataPtr; +typedef const LADSPA_Data* LADSPA_DataConstPtr; +typedef std::vector LADSPA_DataVector; +typedef std::vector LADSPA_DataPtrVector; + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +class PluginLadspaDssi +{ +public: + PluginLadspaDssi() + : lastBufferSize(d_lastBufferSize), + lastSampleRate(d_lastSampleRate) + { + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + portAudioIns[i] = nullptr; + + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + portAudioOuts[i] = nullptr; + + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + portControls.push_back(nullptr); + lastControlValues.push_back(plugin.parameterValue(i)); + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + portLatency = nullptr; +#endif + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + portSampleRate = nullptr; +#endif + } + + ~PluginLadspaDssi() + { + portControls.clear(); + lastControlValues.clear(); + } + + void ladspa_activate() + { + plugin.activate(); + updateParameterOutputs(); + } + + void ladspa_deactivate() + { + plugin.deactivate(); + } + + void ladspa_connect_port(unsigned long port, LADSPA_DataPtr dataLocation) + { + unsigned long i, index = 0; + + for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + { + if (port == index++) + { + portAudioIns[i] = dataLocation; + return; + } + } + + for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + { + if (port == index++) + { + portAudioOuts[i] = dataLocation; + return; + } + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (port == index++) + { + portLatency = dataLocation; + return; + } +#endif + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + if (port == index++) + { + portSampleRate = dataLocation; + return; + } +#endif + + for (i=0; i < plugin.parameterCount(); i++) + { + if (port == index++) + { + portControls[i] = dataLocation; + return; + } + } + } + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# if DISTRHO_PLUGIN_WANT_STATE + char* dssi_configure(const char* key, const char* value) + { + if (strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) + return nullptr; + if (strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) + return nullptr; + + plugin.setState(key, value); + return nullptr; + } +# endif + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + const DSSI_Program_Descriptor* dssi_get_program(unsigned long index) + { + if (index >= plugin.programCount()) + return nullptr; + + static DSSI_Program_Descriptor desc; + desc.Bank = index / 128; + desc.Program = index % 128; + desc.Name = plugin.programName(index); + return &desc; + } + + void dssi_select_program(unsigned long bank, unsigned long program) + { + const unsigned long realProgram = bank * 128 + program; + + if (realProgram >= plugin.programCount()) + return; + + plugin.setProgram(realProgram); + + // Update parameters + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + if (! plugin.parameterIsOutput(i)) + { + lastControlValues[i] = plugin.parameterValue(i); + + if (portControls[i]) + *portControls[i] = lastControlValues[i]; + } + } + } +# endif + + void ladspa_run(unsigned long bufferSize) + { + dssi_run_synth(bufferSize, nullptr, 0); + } + + void dssi_run_synth(unsigned long bufferSize, snd_seq_event_t* events, unsigned long eventCount) +#else + void ladspa_run(unsigned long bufferSize) +#endif + { + if (bufferSize <= 1) + return; + + // Check for updated bufferSize + if (bufferSize != lastBufferSize) + { + lastBufferSize = bufferSize; + d_lastBufferSize = bufferSize; + plugin.setBufferSize(bufferSize, true); + } + + // Check for updated parameters + float curValue; + + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + curValue = *portControls[i]; + + if (lastControlValues[i] != curValue && ! plugin.parameterIsOutput(i)) + { + lastControlValues[i] = curValue; + plugin.setParameterValue(i, curValue); + } + } + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# if DISTRHO_PLUGIN_IS_SYNTH + // Get MIDI Events + uint32_t midiEventCount = 0; + + for (uint32_t i=0, j=0; i < eventCount && midiEventCount < MAX_MIDI_EVENTS; i++) + { + snd_seq_event_t* event = &events[i]; + memset(&midiEvents[midiEventCount], 0, sizeof(MidiEvent)); + + if (events[i].type == SND_SEQ_EVENT_NOTEON) + { + j = midiEventCount++; + midiEvents[j].frame = event->time.tick; + midiEvents[j].buffer[0] = 0x90 + event->data.note.channel; + midiEvents[j].buffer[1] = event->data.note.note; + midiEvents[j].buffer[2] = event->data.note.velocity; + } + else if (events[i].type == SND_SEQ_EVENT_NOTEOFF) + { + j = midiEventCount++; + midiEvents[j].frame = event->time.tick; + midiEvents[j].buffer[0] = 0x80 + event->data.note.channel; + midiEvents[j].buffer[1] = event->data.note.note; + } + else if (events[i].type == SND_SEQ_EVENT_KEYPRESS) + { + j = midiEventCount++; + midiEvents[j].frame = event->time.tick; + midiEvents[j].buffer[0] = 0xA0 + event->data.note.channel; + midiEvents[j].buffer[1] = event->data.note.note; + midiEvents[j].buffer[2] = event->data.note.velocity; + } + else if (events[i].type == SND_SEQ_EVENT_CONTROLLER) + { + j = midiEventCount++; + midiEvents[j].frame = event->time.tick; + midiEvents[j].buffer[0] = 0xB0 + event->data.control.channel; + midiEvents[j].buffer[1] = event->data.control.param; + midiEvents[j].buffer[2] = event->data.control.value; + } + else if (events[i].type == SND_SEQ_EVENT_CHANPRESS) + { + j = midiEventCount++; + midiEvents[j].frame = event->time.tick; + midiEvents[j].buffer[0] = 0xD0 + event->data.control.channel; + midiEvents[j].buffer[1] = event->data.control.value; + } + else if (events[i].type == SND_SEQ_EVENT_PITCHBEND) + { + // TODO + //j = midiEventCount++; + //midiEvents[j].frame = event->time.tick; + //midiEvents[j].buffer[0] = 0xE0 + event->data.control.channel; + //midiEvents[j].buffer[1] = 0; + //midiEvents[j].buffer[2] = 0; + } + } +# else + // unused + (void)events; + (void)eventCount; +# endif +#endif + + // Run plugin for this cycle +#if DISTRHO_PLUGIN_IS_SYNTH + plugin.run(portAudioIns, portAudioOuts, bufferSize, midiEventCount, midiEvents); +#else + plugin.run(portAudioIns, portAudioOuts, bufferSize, 0, nullptr); +#endif + + updateParameterOutputs(); + } + +private: + PluginInternal plugin; + + // LADSPA ports + LADSPA_DataConstPtr portAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; + LADSPA_DataPtr portAudioOuts[DISTRHO_PLUGIN_NUM_INPUTS]; + LADSPA_DataPtrVector portControls; +#if DISTRHO_PLUGIN_WANT_LATENCY + LADSPA_DataPtr portLatency; +#endif +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + LADSPA_DataPtr portSampleRate; +#endif + + // Temporary data + unsigned long lastBufferSize; + const double lastSampleRate; + LADSPA_DataVector lastControlValues; + +#if DISTRHO_PLUGIN_IS_SYNTH + MidiEvent midiEvents[MAX_MIDI_EVENTS]; +#endif + + void updateParameterOutputs() + { + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + if (plugin.parameterIsOutput(i)) + { + lastControlValues[i] = plugin.parameterValue(i); + + if (portControls[i]) + *portControls[i] = lastControlValues[i]; + } + } + +#if DISTRHO_PLUGIN_WANT_LATENCY + if (portLatency) + *portLatency = plugin.latency(); +#endif + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + if (portSampleRate) + *portSampleRate = lastSampleRate; +#endif + } +}; + +// ------------------------------------------------- + +static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, unsigned long sampleRate) +{ + if (d_lastBufferSize == 0) + d_lastBufferSize = 512; + d_lastSampleRate = sampleRate; + + return new PluginLadspaDssi(); +} + +static void ladspa_connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data* dataLocation) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->ladspa_connect_port(port, dataLocation); +} + +static void ladspa_activate(LADSPA_Handle instance) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->ladspa_activate(); +} + +static void ladspa_run(LADSPA_Handle instance, unsigned long sampleCount) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->ladspa_run(sampleCount); +} + +static void ladspa_deactivate(LADSPA_Handle instance) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->ladspa_deactivate(); +} + +static void ladspa_cleanup(LADSPA_Handle instance) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + delete plugin; +} + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +# if DISTRHO_PLUGIN_WANT_STATE +static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + return plugin->dssi_configure(key, value); +} +# endif + +# if DISTRHO_PLUGIN_WANT_PROGRAMS +static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, unsigned long index) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + return plugin->dssi_get_program(index); +} + +static void dssi_select_program(LADSPA_Handle instance, unsigned long bank, unsigned long program) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->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) +{ + PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; + assert(plugin); + + plugin->dssi_run_synth(sampleCount, events, eventCount); +} +# endif +#endif + +// ------------------------------------------------- + +static LADSPA_Descriptor ldescriptor = { + /* 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 descriptor = { + 1, + &ldescriptor, +# 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; + PluginInternal plugin; + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + // Get port count, init + unsigned long i, port = 0; + unsigned long portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.parameterCount(); +#if DISTRHO_PLUGIN_WANT_LATENCY + portCount += 1; +#endif +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + portCount += 1; // sample-rate +#endif + const char** portNames = new const char* [portCount]; + LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor [portCount]; + LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; + + // Set ports + for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++, port++) + { + char portName[24] = { 0 }; + sprintf(portName, "Audio Input %lu", i+1); + + portNames[port] = strdup(portName); + portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; + + portRangeHints[port].HintDescriptor = 0; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 1.0f; + } + + for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++, port++) + { + char portName[24] = { 0 }; + sprintf(portName, "Audio Output %lu", i+1); + + portNames[port] = strdup(portName); + portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; + + portRangeHints[port].HintDescriptor = 0; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 1.0f; + } + +#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 + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + // Set sample-rate port + portNames[port] = strdup("_sample-rate"); + portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; + portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; + portRangeHints[port].LowerBound = 0.0f; + portRangeHints[port].UpperBound = 512000.0f; + port++; +#endif + + for (i=0; i < plugin.parameterCount(); i++, port++) + { + portNames[port] = strdup(plugin.parameterName(i)); + portDescriptors[port] = LADSPA_PORT_CONTROL; + + if (plugin.parameterIsOutput(i)) + portDescriptors[port] |= LADSPA_PORT_OUTPUT; + else + portDescriptors[port] |= LADSPA_PORT_INPUT; + + { + const ParameterRanges* ranges = plugin.parameterRanges(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 + ranges->max/2; + const float middleLow = (ranges->min/2 + middleValue/2)/2 + middleValue/2; + const float middleHigh = (ranges->max/2 + middleValue/2)/2 + middleValue/2; + + 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; + } + } + + { + uint32_t hints = plugin.parameterHints(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 + ldescriptor.UniqueID = plugin.uniqueId(); + ldescriptor.Label = strdup(plugin.label()); + ldescriptor.Name = strdup(plugin.name()); + ldescriptor.Maker = strdup(plugin.maker()); + ldescriptor.Copyright = strdup(plugin.license()); + ldescriptor.PortCount = portCount; + ldescriptor.PortNames = portNames; + ldescriptor.PortDescriptors = portDescriptors; + ldescriptor.PortRangeHints = portRangeHints; + } + + ~DescriptorInitializer() + { + if (ldescriptor.Label) + free((void*)ldescriptor.Label); + + if (ldescriptor.Name) + free((void*)ldescriptor.Name); + + if (ldescriptor.Maker) + free((void*)ldescriptor.Maker); + + if (ldescriptor.Copyright) + free((void*)ldescriptor.Copyright); + + if (ldescriptor.PortDescriptors) + delete[] ldescriptor.PortDescriptors; + + if (ldescriptor.PortRangeHints) + delete[] ldescriptor.PortRangeHints; + + if (ldescriptor.PortNames) + { + for (unsigned long i=0; i < ldescriptor.PortCount; i++) + { + if (ldescriptor.PortNames[i]) + free((void*)ldescriptor.PortNames[i]); + } + + delete[] ldescriptor.PortNames; + } + } +}; + +static DescriptorInitializer init; + +END_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +DISTRHO_PLUGIN_EXPORT +const LADSPA_Descriptor* ladspa_descriptor(unsigned long index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &ldescriptor : nullptr; +} + +#ifdef DISTRHO_PLUGIN_TARGET_DSSI +DISTRHO_PLUGIN_EXPORT +const DSSI_Descriptor* dssi_descriptor(unsigned long index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &descriptor : nullptr; +} +#endif + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_LADSPA || DISTRHO_PLUGIN_TARGET_DSSI diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp new file mode 100644 index 0000000..1d32f17 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2.cpp @@ -0,0 +1,597 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifdef DISTRHO_PLUGIN_TARGET_LV2 + +#include "DistrhoPluginInternal.h" + +#include "lv2-sdk/lv2.h" +#include "lv2-sdk/atom.h" +#include "lv2-sdk/atom-util.h" +#include "lv2-sdk/midi.h" +#include "lv2-sdk/patch.h" +#include "lv2-sdk/programs.h" +#include "lv2-sdk/state.h" +#include "lv2-sdk/urid.h" +#include "lv2-sdk/worker.h" + +#include +#include + +#ifndef DISTRHO_PLUGIN_URI +# error DISTRHO_PLUGIN_URI undefined! +#endif + +#define DISTRHO_LV2_USE_EVENTS (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_STATE) +#define DISTRHO_LV2_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_STATE) + +typedef float* floatptr; +typedef std::vector floatVector; +typedef std::vector floatptrVector; + +typedef std::map stringMap; + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +class PluginLv2 +{ +public: + PluginLv2(const LV2_Feature* const* features) + : lastBufferSize(d_lastBufferSize), + lastSampleRate(d_lastSampleRate) + { + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + portAudioIns.push_back(nullptr); + + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + portAudioOuts.push_back(nullptr); + + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + portControls.push_back(nullptr); + lastControlValues.push_back(plugin.parameterValue(i)); + } + + portLatency = nullptr; +#if DISTRHO_PLUGIN_HAS_UI + portSampleRate = nullptr; +#endif + +#if DISTRHO_LV2_USE_EVENTS + portEventsIn = nullptr; + + // URIDs + uridMap = nullptr; + + uridIdAtomString = 0; +# if DISTRHO_PLUGIN_IS_SYNTH + uridIdMidiEvent = 0; +# endif +# if DISTRHO_PLUGIN_WANT_STATE + uridIdPatchMessage = 0; + workerSchedule = nullptr; +# endif + + for (uint32_t i=0; features[i]; i++) + { + if (strcmp(features[i]->URI, LV2_URID__map) == 0) + { + uridMap = (LV2_URID_Map*)features[i]->data; + + uridIdAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String); +# if DISTRHO_PLUGIN_IS_SYNTH + uridIdMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); +# endif +# if DISTRHO_PLUGIN_WANT_STATE + uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message); +# endif + } + else if (strcmp(features[i]->URI, LV2_WORKER__schedule) == 0) + { + workerSchedule = (LV2_Worker_Schedule*)features[i]->data; + } + } +#else + // unused + (void)features; +#endif + } + + ~PluginLv2() + { + portAudioIns.clear(); + portAudioOuts.clear(); + portControls.clear(); + lastControlValues.clear(); + +#if DISTRHO_PLUGIN_WANT_STATE + stateMap.clear(); +#endif + } + + void lv2_activate() + { + plugin.activate(); + updateParameterOutputs(); + } + + void lv2_deactivate() + { + plugin.deactivate(); + } + + void lv2_connect_port(uint32_t port, void* dataLocation) + { + uint32_t i, index = 0; + + for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + { + if (port == index++) + { + portAudioIns[i] = (floatptr)dataLocation; + return; + } + } + + for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + { + if (port == index++) + { + portAudioOuts[i] = (floatptr)dataLocation; + return; + } + } + + for (i=0; i < plugin.parameterCount(); i++) + { + if (port == index++) + { + portControls[i] = (floatptr)dataLocation; + return; + } + } + +#if DISTRHO_LV2_USE_EVENTS + if (port == index++) + { + portEventsIn = (LV2_Atom_Sequence*)dataLocation; + return; + } +#endif + + if (port == index++) + { + portLatency = (floatptr)dataLocation; + return; + } + +#if DISTRHO_PLUGIN_HAS_UI + if (port == index++) + { + portSampleRate = (floatptr)dataLocation; + return; + } +#endif + } + + void lv2_run(uint32_t bufferSize) + { + if (bufferSize <= 1) + return; + + // Check for updated bufferSize + if (bufferSize != lastBufferSize) + { + lastBufferSize = bufferSize; + d_lastBufferSize = bufferSize; + plugin.setBufferSize(bufferSize, true); + } + + // Check for updated parameters + float curValue; + + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + curValue = *portControls[i]; + + if (lastControlValues[i] != curValue && ! plugin.parameterIsOutput(i)) + { + lastControlValues[i] = curValue; + plugin.setParameterValue(i, curValue); + } + } + + // Get Events, xxx + uint32_t midiEventCount = 0; + +#if DISTRHO_LV2_USE_EVENTS + LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter) + { + const LV2_Atom_Event* event = /*(const LV2_Atom_Event*)*/iter; + + if (! event) + continue; + +# if DISTRHO_PLUGIN_IS_SYNTH + if (event->body.type == uridIdMidiEvent) + { + if (event->time.frames >= bufferSize || midiEventCount >= MAX_MIDI_EVENTS) + break; + + if (event->body.size > 3) + continue; + + const uint8_t* data = (const uint8_t*)(event + 1); + + midiEvents[midiEventCount].frame = event->time.frames; + memcpy(midiEvents[midiEventCount].buffer, data, event->body.size); + + midiEventCount += 1; + continue; + } +# endif +# if DISTRHO_PLUGIN_WANT_STATE + if (event->body.type == uridIdPatchMessage) + { + // TODO + //if (workerSchedule) + // workerSchedule->schedule_work(workerSchedule->handle, event->body.size, ) + } +# endif + } +#endif + + // Run plugin for this cycle + const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; + float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + inputs[i] = portAudioIns[i]; + + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + outputs[i] = portAudioOuts[i]; + + plugin.run(inputs, outputs, bufferSize, midiEventCount, midiEvents); + + updateParameterOutputs(); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + const LV2_Program_Descriptor* lv2_get_program(uint32_t index) + { + if (index >= plugin.programCount()) + return nullptr; + + static LV2_Program_Descriptor desc; + desc.bank = index / 128; + desc.program = index % 128; + desc.name = plugin.programName(index); + return &desc; + } + + void lv2_select_program(uint32_t bank, uint32_t program) + { + const uint32_t realProgram = bank * 128 + program; + + if (realProgram >= plugin.programCount()) + return; + + plugin.setProgram(realProgram); + + // Update parameters + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + if (! plugin.parameterIsOutput(i)) + lastControlValues[i] = *portControls[i] = plugin.parameterValue(i); + } + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + LV2_State_Status lv2_save(LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* /*features*/) + { + flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; + + for (auto i = stateMap.begin(); i != stateMap.end(); i++) + { + const d_string& key = i->first; + const d_string& value = i->second; + + store(handle, uridMap->map(uridMap->handle, (const char*)key), (const char*)value, value.length(), uridIdAtomString, flags); + } + + return LV2_STATE_SUCCESS; + } + + LV2_State_Status lv2_restore(LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* /*features*/) + { + size_t size; + uint32_t type; + + for (uint32_t i=0; i < plugin.stateCount(); i++) + { + const d_string& key = plugin.stateKey(i); + + const void* data = retrieve(handle, uridMap->map(uridMap->handle, (const char*)key), &size, &type, &flags); + + if (size == 0 || ! data) + continue; + if (type != uridIdAtomString) + continue; + + const char* value = (const char*)data; + setState(key, value); + } + + return LV2_STATE_SUCCESS; + } + + LV2_Worker_Status lv2_work(LV2_Worker_Respond_Function /*respond*/, LV2_Worker_Respond_Handle /*handle*/, uint32_t /*size*/, const void* /*data*/) + { + // TODO + return LV2_WORKER_SUCCESS; + } + + LV2_Worker_Status lv2_work_response(uint32_t /*size*/, const void* /*body*/) + { + // TODO + return LV2_WORKER_SUCCESS; + } +#endif + +private: + PluginInternal plugin; + + // LV2 ports + floatptrVector portAudioIns; + floatptrVector portAudioOuts; + floatptrVector portControls; + floatptr portLatency; +#if DISTRHO_PLUGIN_HAS_UI + floatptr portSampleRate; +#endif + + // xxx +#if DISTRHO_LV2_USE_EVENTS + LV2_Atom_Sequence* portEventsIn; + + // URIDs + LV2_URID_Map* uridMap; + LV2_URID uridIdAtomString; +# if DISTRHO_PLUGIN_IS_SYNTH + LV2_URID uridIdMidiEvent; +# endif +# if DISTRHO_PLUGIN_WANT_STATE + LV2_URID uridIdPatchMessage; + LV2_Worker_Schedule* workerSchedule; +# endif +#endif + + // Temporary data + uint32_t lastBufferSize; + const double lastSampleRate; + floatVector lastControlValues; + +#if DISTRHO_PLUGIN_IS_SYNTH + MidiEvent midiEvents[MAX_MIDI_EVENTS]; +#else + MidiEvent midiEvents[0]; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + stringMap stateMap; + + void setState(const char* newKey, const char* newValue) + { + plugin.setState(newKey, newValue); + + // check if key already exists + for (auto i = stateMap.begin(); i != stateMap.end(); i++) + { + const d_string& key = i->first; + + if (key == newKey) + { + i->second = newValue; + return; + } + } + + // add a new one then + stateMap[newKey] = newValue; + } +#endif + + void updateParameterOutputs() + { + for (uint32_t i=0; i < plugin.parameterCount(); i++) + { + if (plugin.parameterIsOutput(i)) + lastControlValues[i] = *portControls[i] = plugin.parameterValue(i); + } + + if (portLatency) + *portLatency = plugin.latency(); + +#if DISTRHO_PLUGIN_HAS_UI + if (portSampleRate) + *portSampleRate = lastSampleRate; +#endif + } +}; + +// ------------------------------------------------- + +static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) +{ + // TODO - search features for initial bufferSize + + if (d_lastBufferSize == 0) + d_lastBufferSize = 512; + d_lastSampleRate = sampleRate; + + return new PluginLv2(features); +} + +static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + plugin->lv2_connect_port(port, dataLocation); +} + +static void lv2_activate(LV2_Handle instance) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + plugin->lv2_activate(); +} + +static void lv2_run(LV2_Handle instance, uint32_t sampleCount) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + plugin->lv2_run(sampleCount); +} + +static void lv2_deactivate(LV2_Handle instance) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + plugin->lv2_deactivate(); +} + +static void lv2_cleanup(LV2_Handle instance) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + delete plugin; +} + +#if DISTRHO_LV2_USE_EXTENSION_DATA +# if DISTRHO_PLUGIN_WANT_PROGRAMS +static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + return plugin->lv2_get_program(index); +} + +static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + plugin->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* features) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + return plugin->lv2_save(store, handle, flags, features); +} + +static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + return plugin->lv2_restore(retrieve, handle, flags, features); +} + +LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle, uint32_t size, const void* data) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + return plugin->lv2_work(respond, handle, size, data); +} + +LV2_Worker_Status lv2_work_response(LV2_Handle instance, uint32_t size, const void* body) +{ + PluginLv2* plugin = (PluginLv2*)instance; + assert(plugin); + + return plugin->lv2_work_response(size, body); +} +# endif + +static const void* lv2_extension_data(const char* uri) +{ +# if DISTRHO_PLUGIN_WANT_PROGRAMS + static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; + if (strcmp(uri, LV2_PROGRAMS__Interface) == 0) + return &programs; +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + static const LV2_State_Interface state = { lv2_save, lv2_restore }; + if (strcmp(uri, LV2_STATE__interface) == 0) + return &state; + + static const LV2_Worker_Interface worker = { lv2_work, lv2_work_response, nullptr }; + if (strcmp(uri, LV2_WORKER__interface) == 0) + return &worker; +# endif + + return nullptr; +} +#endif + +// ------------------------------------------------- + +static LV2_Descriptor descriptor = { + DISTRHO_PLUGIN_URI, + lv2_instantiate, + lv2_connect_port, + lv2_activate, + lv2_run, + lv2_deactivate, + lv2_cleanup, +#if DISTRHO_LV2_USE_EXTENSION_DATA + lv2_extension_data +#else + /* extension_data */ nullptr +#endif +}; + +END_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +DISTRHO_PLUGIN_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &descriptor : nullptr; +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_LV2 diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp new file mode 100644 index 0000000..7066217 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginLV2export.cpp @@ -0,0 +1,347 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifdef DISTRHO_PLUGIN_TARGET_LV2 + +#include "DistrhoPluginInternal.h" + +#include "lv2-sdk/lv2.h" +#include "lv2-sdk/atom.h" +#include "lv2-sdk/midi.h" +#include "lv2-sdk/patch.h" +#include "lv2-sdk/programs.h" +#include "lv2-sdk/state.h" +#include "lv2-sdk/urid.h" +#include "lv2-sdk/ui.h" +#include "lv2-sdk/units.h" +#include "lv2-sdk/worker.h" + +#include +#include + +#ifndef DISTRHO_PLUGIN_URI +# error DISTRHO_PLUGIN_URI undefined! +#endif + +#define DISTRHO_LV2_USE_EVENTS (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_STATE) + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +void lv2_generate_ttl_func() +{ + PluginInternal plugin; + + d_string pluginLabel = plugin.label(); + 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: .\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_EXT "> ;\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_EXT "> ;\n"; +# if DISTRHO_LV2_USE_EVENTS + manifestString += " lv2:optionalFeature <" LV2_URID__map "> ,\n"; + manifestString += " ui:noUserResize .\n"; +# else + manifestString += " lv2:optionalFeature ui:noUserResize .\n"; +# endif + manifestString += "\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; +#if DISTRHO_LV2_USE_EVENTS + pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; +#endif + pluginString += "@prefix doap: .\n"; + pluginString += "@prefix foaf: .\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"; + + pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; +#if DISTRHO_PLUGIN_IS_SYNTH + pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; +#else + pluginString += " a lv2:Plugin ;\n"; +#endif + +#if (DISTRHO_PLUGIN_IS_SYNTH && DISTRHO_PLUGIN_WANT_STATE) + pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; + pluginString += " <" LV2_WORKER__schedule "> ;\n"; + pluginString += " lv2:requiredFeature <" LV2_URID__map "> ;\n"; +#elif DISTRHO_PLUGIN_IS_SYNTH + pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ;\n"; + pluginString += " lv2:requiredFeature <" LV2_URID__map "> ;\n"; +#elif DISTRHO_PLUGIN_WANT_STATE + pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; + pluginString += " <" LV2_URID__map "> ,\n"; + pluginString += " <" LV2_WORKER__schedule "> ;\n"; +#endif + +#if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_WANT_PROGRAMS) + pluginString += " lv2:extensionData <" LV2_STATE__interface "> ,\n"; + pluginString += " <" LV2_WORKER__interface "> ,\n"; + pluginString += " <" LV2_PROGRAMS__Interface "> ;\n"; +#elif DISTRHO_PLUGIN_WANT_STATE + pluginString += " lv2:extensionData <" LV2_STATE__interface "> ,\n"; + pluginString += " <" LV2_WORKER__interface "> ;\n"; +#elif DISTRHO_PLUGIN_WANT_PROGRAMS + pluginString += " lv2:extensionData <" LV2_PROGRAMS__Interface "> ;\n"; +#endif + pluginString += "\n"; + +#if DISTRHO_PLUGIN_HAS_UI + pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; + pluginString += "\n"; +#endif + + { + uint32_t i, portIndex = 0; + + for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) + { + 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"; + } + + for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) + { + 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"; + } + + for (i=0; i < plugin.parameterCount(); i++) + { + if (i == 0) + pluginString += " lv2:port [\n"; + else + pluginString += " [\n"; + + if (plugin.parameterIsOutput(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.parameterName(i) + "\" ;\n"; + + // symbol + { + d_string symbol(plugin.parameterSymbol(i)); + + if (symbol.isEmpty()) + symbol = "lv2_port_" + d_string(portIndex-1); + + pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; + } + + // ranges + { + const ParameterRanges* ranges = plugin.parameterRanges(i); + + if (plugin.parameterHints(i) & PARAMETER_IS_INTEGER) + { + pluginString += " lv2:default " + d_string(int(plugin.parameterValue(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.parameterValue(i)) + " ;\n"; + pluginString += " lv2:minimum " + d_string(ranges->min) + " ;\n"; + pluginString += " lv2:maximum " + d_string(ranges->max) + " ;\n"; + } + } + + // unit + { + const d_string& unit = plugin.parameterUnit(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 + { + pluginString += " unit:unit [\n"; + pluginString += " a unit:Unit ;\n"; + pluginString += " unit:name \"" + unit + "\" ;\n"; + pluginString += " unit:symbol \"" + unit + "\" ;\n"; + pluginString += " unit:render \"%f f\" ;\n"; + pluginString += " ] ;\n"; + } + } + } + + // hints + { + uint32_t hints = plugin.parameterHints(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 ;\n"; + } + + if (i+1 == plugin.parameterCount()) + pluginString += " ] ;\n\n"; + else + pluginString += " ] ,\n"; + } + + pluginString += " lv2:port [\n"; + +#if DISTRHO_LV2_USE_EVENTS + 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_IS_SYNTH && DISTRHO_PLUGIN_WANT_STATE) + pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ,\n"; + pluginString += " <" LV2_PATCH__Message "> ;\n"; +# elif DISTRHO_PLUGIN_IS_SYNTH + pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; +# else + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; +# endif + pluginString += " ] ,\n"; + pluginString += " [\n"; +#endif + 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"; +#if ! DISTRHO_PLUGIN_HAS_UI + pluginString += " ] ;\n\n"; +#else + pluginString += " ] ,\n"; + pluginString += " [\n"; + pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex++) + " ;\n"; + pluginString += " lv2:name \"Sample Rate\" ;\n"; + pluginString += " lv2:symbol \"lv2_sample_rate\" ;\n"; + pluginString += " lv2:designation ;\n"; + pluginString += " ] ;\n\n"; +#endif + } + + pluginString += " doap:name \"" + d_string(plugin.name()) + "\" ;\n"; + pluginString += " doap:maintainer [ foaf:name \"" + d_string(plugin.maker()) + "\" ] .\n"; + + pluginFile << pluginString << std::endl; + pluginFile.close(); + std::cout << " done!" << std::endl; + } +} + +// unused stuff +void d_unusedStuff() +{ + (void)d_lastBufferSize; + (void)d_lastSampleRate; +} + +END_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DISTRHO + lv2_generate_ttl_func(); +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_LV2 diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp new file mode 100644 index 0000000..dee6e9e --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoPluginVST.cpp @@ -0,0 +1,724 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifdef DISTRHO_PLUGIN_TARGET_VST + +#include "DistrhoPluginInternal.h" + +#if DISTRHO_PLUGIN_HAS_UI +# include "DistrhoUIInternal.h" +#endif + +#define VST_FORCE_DEPRECATED 0 +#include + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +#if DISTRHO_PLUGIN_HAS_UI +class UIVst +{ +public: + UIVst(audioMasterCallback audioMaster, AEffect* effect, PluginInternal* plugin, intptr_t winId) + : m_audioMaster(audioMaster), + m_effect(effect), + m_plugin(plugin), + ui(this, winId, setParameterCallback, setStateCallback, uiEditParameterCallback, uiSendNoteCallback, uiResizeCallback) + { + uint32_t paramCount = plugin->parameterCount(); + + if (paramCount > 0) + { + parameterChecks = new bool [paramCount]; + parameterValues = new float [paramCount]; + + for (uint32_t i=0; i < paramCount; i++) + { + parameterChecks[i] = false; + parameterValues[i] = 0.0f; + } + } + else + { + parameterChecks = nullptr; + parameterValues = nullptr; + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + nextProgram = -1; +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + midiEventCount = 0; +#endif + } + + ~UIVst() + { + if (parameterChecks) + delete[] parameterChecks; + + if (parameterValues) + delete[] parameterValues; + } + + // --------------------------------------------- + + void idle() + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (nextProgram != -1) + { + ui.programChanged(nextProgram); + nextProgram = -1; + } +#endif + + for (uint32_t i=0, count = m_plugin->parameterCount(); i < count; i++) + { + if (parameterChecks[i]) + { + parameterChecks[i] = false; + + ui.parameterChanged(i, parameterValues[i]); + } + } + +#if DISTRHO_PLUGIN_IS_SYNTH + // TODO - notes +#endif + + ui.idle(); + } + + int16_t getWidth() + { + return ui.getWidth(); + } + + int16_t getHeight() + { + return ui.getHeight(); + } + + void setParameterValueFromPlugin(uint32_t index, float perValue) + { + parameterChecks[index] = true; + parameterValues[index] = perValue; + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void setProgramFromPlugin(uint32_t index) + { + nextProgram = index; + + // set previous parameters invalid + for (uint32_t i=0, count = m_plugin->parameterCount(); i < count; i++) + parameterChecks[i] = false; + } +#endif + + // --------------------------------------------- + +protected: + intptr_t hostCallback(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) + { + return m_audioMaster(m_effect, opcode, index, value, ptr, opt); + } + + void setParameterValue(uint32_t index, float realValue) + { + const ParameterRanges* ranges = m_plugin->parameterRanges(index); + float perValue = (realValue - ranges->min) / (ranges->max - ranges->min); + + m_plugin->setParameterValue(index, realValue); + + hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + } + +#if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* key, const char* value) + { + m_plugin->setState(key, value); + } +#endif + + void uiEditParameter(uint32_t index, bool started) + { + if (started) + hostCallback(audioMasterBeginEdit, index, 0, nullptr, 0.0f); + else + hostCallback(audioMasterEndEdit, index, 0, nullptr, 0.0f); + } + +#if DISTRHO_PLUGIN_IS_SYNTH + void uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { + // TODO + } +#endif + + void uiResize(unsigned int width, unsigned int height) + { + hostCallback(audioMasterSizeWindow, width, height, nullptr, 0.0f); + } + +private: + // Vst stuff + audioMasterCallback const m_audioMaster; + AEffect* const m_effect; + PluginInternal* const m_plugin; + + // Plugin UI + UIInternal ui; + + // Temporary data + bool* parameterChecks; + float* parameterValues; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + int32_t nextProgram; +#endif +#if DISTRHO_PLUGIN_IS_SYNTH + uint32_t midiEventCount; + MidiEvent midiEvents[MAX_MIDI_EVENTS]; +#endif + + // --------------------------------------------- + // Callbacks + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + UIVst* _this_ = (UIVst*)ptr; + assert(_this_); + + _this_->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { +#if DISTRHO_PLUGIN_WANT_STATE + UIVst* _this_ = (UIVst*)ptr; + assert(_this_); + + _this_->setState(key, value); +#else + // unused + (void)ptr; + (void)key; + (void)value; +#endif + } + + static void uiEditParameterCallback(void* ptr, uint32_t index, bool started) + { + UIVst* _this_ = (UIVst*)ptr; + assert(_this_); + + _this_->uiEditParameter(index, started); + } + + static void uiSendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { +#if DISTRHO_PLUGIN_IS_SYNTH + UIVst* _this_ = (UIVst*)ptr; + assert(_this_); + + _this_->uiSendNote(onOff, channel, note, velocity); +#else + // unused + (void)ptr; + (void)onOff; + (void)channel; + (void)note; + (void)velocity; +#endif + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + UIVst* _this_ = (UIVst*)ptr; + assert(_this_); + + _this_->uiResize(width, height); + } +}; +#endif + +class PluginVst +{ +public: + PluginVst(audioMasterCallback audioMaster, AEffect* effect) + : m_audioMaster(audioMaster), + m_effect(effect) + { +#if DISTRHO_PLUGIN_HAS_UI + vstui = nullptr; + rect.top = 0; + rect.left = 0; + rect.bottom = 0; + rect.right = 0; +#endif + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + curProgram = -1; +#endif +#if DISTRHO_PLUGIN_IS_SYNTH + midiEventCount = 0; +#endif + } + + ~PluginVst() + { + } + + intptr_t vst_dispatcher(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) + { + int32_t ret = 0; + + switch (opcode) + { +#if DISTRHO_PLUGIN_WANT_PROGRAMS + case effSetProgram: + if (value >= 0 && value < plugin.programCount()) + { + curProgram = value; + plugin.setProgram(curProgram); + + if (vstui) + vstui->setProgramFromPlugin(curProgram); + + ret = 1; + } + break; + + case effGetProgram: + ret = curProgram; + break; + + case effSetProgramName: + break; + + case effGetProgramName: + if (ptr && curProgram >= 0 && curProgram < (int32_t)plugin.programCount()) + { + strncpy((char*)ptr, plugin.programName(curProgram), kVstMaxProgNameLen); + ret = 1; + } + break; +#endif + + case effGetParamDisplay: + if (ptr && index < (int32_t)plugin.parameterCount()) + { + snprintf((char*)ptr, kVstMaxParamStrLen, "%f", plugin.parameterValue(index)); + ret = 1; + } + break; + + case effSetSampleRate: + // should not happen + break; + + case effSetBlockSize: + plugin.setBufferSize(value, true); + break; + + case effMainsChanged: + if (value) + plugin.activate(); + else + plugin.deactivate(); + break; + +#if DISTRHO_PLUGIN_HAS_UI + case effEditGetRect: + if (rect.bottom == 0 && ! vstui) + { + // This is stupid, but some hosts want to know the UI size before creating it, + // so we have to create a temporary UI here + setLastUiSampleRate(d_lastSampleRate); + + UIInternal tempUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr); + rect.bottom = tempUI.getHeight(); + rect.right = tempUI.getWidth(); + } + else + { + rect.bottom = vstui->getHeight(); + rect.right = vstui->getWidth(); + } + + *(ERect**)ptr = ▭ + ret = 1; + + break; + + case effEditOpen: + if (! vstui) + { + setLastUiSampleRate(d_lastSampleRate); + vstui = new UIVst(m_audioMaster, m_effect, &plugin, (intptr_t)ptr); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (curProgram >= 0) + vstui->setProgramFromPlugin(curProgram); +#endif + + for (uint32_t i=0, count = plugin.parameterCount(); i < count; i++) + vstui->setParameterValueFromPlugin(i, plugin.parameterValue(i)); + + ret = 1; + break; + + case effEditClose: + if (vstui) + { + delete vstui; + vstui = nullptr; + ret = 1; + } + break; + + case effEditIdle: + if (vstui) + vstui->idle(); + break; +#endif + + case effGetChunk: + case effSetChunk: + // TODO + break; + +#if DISTRHO_PLUGIN_IS_SYNTH + case effProcessEvents: + if (ptr) + { + //VstEvents* events = (VstEvents*)ptr; + // TODO + } + break; +#endif + + case effCanBeAutomated: + if (index < (int32_t)plugin.parameterCount()) + { + uint32_t hints = plugin.parameterHints(index); + + // must be automable, and not output + if ((hints & PARAMETER_IS_AUTOMABLE) > 0 && (hints & PARAMETER_IS_OUTPUT) == 0) + ret = 1; + } + break; + + case effCanDo: + // TODO + break; + + case effStartProcess: + case effStopProcess: + break; + } + + return ret; + + // unused + (void)opt; + } + + float vst_getParameter(int32_t index) + { + const ParameterRanges* ranges = plugin.parameterRanges(index); + float realValue = plugin.parameterValue(index); + float perValue = (realValue - ranges->min) / (ranges->max - ranges->min); + return perValue; + } + + void vst_setParameter(int32_t index, float value) + { + const ParameterRanges* ranges = plugin.parameterRanges(index); + float realValue = ranges->min + (ranges->max - ranges->min) * value; + plugin.setParameterValue(index, realValue); + + if (vstui) + vstui->setParameterValueFromPlugin(index, realValue); + } + + void vst_processReplacing(float** inputs, float** outputs, int32_t sampleFrames) + { +#if DISTRHO_PLUGIN_IS_SYNTH + plugin.run((const float**)inputs, outputs, sampleFrames, midiEventCount, midiEvents); + + // TODO - send notes to UI + +#else + plugin.run((const float**)inputs, outputs, sampleFrames, 0, nullptr); +#endif + } + + // --------------------------------------------- + +private: + // VST stuff + audioMasterCallback const m_audioMaster; + AEffect* const m_effect; + + // Plugin + PluginInternal plugin; + +#if DISTRHO_PLUGIN_HAS_UI + // UI + UIVst* vstui; + ERect rect; +#endif + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + int32_t curProgram; +#endif +#if DISTRHO_PLUGIN_IS_SYNTH + uint32_t midiEventCount; + MidiEvent midiEvents[MAX_MIDI_EVENTS]; +#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 == -1 && index == 0xdead && value == 0xf00d); + + if (doInternalInit) + { + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + } + + // Create dummy plugin to get data from + static PluginInternal plugin; + + if (doInternalInit) + { + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + *(PluginInternal**)ptr = &plugin; + return 0; + } + + // handle opcodes + switch (opcode) + { + case effOpen: + if (! effect->object) + { + audioMasterCallback audioMaster = (audioMasterCallback)effect->user; + d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); + d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + effect->object = new PluginVst(audioMaster, effect); + return 1; + } + return 0; + + case effClose: + if (effect->object) + { + delete (PluginVst*)effect->object; + effect->object = nullptr; + delete effect; + return 1; + } + return 0; + + case effGetParamLabel: + if (ptr && index < (int32_t)plugin.parameterCount()) + { + strncpy((char*)ptr, plugin.parameterUnit(index), kVstMaxParamStrLen); + return 1; + } + return 0; + + case effGetParamName: + if (ptr && index < (int32_t)plugin.parameterCount()) + { + strncpy((char*)ptr, plugin.parameterName(index), kVstMaxParamStrLen); + return 1; + } + return 0; + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + case effGetProgramNameIndexed: + if (ptr && index < (int32_t)plugin.programCount()) + { + strncpy((char*)ptr, plugin.programName(index), kVstMaxProgNameLen); + return 1; + } + return 0; +#endif + + case effGetPlugCategory: +#if DISTRHO_PLUGIN_IS_SYNTH + return kPlugCategSynth; +#else + return kPlugCategUnknown; +#endif + + case effGetEffectName: + if (ptr) + { + strncpy((char*)ptr, plugin.name(), kVstMaxProductStrLen); + return 1; + } + return 0; + + case effGetVendorString: + if (ptr) + { + strncpy((char*)ptr, plugin.maker(), kVstMaxVendorStrLen); + return 1; + } + return 0; + + case effGetProductString: + if (ptr) + { + strncpy((char*)ptr, plugin.label(), kVstMaxEffectNameLen); + return 1; + } + return 0; + + case effGetVendorVersion: + return plugin.version(); + + case effGetVstVersion: + return kVstVersion; + }; + + if (effect->object) + { + PluginVst* _this_ = (PluginVst*)effect->object; + return _this_->vst_dispatcher(opcode, index, value, ptr, opt); + } + + return 0; +} + +static float vst_getParameterCallback(AEffect* effect, int32_t index) +{ + PluginVst* _this_ = (PluginVst*)effect->object; + assert(_this_); + + if (_this_) + return _this_->vst_getParameter(index); + + return 0.0f; +} + +static void vst_setParameterCallback(AEffect* effect, int32_t index, float value) +{ + PluginVst* _this_ = (PluginVst*)effect->object; + assert(_this_); + + if (_this_) + _this_->vst_setParameter(index, value); +} + +static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +{ + PluginVst* _this_ = (PluginVst*)effect->object; + assert(_this_); + + if (_this_) + _this_->vst_processReplacing(inputs, outputs, sampleFrames); +} + +static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +{ + PluginVst* _this_ = (PluginVst*)effect->object; + assert(_this_); + + if (_this_) + _this_->vst_processReplacing(inputs, outputs, sampleFrames); +} + +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)) + return nullptr; + + PluginInternal* plugin = nullptr; + vst_dispatcherCallback(nullptr, -1, 0xdead, 0xf00d, &plugin, 0.0f); + + AEffect* effect = new AEffect; + memset(effect, 0, sizeof(AEffect)); + + // vst fields + effect->magic = kEffectMagic; + effect->uniqueID = plugin->uniqueId(); + effect->version = plugin->version(); + + // plugin fields + effect->numParams = plugin->parameterCount(); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + effect->numPrograms = plugin->programCount(); +#endif + effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS; + effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; + + // static calls + effect->dispatcher = vst_dispatcherCallback; + effect->process = vst_processCallback; + effect->getParameter = vst_getParameterCallback; + effect->setParameter = vst_setParameterCallback; + effect->processReplacing = vst_processReplacingCallback; + effect->processDoubleReplacing = nullptr; + + // plugin flags + effect->flags |= effFlagsCanReplacing; + +#if DISTRHO_PLUGIN_HAS_UI +# ifdef DISTRHO_UI_QT4 + if (QApplication::instance()) +# endif + effect->flags |= effFlagsHasEditor; +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + effect->flags |= effFlagsProgramChunks; +#endif + + // pointers + effect->object = nullptr; + effect->user = (void*)audioMaster; + + return effect; +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_VST diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp new file mode 100644 index 0000000..70e320b --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUI.cpp @@ -0,0 +1,104 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoUIInternal.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +void setLastUiSampleRate(double sampleRate) +{ + d_lastUiSampleRate = sampleRate; +} + +// ------------------------------------------------- + +UI::UI() +{ + data = new UIPrivateData; + +#if (defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)) + data->parameterOffset = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; +# if DISTRHO_PLUGIN_WANT_LATENCY + data->parameterOffset += 1; +# endif + data->parameterOffset += 1; // sample-rate +#endif + +#ifdef DISTRHO_UI_QT4 + data->widget = (Qt4UI*)this; +#endif +} + +UI::~UI() +{ + delete data; +} + +// ------------------------------------------------- +// Host DSP State + +double UI::d_sampleRate() const +{ + return data->sampleRate; +} + +void UI::d_setParameterValue(uint32_t index, float value) +{ + data->setParamCallback(index + data->parameterOffset, value); +} + +#if DISTRHO_PLUGIN_WANT_STATE +void UI::d_setState(const char* key, const char* value) +{ + data->setStateCallback(key, value); +} +#endif + +// ------------------------------------------------- +// Host UI State + +void UI::d_uiEditParameter(uint32_t index, bool started) +{ + data->uiEditParamCallback(index, started); +} + +#if DISTRHO_PLUGIN_IS_SYNTH +void UI::d_uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) +{ + data->uiSendNoteCallback(onOff, channel, note, velocity); +} +#endif + +void UI::d_uiResize(unsigned int width, unsigned int height) +{ + data->uiResizeCallback(width, height); +} + +// ------------------------------------------------- +// DSP Callbacks + +#if DISTRHO_PLUGIN_IS_SYNTH +void UI::d_uiNoteReceived(bool, uint8_t, uint8_t, uint8_t) +{ +} +#endif + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp new file mode 100644 index 0000000..3f15b02 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIDSSI.cpp @@ -0,0 +1,612 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoDefines.h" + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI + +#include "DistrhoUIInternal.h" + +#include +#include +#include + +#include + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +struct OscData { + lo_address addr; + const char* path; + lo_server server; +}; + +struct StringData { + d_string key; + d_string value; +}; + +#if DISTRHO_PLUGIN_WANT_STATE +void osc_send_configure(const OscData* oscData, const char* const key, const char* const value) +{ + char targetPath[strlen(oscData->path)+11]; + strcpy(targetPath, oscData->path); + strcat(targetPath, "/configure"); + lo_send(oscData->addr, targetPath, "ss", key, value); +} +#endif + +void osc_send_control(const OscData* oscData, const int32_t index, const float value) +{ + char targetPath[strlen(oscData->path)+9]; + strcpy(targetPath, oscData->path); + strcat(targetPath, "/control"); + lo_send(oscData->addr, targetPath, "if", index, value); +} + +#if DISTRHO_PLUGIN_IS_SYNTH +void osc_send_midi(const OscData* oscData, unsigned char data[4]) +{ + char targetPath[strlen(oscData->path)+6]; + strcpy(targetPath, oscData->path); + strcat(targetPath, "/midi"); + lo_send(oscData->addr, targetPath, "m", data); +} +#endif + +void osc_send_update(const OscData* oscData, const char* const url) +{ + char targetPath[strlen(oscData->path)+8]; + strcpy(targetPath, oscData->path); + strcat(targetPath, "/update"); + lo_send(oscData->addr, targetPath, "s", url); +} + +void osc_send_exiting(const OscData* oscData) +{ + char targetPath[strlen(oscData->path)+9]; + strcpy(targetPath, oscData->path); + strcat(targetPath, "/exiting"); + lo_send(oscData->addr, targetPath, ""); +} + +// stuff that we might receive while waiting for sample-rate +static bool globalShow = false; +#if DISTRHO_PLUGIN_WANT_STATE +static std::vector globalConfigures; +#endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS +static int32_t globalProgram[2] = { -1, -1 }; +#endif +static std::vector globalControls; + +class UIDssi : public QMainWindow +{ +public: + UIDssi(const OscData* oscData_, const char* title) + : QMainWindow(nullptr), + widget(this), + settings("DISTRHO", DISTRHO_PLUGIN_NAME), + ui(this, widget.winId(), setParameterCallback, setStateCallback, nullptr, uiSendNoteCallback, uiResizeCallback), + oscData(oscData_) + { + setCentralWidget(&widget); + setFixedSize(ui.getWidth(), ui.getHeight()); + setWindowTitle(title); + + uiTimer = startTimer(30); + + // load settings + restoreGeometry(settings.value("Global/Geometry", QByteArray()).toByteArray()); + +#if DISTRHO_PLUGIN_WANT_STATE + for (size_t i=0; i < globalConfigures.size(); i++) + dssiui_configure(globalConfigures.at(i).key, globalConfigures.at(i).value); +#endif + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (globalProgram[0] >= 0 && globalProgram[1] >= 0) + dssiui_program(globalProgram[0], globalProgram[1]); +#endif + + for (size_t i=0; i < globalControls.size(); i++) + dssiui_control(i, globalControls.at(i)); + + if (globalShow) + show(); + } + + ~UIDssi() + { + // save settings + settings.setValue("Global/Geometry", saveGeometry()); + + if (uiTimer) + { + killTimer(uiTimer); + osc_send_exiting(oscData); + } + } + + // --------------------------------------------- + +#if DISTRHO_PLUGIN_WANT_STATE + void dssiui_configure(const char* key, const char* value) + { + ui.stateChanged(key, value); + } +#endif + + void dssiui_control(unsigned long index, float value) + { + ui.parameterChanged(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void dssiui_program(unsigned long bank, unsigned long program) + { + unsigned long index = bank * 128 + program; + ui.programChanged(index); + } +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + void dssiui_midi(uint8_t data[4]) + { + uint8_t status = data[1] & 0xF0; + uint8_t channel = data[1] & 0x0F; + + // fix bad note-off + if (status == 0x90 && data[3] == 0) + status -= 0x10; + + if (status == 0x80) + { + uint8_t note = data[2]; + ui.noteReceived(false, channel, note, 0); + } + else if (status == 0x90) + { + uint8_t note = data[2]; + uint8_t velo = data[3]; + ui.noteReceived(true, channel, note, velo); + } + } +#endif + + void dssiui_quit() + { + if (uiTimer) + { + killTimer(uiTimer); + uiTimer = 0; + } + close(); + qApp->quit(); + } + + // --------------------------------------------- + +protected: + void setParameterValue(uint32_t rindex, float value) + { + osc_send_control(oscData, rindex, value); + } + +#if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* key, const char* value) + { + osc_send_configure(oscData, key, value); + } +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + void uiSendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { + uint8_t mdata[4] = { 0, channel, note, velocity }; + mdata[1] += onOff ? 0x90 : 0x80; + + osc_send_midi(oscData, mdata); + } +#endif + + void uiResize(unsigned int width, unsigned int height) + { + widget.setFixedSize(width, height); + setFixedSize(width, height); + } + + void timerEvent(QTimerEvent* event) + { + if (event->timerId() == uiTimer) + { + ui.idle(); + + while (lo_server_recv_noblock(oscData->server, 0) != 0) {} + } + + QObject::timerEvent(event); + } + +private: + // Qt4 stuff + int uiTimer; + QWidget widget; + QSettings settings; + + // Plugin UI + UIInternal ui; + + const OscData* const oscData; + + // --------------------------------------------- + // Callbacks + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + UIDssi* _this_ = (UIDssi*)ptr; + assert(_this_); + + _this_->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { +#if DISTRHO_PLUGIN_WANT_STATE + UIDssi* _this_ = (UIDssi*)ptr; + assert(_this_); + + _this_->setState(key, value); +#else + Q_UNUSED(ptr); + Q_UNUSED(key); + Q_UNUSED(value); +#endif + } + + static void uiSendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { +#if DISTRHO_PLUGIN_IS_SYNTH + UIDssi* _this_ = (UIDssi*)ptr; + assert(_this_); + + _this_->uiSendNote(onOff, channel, note, velocity); +#else + Q_UNUSED(ptr); + Q_UNUSED(onOff); + Q_UNUSED(channel); + Q_UNUSED(note); + Q_UNUSED(velocity); +#endif + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + UIDssi* _this_ = (UIDssi*)ptr; + assert(_this_); + + _this_->uiResize(width, height); + } +}; + +// ------------------------------------------------- + +static UIDssi* globalUI = nullptr; + +void osc_error_handler(int num, const char* msg, const char* path) +{ + qCritical("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; + qDebug("osc_configure_handler(\"%s\", \"%s\")", key, value); + + if (globalUI) + { + globalUI->dssiui_configure(key, value); + } + else + { + StringData data = { key, value }; + globalConfigures.push_back(data); + } + + 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; + qDebug("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 + // sample-rate + index -= 1; + + if (index == -1) + { + setLastUiSampleRate(value); + return 0; + } + + if (index < 0) + return 0; + + if (globalUI) + { + globalUI->dssiui_control(index, value); + } + else + { + if (index >= (int32_t)globalControls.size()) + { + for (int32_t i=globalControls.size(); i < index; i++) + globalControls.push_back(0.0f); + } + + globalControls[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; + qDebug("osc_program_handler(%i, %i)", bank, program); + + if (globalUI) + { + globalUI->dssiui_program(bank, program); + } + else + { + globalProgram[0] = bank; + globalProgram[1] = program; + } + + return 0; +} +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH +int osc_midi_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) +{ + qDebug("osc_midi_handler()"); + + if (globalUI) + globalUI->dssiui_midi(argv[0]->m); + + 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; + qDebug("osc_sample_rate_handler(%i)", sampleRate); + + setLastUiSampleRate(sampleRate); + + return 0; +} + +int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + qDebug("osc_show_handler()"); + + if (globalUI) + globalUI->show(); + else + globalShow = true; + + return 0; +} + +int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + qDebug("osc_hide_handler()"); + + if (globalUI) + globalUI->hide(); + else + globalShow = false; + + return 0; +} + +int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) +{ + qDebug("osc_quit_handler()"); + + if (globalUI) + { + globalUI->dssiui_quit(); + } + else + { + if (QApplication* app = qApp) + app->quit(); + } + + return 0; +} + +int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) +{ + qDebug("osc_debug_handler(\"%s\")", path); + + return 0; +} + +END_NAMESPACE_DISTRHO + +int main(int argc, char* argv[]) +{ + USE_NAMESPACE_DISTRHO + + if (argc != 5) + { + qWarning("Usage: %s ", argv[0]); + return 1; + } + + QApplication app(argc, argv, true); + + 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(nullptr, osc_error_handler); + + OscData oscData = { oscAddr, oscPath, oscServer }; + +#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); + +#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 + +#if DISTRHO_PLUGIN_IS_SYNTH + char oscPathMidi[oscPathSize+6]; + strcpy(oscPathMidi, oscPath); + strcat(oscPathMidi, "/midi"); + lo_server_add_method(oscServer, oscPathMidi, "m", osc_midi_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); + + char* const serverPath = lo_server_get_url(oscServer); + char* const pluginPath = strdup(QString("%1%2").arg(serverPath).arg(oscPathSize > 1 ? oscPath + 1 : oscPath).toUtf8().constData()); + free(serverPath); + + // send update msg and wait for sample-rate + osc_send_update(&oscData, pluginPath); + + // wait for sample-rate + for (int i=0; i < 1000; i++) + { + if (d_lastUiSampleRate != 0.0) + break; + + lo_server_recv(oscServer); + d_msleep(50); + } + + int ret; + + if (d_lastUiSampleRate != 0.0) + { + globalUI = new UIDssi(&oscData, uiTitle); + + ret = app.exec(); + + delete globalUI; + globalUI = nullptr; + } + else + { + ret = 1; + } + +#if DISTRHO_PLUGIN_WANT_STATE + globalConfigures.clear(); +#endif + globalControls.clear(); + +#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 +#if DISTRHO_PLUGIN_IS_SYNTH + lo_server_del_method(oscServer, oscPathMidi, "m"); +#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); + + free(oscHost); + free(oscPort); + free(oscPath); + free(pluginPath); + + lo_address_free(oscAddr); + lo_server_free(oscServer); + + return ret; +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_DSSI && DISTRHO_PLUGIN_HAS_UI diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h new file mode 100644 index 0000000..bd765f3 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIInternal.h @@ -0,0 +1,548 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#ifndef __DISTRHO_UI_INTERNAL_H__ +#define __DISTRHO_UI_INTERNAL_H__ + +#include "DistrhoUI.h" + +#ifdef DISTRHO_UI_OPENGL +# include "DistrhoUIOpenGL.h" +# include "pugl/pugl.h" +#else +# include "DistrhoUIQt4.h" +# include +# include +# include +# include +# ifdef Q_WS_X11 +# include +# endif +#endif + +#include + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +#ifdef DISTRHO_UI_OPENGL +typedef PuglView* NativeWidget; +#else +typedef QWidget* NativeWidget; +#endif + +typedef void (*setParamFunc) (void* ptr, uint32_t index, float value); +typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); +typedef void (*uiEditParamFunc) (void* ptr, uint32_t index, bool started); +typedef void (*uiSendNoteFunc) (void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velo); +typedef void (*uiResizeFunc) (void* ptr, unsigned int width, unsigned int height); + +static double d_lastUiSampleRate = 0.0; + +void setLastUiSampleRate(double sampleRate); + +// ------------------------------------------------- + +#ifdef DISTRHO_UI_QT4 +# ifdef Q_WS_X11 +class QEmbedWidget : public QX11EmbedWidget +#else +class QEmbedWidget : public QWidget +#endif +{ +public: + QEmbedWidget(); + ~QEmbedWidget(); + + void embedInto(WId id); + WId containerWinId() const; +}; +#endif + +// ------------------------------------------------- + +struct UIPrivateData { + // DSP + double sampleRate; + uint32_t parameterOffset; + + // UI + void* ptr; + NativeWidget widget; + + // Callbacks + setParamFunc setParamCallbackFunc; + setStateFunc setStateCallbackFunc; + uiEditParamFunc uiEditParamCallbackFunc; + uiSendNoteFunc uiSendNoteCallbackFunc; + uiResizeFunc uiResizeCallbackFunc; + + UIPrivateData() + : sampleRate(d_lastUiSampleRate), + parameterOffset(0), + ptr(nullptr), + widget(nullptr), + setParamCallbackFunc(nullptr), + setStateCallbackFunc(nullptr), + uiEditParamCallbackFunc(nullptr), + uiSendNoteCallbackFunc(nullptr), + uiResizeCallbackFunc(nullptr) + { + assert(d_lastUiSampleRate != 0.0); + } + + ~UIPrivateData() + { + } + + void setParamCallback(uint32_t rindex, float value) + { + if (setParamCallbackFunc) + setParamCallbackFunc(ptr, rindex, value); + } + + void setStateCallback(const char* key, const char* value) + { + if (setStateCallbackFunc) + setStateCallbackFunc(ptr, key, value); + } + + void uiEditParamCallback(uint32_t index, bool started) + { + if (uiEditParamCallbackFunc) + uiEditParamCallbackFunc(ptr, index, started); + } + + void uiSendNoteCallback(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { + if (uiSendNoteCallbackFunc) + uiSendNoteCallbackFunc(ptr, onOff, channel, note, velocity); + } + + void uiResizeCallback(unsigned int width, unsigned int height) + { + if (uiResizeCallbackFunc) + uiResizeCallbackFunc(ptr, width, height); + } +}; + +// ------------------------------------------------- + +#ifdef DISTRHO_UI_QT4 +class UIInternal : public QObject +#else +class UIInternal +#endif +{ +public: + UIInternal(void* ptr, intptr_t winId, setParamFunc setParamCall, setStateFunc setStateCall, uiEditParamFunc uiEditParamCall, uiSendNoteFunc uiSendNoteCall, uiResizeFunc uiResizeCall) + : ui(createUI()), + data(nullptr) + { + assert(ui); + +#ifdef DISTRHO_UI_QT4 + qt_grip = nullptr; + qt_widget = nullptr; +#else + gl_initiated = false; +#endif + + if (winId == 0 || ! ui) + return; + + data = ui->data; + + data->ptr = ptr; + data->setParamCallbackFunc = setParamCall; + data->setStateCallbackFunc = setStateCall; + data->uiEditParamCallbackFunc = uiEditParamCall; + data->uiSendNoteCallbackFunc = uiSendNoteCall; + data->uiResizeCallbackFunc = uiResizeCall; + + createWindow(winId); + } + + ~UIInternal() + { + if (ui) + { + destroyWindow(); + delete ui; + } + } + + // --------------------------------------------- + + void idle() + { + if (ui) + ui->d_uiIdle(); + } + + unsigned int getWidth() + { + return ui ? ui->d_width() : 0; + } + + unsigned int getHeight() + { + return ui ? ui->d_height() : 0; + } + + intptr_t getWindowId() + { +#ifdef DISTRHO_UI_QT4 + return qt_widget ? qt_widget->winId() : 0; +#else + return (data && data->widget) ? puglGetNativeWindow(data->widget) : 0; +#endif + } + + // --------------------------------------------- + + void parameterChanged(uint32_t index, float value) + { + if (ui) + ui->d_parameterChanged(index, value); + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void programChanged(uint32_t index) + { + if (ui) + ui->d_programChanged(index); + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + void stateChanged(const char* key, const char* value) + { + if (ui) + ui->d_stateChanged(key, value); + } +#endif + +#if DISTRHO_PLUGIN_IS_SYNTH + void noteReceived(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { + if (ui) + ui->d_uiNoteReceived(onOff, channel, note, velocity); + } +#endif + + // --------------------------------------------- + + void createWindow(intptr_t parent) + { +#ifdef DISTRHO_UI_QT4 + // create embedable widget + qt_widget = new QEmbedWidget; + + // set layout + qt_widget->setLayout(new QVBoxLayout(qt_widget)); + qt_widget->layout()->addWidget(data->widget); + qt_widget->layout()->setContentsMargins(0, 0, 0, 0); + qt_widget->setFixedSize(ui->d_width(), ui->d_height()); + + // listen for resize on the plugin widget + data->widget->installEventFilter(this); + + // set resize grip + if (((Qt4UI*)ui)->d_resizable()) + { + qt_grip = new QSizeGrip(qt_widget); + qt_grip->resize(qt_grip->sizeHint()); + qt_grip->setCursor(Qt::SizeFDiagCursor); + qt_grip->move(ui->d_width() - qt_grip->width(), ui->d_height() - qt_grip->height()); + qt_grip->show(); + qt_grip->raise(); + qt_grip->installEventFilter(this); + } + + // reparent widget + qt_widget->embedInto(parent); + + // show it + qt_widget->show(); + +#else + if ((data && data->widget) || ! ui) + return; + + data->widget = puglCreate(parent, DISTRHO_PLUGIN_NAME, ui->d_width(), ui->d_height(), false); + + assert(data->widget); + + if (! data->widget) + return; + + puglSetHandle(data->widget, this); + puglSetDisplayFunc(data->widget, gl_onDisplayCallback); + puglSetKeyboardFunc(data->widget, gl_onKeyboardCallback); + puglSetMotionFunc(data->widget, gl_onMotionCallback); + puglSetMouseFunc(data->widget, gl_onMouseCallback); + puglSetScrollFunc(data->widget, gl_onScrollCallback); + puglSetSpecialFunc(data->widget, gl_onSpecialCallback); + puglSetReshapeFunc(data->widget, gl_onReshapeCallback); + puglSetCloseFunc(data->widget, gl_onCloseCallback); +#endif + } + + void destroyWindow() + { +#ifdef DISTRHO_UI_QT4 + if (qt_widget) + { + // remove main widget, to prevent it from being auto-deleted + qt_widget->layout()->removeWidget(data->widget); + data->widget->close(); + data->widget->setParent(nullptr); + + qt_widget->close(); + + if (qt_grip) + delete qt_grip; + + delete qt_widget; + } +#else + ((OpenGLUI*)ui)->d_onClose(); + + if (data && data->widget) + { + puglDestroy(data->widget); + data->widget = nullptr; + } +#endif + } + + // --------------------------------------------- + +#ifdef DISTRHO_UI_OPENGL + void gl_onDisplay() + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onDisplay(); + } + + void gl_onKeyboard(bool press, uint32_t key) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onKeyboard(press, key); + } + + void gl_onMotion(int x, int y) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onMotion(x, y); + } + + void gl_onMouse(int button, bool press, int x, int y) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onMouse(button, press, x, y); + } + + void gl_onReshape(int width, int height) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + { + if (! gl_initiated) + { + uiGL->d_onInit(); + gl_initiated = true; + } + else + uiGL->d_onReshape(width, height); + } + } + + void gl_onScroll(float dx, float dy) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onScroll(dx, dy); + } + + void gl_onSpecial(bool press, Key key) + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onSpecial(press, key); + } + + void gl_onClose() + { + OpenGLUI* uiGL = (OpenGLUI*)ui; + assert(uiGL); + + if (uiGL) + uiGL->d_onClose(); + } + + // --------------------------------------------- + + static void gl_onDisplayCallback(PuglView* view) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onDisplay(); + } + + static void gl_onKeyboardCallback(PuglView* view, bool press, uint32_t key) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onKeyboard(press, key); + } + + static void gl_onMotionCallback(PuglView* view, int x, int y) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onMotion(x, y); + } + + static void gl_onMouseCallback(PuglView* view, int button, bool press, int x, int y) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onMouse(button, press, x, y); + } + + static void gl_onReshapeCallback(PuglView* view, int width, int height) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onReshape(width, height); + } + + static void gl_onScrollCallback(PuglView* view, float dx, float dy) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onScroll(dx, dy); + } + + static void gl_onSpecialCallback(PuglView* view, bool press, PuglKey key) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onSpecial(press, (Key)key); + } + + static void gl_onCloseCallback(PuglView* view) + { + UIInternal* _this_ = (UIInternal*)puglGetHandle(view); + _this_->gl_onClose(); + } +#endif + + // --------------------------------------------- + +protected: + UI* const ui; + UIPrivateData* data; + +#ifdef DISTRHO_UI_QT4 + bool eventFilter(QObject* obj, QEvent* event) + { + if (obj == qt_grip) + { + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* mEvent = (QMouseEvent*)event; + if (mEvent->button() == Qt::LeftButton) + qt_mouseDown = true; + return true; + } + + if (event->type() == QEvent::MouseMove) + { + if (qt_mouseDown) + { + Qt4UI* qt_ui = (Qt4UI*)ui; + QMouseEvent* mEvent = (QMouseEvent*)event; + int width = ui->d_width() + mEvent->x() - qt_grip->width(); + int height = ui->d_height() + mEvent->y() - qt_grip->height(); + + if (width < qt_ui->d_minimumWidth()) + width = qt_ui->d_minimumWidth(); + if (height < qt_ui->d_minimumHeight()) + height = qt_ui->d_minimumHeight(); + + data->widget->setFixedSize(width, height); + } + + return true; + } + + if (event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent* mEvent = (QMouseEvent*)event; + if (mEvent->button() == Qt::LeftButton) + qt_mouseDown = false; + return true; + } + } + else if (data && obj == data->widget) + { + if (event->type() == QEvent::Resize) + { + QResizeEvent* rEvent = (QResizeEvent*)event; + const QSize& size = rEvent->size(); + + qt_widget->setFixedSize(size.width(), size.height()); + qt_grip->move(size.width() - qt_grip->width(), size.height() - qt_grip->height()); + + ui->d_uiResize(size.width(), size.height()); + } + } + + return QObject::eventFilter(obj, event); + } +#endif + +private: +#ifdef DISTRHO_UI_QT4 + bool qt_mouseDown; + QSizeGrip* qt_grip; + QEmbedWidget* qt_widget; +#else + bool gl_initiated; +#endif +}; + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // __DISTRHO_UI_INTERNAL_H__ diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp new file mode 100644 index 0000000..09faf86 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUILV2.cpp @@ -0,0 +1,372 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoDefines.h" + +#if defined(DISTRHO_PLUGIN_TARGET_LV2) && DISTRHO_PLUGIN_HAS_UI + +#include "DistrhoUIInternal.h" + +#include "lv2-sdk/lv2.h" +//#include "lv2-sdk/atom.h" +//#include "lv2-sdk/atom-util.h" +//#include "lv2-sdk/midi.h" +#include "lv2-sdk/patch.h" +#include "lv2-sdk/programs.h" +//#include "lv2-sdk/state.h" +#include "lv2-sdk/urid.h" +#include "lv2-sdk/ui.h" + +#include +#include +#include + +#ifdef DISTRHO_UI_QT4 +# include +#endif + +#ifndef DISTRHO_PLUGIN_URI +# error DISTRHO_PLUGIN_URI undefined! +#endif + +#define DISTRHO_LV2UI_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS) + +// ------------------------------------------------- + +START_NAMESPACE_DISTRHO + +#ifdef DISTRHO_UI_QT4 +class UILv2 : public QObject +#else +class UILv2 +#endif +{ +public: + UILv2(intptr_t parent, LV2UI_Controller controller, LV2UI_Write_Function writeFunction, LV2UI_Widget* widget, const LV2_Feature* const* features) + : ui(this, parent, setParameterCallback, setStateCallback, uiNoteCallback, uiResizeCallback), + lv2Controller(controller), + lv2WriteFunction(writeFunction), + lv2UiResize(nullptr), +#if DISTRHO_PLUGIN_WANT_STATE + uridIdPatchMessage(0), +#endif +#ifdef DISTRHO_UI_QT4 + uiTimer(0) +#else + threadExitNow(false), + threadRunning(false), + thread(uiThreadCallback, this) +#endif + { + // Get Features + for (uint32_t i = 0; features[i]; i++) + { + if (strcmp(features[i]->URI, LV2_URID__map) == 0) + { +#if DISTRHO_PLUGIN_WANT_STATE + LV2_URID_Map* uridMap = (LV2_URID_Map*)features[i]->data; + uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message); +#endif + } + + else if (strcmp(features[i]->URI, LV2_UI__resize) == 0) + lv2UiResize = (LV2UI_Resize*)features[i]->data; + } + +#ifndef DISTRHO_UI_QT4 + assert(parent); + + if (parent == 0) + return; +#endif + + if (lv2UiResize) + lv2UiResize->ui_resize(lv2UiResize->handle, ui.getWidth(), ui.getHeight()); + +#ifdef DISTRHO_UI_QT4 + uiTimer = startTimer(30); +#endif + + *widget = (void*)ui.getWindowId(); + } + + ~UILv2() + { +#ifdef DISTRHO_UI_QT4 + if (uiTimer) + killTimer(uiTimer); +#else + uiThreadClose(); +#endif + } + + // --------------------------------------------- + + void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) + { + if (format == 0) + { + if (bufferSize != sizeof(float)) + return; + if (int32_t(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS) < 0) + return; + + float value = *(float*)buffer; + ui.parameterChanged(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS, value); + } + // TODO - atom events + } + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void lv2ui_select_program(uint32_t bank, uint32_t program) + { + const uint32_t index = bank * 128 + program; + ui.programChanged(index); + } +#endif + + // --------------------------------------------- + +protected: + void setParameterValue(uint32_t index, float value) + { + if (lv2WriteFunction) + lv2WriteFunction(lv2Controller, DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + index, sizeof(float), 0, &value); + } + + void setState(const char* key, const char* value) + { + // TODO + (void)key; + (void)value; + + //if (lv2WriteFunction && uridMap) + { + + } + } + + void uiResize(unsigned int width, unsigned int height) + { + if (lv2UiResize) + lv2UiResize->ui_resize(lv2UiResize->handle, width, height); + } + +#ifdef DISTRHO_UI_QT4 + void timerEvent(QTimerEvent* event) + { + if (event->timerId() == uiTimer) + ui.idle(); + + QObject::timerEvent(event); + } +#else + void uiThreadRun() + { + threadRunning = true; + + while (! threadExitNow) + { + ui.idle(); + d_msleep(1000 / 25); // 25 FPS + } + + thread.detach(); + threadRunning = false; + } + + void uiThreadClose() + { + threadExitNow = true; + + while (threadRunning) + d_msleep(1000 / 25); // 25 FPS + } +#endif + +private: + UIInternal ui; + + // LV2 Stuff + LV2UI_Controller const lv2Controller; + LV2UI_Write_Function const lv2WriteFunction; + const LV2UI_Resize* lv2UiResize; + +#if DISTRHO_PLUGIN_WANT_STATE + LV2_URID uridIdPatchMessage; +#endif + +#ifdef DISTRHO_UI_QT4 + int uiTimer; +#else + // UI Thread + bool threadExitNow; + bool threadRunning; + std::thread thread; +#endif + + // --------------------------------------------- + // Callbacks + + static void setParameterCallback(void* ptr, uint32_t index, float value) + { + UILv2* _this_ = (UILv2*)ptr; + assert(_this_); + + _this_->setParameterValue(index, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { +#if DISTRHO_PLUGIN_WANT_STATE + UILv2* _this_ = (UILv2*)ptr; + assert(_this_); + + _this_->setState(key, value); +#else + // unused + (void)ptr; + (void)key; + (void)value; +#endif + } + + static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) + { +#if DISTRHO_PLUGIN_IS_SYNTH + UILv2* _this_ = (UILv2*)ptr; + assert(_this_); + + _this_->uiNote(onOff, channel, note, velocity); +#else + // unused + (void)ptr; + (void)onOff; + (void)channel; + (void)note; + (void)velocity; +#endif + } + + static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) + { + UILv2* _this_ = (UILv2*)ptr; + assert(_this_); + + _this_->uiResize(width, height); + } + +#ifndef DISTRHO_UI_QT4 + static void uiThreadCallback(void* ptr) + { + UILv2* _this_ = (UILv2*)ptr; + assert(_this_); + + _this_->uiThreadRun(); + } +#endif +}; + +// ------------------------------------------------- + +static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char*, LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + if (strcmp(uri, DISTRHO_PLUGIN_URI) != 0) + return nullptr; + + // Get parent + intptr_t parent = 0; + + for (uint32_t i = 0; features[i]; i++) + { + if (strcmp(features[i]->URI, LV2_UI__parent) == 0) + { + parent = (intptr_t)features[i]->data; + break; + } + } + + return new UILv2(parent, controller, writeFunction, widget, features); +} + +static void lv2ui_cleanup(LV2UI_Handle instance) +{ + UILv2* ui = (UILv2*)instance; + assert(ui); + + delete ui; +} + +static void lv2ui_port_event(LV2UI_Handle instance, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) +{ + UILv2* ui = (UILv2*)instance; + assert(ui); + + ui->lv2ui_port_event(portIndex, bufferSize, format, buffer); +} + +#if DISTRHO_LV2UI_USE_EXTENSION_DATA +# if DISTRHO_PLUGIN_WANT_PROGRAMS +static void lv2ui_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) +{ + UILv2* ui = (UILv2*)instance; + assert(ui); + + ui->lv2ui_select_program(bank, program); +} +# endif + +static const void* lv2ui_extension_data(const char* uri) +{ +# if DISTRHO_PLUGIN_WANT_PROGRAMS + static const LV2_Programs_UI_Interface programs = { lv2ui_select_program }; + if (strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) + return &programs; +# endif + + return nullptr; +} +#endif + +// ------------------------------------------------- + +static LV2UI_Descriptor uidescriptor = { + DISTRHO_UI_URI, + lv2ui_instantiate, + lv2ui_cleanup, + lv2ui_port_event, +#if DISTRHO_LV2UI_USE_EXTENSION_DATA + lv2ui_extension_data +#else + /* extension_data */ nullptr +#endif +}; + +END_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +DISTRHO_PLUGIN_EXPORT +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) +{ + USE_NAMESPACE_DISTRHO + return (index == 0) ? &uidescriptor : nullptr; +} + +// ------------------------------------------------- + +#endif // DISTRHO_PLUGIN_TARGET_LV2 && DISTRHO_PLUGIN_HAS_UI diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp new file mode 100644 index 0000000..6b5b79d --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGL.cpp @@ -0,0 +1,80 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoDefines.h" + +#ifdef DISTRHO_UI_OPENGL + +#include "DistrhoUIInternal.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- + +OpenGLUI::OpenGLUI() + : UI() +{ +} + +OpenGLUI::~OpenGLUI() +{ +} + +// ------------------------------------------------- +// Host UI State + +int OpenGLUI::d_uiGetModifiers() +{ + if (data && data->widget) + return puglGetModifiers(data->widget); + return 0; +} + +void OpenGLUI::d_uiIgnoreKeyRepeat(bool ignore) +{ + if (data && data->widget) + puglIgnoreKeyRepeat(data->widget, ignore); +} + +void OpenGLUI::d_uiRepaint() +{ + if (data && data->widget) + puglPostRedisplay(data->widget); +} + +// ------------------------------------------------- +// UI Callbacks + +void OpenGLUI::d_uiIdle() +{ + if (data && data->widget) + puglProcessEvents(data->widget); +} + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#if DISTRHO_OS_WINDOWS +# include "pugl/pugl_win.cpp" +#elif DISTRHO_OS_MAC +# include "pugl/pugl_osx.m" +#else +# include "pugl/pugl_x11.c" +#endif + +#endif // DISTRHO_UI_OPENGL diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp new file mode 100644 index 0000000..353ae6c --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIOpenGLExt.cpp @@ -0,0 +1,1486 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoDefines.h" + +#ifdef DISTRHO_UI_OPENGL + +# include "DistrhoUIOpenGLExt.h" + +#include +#include +#include + +#if DISTRHO_OS_LINUX +# include +#endif + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- +// Point + +Point::Point(int x, int y) + : _x(x), + _y(y) +{ +} + +Point::Point(const Point& pos) + : _x(pos._x), + _y(pos._y) +{ +} + +int Point::getX() const +{ + return _x; +} + +int Point::getY() const +{ + return _y; +} + +void Point::setX(int x) +{ + _x = x; +} + +void Point::setY(int y) +{ + _y = y; +} + +Point& Point::operator=(const Point& pos) +{ + _x = pos._x; + _y = pos._y; + return *this; +} + +Point& Point::operator+=(const Point& pos) +{ + _x += pos._x; + _y += pos._y; + return *this; +} + +Point& Point::operator-=(const Point& pos) +{ + _x -= pos._x; + _y -= pos._y; + return *this; +} + +bool Point::operator==(const Point& pos) const +{ + return (_x == pos._x && _y == pos._y); +} + +bool Point::operator!=(const Point& pos) const +{ + return !operator==(pos); +} + +// ------------------------------------------------- +// Point + +Size::Size(int width, int height) + : _width(width), + _height(height) +{ +} + +Size::Size(const Size& size) + : _width(size._width), + _height(size._height) +{ +} + +int Size::getWidth() const +{ + return _width; +} + +int Size::getHeight() const +{ + return _height; +} + +void Size::setWidth(int width) +{ + _width = width; +} + +void Size::setHeight(int height) +{ + _height = height; +} + +Size& Size::operator=(const Size& size) +{ + _width = size._width; + _height = size._height; + return *this; +} + +Size& Size::operator+=(const Size& size) +{ + _width += size._width; + _height += size._height; + return *this; +} + +Size& Size::operator-=(const Size& size) +{ + _width -= size._width; + _height -= size._height; + return *this; +} + +Size& Size::operator*=(int m) +{ + _width *= m; + _height *= m; + return *this; +} + +Size& Size::operator/=(int d) +{ + _width /= d; + _height /= d; + return *this; +} + +Size& Size::operator*=(float m) +{ + _width *= m; + _height *= m; + return *this; +} + +Size& Size::operator/=(float d) +{ + _width /= d; + _height /= d; + return *this; +} + +// ------------------------------------------------- +// Rectangle + +Rectangle::Rectangle(int x, int y, int width, int height) + : _pos(x, y), + _size(width, height) +{ +} + +Rectangle::Rectangle(int x, int y, const Size& size) + : _pos(x, y), + _size(size) +{ +} + +Rectangle::Rectangle(const Point& pos, int width, int height) + : _pos(pos), + _size(width, height) +{ +} + +Rectangle::Rectangle(const Point& pos, const Size& size) + : _pos(pos), + _size(size) +{ +} + +Rectangle::Rectangle(const Rectangle& rect) + : _pos(rect._pos), + _size(rect._size) +{ +} + +int Rectangle::getX() const +{ + return _pos._x; +} + +int Rectangle::getY() const +{ + return _pos._y; +} + +int Rectangle::getWidth() const +{ + return _size._width; +} + +int Rectangle::getHeight() const +{ + return _size._height; +} + +const Point& Rectangle::getPos() const +{ + return _pos; +} + +const Size& Rectangle::getSize() const +{ + return _size; +} + +bool Rectangle::contains(int x, int y) const +{ + return (x >= _pos._x && y >= _pos._y && x <= _pos._x+_size._width && y <= _pos._y+_size._height); +} + +bool Rectangle::contains(const Point& pos) const +{ + return contains(pos._x, pos._y); +} + +bool Rectangle::containsX(int x) const +{ + return (x >= _pos._x && x <= _pos._x+_size._width); +} + +bool Rectangle::containsY(int y) const +{ + return (y >= _pos._y && y <= _pos._y+_size._height); +} + +void Rectangle::setX(int x) +{ + _pos._x = x; +} + +void Rectangle::setY(int y) +{ + _pos._y = y; +} + +void Rectangle::setPos(int x, int y) +{ + _pos._x = x; + _pos._y = y; +} + +void Rectangle::setPos(const Point& pos) +{ + _pos = pos; +} + +void Rectangle::move(int x, int y) +{ + _pos._x += x; + _pos._y += y; +} + +void Rectangle::move(const Point& pos) +{ + _pos += pos; +} + +void Rectangle::setWidth(int width) +{ + _size._width = width; +} + +void Rectangle::setHeight(int height) +{ + _size._height = height; +} + +void Rectangle::setSize(int width, int height) +{ + _size._width = width; + _size._height = height; +} + +void Rectangle::setSize(const Size& size) +{ + _size = size; +} + +void Rectangle::grow(int m) +{ + _size *= m; +} + +void Rectangle::grow(float m) +{ + _size *= m; +} + +void Rectangle::grow(int width, int height) +{ + _size._width += width; + _size._height += height; +} + +void Rectangle::grow(const Size& size) +{ + _size += size; +} + +void Rectangle::shrink(int m) +{ + _size /= m; +} + +void Rectangle::shrink(float m) +{ + _size /= m; +} + +void Rectangle::shrink(int width, int height) +{ + _size._width -= width; + _size._height -= height; +} + +void Rectangle::shrink(const Size& size) +{ + _size -= size; +} + +Rectangle& Rectangle::operator=(const Rectangle& rect) +{ + _pos = rect._pos; + _size = rect._size; + return *this; +} + +Rectangle& Rectangle::operator+=(const Point& pos) +{ + _pos += pos; + return *this; +} + +Rectangle& Rectangle::operator-=(const Point& pos) +{ + _pos -= pos; + return *this; +} + +Rectangle& Rectangle::operator+=(const Size& size) +{ + _size += size; + return *this; +} + +Rectangle& Rectangle::operator-=(const Size& size) +{ + _size -= size; + return *this; +} + +// ------------------------------------------------- +// Image + +Image::Image(const char* data, int width, int height, GLenum format, GLenum type) + : _data(data), + _size(width, height), + _format(format), + _type(type) +{ +} + +Image::Image(const char* data, const Size& size, GLenum format, GLenum type) + : _data(data), + _size(size), + _format(format), + _type(type) +{ +} + +Image::Image(const Image& image) + : _data(image._data), + _size(image._size), + _format(image._format), + _type(image._type) +{ +} + +bool Image::isValid() const +{ + return (_data && getWidth() > 0 && getHeight() > 0); +} + +int Image::getWidth() const +{ + return _size.getWidth(); +} + +int Image::getHeight() const +{ + return _size.getHeight(); +} + +const Size& Image::getSize() const +{ + return _size; +} + +const char* Image::getData() const +{ + return _data; +} + +GLenum Image::getFormat() const +{ + return _format; +} + +GLenum Image::getType() const +{ + return _type; +} + +Image& Image::operator=(const Image& image) +{ + _data = image._data; + _size = image._size; + _format = image._format; + _type = image._type; + return *this; +} + +// ------------------------------------------------- +// ImageButton + +ImageButton::ImageButton(const Image& imageNormal, const Image& imageHover, const Image& imageDown, const Point& pos) + : _imageNormal(imageNormal), + _imageHover(imageHover), + _imageDown(imageDown), + _curImage(&_imageNormal), + _pos(pos), + _area(pos, imageNormal.getSize()) +{ +} + +ImageButton::ImageButton(const ImageButton& imageButton) + : _imageNormal(imageButton._imageNormal), + _imageHover(imageButton._imageHover), + _imageDown(imageButton._imageDown), + _curImage(&_imageNormal), + _pos(imageButton._pos), + _area(imageButton._area) +{ +} + +int ImageButton::getWidth() const +{ + return _area.getWidth(); +} + +int ImageButton::getHeight() const +{ + return _area.getHeight(); +} + +const Size& ImageButton::getSize() const +{ + return _area.getSize(); +} + +ImageButton& ImageButton::operator=(const ImageButton& imageButton) +{ + _imageNormal = imageButton._imageNormal; + _imageHover = imageButton._imageHover; + _imageDown = imageButton._imageDown; + _curImage = &_imageNormal; + _pos = imageButton._pos; + _area = imageButton._area; + return *this; +} + +// ------------------------------------------------- +// ImageKnob + +ImageKnob::ImageKnob(const Image& image, const Point& pos, Orientation orientation) + : _image(image), + _pos(pos), + _orientation(orientation), + _isVertical(image.getHeight() > image.getWidth()), + _layerSize(_isVertical ? image.getWidth() : image.getHeight()), + _layerCount(_isVertical ? image.getHeight()/_layerSize : image.getWidth()/_layerSize), + _area(_pos, _layerSize, _layerSize) +{ + _min = 0.0f; + _max = 1.0f; + _value = _min; +} + +ImageKnob::ImageKnob(const ImageKnob& imageKnob) + : _image(imageKnob._image), + _pos(imageKnob._pos), + _orientation(imageKnob._orientation), + _isVertical(imageKnob._isVertical), + _layerSize(imageKnob._layerSize), + _layerCount(imageKnob._layerCount), + _area(imageKnob._area) +{ + _min = imageKnob._min; + _max = imageKnob._max; + _value = imageKnob._value; +} + +void ImageKnob::setOrientation(Orientation orientation) +{ + _orientation = orientation; +} + +void ImageKnob::setRange(float min, float max) +{ + _min = min; + _max = max; + + if (_value < _min) + _value = _min; + else if (_value > _max) + _value = _max; +} + +void ImageKnob::setValue(float value) +{ + if (value < _min) + value = _min; + else if (value > _max) + value = _max; + + _value = value; +} + +ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) +{ + _image = imageKnob._image; + _pos = imageKnob._pos; + _orientation = imageKnob._orientation; + _isVertical = imageKnob._isVertical; + _layerSize = imageKnob._layerSize; + _layerCount = imageKnob._layerCount; + _area = imageKnob._area; + _min = imageKnob._min; + _max = imageKnob._max; + _value = imageKnob._value; + return *this; +} + +// ------------------------------------------------- +// ImageSlider + +ImageSlider::ImageSlider(const Image& image, const Point& startPos, const Point& endPos) + : _image(image), + _startPos(startPos), + _endPos(endPos), + _area(startPos.getX(), startPos.getY(), + endPos.getX() > startPos.getX() ? endPos.getX() + image.getWidth() - startPos.getX() : image.getWidth(), + endPos.getY() > startPos.getY() ? endPos.getY() + image.getHeight() - startPos.getY() : image.getHeight()) +{ + _min = 0.0f; + _max = 1.0f; + _value = _min; +} + +ImageSlider::ImageSlider(const ImageSlider& imageSlider) + : _image(imageSlider._image), + _startPos(imageSlider._startPos), + _endPos(imageSlider._endPos), + _area(imageSlider._area) +{ + _min = imageSlider._min; + _max = imageSlider._max; + _value = imageSlider._value; +} + +int ImageSlider::getWidth() const +{ + return _image.getWidth(); +} + +int ImageSlider::getHeight() const +{ + return _image.getHeight(); +} + +void ImageSlider::setRange(float min, float max) +{ + _min = min; + _max = max; + + if (_value < _min) + _value = _min; + else if (_value > _max) + _value = _max; +} + +void ImageSlider::setValue(float value) +{ + if (value < _min) + value = _min; + else if (value > _max) + value = _max; + + _value = value; +} + +ImageSlider& ImageSlider::operator=(const ImageSlider& imageSlider) +{ + _image = imageSlider._image; + _startPos = imageSlider._startPos; + _endPos = imageSlider._endPos; + _area = imageSlider._area; + _min = imageSlider._min; + _max = imageSlider._max; + _value = imageSlider._value; + return *this; +} + +// ------------------------------------------------- + +class OpenGLDialog +{ +public: + OpenGLDialog(PuglView* parentView, const Size& parentSize, const Image& image_, const char* title) + : image(image_) + { +#if DISTRHO_OS_LINUX + bool addToDesktop = false; +#else + bool addToDesktop = true; +#endif + + view = puglCreate(0, title, image.getWidth(), image.getHeight(), false, addToDesktop); + closed = bool(!view); + + if (closed) + return; + + puglSetHandle(view, this); + puglSetDisplayFunc(view, onDisplayCallback); + puglSetKeyboardFunc(view, onKeyboardCallback); + puglSetMotionFunc(view, onMotionCallback); + puglSetMouseFunc(view, onMouseCallback); + puglSetScrollFunc(view, onScrollCallback); + puglSetSpecialFunc(view, onSpecialCallback); + puglSetReshapeFunc(view, onReshapeCallback); + puglSetCloseFunc(view, onCloseCallback); + +#if DISTRHO_OS_LINUX + Display* display = view->impl->display; + Window thisWindow = view->impl->win; + Window parentWindow = parentView->impl->win; + + int x = (parentSize.getWidth()-image.getWidth())/2; + int y = (parentSize.getHeight()-image.getHeight())/2; + Window childRet; + + if (XTranslateCoordinates(display, parentWindow, thisWindow, x, y, &x, &y, &childRet)) + XMoveWindow(display, thisWindow, x, y); + + XSetTransientForHint(display, thisWindow, parentWindow); + XMapRaised(display, thisWindow); +#endif + } + + ~OpenGLDialog() + { + if (view) + puglDestroy(view); + } + + bool idle() + { + if (view) + puglProcessEvents(view); + + return !closed; + } + + void close() + { + closed = true; + } + + void raise() + { + if (view) + { +#if DISTRHO_OS_LINUX + Display* display = view->impl->display; + Window window = view->impl->win; + XRaiseWindow(display, window); + XSetInputFocus(display, window, RevertToPointerRoot, CurrentTime); +#endif + } + } + +protected: + void onDisplay() + { + glClear(GL_COLOR_BUFFER_BIT); + + glRasterPos2i(0, image.getHeight()); + glDrawPixels(image.getWidth(), image.getHeight(), image.getFormat(), image.getType(), image.getData()); + } + + void onKeyboard(bool press, uint32_t key) + { + if (press && key == CHAR_ESCAPE) + closed = true; + } + + void onMotion(int, int) + { + } + + void onMouse(int, bool, int, int) + { + } + + void onReshape(int width, int height) + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, 0, 1); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + void onScroll(float, float) + { + } + + void onSpecial(bool, Key) + { + } + + void onClose() + { + closed = true; + } + +private: + PuglView* view; + bool closed; + + const Image image; + + // Callbacks + static void onDisplayCallback(PuglView* view) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onDisplay(); + } + + static void onKeyboardCallback(PuglView* view, bool press, uint32_t key) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onKeyboard(press, key); + } + + static void onMotionCallback(PuglView* view, int x, int y) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onMotion(x, y); + } + + static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onMouse(button, press, x, y); + } + + static void onReshapeCallback(PuglView* view, int width, int height) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onReshape(width, height); + } + + static void onScrollCallback(PuglView* view, float dx, float dy) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onScroll(dx, dy); + } + + static void onSpecialCallback(PuglView* view, bool press, PuglKey key) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onSpecial(press, (Key)key); + } + + static void onCloseCallback(PuglView* view) + { + OpenGLDialog* _this_ = (OpenGLDialog*)puglGetHandle(view); + _this_->onClose(); + } +}; + +enum ObjectType { + OBJECT_NULL = 0, + OBJECT_BUTTON = 1, + OBJECT_KNOB = 2, + OBJECT_SLIDER = 3 +}; + +#if DISTRHO_OS_LINUX +struct LinuxData { + Display* display; + Window window; + + char colorData[8]; + XColor colorBlack; + Pixmap pixmapBlack; + Cursor cursorBlack; + + LinuxData() + : display(nullptr), + window(0), + colorData{0}, + colorBlack{0, 0, 0, 0, 0, 0} + { + } + + ~LinuxData() + { + XFreeCursor(display, cursorBlack); + } +}; +#endif + +struct OpenGLExtUIPrivateData { + int initialPosX; + int initialPosY; + void* lastObj; + Point lastCursorPos; + ObjectType lastObjType; + + Image background; + std::vector buttons; + std::vector knobs; + std::vector sliders; + OpenGLDialog* dialog; + +#if DISTRHO_OS_LINUX + LinuxData linuxData; +#endif + + OpenGLExtUIPrivateData() + : initialPosX(0), + initialPosY(0), + lastObj(nullptr), + lastCursorPos(0, 0), + lastObjType(OBJECT_NULL), + background(nullptr, 0, 0), + dialog(nullptr) + { + } + + void showCursor() + { +#if DISTRHO_OS_LINUX + if (lastCursorPos != Point(-1, -2)) + XWarpPointer(linuxData.display, None, DefaultRootWindow(linuxData.display), 0, 0, 0, 0, lastCursorPos.getX(), lastCursorPos.getY()); + + XUndefineCursor(linuxData.display, linuxData.window); +#endif + } + + void hideCursor() + { +#if DISTRHO_OS_LINUX + Window root, child; + int rootX, rootY, winX, winY; + unsigned int mask; + + if (XQueryPointer(linuxData.display, DefaultRootWindow(linuxData.display), &root, &child, &rootX, &rootY, &winX, &winY, &mask)) + lastCursorPos = Point(rootX, rootY); + else + lastCursorPos = Point(-1, -2); + + XDefineCursor(linuxData.display, linuxData.window, linuxData.cursorBlack); +#endif + } +}; + +// ------------------------------------------------- + +OpenGLExtUI::OpenGLExtUI() + : OpenGLUI() +{ + data = new OpenGLExtUIPrivateData; +} + +OpenGLExtUI::~OpenGLExtUI() +{ + assert(! data->dialog); + delete data; +} + +// ------------------------------------------------- +// UI Callbacks + +void OpenGLExtUI::d_uiIdle() +{ + if (data->dialog && ! data->dialog->idle()) + { + delete data->dialog; + data->dialog = nullptr; + } + + OpenGLUI::d_uiIdle(); +} + +// ------------------------------------------------- +// Extended Calls + +void OpenGLExtUI::setBackgroundImage(const Image& image) +{ + data->background = image; + d_uiRepaint(); +} + +void OpenGLExtUI::addImageButton(ImageButton* button) +{ + data->buttons.push_back(button); +} + +void OpenGLExtUI::addImageKnob(ImageKnob* knob) +{ + data->knobs.push_back(knob); +} + +void OpenGLExtUI::addImageSlider(ImageSlider* slider) +{ + data->sliders.push_back(slider); +} + +void OpenGLExtUI::showImageModalDialog(const Image& image, const char* title) +{ + data->dialog = new OpenGLDialog(OpenGLUI::data->widget, Size(d_width(), d_height()), image, title); +} + +// ------------------------------------------------- +// Extended Callbacks + +void OpenGLExtUI::imageButtonClicked(ImageButton*) +{ +} + +void OpenGLExtUI::imageKnobDragStarted(ImageKnob*) +{ +} + +void OpenGLExtUI::imageKnobDragFinished(ImageKnob*) +{ +} + +void OpenGLExtUI::imageKnobValueChanged(ImageKnob*, float) +{ +} + +void OpenGLExtUI::imageSliderValueChanged(ImageSlider*, float) +{ +} + +void OpenGLExtUI::imageSliderDragStarted(ImageSlider*) +{ +} + +void OpenGLExtUI::imageSliderDragFinished(ImageSlider*) +{ +} + +void OpenGLExtUI::d_onInit() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, d_width(), d_height(), 0, 0, 1); + glViewport(0, 0, d_width(), d_height()); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + +#if DISTRHO_OS_LINUX + data->linuxData.display = OpenGLUI::data->widget->impl->display; + data->linuxData.window = OpenGLUI::data->widget->impl->win; + data->linuxData.pixmapBlack = XCreateBitmapFromData(data->linuxData.display, data->linuxData.window, data->linuxData.colorData, 8, 8); + data->linuxData.cursorBlack = XCreatePixmapCursor(data->linuxData.display, data->linuxData.pixmapBlack, data->linuxData.pixmapBlack, + &data->linuxData.colorBlack, &data->linuxData.colorBlack, 0, 0); +#endif +} + +void OpenGLExtUI::d_onDisplay() +{ + glClear(GL_COLOR_BUFFER_BIT); + + // Background + if (data->background.isValid()) + { + glRasterPos2i(0, d_height()); + glDrawPixels(d_width(), d_height(), data->background._format, data->background._type, data->background._data); + } + + // Buttons + if (data->buttons.size() > 0) + { + for (auto it = data->buttons.begin(); it != data->buttons.end(); it++) + { + ImageButton* button(*it); + + glRasterPos2i(button->_pos.getX(), button->_pos.getY() + button->_area.getHeight()); + glDrawPixels(button->getWidth(), button->getHeight(), button->_curImage->getFormat(), button->_curImage->getType(), button->_curImage->getData()); + } + } + + // Knobs + if (data->knobs.size() > 0) + { + for (auto it = data->knobs.begin(); it != data->knobs.end(); it++) + { + ImageKnob* knob(*it); + + float vper = (knob->_value - knob->_min) / (knob->_max - knob->_min); + + int layerDataSize = knob->_layerSize * knob->_layerSize * 4; + int imageDataSize = layerDataSize * knob->_layerCount; + int imageDataOffset = imageDataSize - layerDataSize - layerDataSize * rint(vper*(knob->_layerCount-1)); + + glRasterPos2i(knob->_pos.getX(), knob->_pos.getY()+knob->_area.getHeight()); + glDrawPixels(knob->_layerSize, knob->_layerSize, knob->_image.getFormat(), knob->_image.getType(), knob->_image.getData() + imageDataOffset); + } + } + + // Sliders + if (data->sliders.size() > 0) + { + for (auto it = data->sliders.begin(); it != data->sliders.end(); it++) + { + ImageSlider* slider(*it); + + float vper = (slider->_value - slider->_min) / (slider->_max - slider->_min); + int x = slider->_area.getX(); + int y = slider->_area.getY(); + + if (slider->_endPos.getX() > slider->_startPos.getX()) + // horizontal + x += rint(vper * (slider->_area.getWidth()-slider->getWidth())); + else + // vertical + y += slider->_area.getHeight() - rint(vper * (slider->_area.getHeight()-slider->getHeight())); + +#if 0 // DEBUG + glColor3i(160, 90, 161); + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex3f(slider->_area.getX(), slider->_area.getY(), 0); + glTexCoord2f(1.0f, 0.0f); glVertex3f(slider->_area.getX()+slider->_area.getWidth(), slider->_area.getY(), 0); + glTexCoord2f(1.0f, 1.0f); glVertex3f(slider->_area.getX()+slider->_area.getWidth(), slider->_area.getY()+slider->_area.getHeight(), 0); + glTexCoord2f(0.0f, 1.0f); glVertex3f(slider->_area.getX(), slider->_area.getY()+slider->_area.getHeight(), 0); + glEnd(); + glColor3i(160, 90, 161); +#endif + + glRasterPos2i(x, y); + glDrawPixels(slider->getWidth(), slider->getHeight(), slider->_image._format, slider->_image._type, slider->_image._data); + } + } +} + +void OpenGLExtUI::d_onKeyboard(bool press, uint32_t key) +{ + if (data->dialog) + return; + + (void)press; + (void)key; +} + +void OpenGLExtUI::d_onMotion(int x, int y) +{ + if (data->dialog) + return; + + // Buttons + if (data->buttons.size() > 0) + { + for (auto it = data->buttons.begin(); it != data->buttons.end(); it++) + { + ImageButton* button(*it); + + if (button == data->lastObj) + continue; + + if (button->_area.contains(x, y)) + { + if (button->_curImage != &button->_imageHover) + d_uiRepaint(); + + button->_curImage = &button->_imageHover; + } + else + { + if (button->_curImage != &button->_imageNormal) + d_uiRepaint(); + + button->_curImage = &button->_imageNormal; + } + } + } + + if (data->lastObjType == OBJECT_NULL || ! data->lastObj) + return; + + // Knobs + if (data->lastObjType == OBJECT_KNOB && data->knobs.size() > 0) + { + for (auto it = data->knobs.begin(); it != data->knobs.end(); it++) + { + ImageKnob* knob(*it); + + if (knob != data->lastObj) + continue; + + if (knob->_orientation == ImageKnob::Horizontal) + { + int movX = x - data->initialPosX; + + if (movX != 0) + { + int d = (d_uiGetModifiers() & MOD_SHIFT) ? 2000 : 200; + float value = knob->_value + (knob->_max - knob->_min) / d * movX; + + if (value < knob->_min) + value = knob->_min; + else if (value > knob->_max) + value = knob->_max; + + if (knob->_value != value) + { + knob->_value = value; + imageKnobValueChanged(knob, value); + + d_uiRepaint(); + } + } + } + else if (knob->_orientation == ImageKnob::Vertical) + { + int movY = data->initialPosY - y; + + if (movY != 0) + { + int d = (d_uiGetModifiers() & MOD_SHIFT) ? 2000 : 200; + float value = knob->_value + (knob->_max - knob->_min) / d * movY; + + if (value < knob->_min) + value = knob->_min; + else if (value > knob->_max) + value = knob->_max; + + if (knob->_value != value) + { + knob->_value = value; + imageKnobValueChanged(knob, value); + + d_uiRepaint(); + } + } + } + + data->initialPosX = x; + data->initialPosY = y; + + return; + } + } + + // Sliders + if (data->lastObjType == OBJECT_SLIDER && data->sliders.size() > 0) + { + for (auto it = data->sliders.begin(); it != data->sliders.end(); it++) + { + ImageSlider* slider(*it); + + if (slider != data->lastObj) + continue; + + bool horizontal = slider->_endPos.getX() > slider->_startPos.getX(); + + if ((horizontal && slider->_area.containsX(x)) || (slider->_area.containsY(y) && ! horizontal)) + { + float vper; + + if (horizontal) + // horizontal + vper = float(x - slider->_area.getX()) / slider->_area.getWidth(); + else + // vertical + vper = float(y - slider->_area.getY()) / slider->_area.getHeight(); + + float value = slider->_max - vper * (slider->_max - slider->_min); + + if (value < slider->_min) + value = slider->_min; + else if (value > slider->_max) + value = slider->_max; + + if (slider->_value != value) + { + slider->_value = value; + imageSliderValueChanged(slider, value); + + d_uiRepaint(); + } + } + else if (y < slider->_area.getY()) + { + if (slider->_value != slider->_max) + { + slider->_value = slider->_max; + imageSliderValueChanged(slider, slider->_max); + + d_uiRepaint(); + } + } + else + { + if (slider->_value != slider->_min) + { + slider->_value = slider->_min; + imageSliderValueChanged(slider, slider->_min); + + d_uiRepaint(); + } + } + + return; + } + } +} + +void OpenGLExtUI::d_onMouse(int button, bool press, int x, int y) +{ + if (data->dialog) + { + data->dialog->raise(); + return; + } + + if ((!press) && data->lastObjType == OBJECT_BUTTON && data->lastObj && data->buttons.size() > 0) + { + for (auto it = data->buttons.begin(); it != data->buttons.end(); it++) + { + ImageButton* button(*it); + + if (button == data->lastObj) + { + if (button->_area.contains(x, y)) + imageButtonClicked(button); + + button->_curImage = &button->_imageNormal; + d_uiRepaint(); + + break; + } + } + } + + if (button != 1) + return; + + if (data->lastObjType != OBJECT_NULL && data->lastObj) + { + if (data->lastObjType == OBJECT_KNOB) + { + data->showCursor(); + imageKnobDragFinished((ImageKnob*)data->lastObj); + } + else if (data->lastObjType == OBJECT_SLIDER) + imageSliderDragFinished((ImageSlider*)data->lastObj); + } + + data->initialPosX = 0; + data->initialPosY = 0; + data->lastObj = nullptr; + data->lastObjType = OBJECT_NULL; + + if (! press) + return; + + // Buttons + if (data->buttons.size() > 0) + { + for (auto it = data->buttons.begin(); it != data->buttons.end(); it++) + { + ImageButton* button(*it); + + if (button->_area.contains(x, y)) + { + data->initialPosX = x; + data->initialPosY = y; + data->lastObj = button; + data->lastObjType = OBJECT_BUTTON; + + button->_curImage = &button->_imageDown; + d_uiRepaint(); + + return; + } + } + } + + // Knobs + if (data->knobs.size() > 0) + { + for (auto it = data->knobs.begin(); it != data->knobs.end(); it++) + { + ImageKnob* knob(*it); + + if (knob->_area.contains(x, y)) + { + data->initialPosX = x; + data->initialPosY = y; + data->lastObj = knob; + data->lastObjType = OBJECT_KNOB; + data->hideCursor(); + imageKnobDragStarted(knob); + return; + } + } + } + + // Sliders + if (data->sliders.size() > 0) + { + for (auto it = data->sliders.begin(); it != data->sliders.end(); it++) + { + ImageSlider* slider(*it); + + if (slider->_area.contains(x, y)) + { + data->initialPosX = x; + data->initialPosY = y; + data->lastObj = slider; + data->lastObjType = OBJECT_SLIDER; + imageSliderDragStarted(slider); + + float vper; + + if (slider->_endPos.getX() > slider->_startPos.getX()) + // horizontal + vper = float(x - slider->_area.getX()) / slider->_area.getWidth(); + else + // vertical + vper = float(y - slider->_area.getY()) / slider->_area.getHeight(); + + float value = slider->_max - vper * (slider->_max - slider->_min); + + if (value < slider->_min) + value = slider->_min; + else if (value > slider->_max) + value = slider->_max; + + if (slider->_value != value) + { + slider->_value = value; + imageSliderValueChanged(slider, value); + + d_uiRepaint(); + } + + return; + } + } + } +} + +void OpenGLExtUI::d_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, 1); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void OpenGLExtUI::d_onScroll(float dx, float dy) +{ + if (data->dialog) + return; + + // unused + (void)dx; + (void)dy; +} + +void OpenGLExtUI::d_onSpecial(bool press, Key key) +{ + if (data->dialog) + return; + + // unused + (void)press; + (void)key; +} + +void OpenGLExtUI::d_onClose() +{ + if (data->dialog) + { + data->dialog->close(); + delete data->dialog; + data->dialog = nullptr; + } + + if (data->lastObjType != OBJECT_NULL && data->lastObj) + { + if (data->lastObjType == OBJECT_KNOB) + { + data->showCursor(); + imageKnobDragFinished((ImageKnob*)data->lastObj); + } + else if (data->lastObjType == OBJECT_SLIDER) + imageSliderDragFinished((ImageSlider*)data->lastObj); + } + + data->initialPosX = 0; + data->initialPosY = 0; + data->lastObj = nullptr; + data->lastObjType = OBJECT_NULL; + + data->buttons.clear(); + data->knobs.clear(); + data->sliders.clear(); +} + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_OPENGL diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp new file mode 100644 index 0000000..48bd887 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/DistrhoUIQt4.cpp @@ -0,0 +1,75 @@ +/* + * DISTHRO Plugin Toolkit (DPT) + * Copyright (C) 2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the license see the GPL.txt file + */ + +#include "DistrhoDefines.h" + +#ifdef DISTRHO_UI_QT4 + +#include "DistrhoUIInternal.h" + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------- +// QEmbedWidget + +QEmbedWidget::QEmbedWidget() +{ +} + +QEmbedWidget::~QEmbedWidget() +{ +} + +void QEmbedWidget::embedInto(WId id) +{ +#ifdef Q_WS_X11 + QX11EmbedWidget::embedInto(id); +#endif +} + +WId QEmbedWidget::containerWinId() const +{ +#ifdef Q_WS_X11 + return QX11EmbedWidget::containerWinId(); +#endif +} + +// ------------------------------------------------- +// Qt4UI + +Qt4UI::Qt4UI() + : UI(), + QWidget(nullptr) +{ +} + +Qt4UI::~Qt4UI() +{ +} + +// ------------------------------------------------- +// UI Callbacks + +void Qt4UI::d_uiIdle() +{ +} + +// ------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_QT4 diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/dssi b/c++/carla-backend/distrho-plugin-toolkit/src/dssi new file mode 120000 index 0000000..7293726 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/dssi @@ -0,0 +1 @@ +../../../carla-includes/dssi \ No newline at end of file diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/ladspa b/c++/carla-backend/distrho-plugin-toolkit/src/ladspa new file mode 120000 index 0000000..361ed56 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/ladspa @@ -0,0 +1 @@ +../../../carla-includes/ladspa \ No newline at end of file diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk b/c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk new file mode 120000 index 0000000..e7df02c --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/lv2-sdk @@ -0,0 +1 @@ +../../../carla-includes/lv2 \ No newline at end of file diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h new file mode 100644 index 0000000..9467c31 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl.h @@ -0,0 +1,346 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software 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 + +/* + 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 /* 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 +#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. +*/ +PUGL_API PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool addToDesktop = true); + +/** + Set the handle to be passed to all callbacks. + + This is generally a pointer to a struct which contains all necessary state. + Everything needed in callbacks should be here, not in static variables. + + Note the lack of this facility makes GLUT unsuitable for plugins or + non-trivial programs; this mistake is largely why Pugl exists. +*/ +PUGL_API void +puglSetHandle(PuglView* view, PuglHandle handle); + +/** + Get the handle to be passed to all callbacks. +*/ +PUGL_API PuglHandle +puglGetHandle(PuglView* view); + +/** + Get the 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 */ diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h new file mode 100644 index 0000000..ea890bb --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_internal.h @@ -0,0 +1,132 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software 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 ignoreKeyRepeat; + bool redisplay; +}; + +void +puglSetHandle(PuglView* view, PuglHandle handle) +{ + view->handle = handle; +} + +PuglHandle +puglGetHandle(PuglView* view) +{ + return view->handle; +} + +int +puglGetModifiers(PuglView* view) +{ + return view->mods; +} + +static inline 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(); + (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; +} diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m new file mode 100644 index 0000000..b3b6bae --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_osx.m @@ -0,0 +1,325 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software 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 + +#import + +#include "pugl_internal.h" + +@interface PuglOpenGLView : NSOpenGLView +{ + int colorBits; + int depthBits; +@public + PuglView* view; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; +- (void) mouseMoved:(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 (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +- (void) drawRect:(NSRect)rect +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (self->view->displayFunc) { + self->view->displayFunc(self->view); + } + + glFlush(); + glSwapAPPLE(); +} + +static int +getModifiers(unsigned modifierFlags) +{ + int 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) mouseMoved:(NSEvent*)event +{ + if (view->motionFunc) { + NSPoint loc = [event locationInWindow]; + view->mods = getModifiers([event modifierFlags]); + view->motionFunc(view, loc.x, loc.y); + } +} + +- (void) mouseDown:(NSEvent*)event +{ + if (view->mouseFunc) { + NSPoint loc = [event locationInWindow]; + view->mods = getModifiers([event modifierFlags]); + view->mouseFunc(view, 1, true, loc.x, loc.y); + } +} + +- (void) mouseUp:(NSEvent*)event +{ + if (view->mouseFunc) { + NSPoint loc = [event locationInWindow]; + view->mods = getModifiers([event modifierFlags]); + view->mouseFunc(view, 1, false, loc.x, loc.y); + } +} + +- (void) rightMouseDown:(NSEvent*)event +{ + if (view->mouseFunc) { + NSPoint loc = [event locationInWindow]; + view->mods = getModifiers([event modifierFlags]); + view->mouseFunc(view, 3, true, loc.x, loc.y); + } +} + +- (void) rightMouseUp:(NSEvent*)event +{ + if (view->mouseFunc) { + NSPoint loc = [event locationInWindow]; + view->mods = getModifiers([event modifierFlags]); + view->mouseFunc(view, 3, false, loc.x, loc.y); + } +} + +- (void) scrollWheel:(NSEvent*)event +{ + if (view->scrollFunc) { + view->mods = getModifiers([event modifierFlags]); + view->scrollFunc(view, [event deltaX], [event deltaY]); + } +} + +- (void) keyDown:(NSEvent*)event +{ + if (view->keyboardFunc && !(view->ignoreKeyRepeat && [event isARepeat])) { + NSString* chars = [event characters]; + view->mods = getModifiers([event modifierFlags]); + view->keyboardFunc(view, true, [chars characterAtIndex:0]); + } +} + +- (void) keyUp:(NSEvent*)event +{ + if (view->keyboardFunc) { + NSString* chars = [event characters]; + view->mods = getModifiers([event modifierFlags]); + view->keyboardFunc(view, false, [chars characterAtIndex:0]); + } +} + +- (void) flagsChanged:(NSEvent*)event +{ + if (view->specialFunc) { + int mods = getModifiers([event modifierFlags]); + if ((mods & PUGL_MOD_SHIFT) != (view->mods & PUGL_MOD_SHIFT)) { + view->specialFunc(view, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); + } else if ((mods & PUGL_MOD_CTRL) != (view->mods & PUGL_MOD_CTRL)) { + view->specialFunc(view, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); + } else if ((mods & PUGL_MOD_ALT) != (view->mods & PUGL_MOD_ALT)) { + view->specialFunc(view, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); + } else if ((mods & PUGL_MOD_SUPER) != (view->mods & PUGL_MOD_SUPER)) { + view->specialFunc(view, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); + } + view->mods = mods; + } +} + +@end + +struct PuglInternalsImpl { + PuglOpenGLView* view; + NSModalSession session; + id window; +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable) +{ + 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]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + id window = [[[NSWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 512, 512) + styleMask:NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO] + autorelease]; + + [window cascadeTopLeftFromPoint:NSMakePoint(20, 20)]; + [window setTitle:titleString]; + [window setAcceptsMouseMovedEvents:YES]; + + impl->view = [PuglOpenGLView new]; + impl->window = window; + impl->view->view = view; + + [window setContentView:impl->view]; + [NSApp activateIgnoringOtherApps:YES]; + [window makeFirstResponder:impl->view]; + + impl->session = [NSApp beginModalSessionForWindow:view->impl->window]; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + [NSApp endModalSession:view->impl->session]; + [view->impl->view release]; + free(view->impl); + free(view); +} + +void +puglDisplay(PuglView* view) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + view->redisplay = false; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + NSInteger response = [NSApp runModalSession:view->impl->session]; + if (response != NSRunContinuesResponse) { + if (view->closeFunc) { + view->closeFunc(view); + } + } + + if (view->redisplay) { + puglDisplay(view); + } + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->view; +} diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp new file mode 100644 index 0000000..64d15a6 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_win.cpp @@ -0,0 +1,339 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software 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 +#include +#include + +#include "pugl_internal.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; +}; + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable) +{ + 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; + + WNDCLASS wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = wndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = 0; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "Pugl"; + RegisterClass(&wc); + + impl->hwnd = CreateWindow( + "Pugl", title, + WS_VISIBLE | (parent ? WS_CHILD : (WS_POPUPWINDOW | WS_CAPTION)), + 0, 0, width, height, + (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); + free(view->impl); + free(view); +} + +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) +{ + 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) +{ + MSG msg; + PAINTSTRUCT ps; + PuglKey key; + + setModifiers(view); + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + puglReshape(view, view->width, view->height); + break; + case WM_PAINT: + BeginPaint(view->impl->hwnd, &ps); + puglDisplay(view); + EndPaint(view->impl->hwnd, &ps); + break; + case WM_MOUSEMOVE: + if (view->motionFunc) { + view->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: + 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: + 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) { + puglDisplay(view); + } + + 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: + PostQuitMessage(0); + return 0; + case WM_DESTROY: + return 0; + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + PostMessage(hwnd, message, wParam, lParam); + 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; +} diff --git a/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c new file mode 100644 index 0000000..1467f64 --- /dev/null +++ b/c++/carla-backend/distrho-plugin-toolkit/src/pugl/pugl_x11.c @@ -0,0 +1,384 @@ +/* + Copyright 2012 David Robillard + Copyright 2011-2012 Ben Loftis, Harrison Consoles + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_x11.c X11 Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pugl_internal.h" + +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 addToDesktop) +{ + 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; + printf("singlebuffered rendering will be used, no doublebuffering available\n"); + } else { + impl->doubleBuffered = True; + printf("doublebuffered rendering available\n"); + } + + int glxMajor, glxMinor; + glXQueryVersion(impl->display, &glxMajor, &glxMinor); + printf("GLX-Version %d.%d\n", glxMajor, glxMinor); + + 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 + | 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 (addToDesktop) + XMapRaised(impl->display, impl->win); + + if (glXIsDirect(impl->display, impl->ctx)) { + printf("DRI enabled\n"); + } else { + printf("no DRI available\n"); + } + + 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, int xstate) +{ + 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); + view->redisplay = false; + break; + case MotionNotify: + setModifiers(view, event.xmotion.state); + if (view->motionFunc) { + view->motionFunc(view, event.xmotion.x, event.xmotion.y); + } + break; + case ButtonPress: + setModifiers(view, event.xbutton.state); + 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); + 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); + 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); + 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 = XKeycodeToKeysym( + view->impl->display, event.xkey.keycode, 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; + 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; +}