@@ -1,5 +1,5 @@ | |||
DISTRHO Plugin Framework (DPF) | |||
Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> | |||
Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -77,12 +77,12 @@ all: | |||
# Common | |||
$(BUILD_DIR)/%.c.o: %.c | |||
-@mkdir -p $(BUILD_DIR) | |||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||
@echo "Compiling $<" | |||
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||
$(BUILD_DIR)/%.cpp.o: %.cpp | |||
-@mkdir -p $(BUILD_DIR) | |||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
@@ -27,6 +27,9 @@ List of plugins made with DPF:<br/> | |||
- [DISTRHO ndc Plugs](https://github.com/DISTRHO/ndc-Plugs) | |||
- [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) (work in progress) | |||
- [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | |||
- [DragonFly-Reverb](https://github.com/michaelwillis/dragonfly-reverb) | |||
- [Wolf-Shaper](https://github.com/pdesaulniers/wolf-shaper) | |||
- [YK Chorus](https://github.com/SpotlightKid/ykchorus) | |||
Plugin examples are available in the `example/` folder inside this repo.<br/> | |||
@@ -240,7 +240,7 @@ public: | |||
{ | |||
public: | |||
virtual ~Callback() {} | |||
virtual void imageSwitchClicked(ImageSwitch* imageButton, bool down) = 0; | |||
virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0; | |||
}; | |||
explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; | |||
@@ -92,6 +92,8 @@ public: | |||
bool openFileBrowser(const FileBrowserOptions& options); | |||
#endif | |||
bool isEmbed() const noexcept; | |||
bool isVisible() const noexcept; | |||
void setVisible(bool yesNo); | |||
@@ -107,8 +109,15 @@ public: | |||
const char* getTitle() const noexcept; | |||
void setTitle(const char* title); | |||
void setGeometryConstraints(uint width, uint height, bool aspect); | |||
void setTransientWinId(uintptr_t winId); | |||
double getScaling() const noexcept; | |||
void setScaling(double scaling) noexcept; | |||
bool getIgnoringKeyRepeat() const noexcept; | |||
void setIgnoringKeyRepeat(bool ignore) noexcept; | |||
Application& getApp() const noexcept; | |||
intptr_t getWindowId() const noexcept; | |||
@@ -237,7 +237,7 @@ bool Size<T>::isNotNull() const noexcept | |||
template<typename T> | |||
bool Size<T>::isValid() const noexcept | |||
{ | |||
return fWidth > 1 && fHeight > 1; | |||
return fWidth > 0 && fHeight > 0; | |||
} | |||
template<typename T> | |||
@@ -447,10 +447,8 @@ void NanoVG::translate(float x, float y) | |||
void NanoVG::rotate(float angle) | |||
{ | |||
if (fContext == nullptr) return; | |||
DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); | |||
nvgRotate(fContext, angle); | |||
if (fContext != nullptr) | |||
nvgRotate(fContext, angle); | |||
} | |||
void NanoVG::skewX(float angle) | |||
@@ -63,7 +63,7 @@ struct Widget::PrivateData { | |||
subWidgets.clear(); | |||
} | |||
void display(const uint width, const uint height, const bool renderingSubWidget) | |||
void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget) | |||
{ | |||
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) | |||
return; | |||
@@ -76,29 +76,32 @@ struct Widget::PrivateData { | |||
if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height))) | |||
{ | |||
// full viewport size | |||
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
glViewport(0, | |||
-(height * scaling - height), | |||
width * scaling, | |||
height * scaling); | |||
} | |||
else if (needsScaling) | |||
{ | |||
// limit viewport to widget bounds | |||
glViewport(absolutePos.getX(), | |||
static_cast<int>(height - self->getHeight()) - absolutePos.getY(), | |||
static_cast<GLsizei>(self->getWidth()), | |||
static_cast<GLsizei>(self->getHeight())); | |||
height - self->getHeight() - absolutePos.getY(), | |||
self->getWidth(), | |||
self->getHeight()); | |||
} | |||
else | |||
{ | |||
// only set viewport pos | |||
glViewport(absolutePos.getX(), | |||
/*static_cast<int>(height - self->getHeight())*/ - absolutePos.getY(), | |||
static_cast<GLsizei>(width), | |||
static_cast<GLsizei>(height)); | |||
glViewport(absolutePos.getX() * scaling, | |||
-std::round((height * scaling - height) + (absolutePos.getY() * scaling)), | |||
std::round(width * scaling), | |||
std::round(height * scaling)); | |||
// then cut the outer bounds | |||
glScissor(absolutePos.getX(), | |||
static_cast<int>(height - self->getHeight()) - absolutePos.getY(), | |||
static_cast<GLsizei>(self->getWidth()), | |||
static_cast<GLsizei>(self->getHeight())); | |||
glScissor(absolutePos.getX() * scaling, | |||
height - std::round((self->getHeight() + absolutePos.getY()) * scaling), | |||
std::round(self->getWidth() * scaling), | |||
std::round(self->getHeight() * scaling)); | |||
glEnable(GL_SCISSOR_TEST); | |||
needsDisableScissor = true; | |||
@@ -113,17 +116,17 @@ struct Widget::PrivateData { | |||
needsDisableScissor = false; | |||
} | |||
displaySubWidgets(width, height); | |||
displaySubWidgets(width, height, scaling); | |||
} | |||
void displaySubWidgets(const uint width, const uint height) | |||
void displaySubWidgets(const uint width, const uint height, const double scaling) | |||
{ | |||
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||
{ | |||
Widget* const widget(*it); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | |||
widget->pData->display(width, height, true); | |||
widget->pData->display(width, height, scaling, true); | |||
} | |||
} | |||
@@ -19,10 +19,6 @@ | |||
#include "../Base.hpp" | |||
#undef PUGL_HAVE_CAIRO | |||
#undef PUGL_HAVE_GL | |||
#define PUGL_HAVE_GL 1 | |||
#include "pugl/pugl.h" | |||
#if defined(__GNUC__) && (__GNUC__ >= 7) | |||
@@ -61,7 +57,7 @@ extern "C" { | |||
#define FOR_EACH_WIDGET_INV(rit) \ | |||
for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) | |||
#ifdef DEBUG | |||
#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |||
# define DBG(msg) std::fprintf(stderr, "%s", msg); | |||
# define DBGp(...) std::fprintf(stderr, __VA_ARGS__); | |||
# define DBGF std::fflush(stderr); | |||
@@ -87,6 +83,7 @@ struct Window::PrivateData { | |||
fUsingEmbed(false), | |||
fWidth(1), | |||
fHeight(1), | |||
fScaling(1.0), | |||
fTitle(nullptr), | |||
fWidgets(), | |||
fModal(), | |||
@@ -117,6 +114,7 @@ struct Window::PrivateData { | |||
fUsingEmbed(false), | |||
fWidth(1), | |||
fHeight(1), | |||
fScaling(1.0), | |||
fTitle(nullptr), | |||
fWidgets(), | |||
fModal(parent.pData), | |||
@@ -159,6 +157,7 @@ struct Window::PrivateData { | |||
fUsingEmbed(parentId != 0), | |||
fWidth(1), | |||
fHeight(1), | |||
fScaling(1.0), | |||
fTitle(nullptr), | |||
fWidgets(), | |||
fModal(), | |||
@@ -204,7 +203,6 @@ struct Window::PrivateData { | |||
return; | |||
} | |||
puglInitContextType(fView, PUGL_GL); | |||
puglInitUserResizable(fView, fResizable); | |||
puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); | |||
@@ -539,6 +537,7 @@ struct Window::PrivateData { | |||
DBG("Window setResizable called\n"); | |||
fResizable = yesNo; | |||
fView->user_resizable = yesNo; | |||
#if defined(DISTRHO_OS_WINDOWS) | |||
const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX | |||
@@ -554,6 +553,17 @@ struct Window::PrivateData { | |||
// ------------------------------------------------------------------- | |||
void setGeometryConstraints(uint width, uint height, bool aspect) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fResizable,); | |||
fView->min_width = width; | |||
fView->min_height = height; | |||
puglUpdateGeometryConstraints(fView, width, height, aspect); | |||
} | |||
// ------------------------------------------------------------------- | |||
void setSize(uint width, uint height, const bool forced = false) | |||
{ | |||
if (width <= 1 || height <= 1) | |||
@@ -605,7 +615,6 @@ struct Window::PrivateData { | |||
} | |||
} | |||
#else | |||
XResizeWindow(xDisplay, xWindow, width, height); | |||
if (! fResizable) | |||
{ | |||
@@ -620,9 +629,11 @@ struct Window::PrivateData { | |||
sizeHints.max_width = static_cast<int>(width); | |||
sizeHints.max_height = static_cast<int>(height); | |||
XSetNormalHints(xDisplay, xWindow, &sizeHints); | |||
XSetWMNormalHints(xDisplay, xWindow, &sizeHints); | |||
} | |||
XResizeWindow(xDisplay, xWindow, width, height); | |||
if (! forced) | |||
XFlush(xDisplay); | |||
#endif | |||
@@ -685,6 +696,32 @@ struct Window::PrivateData { | |||
// ------------------------------------------------------------------- | |||
double getScaling() const noexcept | |||
{ | |||
return fScaling; | |||
} | |||
void setScaling(double scaling) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); | |||
fScaling = scaling; | |||
} | |||
// ------------------------------------------------------------------- | |||
bool getIgnoringKeyRepeat() const noexcept | |||
{ | |||
return fView->ignoreKeyRepeat; | |||
} | |||
void setIgnoringKeyRepeat(bool ignore) noexcept | |||
{ | |||
puglIgnoreKeyRepeat(fView, ignore); | |||
} | |||
// ------------------------------------------------------------------- | |||
void addWidget(Widget* const widget) | |||
{ | |||
fWidgets.push_back(widget); | |||
@@ -736,7 +773,7 @@ struct Window::PrivateData { | |||
FOR_EACH_WIDGET(it) | |||
{ | |||
Widget* const widget(*it); | |||
widget->pData->display(fWidth, fHeight, false); | |||
widget->pData->display(fWidth, fHeight, fScaling, false); | |||
} | |||
fSelf->onDisplayAfter(); | |||
@@ -796,7 +833,7 @@ struct Window::PrivateData { | |||
return 1; | |||
} | |||
void onPuglMouse(const int button, const bool press, const int x, const int y) | |||
void onPuglMouse(const int button, const bool press, int x, int y) | |||
{ | |||
DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y); | |||
@@ -806,6 +843,9 @@ struct Window::PrivateData { | |||
if (fModal.childFocus != nullptr) | |||
return fModal.childFocus->focus(); | |||
x /= fScaling; | |||
y /= fScaling; | |||
Widget::MouseEvent ev; | |||
ev.button = button; | |||
ev.press = press; | |||
@@ -823,13 +863,16 @@ struct Window::PrivateData { | |||
} | |||
} | |||
void onPuglMotion(const int x, const int y) | |||
void onPuglMotion(int x, int y) | |||
{ | |||
DBGp("PUGL: onMotion : %i %i\n", x, y); | |||
// DBGp("PUGL: onMotion : %i %i\n", x, y); | |||
if (fModal.childFocus != nullptr) | |||
return; | |||
x /= fScaling; | |||
y /= fScaling; | |||
Widget::MotionEvent ev; | |||
ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | |||
ev.time = puglGetEventTimestamp(fView); | |||
@@ -845,13 +888,18 @@ struct Window::PrivateData { | |||
} | |||
} | |||
void onPuglScroll(const int x, const int y, const float dx, const float dy) | |||
void onPuglScroll(int x, int y, float dx, float dy) | |||
{ | |||
DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy); | |||
if (fModal.childFocus != nullptr) | |||
return; | |||
x /= fScaling; | |||
y /= fScaling; | |||
dx /= fScaling; | |||
dy /= fScaling; | |||
Widget::ScrollEvent ev; | |||
ev.delta = Point<float>(dx, dy); | |||
ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | |||
@@ -1000,6 +1048,7 @@ struct Window::PrivateData { | |||
bool fUsingEmbed; | |||
uint fWidth; | |||
uint fHeight; | |||
double fScaling; | |||
char* fTitle; | |||
std::list<Widget*> fWidgets; | |||
@@ -1221,6 +1270,11 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||
} | |||
#endif | |||
bool Window::isEmbed() const noexcept | |||
{ | |||
return pData->fUsingEmbed; | |||
} | |||
bool Window::isVisible() const noexcept | |||
{ | |||
return pData->fVisible; | |||
@@ -1241,6 +1295,11 @@ void Window::setResizable(bool yesNo) | |||
pData->setResizable(yesNo); | |||
} | |||
void Window::setGeometryConstraints(uint width, uint height, bool aspect) | |||
{ | |||
pData->setGeometryConstraints(width, height, aspect); | |||
} | |||
uint Window::getWidth() const noexcept | |||
{ | |||
return pData->fWidth; | |||
@@ -1281,6 +1340,26 @@ void Window::setTransientWinId(uintptr_t winId) | |||
pData->setTransientWinId(winId); | |||
} | |||
double Window::getScaling() const noexcept | |||
{ | |||
return pData->getScaling(); | |||
} | |||
void Window::setScaling(double scaling) noexcept | |||
{ | |||
pData->setScaling(scaling); | |||
} | |||
bool Window::getIgnoringKeyRepeat() const noexcept | |||
{ | |||
return pData->getIgnoringKeyRepeat(); | |||
} | |||
void Window::setIgnoringKeyRepeat(bool ignore) noexcept | |||
{ | |||
pData->setIgnoringKeyRepeat(ignore); | |||
} | |||
Application& Window::getApp() const noexcept | |||
{ | |||
return pData->fApp; | |||
@@ -1,121 +0,0 @@ | |||
/* | |||
Copyright 2014 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef PUGL_COMMON_H_INCLUDED | |||
#define PUGL_COMMON_H_INCLUDED | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/** | |||
@addtogroup pugl | |||
@{ | |||
*/ | |||
/** | |||
A Pugl view. | |||
*/ | |||
typedef struct PuglViewImpl PuglView; | |||
/** | |||
A native window handle. | |||
On X11, this is a Window. | |||
On OSX, this is an NSView*. | |||
On Windows, this is a HWND. | |||
*/ | |||
typedef intptr_t PuglNativeWindow; | |||
/** | |||
Handle for opaque user data. | |||
*/ | |||
typedef void* PuglHandle; | |||
/** | |||
Return status code. | |||
*/ | |||
typedef enum { | |||
PUGL_SUCCESS = 0 | |||
} PuglStatus; | |||
/** | |||
Drawing context type. | |||
*/ | |||
typedef enum { | |||
PUGL_GL, | |||
PUGL_CAIRO | |||
} PuglContextType; | |||
/** | |||
Convenience symbols for ASCII control characters. | |||
*/ | |||
typedef enum { | |||
PUGL_CHAR_BACKSPACE = 0x08, | |||
PUGL_CHAR_ESCAPE = 0x1B, | |||
PUGL_CHAR_DELETE = 0x7F | |||
} PuglChar; | |||
/** | |||
Keyboard modifier flags. | |||
*/ | |||
typedef enum { | |||
PUGL_MOD_SHIFT = 1 << 0, /**< Shift key */ | |||
PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||
PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||
PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
} PuglMod; | |||
/** | |||
Special (non-Unicode) keyboard keys. | |||
*/ | |||
typedef enum { | |||
PUGL_KEY_F1 = 1, | |||
PUGL_KEY_F2, | |||
PUGL_KEY_F3, | |||
PUGL_KEY_F4, | |||
PUGL_KEY_F5, | |||
PUGL_KEY_F6, | |||
PUGL_KEY_F7, | |||
PUGL_KEY_F8, | |||
PUGL_KEY_F9, | |||
PUGL_KEY_F10, | |||
PUGL_KEY_F11, | |||
PUGL_KEY_F12, | |||
PUGL_KEY_LEFT, | |||
PUGL_KEY_UP, | |||
PUGL_KEY_RIGHT, | |||
PUGL_KEY_DOWN, | |||
PUGL_KEY_PAGE_UP, | |||
PUGL_KEY_PAGE_DOWN, | |||
PUGL_KEY_HOME, | |||
PUGL_KEY_END, | |||
PUGL_KEY_INSERT, | |||
PUGL_KEY_SHIFT, | |||
PUGL_KEY_CTRL, | |||
PUGL_KEY_ALT, | |||
PUGL_KEY_SUPER | |||
} PuglKey; | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* PUGL_COMMON_H_INCLUDED */ |
@@ -1,41 +0,0 @@ | |||
/* | |||
Copyright 2014 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef PUGL_EVENT_H_INCLUDED | |||
#define PUGL_EVENT_H_INCLUDED | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
#include "pugl/common.h" | |||
/** | |||
@addtogroup pugl | |||
@{ | |||
*/ | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* PUGL_EVENT_H_INCLUDED */ |
@@ -1,32 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
/** | |||
@file gl.h Portable header wrapper for gl.h. | |||
Unfortunately, GL includes vary across platforms so this header allows for | |||
pure portable programs. | |||
*/ | |||
#ifdef __APPLE__ | |||
# include "OpenGL/gl.h" | |||
#else | |||
# ifdef _WIN32 | |||
# include <windows.h> /* Broken Windows GL headers require this */ | |||
# endif | |||
# include "GL/gl.h" | |||
#endif | |||
@@ -1,32 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
/** | |||
@file glu.h Portable header wrapper for glu.h. | |||
Unfortunately, GL includes vary across platforms so this header allows for | |||
pure portable programs. | |||
*/ | |||
#ifdef __APPLE__ | |||
# include "OpenGL/glu.h" | |||
#else | |||
# ifdef _WIN32 | |||
# include <windows.h> /* Broken Windows GL headers require this */ | |||
# endif | |||
# include "GL/glu.h" | |||
#endif | |||
@@ -23,28 +23,25 @@ | |||
#include <stdint.h> | |||
#include "pugl/common.h" | |||
#include "pugl/event.h" | |||
#ifdef PUGL_SHARED | |||
# ifdef _WIN32 | |||
# define PUGL_LIB_IMPORT __declspec(dllimport) | |||
# define PUGL_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef PUGL_INTERNAL | |||
# define PUGL_API PUGL_LIB_EXPORT | |||
# else | |||
# define PUGL_API PUGL_LIB_IMPORT | |||
# endif | |||
/* | |||
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 | |||
# define PUGL_API | |||
# else | |||
# define PUGL_API __attribute__((visibility("hidden"))) | |||
# include <winsock2.h> | |||
# include <windows.h> /* Broken Windows GL headers require this */ | |||
# endif | |||
# include "GL/gl.h" | |||
#endif | |||
#ifdef _WIN32 | |||
# define PUGL_API | |||
#else | |||
# define PUGL_API __attribute__((visibility("hidden"))) | |||
#endif | |||
#ifdef __cplusplus | |||
@@ -59,6 +56,82 @@ extern "C" { | |||
@{ | |||
*/ | |||
/** | |||
A Pugl view. | |||
*/ | |||
typedef struct PuglViewImpl PuglView; | |||
/** | |||
A native window handle. | |||
On X11, this is a Window. | |||
On OSX, this is an NSView*. | |||
On Windows, this is a HWND. | |||
*/ | |||
typedef intptr_t PuglNativeWindow; | |||
/** | |||
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 << 0, /**< Shift key */ | |||
PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||
PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||
PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
} PuglMod; | |||
/** | |||
Handle for opaque user data. | |||
*/ | |||
typedef void* PuglHandle; | |||
/** | |||
A function called when the window is closed. | |||
*/ | |||
@@ -105,6 +178,16 @@ typedef void (*PuglMouseFunc)( | |||
*/ | |||
typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | |||
/** | |||
A function called outside of gl-context when the plugin schedules a resize via puglPostResize. | |||
@param view The view being resized. | |||
@param width The new width to resize to (variable is initialized to current size) | |||
@param height The new height to resize to (variable is initialized to current size) | |||
@param set_hints If not null, set window-hints | |||
*/ | |||
typedef void (*PuglResizeFunc)(PuglView* view, int *width, int *height, int *set_hints); | |||
/** | |||
A function called on scrolling (e.g. mouse wheel or track pad). | |||
@@ -190,12 +273,6 @@ puglInitUserResizable(PuglView* view, bool resizable); | |||
PUGL_API void | |||
puglInitTransientFor(PuglView* view, uintptr_t parent); | |||
/** | |||
Set the context type before creating a window. | |||
*/ | |||
PUGL_API void | |||
puglInitContextType(PuglView* view, PuglContextType type); | |||
/** | |||
@} | |||
*/ | |||
@@ -215,13 +292,31 @@ PUGL_API int | |||
puglCreateWindow(PuglView* view, const char* title); | |||
/** | |||
Show the current window. | |||
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 min_width, | |||
int min_height, | |||
int width, | |||
int height, | |||
bool resizable, | |||
unsigned long transientId); | |||
/** | |||
Show Window (external ui) | |||
*/ | |||
PUGL_API void | |||
puglShowWindow(PuglView* view); | |||
/** | |||
Hide the current window. | |||
Hide Window (external ui) | |||
*/ | |||
PUGL_API void | |||
puglHideWindow(PuglView* view); | |||
@@ -254,15 +349,6 @@ puglSetHandle(PuglView* view, PuglHandle handle); | |||
PUGL_API PuglHandle | |||
puglGetHandle(PuglView* view); | |||
/** | |||
Get the drawing context. | |||
For PUGL_GL contexts, this is unused and returns NULL. | |||
For PUGL_CAIRO contexts, this returns a pointer to a cairo_t. | |||
*/ | |||
PUGL_API void* | |||
puglGetContext(PuglView* view); | |||
/** | |||
Return the timestamp (if any) of the currently-processing event. | |||
*/ | |||
@@ -337,6 +423,12 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||
PUGL_API void | |||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||
/** | |||
Set callback function to change window size. | |||
*/ | |||
PUGL_API void | |||
puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc); | |||
/** | |||
Set the function to call on file-browser selections. | |||
*/ | |||
@@ -347,6 +439,12 @@ puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); | |||
@} | |||
*/ | |||
/** | |||
TODO document this. | |||
*/ | |||
PUGL_API int | |||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect); | |||
/** | |||
Grab the input focus. | |||
*/ | |||
@@ -368,6 +466,12 @@ puglProcessEvents(PuglView* view); | |||
PUGL_API void | |||
puglPostRedisplay(PuglView* view); | |||
/** | |||
Request a resize on the next call to puglProcessEvents(). | |||
*/ | |||
PUGL_API void | |||
puglPostResize(PuglView* view); | |||
/** | |||
Destroy a GL window. | |||
*/ | |||
@@ -20,27 +20,9 @@ | |||
Note this file contains function definitions, so it must be compiled into | |||
the final binary exactly once. Each platform specific implementation file | |||
including it once should achieve this. | |||
If you are copying the pugl code into your source tree, the following | |||
symbols can be defined to tweak pugl behaviour: | |||
PUGL_HAVE_CAIRO: Include Cairo support code. | |||
PUGL_HAVE_GL: Include OpenGL support code. | |||
PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | |||
PUGL_VERBOSE: Print GL information to console. | |||
*/ | |||
#include "pugl/pugl.h" | |||
#include "pugl/event.h" | |||
#ifdef PUGL_VERBOSE | |||
# include <stdio.h> | |||
# define PUGL_LOG(str) fprintf(stderr, "pugl: " str) | |||
# define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__) | |||
#else | |||
# define PUGL_LOG(str) | |||
# define PUGL_LOGF(fmt, ...) | |||
#endif | |||
#include "pugl.h" | |||
typedef struct PuglInternalsImpl PuglInternals; | |||
@@ -52,14 +34,13 @@ struct PuglViewImpl { | |||
PuglMotionFunc motionFunc; | |||
PuglMouseFunc mouseFunc; | |||
PuglReshapeFunc reshapeFunc; | |||
PuglResizeFunc resizeFunc; | |||
PuglScrollFunc scrollFunc; | |||
PuglSpecialFunc specialFunc; | |||
PuglFileSelectedFunc fileSelectedFunc; | |||
PuglInternals* impl; | |||
PuglInternals* impl; | |||
PuglNativeWindow parent; | |||
PuglContextType ctx_type; | |||
uintptr_t transient_parent; | |||
int width; | |||
@@ -70,7 +51,8 @@ struct PuglViewImpl { | |||
bool mouse_in_view; | |||
bool ignoreKeyRepeat; | |||
bool redisplay; | |||
bool resizable; | |||
bool user_resizable; | |||
bool pending_resize; | |||
uint32_t event_timestamp_ms; | |||
}; | |||
@@ -120,7 +102,7 @@ puglInitWindowParent(PuglView* view, PuglNativeWindow parent) | |||
void | |||
puglInitUserResizable(PuglView* view, bool resizable) | |||
{ | |||
view->resizable = resizable; | |||
view->user_resizable = resizable; | |||
} | |||
void | |||
@@ -129,10 +111,33 @@ puglInitTransientFor(PuglView* view, uintptr_t parent) | |||
view->transient_parent = parent; | |||
} | |||
void | |||
puglInitContextType(PuglView* view, PuglContextType type) | |||
PuglView* | |||
puglCreate(PuglNativeWindow parent, | |||
const char* title, | |||
int min_width, | |||
int min_height, | |||
int width, | |||
int height, | |||
bool resizable, | |||
unsigned long transientId) | |||
{ | |||
view->ctx_type = type; | |||
PuglView* view = puglInit(); | |||
if (!view) { | |||
return NULL; | |||
} | |||
puglInitWindowParent(view, parent); | |||
puglInitWindowMinSize(view, min_width, min_height); | |||
puglInitWindowSize(view, width, height); | |||
puglInitUserResizable(view, resizable); | |||
puglInitTransientFor(view, transientId); | |||
if (!puglCreateWindow(view, title)) { | |||
free(view); | |||
return NULL; | |||
} | |||
return view; | |||
} | |||
void | |||
@@ -201,6 +206,12 @@ puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) | |||
view->reshapeFunc = reshapeFunc; | |||
} | |||
void | |||
puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc) | |||
{ | |||
view->resizeFunc = resizeFunc; | |||
} | |||
void | |||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | |||
{ | |||
@@ -225,45 +236,19 @@ puglEnterContext(PuglView* view); | |||
void | |||
puglLeaveContext(PuglView* view, bool flush); | |||
#if 0 | |||
/** Return the code point for buf, or the replacement character on error. */ | |||
static uint32_t | |||
puglDecodeUTF8(const uint8_t* buf) | |||
{ | |||
#define FAIL_IF(cond) { if (cond) return 0xFFFD; } | |||
/* http://en.wikipedia.org/wiki/UTF-8 */ | |||
if (buf[0] < 0x80) { | |||
return buf[0]; | |||
} else if (buf[0] < 0xC2) { | |||
return 0xFFFD; | |||
} else if (buf[0] < 0xE0) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
return (buf[0] << 6) + buf[1] - 0x3080; | |||
} else if (buf[0] < 0xF0) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0); | |||
FAIL_IF((buf[2] & 0xC0) != 0x80); | |||
return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080; | |||
} else if (buf[0] < 0xF5) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90); | |||
FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90); | |||
FAIL_IF((buf[2] & 0xC0) != 0x80); | |||
FAIL_IF((buf[3] & 0xC0) != 0x80); | |||
return ((buf[0] << 18) + | |||
(buf[1] << 12) + | |||
(buf[2] << 6) + | |||
buf[3] - 0x3C82080); | |||
} | |||
return 0xFFFD; | |||
} | |||
#endif | |||
static void | |||
puglDefaultReshape(PuglView* view, int width, int height) | |||
puglDefaultReshape(int width, int height) | |||
{ | |||
#ifdef ROBTK_HERE | |||
glViewport(0, 0, width, height); | |||
glMatrixMode(GL_PROJECTION); | |||
glLoadIdentity(); | |||
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); | |||
glClear(GL_COLOR_BUFFER_BIT); | |||
glMatrixMode(GL_MODELVIEW); | |||
glLoadIdentity(); | |||
#else | |||
glMatrixMode(GL_PROJECTION); | |||
glLoadIdentity(); | |||
glOrtho(0, width, height, 0, 0, 1); | |||
@@ -271,82 +256,5 @@ puglDefaultReshape(PuglView* view, int width, int height) | |||
glMatrixMode(GL_MODELVIEW); | |||
glLoadIdentity(); | |||
return; | |||
// unused | |||
(void)view; | |||
} | |||
#if 0 | |||
static void | |||
puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
{ | |||
if (view->eventFunc) { | |||
view->eventFunc(view, event); | |||
} | |||
switch (event->type) { | |||
case PUGL_CONFIGURE: | |||
puglEnterContext(view); | |||
view->width = event->configure.width; | |||
view->height = event->configure.height; | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, view->width, view->height); | |||
} | |||
puglLeaveContext(view, false); | |||
break; | |||
case PUGL_EXPOSE: | |||
if (event->expose.count == 0) { | |||
puglEnterContext(view); | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
view->redisplay = false; | |||
puglLeaveContext(view, true); | |||
} | |||
break; | |||
case PUGL_MOTION_NOTIFY: | |||
view->event_timestamp_ms = event->motion.time; | |||
view->mods = event->motion.state; | |||
if (view->motionFunc) { | |||
view->motionFunc(view, event->motion.x, event->motion.y); | |||
} | |||
break; | |||
case PUGL_SCROLL: | |||
if (view->scrollFunc) { | |||
view->scrollFunc(view, | |||
event->scroll.x, event->scroll.y, | |||
event->scroll.dx, event->scroll.dy); | |||
} | |||
break; | |||
case PUGL_BUTTON_PRESS: | |||
case PUGL_BUTTON_RELEASE: | |||
view->event_timestamp_ms = event->button.time; | |||
view->mods = event->button.state; | |||
if (view->mouseFunc) { | |||
view->mouseFunc(view, | |||
event->button.button, | |||
event->type == PUGL_BUTTON_PRESS, | |||
event->button.x, | |||
event->button.y); | |||
} | |||
break; | |||
case PUGL_KEY_PRESS: | |||
case PUGL_KEY_RELEASE: | |||
view->event_timestamp_ms = event->key.time; | |||
view->mods = event->key.state; | |||
if (event->key.special && view->specialFunc) { | |||
view->specialFunc(view, | |||
event->type == PUGL_KEY_PRESS, | |||
event->key.special); | |||
} else if (event->key.character && view->keyboardFunc) { | |||
view->keyboardFunc(view, | |||
event->type == PUGL_KEY_PRESS, | |||
event->key.character); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
#endif | |||
} |
@@ -218,7 +218,7 @@ puglDisplay(PuglView* view) | |||
if (puglview->reshapeFunc) { | |||
puglview->reshapeFunc(puglview, width, height); | |||
} else { | |||
puglDefaultReshape(puglview, width, height); | |||
puglDefaultReshape(width, height); | |||
} | |||
puglLeaveContext(puglview, false); | |||
@@ -427,18 +427,13 @@ puglInitInternals() | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
[[view->impl->glview openGLContext] makeCurrentContext]; | |||
} | |||
#endif | |||
[[view->impl->glview openGLContext] makeCurrentContext]; | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL && flush) { | |||
if (flush) { | |||
if (view->impl->glview->doubleBuffered) { | |||
[[view->impl->glview openGLContext] flushBuffer]; | |||
} else { | |||
@@ -446,7 +441,6 @@ puglLeaveContext(PuglView* view, bool flush) | |||
} | |||
//[NSOpenGLContext clearCurrentContext]; | |||
} | |||
#endif | |||
} | |||
int | |||
@@ -465,7 +459,7 @@ puglCreateWindow(PuglView* view, const char* title) | |||
impl->glview->puglview = view; | |||
if (view->resizable) { | |||
if (view->user_resizable) { | |||
[impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | |||
} | |||
@@ -566,11 +560,14 @@ puglGetNativeWindow(PuglView* view) | |||
return (PuglNativeWindow)view->impl->glview; | |||
} | |||
void* | |||
puglGetContext(PuglView* view) | |||
int | |||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||
{ | |||
return NULL; | |||
// TODO | |||
return 1; | |||
// unused | |||
(void)view; | |||
(void)min_width; | |||
(void)min_height; | |||
(void)aspect; | |||
} |
@@ -76,25 +76,17 @@ puglInitInternals() | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
} | |||
#endif | |||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
if (flush) { | |||
glFlush(); | |||
SwapBuffers(view->impl->hdc); | |||
} | |||
wglMakeCurrent(NULL, NULL); | |||
if (flush) { | |||
glFlush(); | |||
SwapBuffers(view->impl->hdc); | |||
} | |||
#endif | |||
wglMakeCurrent(NULL, NULL); | |||
} | |||
int | |||
@@ -137,7 +129,7 @@ puglCreateWindow(PuglView* view, const char* title) | |||
} | |||
int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
if (view->resizable) { | |||
if (view->user_resizable) { | |||
winFlags |= WS_SIZEBOX; | |||
if (view->min_width > 0 && view->min_height > 0) { | |||
// Adjust the minimum window size to accomodate requested view size | |||
@@ -231,7 +223,7 @@ puglReshape(PuglView* view, int width, int height) | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, width, height); | |||
} else { | |||
puglDefaultReshape(view, width, height); | |||
puglDefaultReshape(width, height); | |||
} | |||
view->width = width; | |||
@@ -477,13 +469,14 @@ puglGetNativeWindow(PuglView* view) | |||
return (PuglNativeWindow)view->impl->hwnd; | |||
} | |||
void* | |||
puglGetContext(PuglView* /*view*/) | |||
int | |||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||
{ | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
// TODO | |||
} | |||
#endif | |||
return NULL; | |||
// TODO | |||
return 1; | |||
(void)view; | |||
(void)min_width; | |||
(void)min_height; | |||
(void)aspect; | |||
} |
@@ -1,7 +1,7 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
Copyright 2013 Robin Gareus <robin@gareus.org> | |||
Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||
Copyright 2013,2015 Robin Gareus <robin@gareus.org> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
@@ -24,22 +24,14 @@ | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <GL/gl.h> | |||
#include <GL/glx.h> | |||
#include <X11/Xatom.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#include <X11/keysym.h> | |||
#ifdef PUGL_HAVE_GL | |||
#include <GL/gl.h> | |||
#include <GL/glx.h> | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
#include <cairo/cairo.h> | |||
#include <cairo/cairo-xlib.h> | |||
#endif | |||
#include "pugl/pugl_internal.h" | |||
#include "pugl_internal.h" | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
#define SOFD_HAVE_X11 | |||
@@ -47,212 +39,145 @@ | |||
#include "../sofd/libsofd.c" | |||
#endif | |||
/* work around buggy re-parent & focus issues on some systems | |||
* where no keyboard events are passed through even if the | |||
* app has mouse-focus and all other events are working. | |||
*/ | |||
//#define PUGL_GRAB_FOCUS | |||
/* show messages during initalization | |||
*/ | |||
//#define PUGL_VERBOSE | |||
struct PuglInternalsImpl { | |||
Display* display; | |||
int screen; | |||
Window win; | |||
XIM xim; | |||
XIC xic; | |||
#ifdef PUGL_HAVE_CAIRO | |||
cairo_t* cr; | |||
cairo_surface_t* surface; | |||
#endif | |||
#ifdef PUGL_HAVE_GL | |||
GLXContext ctx; | |||
Bool doubleBuffered; | |||
#endif | |||
}; | |||
PuglInternals* | |||
puglInitInternals(void) | |||
{ | |||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
} | |||
static XVisualInfo* | |||
getVisual(PuglView* view) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
XVisualInfo* vi = NULL; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
/** | |||
Attributes for single-buffered RGBA with at least | |||
4 bits per color and a 16 bit depth buffer. | |||
*/ | |||
int attrListSgl[] = { | |||
GLX_RGBA, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
/** | |||
Attributes for double-buffered RGBA with at least | |||
4 bits per color and a 16 bit depth buffer. | |||
*/ | |||
int attrListDbl[] = { | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
/** | |||
Attributes for double-buffered RGBA with multi-sampling | |||
(antialiasing) | |||
*/ | |||
int attrListDblMS[] = { | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_ALPHA_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_SAMPLE_BUFFERS, 1, | |||
GLX_SAMPLES, 4, | |||
None | |||
}; | |||
impl->doubleBuffered = True; | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||
if (vi == NULL) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||
PUGL_LOG("multisampling (antialiasing) is not available\n"); | |||
} | |||
if (vi == NULL) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||
impl->doubleBuffered = False; | |||
PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n"); | |||
} | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
XVisualInfo pat; | |||
int n; | |||
pat.screen = impl->screen; | |||
vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); | |||
} | |||
#endif | |||
return vi; | |||
} | |||
static bool | |||
createContext(PuglView* view, XVisualInfo* vi) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
/** | |||
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, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
return (impl->ctx != NULL); | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
impl->surface = cairo_xlib_surface_create( | |||
impl->display, impl->win, vi->visual, view->width, view->height); | |||
if (impl->surface == NULL) { | |||
PUGL_LOG("failed to create cairo surface\n"); | |||
return false; | |||
} | |||
impl->cr = cairo_create(impl->surface); | |||
if (impl->cr == NULL) { | |||
cairo_surface_destroy(impl->surface); | |||
impl->surface = NULL; | |||
PUGL_LOG("failed to create cairo context\n"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
#endif | |||
/** | |||
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, True, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
return false; | |||
} | |||
/** | |||
Attributes for double-buffered RGBA with multi-sampling | |||
(antialiasing) | |||
*/ | |||
static int attrListDblMS[] = { | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, True, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_ALPHA_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_SAMPLE_BUFFERS, 1, | |||
GLX_SAMPLES, 4, | |||
None | |||
}; | |||
static void | |||
destroyContext(PuglView* view) | |||
PuglInternals* | |||
puglInitInternals(void) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
glXDestroyContext(impl->display, impl->ctx); | |||
impl->ctx = NULL; | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
cairo_destroy(impl->cr); | |||
impl->cr = NULL; | |||
cairo_surface_destroy(impl->surface); | |||
impl->surface = NULL; | |||
} | |||
#endif | |||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
} | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
} | |||
#endif | |||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
if (flush) { | |||
glFlush(); | |||
if (view->impl->doubleBuffered) { | |||
glXSwapBuffers(view->impl->display, view->impl->win); | |||
} | |||
if (flush) { | |||
glFlush(); | |||
if (view->impl->doubleBuffered) { | |||
glXSwapBuffers(view->impl->display, view->impl->win); | |||
} | |||
glXMakeCurrent(view->impl->display, None, NULL); | |||
} | |||
#endif | |||
glXMakeCurrent(view->impl->display, None, NULL); | |||
} | |||
int | |||
puglCreateWindow(PuglView* view, const char* title) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
if (!impl) { | |||
return 1; | |||
} | |||
view->impl = impl; | |||
impl->display = XOpenDisplay(NULL); | |||
impl->screen = DefaultScreen(impl->display); | |||
if (!impl->display) { | |||
free(impl); | |||
return 1; | |||
} | |||
impl->screen = DefaultScreen(impl->display); | |||
impl->doubleBuffered = True; | |||
XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||
if (!vi) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||
#ifdef PUGL_VERBOSE | |||
printf("puGL: multisampling (antialiasing) is not available\n"); | |||
#endif | |||
} | |||
if (!vi) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||
impl->doubleBuffered = False; | |||
} | |||
XVisualInfo* const vi = getVisual(view); | |||
if (!vi) { | |||
XCloseDisplay(impl->display); | |||
impl->display = NULL; | |||
free(impl); | |||
return 1; | |||
} | |||
#ifdef PUGL_HAVE_GL | |||
#ifdef PUGL_VERBOSE | |||
int glxMajor, glxMinor; | |||
glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||
printf("puGL: GLX-Version : %d.%d\n", glxMajor, glxMinor); | |||
#endif | |||
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
if (!impl->ctx) { | |||
XCloseDisplay(impl->display); | |||
free(impl); | |||
return 1; | |||
} | |||
Window xParent = view->parent | |||
? (Window)view->parent | |||
: RootWindow(impl->display, impl->screen); | |||
@@ -262,76 +187,55 @@ puglCreateWindow(PuglView* view, const char* title) | |||
XSetWindowAttributes attr; | |||
memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||
attr.background_pixel = BlackPixel(impl->display, impl->screen); | |||
attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||
attr.colormap = cmap; | |||
attr.event_mask = (ExposureMask | StructureNotifyMask | | |||
EnterWindowMask | LeaveWindowMask | | |||
KeyPressMask | KeyReleaseMask | | |||
ButtonPressMask | ButtonReleaseMask | | |||
PointerMotionMask | FocusChangeMask); | |||
attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||
attr.colormap = cmap; | |||
attr.event_mask = (ExposureMask | StructureNotifyMask | | |||
EnterWindowMask | LeaveWindowMask | | |||
KeyPressMask | KeyReleaseMask | | |||
ButtonPressMask | ButtonReleaseMask | | |||
PointerMotionMask | FocusChangeMask); | |||
impl->win = XCreateWindow( | |||
impl->display, xParent, | |||
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
if (!createContext(view, vi)) { | |||
XDestroyWindow(impl->display, impl->win); | |||
impl->win = 0; | |||
CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
if (!impl->win) { | |||
XCloseDisplay(impl->display); | |||
impl->display = NULL; | |||
free(impl); | |||
return 1; | |||
} | |||
XSizeHints sizeHints; | |||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||
if (!view->resizable) { | |||
sizeHints.flags = PMinSize|PMaxSize; | |||
sizeHints.min_width = view->width; | |||
sizeHints.min_height = view->height; | |||
sizeHints.max_width = view->width; | |||
sizeHints.max_height = view->height; | |||
XSetNormalHints(impl->display, impl->win, &sizeHints); | |||
} else if (view->min_width > 0 && view->min_height > 0) { | |||
sizeHints.flags = PMinSize; | |||
sizeHints.min_width = view->min_width; | |||
sizeHints.min_height = view->min_height; | |||
XSetNormalHints(impl->display, impl->win, &sizeHints); | |||
if (view->width > 1 || view->height > 1) { | |||
puglUpdateGeometryConstraints(view, view->min_width, view->min_height, view->min_width != view->width); | |||
XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); | |||
} | |||
if (title) { | |||
XStoreName(impl->display, impl->win, title); | |||
} | |||
if (!view->parent) { | |||
if (view->transient_parent > 0) { | |||
XSetTransientForHint(impl->display, impl->win, (Window)view->transient_parent); | |||
} | |||
if (view->parent) { | |||
XMapRaised(impl->display, impl->win); | |||
} else { | |||
Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||
} | |||
#ifdef PUGL_VERBOSE | |||
if (glXIsDirect(impl->display, impl->ctx)) { | |||
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||
printf("puGL: DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||
} else { | |||
PUGL_LOG("No DRI available\n"); | |||
printf("puGL: No DRI available\n"); | |||
} | |||
#endif | |||
XFree(vi); | |||
return PUGL_SUCCESS; | |||
} | |||
void | |||
puglShowWindow(PuglView* view) | |||
{ | |||
XMapRaised(view->impl->display, view->impl->win); | |||
} | |||
void | |||
puglHideWindow(PuglView* view) | |||
{ | |||
XUnmapWindow(view->impl->display, view->impl->win); | |||
return 0; | |||
} | |||
void | |||
@@ -340,18 +244,29 @@ puglDestroy(PuglView* view) | |||
if (!view) { | |||
return; | |||
} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
x_fib_close(view->impl->display); | |||
#endif | |||
destroyContext(view); | |||
glXDestroyContext(view->impl->display, view->impl->ctx); | |||
XDestroyWindow(view->impl->display, view->impl->win); | |||
XCloseDisplay(view->impl->display); | |||
free(view->impl); | |||
free(view); | |||
} | |||
void | |||
puglShowWindow(PuglView* view) | |||
{ | |||
XMapRaised(view->impl->display, view->impl->win); | |||
} | |||
void | |||
puglHideWindow(PuglView* view) | |||
{ | |||
XUnmapWindow(view->impl->display, view->impl->win); | |||
} | |||
static void | |||
puglReshape(PuglView* view, int width, int height) | |||
{ | |||
@@ -360,7 +275,7 @@ puglReshape(PuglView* view, int width, int height) | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, width, height); | |||
} else { | |||
puglDefaultReshape(view, width, height); | |||
puglDefaultReshape(width, height); | |||
} | |||
puglLeaveContext(view, false); | |||
@@ -375,7 +290,6 @@ puglDisplay(PuglView* view) | |||
puglEnterContext(view); | |||
view->redisplay = false; | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
@@ -383,6 +297,36 @@ puglDisplay(PuglView* view) | |||
puglLeaveContext(view, true); | |||
} | |||
static void | |||
puglResize(PuglView* view) | |||
{ | |||
int set_hints = 1; | |||
view->pending_resize = false; | |||
if (!view->resizeFunc) { return; } | |||
/* ask the plugin about the new size */ | |||
view->resizeFunc(view, &view->width, &view->height, &set_hints); | |||
if (set_hints) { | |||
XSizeHints sizeHints; | |||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||
sizeHints.flags = PMinSize|PMaxSize; | |||
sizeHints.min_width = view->width; | |||
sizeHints.min_height = view->height; | |||
sizeHints.max_width = view->user_resizable ? 4096 : view->width; | |||
sizeHints.max_height = view->user_resizable ? 4096 : view->height; | |||
XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); | |||
} | |||
XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); | |||
XFlush(view->impl->display); | |||
#ifdef PUGL_VERBOSE | |||
printf("puGL: window resize (%dx%d)\n", view->width, view->height); | |||
#endif | |||
/* and call Reshape in glX context */ | |||
puglReshape(view, view->width, view->height); | |||
} | |||
static PuglKey | |||
keySymToSpecial(KeySym sym) | |||
{ | |||
@@ -477,6 +421,9 @@ send_event: | |||
PuglStatus | |||
puglProcessEvents(PuglView* view) | |||
{ | |||
int conf_width = -1; | |||
int conf_height = -1; | |||
XEvent event; | |||
while (XPending(view->impl->display) > 0) { | |||
XNextEvent(view->impl->display, &event); | |||
@@ -511,22 +458,26 @@ puglProcessEvents(PuglView* view) | |||
} | |||
switch (event.type) { | |||
case UnmapNotify: | |||
if (view->motionFunc) { | |||
view->motionFunc(view, -1, -1); | |||
} | |||
break; | |||
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); | |||
conf_width = event.xconfigure.width; | |||
conf_height = event.xconfigure.height; | |||
} | |||
break; | |||
case Expose: | |||
if (event.xexpose.count != 0) { | |||
break; | |||
} | |||
puglDisplay(view); | |||
view->redisplay = true; | |||
break; | |||
case MotionNotify: | |||
setModifiers(view, event.xmotion.state, event.xmotion.time); | |||
@@ -602,6 +553,14 @@ puglProcessEvents(PuglView* view) | |||
} | |||
} | |||
if (conf_width != -1) { | |||
puglReshape(view, conf_width, conf_height); | |||
} | |||
if (view->pending_resize) { | |||
puglResize(view); | |||
} | |||
if (view->redisplay) { | |||
puglDisplay(view); | |||
} | |||
@@ -615,22 +574,35 @@ puglPostRedisplay(PuglView* view) | |||
view->redisplay = true; | |||
} | |||
void | |||
puglPostResize(PuglView* view) | |||
{ | |||
view->pending_resize = true; | |||
} | |||
PuglNativeWindow | |||
puglGetNativeWindow(PuglView* view) | |||
{ | |||
return view->impl->win; | |||
} | |||
void* | |||
puglGetContext(PuglView* view) | |||
int | |||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||
{ | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
return view->impl->cr; | |||
XSizeHints sizeHints; | |||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||
sizeHints.flags = PMinSize|PMaxSize; | |||
sizeHints.min_width = min_width; | |||
sizeHints.min_height = min_height; | |||
sizeHints.max_width = view->user_resizable ? 4096 : min_width; | |||
sizeHints.max_height = view->user_resizable ? 4096 : min_height; | |||
if (aspect) { | |||
sizeHints.flags |= PAspect; | |||
sizeHints.min_aspect.x = min_width; | |||
sizeHints.min_aspect.y = min_height; | |||
sizeHints.max_aspect.x = min_width; | |||
sizeHints.max_aspect.y = min_height; | |||
} | |||
#endif | |||
return NULL; | |||
// may be unused | |||
(void)view; | |||
XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); | |||
return 0; | |||
} |
@@ -546,7 +546,7 @@ struct TimePosition { | |||
/** | |||
Current tick within beat.@n | |||
Should always be > 0 and <= @a ticksPerBeat.@n | |||
Should always be >= 0 and < @a ticksPerBeat.@n | |||
The first tick is tick '0'. | |||
*/ | |||
int32_t tick; | |||
@@ -567,7 +567,7 @@ struct TimePosition { | |||
float beatType; | |||
/** | |||
Number of ticks within a bar.@n | |||
Number of ticks within a beat.@n | |||
Usually a moderately large integer with many denominators, such as 1920.0. | |||
*/ | |||
double ticksPerBeat; | |||
@@ -590,6 +590,22 @@ struct TimePosition { | |||
beatType(0.0f), | |||
ticksPerBeat(0.0), | |||
beatsPerMinute(0.0) {} | |||
/** | |||
Reinitialize this position using the default null initialization. | |||
*/ | |||
void clear() noexcept | |||
{ | |||
valid = false; | |||
bar = 0; | |||
beat = 0; | |||
tick = 0; | |||
barStartTick = 0.0; | |||
beatsPerBar = 0.0f; | |||
beatType = 0.0f; | |||
ticksPerBeat = 0.0; | |||
beatsPerMinute = 0.0; | |||
} | |||
} bbt; | |||
/** | |||
@@ -599,6 +615,16 @@ struct TimePosition { | |||
: playing(false), | |||
frame(0), | |||
bbt() {} | |||
/** | |||
Reinitialize this position using the default null initialization. | |||
*/ | |||
void clear() noexcept | |||
{ | |||
playing = false; | |||
frame = 0; | |||
bbt.clear(); | |||
} | |||
}; | |||
/** @} */ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,7 +20,8 @@ | |||
#include "extra/LeakDetector.hpp" | |||
#include "src/DistrhoPluginChecks.h" | |||
#ifndef HAVE_DGL | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../dgl/Base.hpp" | |||
# include "extra/ExternalWindow.hpp" | |||
typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; | |||
#elif DISTRHO_UI_USE_NANOVG | |||
@@ -54,13 +55,28 @@ public: | |||
UI class constructor. | |||
The UI should be initialized to a default state that matches the plugin side. | |||
*/ | |||
UI(uint width = 0, uint height = 0); | |||
UI(uint width = 0, uint height = 0, bool userResizable = false); | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~UI(); | |||
/** | |||
Wherever this UI is resizable by the user. | |||
This simply returns the value passed in the constructor. | |||
*/ | |||
bool isUserResizable() const noexcept; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/** | |||
Set geometry constraints for the UI when resized by the user, and optionally scale UI automatically. | |||
@see Window::setGeometryConstraints(uint,uint,bool) | |||
@see Window::setScaling(double) | |||
*/ | |||
void setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale = false); | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Host state */ | |||
@@ -166,7 +182,7 @@ protected: | |||
*/ | |||
virtual void sampleRateChanged(double newSampleRate); | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* UI Callbacks (optional) */ | |||
@@ -176,13 +192,13 @@ protected: | |||
*/ | |||
virtual void uiIdle() {} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# ifndef DGL_FILE_BROWSER_DISABLED | |||
/** | |||
File browser selected function. | |||
@see Window::fileBrowserSelected(const char*) | |||
*/ | |||
virtual void uiFileBrowserSelected(const char* filename); | |||
#endif | |||
# endif | |||
/** | |||
OpenGL window reshape function, called when parent window is resized. | |||
@@ -210,7 +226,7 @@ private: | |||
friend class UIExporter; | |||
friend class UIExporterWindow; | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// these should not be used | |||
void setAbsoluteX(int) const noexcept {} | |||
void setAbsoluteY(int) const noexcept {} | |||
@@ -40,6 +40,8 @@ public: | |||
: width(w), | |||
height(h), | |||
title(t), | |||
transientWinId(0), | |||
visible(false), | |||
pid(0) {} | |||
virtual ~ExternalWindow() | |||
@@ -62,9 +64,14 @@ public: | |||
return title; | |||
} | |||
void setTitle(const char* const t) noexcept | |||
uintptr_t getTransientWinId() const noexcept | |||
{ | |||
title = t; | |||
return transientWinId; | |||
} | |||
bool isVisible() const noexcept | |||
{ | |||
return visible; | |||
} | |||
bool isRunning() noexcept | |||
@@ -84,6 +91,27 @@ public: | |||
return true; | |||
} | |||
virtual void setSize(uint w, uint h) | |||
{ | |||
width = w; | |||
height = h; | |||
} | |||
virtual void setTitle(const char* const t) | |||
{ | |||
title = t; | |||
} | |||
virtual void setTransientWinId(const uintptr_t winId) | |||
{ | |||
transientWinId = winId; | |||
} | |||
virtual void setVisible(const bool yesNo) | |||
{ | |||
visible = yesNo; | |||
} | |||
protected: | |||
bool startExternalProcess(const char* args[]) | |||
{ | |||
@@ -107,14 +135,6 @@ protected: | |||
} | |||
} | |||
private: | |||
uint width; | |||
uint height; | |||
String title; | |||
pid_t pid; | |||
friend class UIExporter; | |||
void terminateAndWaitForProcess() | |||
{ | |||
if (pid <= 0) | |||
@@ -162,6 +182,16 @@ private: | |||
} | |||
} | |||
private: | |||
uint width; | |||
uint height; | |||
String title; | |||
uintptr_t transientWinId; | |||
bool visible; | |||
pid_t pid; | |||
friend class UIExporter; | |||
DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow) | |||
}; | |||
@@ -66,6 +66,15 @@ | |||
# define nullptr NULL | |||
#endif | |||
/* Define DISTRHO_DEPRECATED */ | |||
#if defined(__GNUC__) | |||
# define DISTRHO_DEPRECATED __attribute__((deprecated)) | |||
#elif defined(_MSC_VER) | |||
# define DISTRHO_DEPRECATED __declspec(deprecated) | |||
#else | |||
# define DISTRHO_DEPRECATED | |||
#endif | |||
/* Define DISTRHO_SAFE_ASSERT* */ | |||
#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__); | |||
#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; } | |||
@@ -126,7 +126,8 @@ | |||
// ----------------------------------------------------------------------- | |||
// Enable full state if plugin exports presets | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE | |||
# warning Plugins with programs and state need to implement full state API | |||
# undef DISTRHO_PLUGIN_WANT_FULL_STATE | |||
# define DISTRHO_PLUGIN_WANT_FULL_STATE 1 | |||
#endif | |||
@@ -16,11 +16,6 @@ | |||
#include "DistrhoPluginInternal.hpp" | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI | |||
# undef DISTRHO_PLUGIN_HAS_UI | |||
# define DISTRHO_PLUGIN_HAS_UI 0 | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
# include "DistrhoUIInternal.hpp" | |||
#else | |||
@@ -286,9 +286,9 @@ public: | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
char* dssi_configure(const char* const key, const char* const value) | |||
{ | |||
if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) | |||
if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX)) == 0) | |||
return nullptr; | |||
if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) | |||
if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX)) == 0) | |||
return nullptr; | |||
fPlugin.setState(key, value); | |||
@@ -169,7 +169,7 @@ public: | |||
void lv2_activate() | |||
{ | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
std::memset(&fTimePosition, 0, sizeof(TimePosition)); | |||
fTimePosition.clear(); | |||
// hosts may not send all values, resulting on some invalid data | |||
fTimePosition.bbt.bar = 1; | |||
@@ -613,7 +613,6 @@ public: | |||
fEventsOutData.initIfNeeded(fURIDs.atomSequence); | |||
LV2_Atom_Event* aev; | |||
uint32_t offset = fEventsOutData.offset; | |||
const uint32_t capacity = fEventsOutData.capacity; | |||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
@@ -635,8 +634,11 @@ public: | |||
// set msg size (key + value + separator + 2x null terminator) | |||
const size_t msgSize(key.length()+value.length()+3); | |||
if (sizeof(LV2_Atom_Event) + msgSize > capacity - offset) | |||
if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | |||
{ | |||
d_stdout("Sending key '%s' to UI failed, out of space", key.buffer()); | |||
break; | |||
} | |||
// reserve msg space | |||
// FIXME create a large enough buffer beforehand | |||
@@ -644,15 +646,15 @@ public: | |||
std::memset(msgBuf, 0, msgSize); | |||
// write key and value in atom bufer | |||
std::memcpy(msgBuf, key.buffer(), key.length()); | |||
std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()); | |||
std::memcpy(msgBuf, key.buffer(), key.length()+1); | |||
std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |||
// put data | |||
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + offset); | |||
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | |||
aev->time.frames = 0; | |||
aev->body.type = fURIDs.distrhoState; | |||
aev->body.size = msgSize; | |||
std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize-1); | |||
std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize); | |||
fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | |||
@@ -139,6 +139,7 @@ void lv2_generate_ttl(const char* const basename) | |||
# endif | |||
manifestString += "\n"; | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
// TODO: pluginUI.isUserResizable() | |||
manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
manifestString += " ui:resize ,\n"; | |||
manifestString += " ui:touch ;\n"; | |||
@@ -148,9 +149,10 @@ void lv2_generate_ttl(const char* const basename) | |||
manifestString += " <" LV2_INSTANCE_ACCESS_URI "> ,\n"; | |||
manifestString += " <" LV2_OPTIONS__options "> ,\n"; | |||
manifestString += " <" LV2_URID__map "> .\n"; | |||
# else | |||
manifestString += " opts:supportedOption <" LV2_PARAMETERS__sampleRate "> .\n"; | |||
# else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | |||
# endif | |||
# endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
manifestString += "\n"; | |||
#endif | |||
@@ -192,7 +194,7 @@ void lv2_generate_ttl(const char* const basename) | |||
String pluginString; | |||
// header | |||
#if DISTRHO_LV2_USE_EVENTS_IN | |||
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
#endif | |||
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
@@ -628,6 +630,7 @@ void lv2_generate_ttl(const char* const basename) | |||
# endif | |||
uiString += "\n"; | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
// TODO: pluginUI.isUserResizable() | |||
uiString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
uiString += " ui:resize ,\n"; | |||
uiString += " ui:touch ;\n"; | |||
@@ -72,8 +72,16 @@ static const writeMidiFunc writeMidiCallback = nullptr; | |||
void strncpy(char* const dst, const char* const src, const size_t size) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||
std::memcpy(dst, src, std::min(std::strlen(src), size-1)); | |||
dst[size-1] = '\0'; | |||
if (const size_t len = std::min(std::strlen(src), size-1U)) | |||
{ | |||
std::memcpy(dst, src, len); | |||
dst[len] = '\0'; | |||
} | |||
else | |||
{ | |||
dst[0] = '\0'; | |||
} | |||
} | |||
void snprintf_param(char* const dst, const float value, const size_t size) | |||
@@ -965,7 +973,7 @@ public: | |||
if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) | |||
{ | |||
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; | |||
const double barBeats = (std::fmod(vstTimeInfo->ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigDenominator; | |||
const double barBeats = (std::fmod(vstTimeInfo->ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigNumerator; | |||
const double rest = std::fmod(barBeats, 1.0); | |||
fTimePosition.bbt.bar = int(vstTimeInfo->ppqPos)/ppqPerBar + 1; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -15,8 +15,7 @@ | |||
*/ | |||
#include "DistrhoUIInternal.hpp" | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "src/WidgetPrivateData.hpp" | |||
#endif | |||
@@ -27,29 +26,30 @@ START_NAMESPACE_DISTRHO | |||
double d_lastUiSampleRate = 0.0; | |||
void* d_lastUiDspPtr = nullptr; | |||
#ifdef HAVE_DGL | |||
Window* d_lastUiWindow = nullptr; | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uintptr_t g_nextWindowId = 0; | |||
const char* g_nextBundlePath = nullptr; | |||
#else | |||
Window* d_lastUiWindow = nullptr; | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI */ | |||
#ifdef HAVE_DGL | |||
UI::UI(uint width, uint height) | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
UI::UI(uint width, uint height, bool userResizable) | |||
: UIWidget(width, height), | |||
pData(new PrivateData(userResizable)) {} | |||
#else | |||
UI::UI(uint width, uint height, bool userResizable) | |||
: UIWidget(*d_lastUiWindow), | |||
pData(new PrivateData()) | |||
pData(new PrivateData(userResizable)) | |||
{ | |||
((UIWidget*)this)->pData->needsFullViewport = false; | |||
if (width > 0 && height > 0) | |||
setSize(width, height); | |||
} | |||
#else | |||
UI::UI(uint width, uint height) | |||
: UIWidget(width, height), | |||
pData(new PrivateData()) {} | |||
#endif | |||
UI::~UI() | |||
@@ -57,6 +57,25 @@ UI::~UI() | |||
delete pData; | |||
} | |||
bool UI::isUserResizable() const noexcept | |||
{ | |||
return pData->userResizable; | |||
} | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(minWidth > 0,); | |||
DISTRHO_SAFE_ASSERT_RETURN(minHeight > 0,); | |||
pData->automaticallyScale = automaticallyScale; | |||
pData->minWidth = minWidth; | |||
pData->minHeight = minHeight; | |||
getParentWindow().setGeometryConstraints(minWidth, minHeight, keepAspectRatio); | |||
} | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Host state */ | |||
@@ -121,15 +140,15 @@ uintptr_t UI::getNextWindowId() noexcept | |||
void UI::sampleRateChanged(double) {} | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI Callbacks (optional) */ | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# ifndef DGL_FILE_BROWSER_DISABLED | |||
void UI::uiFileBrowserSelected(const char*) | |||
{ | |||
} | |||
#endif | |||
# endif | |||
void UI::uiReshape(uint width, uint height) | |||
{ | |||
@@ -148,9 +167,12 @@ void UI::uiReshape(uint width, uint height) | |||
void UI::onResize(const ResizeEvent& ev) | |||
{ | |||
if (pData->resizeInProgress) | |||
return; | |||
pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); | |||
} | |||
#endif | |||
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// ----------------------------------------------------------------------------------------------------------- | |||
@@ -194,7 +194,7 @@ protected: | |||
uint8_t mdata[4] = { | |||
0, | |||
channel + (velocity != 0 ? 0x90 : 0x80), | |||
static_cast<uint8_t>(channel + (velocity != 0 ? 0x90 : 0x80)), | |||
note, | |||
velocity | |||
}; | |||
@@ -19,7 +19,10 @@ | |||
#include "../DistrhoUI.hpp" | |||
#ifdef HAVE_DGL | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../extra/Sleep.hpp" | |||
using DGL_NAMESPACE::IdleCallback; | |||
#else | |||
# include "../../dgl/Application.hpp" | |||
# include "../../dgl/Window.hpp" | |||
using DGL_NAMESPACE::Application; | |||
@@ -34,7 +37,7 @@ START_NAMESPACE_DISTRHO | |||
extern double d_lastUiSampleRate; | |||
extern void* d_lastUiDspPtr; | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
extern Window* d_lastUiWindow; | |||
#endif | |||
extern uintptr_t g_nextWindowId; | |||
@@ -60,6 +63,13 @@ struct UI::PrivateData { | |||
void* dspPtr; | |||
#endif | |||
// UI | |||
const bool userResizable; | |||
bool automaticallyScale; | |||
bool resizeInProgress; | |||
uint minWidth; | |||
uint minHeight; | |||
// Callbacks | |||
void* callbacksPtr; | |||
editParamFunc editParamCallbackFunc; | |||
@@ -68,12 +78,17 @@ struct UI::PrivateData { | |||
sendNoteFunc sendNoteCallbackFunc; | |||
setSizeFunc setSizeCallbackFunc; | |||
PrivateData() noexcept | |||
PrivateData(const bool resizable) noexcept | |||
: sampleRate(d_lastUiSampleRate), | |||
parameterOffset(0), | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
dspPtr(d_lastUiDspPtr), | |||
#endif | |||
userResizable(resizable), | |||
automaticallyScale(false), | |||
resizeInProgress(false), | |||
minWidth(0), | |||
minHeight(0), | |||
callbacksPtr(nullptr), | |||
editParamCallbackFunc(nullptr), | |||
setParamCallbackFunc(nullptr), | |||
@@ -134,7 +149,20 @@ struct UI::PrivateData { | |||
// ----------------------------------------------------------------------- | |||
// Plugin Window, needed to take care of resize properly | |||
#ifdef HAVE_DGL | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
static inline | |||
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath) | |||
{ | |||
d_lastUiDspPtr = dspPtr; | |||
g_nextWindowId = winId; | |||
g_nextBundlePath = bundlePath; | |||
UI* const ret = createUI(); | |||
d_lastUiDspPtr = nullptr; | |||
g_nextWindowId = 0; | |||
g_nextBundlePath = nullptr; | |||
return ret; | |||
} | |||
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
static inline | |||
UI* createUiWrapper(void* const dspPtr, Window* const window) | |||
{ | |||
@@ -155,9 +183,9 @@ public: | |||
fIsReady(false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI->pData != nullptr,); | |||
// set window size | |||
setResizable(false); | |||
setResizable(fUI->pData->userResizable); | |||
setSize(fUI->getWidth(), fUI->getHeight()); | |||
} | |||
@@ -182,11 +210,25 @@ protected: | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
UI::PrivateData* const pData = fUI->pData; | |||
DISTRHO_SAFE_ASSERT_RETURN(pData != nullptr,); | |||
if (pData->automaticallyScale) | |||
{ | |||
const double scaleHorizontal = static_cast<double>(width) / static_cast<double>(pData->minWidth); | |||
const double scaleVertical = static_cast<double>(height) / static_cast<double>(pData->minHeight); | |||
setScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical); | |||
} | |||
pData->resizeInProgress = true; | |||
fUI->setSize(width, height); | |||
pData->resizeInProgress = false; | |||
fUI->uiReshape(width, height); | |||
fIsReady = true; | |||
} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# ifndef DGL_FILE_BROWSER_DISABLED | |||
// custom file-browser selected | |||
void fileBrowserSelected(const char* filename) override | |||
{ | |||
@@ -194,26 +236,13 @@ protected: | |||
fUI->uiFileBrowserSelected(filename); | |||
} | |||
#endif | |||
# endif | |||
private: | |||
UI* const fUI; | |||
bool fIsReady; | |||
}; | |||
#else | |||
static inline | |||
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath) | |||
{ | |||
d_lastUiDspPtr = dspPtr; | |||
g_nextWindowId = winId; | |||
g_nextBundlePath = bundlePath; | |||
UI* const ret = createUI(); | |||
d_lastUiDspPtr = nullptr; | |||
g_nextWindowId = 0; | |||
g_nextBundlePath = nullptr; | |||
return ret; | |||
} | |||
#endif | |||
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// ----------------------------------------------------------------------- | |||
// UI exporter class | |||
@@ -230,13 +259,13 @@ public: | |||
const setSizeFunc setSizeCall, | |||
void* const dspPtr = nullptr, | |||
const char* const bundlePath = nullptr) | |||
#ifdef HAVE_DGL | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
: fUI(createUiWrapper(dspPtr, winId, bundlePath)), | |||
#else | |||
: glApp(), | |||
glWindow(glApp, winId, dspPtr), | |||
fChangingSize(false), | |||
fUI(glWindow.getUI()), | |||
#else | |||
: fUI(createUiWrapper(dspPtr, winId, bundlePath)), | |||
#endif | |||
fData((fUI != nullptr) ? fUI->pData : nullptr) | |||
{ | |||
@@ -250,54 +279,65 @@ public: | |||
fData->sendNoteCallbackFunc = sendNoteCall; | |||
fData->setSizeCallbackFunc = setSizeCall; | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// unused | |||
return; (void)bundlePath; | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
~UIExporter() | |||
{ | |||
delete fUI; | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uint getWidth() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getWidth(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | |||
return fUI->getWidth(); | |||
#endif | |||
} | |||
uint getHeight() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getHeight(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | |||
return fUI->getHeight(); | |||
#endif | |||
} | |||
bool isVisible() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.isVisible(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||
return fUI->isRunning(); | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
intptr_t getWindowId() const noexcept | |||
{ | |||
return 0; | |||
} | |||
#else | |||
uint getWidth() const noexcept | |||
{ | |||
return glWindow.getWidth(); | |||
} | |||
uint getHeight() const noexcept | |||
{ | |||
return glWindow.getHeight(); | |||
} | |||
bool isVisible() const noexcept | |||
{ | |||
return glWindow.isVisible(); | |||
} | |||
intptr_t getWindowId() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getWindowId(); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
@@ -339,7 +379,39 @@ public: | |||
// ------------------------------------------------------------------- | |||
#ifdef HAVE_DGL | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void exec(IdleCallback* const cb) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setVisible(true); | |||
cb->idleCallback(); | |||
while (fUI->isRunning()) | |||
{ | |||
d_msleep(10); | |||
cb->idleCallback(); | |||
} | |||
} | |||
void exec_idle() | |||
{ | |||
} | |||
bool idle() | |||
{ | |||
return true; | |||
} | |||
void quit() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setVisible(false); | |||
fUI->terminateAndWaitForProcess(); | |||
} | |||
#else | |||
void exec(IdleCallback* const cb) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | |||
@@ -355,48 +427,64 @@ public: | |||
if (glWindow.isReady()) | |||
fUI->uiIdle(); | |||
} | |||
#endif | |||
bool idle() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||
#ifdef HAVE_DGL | |||
glApp.idle(); | |||
if (glWindow.isReady()) | |||
fUI->uiIdle(); | |||
return ! glApp.isQuiting(); | |||
#else | |||
return fUI->isRunning(); | |||
#endif | |||
} | |||
void quit() | |||
{ | |||
#ifdef HAVE_DGL | |||
glWindow.close(); | |||
glApp.quit(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->terminateAndWaitForProcess(); | |||
#endif | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void setWindowTitle(const char* const uiTitle) | |||
{ | |||
#ifdef HAVE_DGL | |||
glWindow.setTitle(uiTitle); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setTitle(uiTitle); | |||
#endif | |||
} | |||
#ifdef HAVE_DGL | |||
void setWindowSize(const uint width, const uint height, const bool = false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setSize(width, height); | |||
} | |||
void setWindowTransientWinId(const uintptr_t winId) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setTransientWinId(winId); | |||
} | |||
bool setWindowVisible(const bool yesNo) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||
fUI->setVisible(yesNo); | |||
return fUI->isRunning(); | |||
} | |||
#else | |||
void setWindowTitle(const char* const uiTitle) | |||
{ | |||
glWindow.setTitle(uiTitle); | |||
} | |||
void setWindowSize(const uint width, const uint height, const bool updateUI = false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
@@ -431,12 +519,8 @@ public: | |||
bool handlePluginSpecial(const bool press, const Key key) | |||
{ | |||
return glWindow.handlePluginKeyboard(press, key); | |||
return glWindow.handlePluginSpecial(press, key); | |||
} | |||
#else | |||
void setWindowSize(const uint, const uint, const bool) {} | |||
void setWindowTransientWinId(const uintptr_t) {} | |||
bool setWindowVisible(const bool) { return true; } | |||
#endif | |||
// ------------------------------------------------------------------- | |||
@@ -457,7 +541,7 @@ public: | |||
} | |||
private: | |||
#ifdef HAVE_DGL | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// ------------------------------------------------------------------- | |||
// DGL Application and Window for this widget | |||
@@ -117,13 +117,13 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) | |||
@endcode | |||
*/ | |||
#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ | |||
for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \ | |||
for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(&(seq)->body); \ | |||
!lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ | |||
(iter) = lv2_atom_sequence_next(iter)) | |||
/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ | |||
#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ | |||
for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(body); \ | |||
for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(body); \ | |||
!lv2_atom_sequence_is_end(body, size, (iter)); \ | |||
(iter) = lv2_atom_sequence_next(iter)) | |||
@@ -214,13 +214,13 @@ lv2_atom_tuple_next(const LV2_Atom* i) | |||
@endcode | |||
*/ | |||
#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ | |||
for (const LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \ | |||
for (const LV2_Atom* iter = lv2_atom_tuple_begin(tuple); \ | |||
!lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->size, (iter)); \ | |||
(iter) = lv2_atom_tuple_next(iter)) | |||
/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ | |||
#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ | |||
for (const LV2_Atom* (iter) = (const LV2_Atom*)body; \ | |||
for (const LV2_Atom* iter = (const LV2_Atom*)body; \ | |||
!lv2_atom_tuple_is_end(body, size, (iter)); \ | |||
(iter) = lv2_atom_tuple_next(iter)) | |||
@@ -270,13 +270,13 @@ lv2_atom_object_next(const LV2_Atom_Property_Body* i) | |||
@endcode | |||
*/ | |||
#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ | |||
for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \ | |||
for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(&(obj)->body); \ | |||
!lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ | |||
(iter) = lv2_atom_object_next(iter)) | |||
/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ | |||
#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ | |||
for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(body); \ | |||
for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(body); \ | |||
!lv2_atom_object_is_end(body, size, (iter)); \ | |||
(iter) = lv2_atom_object_next(iter)) | |||
@@ -0,0 +1,113 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# DISTRHO Plugin Framework (DPF) | |||
# Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
# | |||
# Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
# or without fee is hereby granted, provided that the above copyright notice and this | |||
# permission notice appear in all copies. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
# TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
import os, sys | |||
# ----------------------------------------------------- | |||
def png2c(namespace, filenames): | |||
fdH = open("%s.hpp" % namespace, "w") | |||
fdH.write("/* (Auto-generated binary data file). */\n") | |||
fdH.write("\n") | |||
fdH.write("#ifndef BINARY_%s_HPP\n" % namespace.upper()) | |||
fdH.write("#define BINARY_%s_HPP\n" % namespace.upper()) | |||
fdH.write("\n") | |||
fdH.write("namespace %s\n" % namespace) | |||
fdH.write("{\n") | |||
fdC = open("%s.cpp" % namespace, "w") | |||
fdC.write("/* (Auto-generated binary data file). */\n") | |||
fdC.write("\n") | |||
fdC.write("#include \"%s.hpp\"\n" % namespace) | |||
fdC.write("\n") | |||
tempIndex = 1 | |||
for filename in filenames: | |||
shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] | |||
shortFilename = shortFilename.replace("-", "_") | |||
pngData = open(filename, 'rb').read() | |||
print("Generating data for \"%s\"" % (filename)) | |||
fdH.write(" extern const char* %sData;\n" % shortFilename) | |||
fdH.write(" const unsigned int %sDataSize = %i;\n" % (shortFilename, len(pngData))) | |||
if tempIndex != len(filenames): | |||
fdH.write("\n") | |||
fdC.write("static const unsigned char temp_%s_%i[] = {\n" % (shortFilename, tempIndex)) | |||
curColumn = 1 | |||
fdC.write(" ") | |||
for data in pngData: | |||
if curColumn == 0: | |||
fdC.write(" ") | |||
fdC.write(" %3u," % data) | |||
if curColumn > 20: | |||
fdC.write("\n ") | |||
curColumn = 1 | |||
else: | |||
curColumn += 1 | |||
fdC.write("};\n") | |||
fdC.write("const char* %s::%sData = (const char*)temp_%s_%i;\n" % (namespace, shortFilename, shortFilename, tempIndex)) | |||
if tempIndex != len(filenames): | |||
fdC.write("\n") | |||
tempIndex += 1 | |||
fdH.write("}\n") | |||
fdH.write("\n") | |||
fdH.write("#endif // BINARY_%s_HPP\n" % namespace.upper()) | |||
fdH.write("\n") | |||
fdH.close() | |||
fdC.write("\n") | |||
fdC.close() | |||
# ----------------------------------------------------- | |||
if __name__ == '__main__': | |||
if len(sys.argv) != 3: | |||
print("Usage: %s <namespace> <artwork-folder>" % sys.argv[0]) | |||
quit() | |||
namespace = sys.argv[1].replace("-","_") | |||
artFolder = sys.argv[2] | |||
if not os.path.exists(artFolder): | |||
print("Folder '%s' does not exist" % artFolder) | |||
quit() | |||
# find png files | |||
pngFiles = [] | |||
for root, dirs, files in os.walk(artFolder): | |||
for name in [name for name in files if name.lower().endswith(".png")]: | |||
pngFiles.append(os.path.join(root, name)) | |||
pngFiles.sort() | |||
# create code now | |||
png2c(namespace, pngFiles) |
@@ -1,8 +1,8 @@ | |||
#!/usr/bin/env python | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# DISTRHO Plugin Framework (DPF) | |||
# Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
# Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
# | |||
# Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
# or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -52,7 +52,10 @@ def png2rgba(namespace, filenames): | |||
shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] | |||
shortFilename = shortFilename.replace("-", "_") | |||
png = Image.open(filename) | |||
png = Image.open(filename) | |||
if png.getpalette(): | |||
png = png.convert() | |||
pngNumpy = numpy.array(png) | |||
pngData = pngNumpy.tolist() | |||
#pngData.reverse() | |||
@@ -60,8 +60,8 @@ public: | |||
}; | |||
MVerb(){ | |||
DampingFreq = 18000.; | |||
BandwidthFreq = 18000.; | |||
DampingFreq = 0.9; | |||
BandwidthFreq = 0.9; | |||
SampleRate = 44100.; | |||
Decay = 0.5; | |||
Gain = 1.; | |||