@@ -1,5 +1,5 @@ | |||||
DISTRHO Plugin Framework (DPF) | 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 | 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 | or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -77,12 +77,12 @@ all: | |||||
# Common | # Common | ||||
$(BUILD_DIR)/%.c.o: %.c | $(BUILD_DIR)/%.c.o: %.c | ||||
-@mkdir -p $(BUILD_DIR) | |||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | ||||
$(BUILD_DIR)/%.cpp.o: %.cpp | $(BUILD_DIR)/%.cpp.o: %.cpp | ||||
-@mkdir -p $(BUILD_DIR) | |||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | @$(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) | - [DISTRHO ndc Plugs](https://github.com/DISTRHO/ndc-Plugs) | ||||
- [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) (work in progress) | - [Juice Plugins](https://github.com/DISTRHO/JuicePlugins) (work in progress) | ||||
- [ZamAudio Suite](https://github.com/zamaudio/zam-plugins) | - [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/> | Plugin examples are available in the `example/` folder inside this repo.<br/> | ||||
@@ -240,7 +240,7 @@ public: | |||||
{ | { | ||||
public: | public: | ||||
virtual ~Callback() {} | 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; | explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; | ||||
@@ -92,6 +92,8 @@ public: | |||||
bool openFileBrowser(const FileBrowserOptions& options); | bool openFileBrowser(const FileBrowserOptions& options); | ||||
#endif | #endif | ||||
bool isEmbed() const noexcept; | |||||
bool isVisible() const noexcept; | bool isVisible() const noexcept; | ||||
void setVisible(bool yesNo); | void setVisible(bool yesNo); | ||||
@@ -107,8 +109,15 @@ public: | |||||
const char* getTitle() const noexcept; | const char* getTitle() const noexcept; | ||||
void setTitle(const char* title); | void setTitle(const char* title); | ||||
void setGeometryConstraints(uint width, uint height, bool aspect); | |||||
void setTransientWinId(uintptr_t winId); | 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; | Application& getApp() const noexcept; | ||||
intptr_t getWindowId() const noexcept; | intptr_t getWindowId() const noexcept; | ||||
@@ -237,7 +237,7 @@ bool Size<T>::isNotNull() const noexcept | |||||
template<typename T> | template<typename T> | ||||
bool Size<T>::isValid() const noexcept | bool Size<T>::isValid() const noexcept | ||||
{ | { | ||||
return fWidth > 1 && fHeight > 1; | |||||
return fWidth > 0 && fHeight > 0; | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -447,10 +447,8 @@ void NanoVG::translate(float x, float y) | |||||
void NanoVG::rotate(float angle) | 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) | void NanoVG::skewX(float angle) | ||||
@@ -63,7 +63,7 @@ struct Widget::PrivateData { | |||||
subWidgets.clear(); | 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) | if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) | ||||
return; | return; | ||||
@@ -76,29 +76,32 @@ struct Widget::PrivateData { | |||||
if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height))) | if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height))) | ||||
{ | { | ||||
// full viewport size | // 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) | else if (needsScaling) | ||||
{ | { | ||||
// limit viewport to widget bounds | // limit viewport to widget bounds | ||||
glViewport(absolutePos.getX(), | 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 | else | ||||
{ | { | ||||
// only set viewport pos | // 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 | // 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); | glEnable(GL_SCISSOR_TEST); | ||||
needsDisableScissor = true; | needsDisableScissor = true; | ||||
@@ -113,17 +116,17 @@ struct Widget::PrivateData { | |||||
needsDisableScissor = false; | 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) | for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | ||||
{ | { | ||||
Widget* const widget(*it); | Widget* const widget(*it); | ||||
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | 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" | #include "../Base.hpp" | ||||
#undef PUGL_HAVE_CAIRO | |||||
#undef PUGL_HAVE_GL | |||||
#define PUGL_HAVE_GL 1 | |||||
#include "pugl/pugl.h" | #include "pugl/pugl.h" | ||||
#if defined(__GNUC__) && (__GNUC__ >= 7) | #if defined(__GNUC__) && (__GNUC__ >= 7) | ||||
@@ -61,7 +57,7 @@ extern "C" { | |||||
#define FOR_EACH_WIDGET_INV(rit) \ | #define FOR_EACH_WIDGET_INV(rit) \ | ||||
for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++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 DBG(msg) std::fprintf(stderr, "%s", msg); | ||||
# define DBGp(...) std::fprintf(stderr, __VA_ARGS__); | # define DBGp(...) std::fprintf(stderr, __VA_ARGS__); | ||||
# define DBGF std::fflush(stderr); | # define DBGF std::fflush(stderr); | ||||
@@ -87,6 +83,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(false), | fUsingEmbed(false), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fScaling(1.0), | |||||
fTitle(nullptr), | fTitle(nullptr), | ||||
fWidgets(), | fWidgets(), | ||||
fModal(), | fModal(), | ||||
@@ -117,6 +114,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(false), | fUsingEmbed(false), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fScaling(1.0), | |||||
fTitle(nullptr), | fTitle(nullptr), | ||||
fWidgets(), | fWidgets(), | ||||
fModal(parent.pData), | fModal(parent.pData), | ||||
@@ -159,6 +157,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(parentId != 0), | fUsingEmbed(parentId != 0), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fScaling(1.0), | |||||
fTitle(nullptr), | fTitle(nullptr), | ||||
fWidgets(), | fWidgets(), | ||||
fModal(), | fModal(), | ||||
@@ -204,7 +203,6 @@ struct Window::PrivateData { | |||||
return; | return; | ||||
} | } | ||||
puglInitContextType(fView, PUGL_GL); | |||||
puglInitUserResizable(fView, fResizable); | puglInitUserResizable(fView, fResizable); | ||||
puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); | puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); | ||||
@@ -539,6 +537,7 @@ struct Window::PrivateData { | |||||
DBG("Window setResizable called\n"); | DBG("Window setResizable called\n"); | ||||
fResizable = yesNo; | fResizable = yesNo; | ||||
fView->user_resizable = yesNo; | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX | 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) | void setSize(uint width, uint height, const bool forced = false) | ||||
{ | { | ||||
if (width <= 1 || height <= 1) | if (width <= 1 || height <= 1) | ||||
@@ -605,7 +615,6 @@ struct Window::PrivateData { | |||||
} | } | ||||
} | } | ||||
#else | #else | ||||
XResizeWindow(xDisplay, xWindow, width, height); | |||||
if (! fResizable) | if (! fResizable) | ||||
{ | { | ||||
@@ -620,9 +629,11 @@ struct Window::PrivateData { | |||||
sizeHints.max_width = static_cast<int>(width); | sizeHints.max_width = static_cast<int>(width); | ||||
sizeHints.max_height = static_cast<int>(height); | sizeHints.max_height = static_cast<int>(height); | ||||
XSetNormalHints(xDisplay, xWindow, &sizeHints); | |||||
XSetWMNormalHints(xDisplay, xWindow, &sizeHints); | |||||
} | } | ||||
XResizeWindow(xDisplay, xWindow, width, height); | |||||
if (! forced) | if (! forced) | ||||
XFlush(xDisplay); | XFlush(xDisplay); | ||||
#endif | #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) | void addWidget(Widget* const widget) | ||||
{ | { | ||||
fWidgets.push_back(widget); | fWidgets.push_back(widget); | ||||
@@ -736,7 +773,7 @@ struct Window::PrivateData { | |||||
FOR_EACH_WIDGET(it) | FOR_EACH_WIDGET(it) | ||||
{ | { | ||||
Widget* const widget(*it); | Widget* const widget(*it); | ||||
widget->pData->display(fWidth, fHeight, false); | |||||
widget->pData->display(fWidth, fHeight, fScaling, false); | |||||
} | } | ||||
fSelf->onDisplayAfter(); | fSelf->onDisplayAfter(); | ||||
@@ -796,7 +833,7 @@ struct Window::PrivateData { | |||||
return 1; | 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); | DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y); | ||||
@@ -806,6 +843,9 @@ struct Window::PrivateData { | |||||
if (fModal.childFocus != nullptr) | if (fModal.childFocus != nullptr) | ||||
return fModal.childFocus->focus(); | return fModal.childFocus->focus(); | ||||
x /= fScaling; | |||||
y /= fScaling; | |||||
Widget::MouseEvent ev; | Widget::MouseEvent ev; | ||||
ev.button = button; | ev.button = button; | ||||
ev.press = press; | 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) | if (fModal.childFocus != nullptr) | ||||
return; | return; | ||||
x /= fScaling; | |||||
y /= fScaling; | |||||
Widget::MotionEvent ev; | Widget::MotionEvent ev; | ||||
ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | ||||
ev.time = puglGetEventTimestamp(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); | DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy); | ||||
if (fModal.childFocus != nullptr) | if (fModal.childFocus != nullptr) | ||||
return; | return; | ||||
x /= fScaling; | |||||
y /= fScaling; | |||||
dx /= fScaling; | |||||
dy /= fScaling; | |||||
Widget::ScrollEvent ev; | Widget::ScrollEvent ev; | ||||
ev.delta = Point<float>(dx, dy); | ev.delta = Point<float>(dx, dy); | ||||
ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); | ||||
@@ -1000,6 +1048,7 @@ struct Window::PrivateData { | |||||
bool fUsingEmbed; | bool fUsingEmbed; | ||||
uint fWidth; | uint fWidth; | ||||
uint fHeight; | uint fHeight; | ||||
double fScaling; | |||||
char* fTitle; | char* fTitle; | ||||
std::list<Widget*> fWidgets; | std::list<Widget*> fWidgets; | ||||
@@ -1221,6 +1270,11 @@ bool Window::openFileBrowser(const FileBrowserOptions& options) | |||||
} | } | ||||
#endif | #endif | ||||
bool Window::isEmbed() const noexcept | |||||
{ | |||||
return pData->fUsingEmbed; | |||||
} | |||||
bool Window::isVisible() const noexcept | bool Window::isVisible() const noexcept | ||||
{ | { | ||||
return pData->fVisible; | return pData->fVisible; | ||||
@@ -1241,6 +1295,11 @@ void Window::setResizable(bool yesNo) | |||||
pData->setResizable(yesNo); | pData->setResizable(yesNo); | ||||
} | } | ||||
void Window::setGeometryConstraints(uint width, uint height, bool aspect) | |||||
{ | |||||
pData->setGeometryConstraints(width, height, aspect); | |||||
} | |||||
uint Window::getWidth() const noexcept | uint Window::getWidth() const noexcept | ||||
{ | { | ||||
return pData->fWidth; | return pData->fWidth; | ||||
@@ -1281,6 +1340,26 @@ void Window::setTransientWinId(uintptr_t winId) | |||||
pData->setTransientWinId(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 | Application& Window::getApp() const noexcept | ||||
{ | { | ||||
return pData->fApp; | 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 <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 | #else | ||||
# ifdef _WIN32 | # 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 | # endif | ||||
# include "GL/gl.h" | |||||
#endif | |||||
#ifdef _WIN32 | |||||
# define PUGL_API | |||||
#else | |||||
# define PUGL_API __attribute__((visibility("hidden"))) | |||||
#endif | #endif | ||||
#ifdef __cplusplus | #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. | A function called when the window is closed. | ||||
*/ | */ | ||||
@@ -105,6 +178,16 @@ typedef void (*PuglMouseFunc)( | |||||
*/ | */ | ||||
typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | 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). | A function called on scrolling (e.g. mouse wheel or track pad). | ||||
@@ -190,12 +273,6 @@ puglInitUserResizable(PuglView* view, bool resizable); | |||||
PUGL_API void | PUGL_API void | ||||
puglInitTransientFor(PuglView* view, uintptr_t parent); | 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); | 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 | PUGL_API void | ||||
puglShowWindow(PuglView* view); | puglShowWindow(PuglView* view); | ||||
/** | /** | ||||
Hide the current window. | |||||
Hide Window (external ui) | |||||
*/ | */ | ||||
PUGL_API void | PUGL_API void | ||||
puglHideWindow(PuglView* view); | puglHideWindow(PuglView* view); | ||||
@@ -254,15 +349,6 @@ puglSetHandle(PuglView* view, PuglHandle handle); | |||||
PUGL_API PuglHandle | PUGL_API PuglHandle | ||||
puglGetHandle(PuglView* view); | 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. | Return the timestamp (if any) of the currently-processing event. | ||||
*/ | */ | ||||
@@ -337,6 +423,12 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||||
PUGL_API void | PUGL_API void | ||||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | 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. | 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. | Grab the input focus. | ||||
*/ | */ | ||||
@@ -368,6 +466,12 @@ puglProcessEvents(PuglView* view); | |||||
PUGL_API void | PUGL_API void | ||||
puglPostRedisplay(PuglView* view); | puglPostRedisplay(PuglView* view); | ||||
/** | |||||
Request a resize on the next call to puglProcessEvents(). | |||||
*/ | |||||
PUGL_API void | |||||
puglPostResize(PuglView* view); | |||||
/** | /** | ||||
Destroy a GL window. | Destroy a GL window. | ||||
*/ | */ | ||||
@@ -20,27 +20,9 @@ | |||||
Note this file contains function definitions, so it must be compiled into | Note this file contains function definitions, so it must be compiled into | ||||
the final binary exactly once. Each platform specific implementation file | the final binary exactly once. Each platform specific implementation file | ||||
including it once should achieve this. | 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; | typedef struct PuglInternalsImpl PuglInternals; | ||||
@@ -52,14 +34,13 @@ struct PuglViewImpl { | |||||
PuglMotionFunc motionFunc; | PuglMotionFunc motionFunc; | ||||
PuglMouseFunc mouseFunc; | PuglMouseFunc mouseFunc; | ||||
PuglReshapeFunc reshapeFunc; | PuglReshapeFunc reshapeFunc; | ||||
PuglResizeFunc resizeFunc; | |||||
PuglScrollFunc scrollFunc; | PuglScrollFunc scrollFunc; | ||||
PuglSpecialFunc specialFunc; | PuglSpecialFunc specialFunc; | ||||
PuglFileSelectedFunc fileSelectedFunc; | PuglFileSelectedFunc fileSelectedFunc; | ||||
PuglInternals* impl; | |||||
PuglInternals* impl; | |||||
PuglNativeWindow parent; | PuglNativeWindow parent; | ||||
PuglContextType ctx_type; | |||||
uintptr_t transient_parent; | uintptr_t transient_parent; | ||||
int width; | int width; | ||||
@@ -70,7 +51,8 @@ struct PuglViewImpl { | |||||
bool mouse_in_view; | bool mouse_in_view; | ||||
bool ignoreKeyRepeat; | bool ignoreKeyRepeat; | ||||
bool redisplay; | bool redisplay; | ||||
bool resizable; | |||||
bool user_resizable; | |||||
bool pending_resize; | |||||
uint32_t event_timestamp_ms; | uint32_t event_timestamp_ms; | ||||
}; | }; | ||||
@@ -120,7 +102,7 @@ puglInitWindowParent(PuglView* view, PuglNativeWindow parent) | |||||
void | void | ||||
puglInitUserResizable(PuglView* view, bool resizable) | puglInitUserResizable(PuglView* view, bool resizable) | ||||
{ | { | ||||
view->resizable = resizable; | |||||
view->user_resizable = resizable; | |||||
} | } | ||||
void | void | ||||
@@ -129,10 +111,33 @@ puglInitTransientFor(PuglView* view, uintptr_t parent) | |||||
view->transient_parent = 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 | void | ||||
@@ -201,6 +206,12 @@ puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) | |||||
view->reshapeFunc = reshapeFunc; | view->reshapeFunc = reshapeFunc; | ||||
} | } | ||||
void | |||||
puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc) | |||||
{ | |||||
view->resizeFunc = resizeFunc; | |||||
} | |||||
void | void | ||||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | ||||
{ | { | ||||
@@ -225,45 +236,19 @@ puglEnterContext(PuglView* view); | |||||
void | void | ||||
puglLeaveContext(PuglView* view, bool flush); | 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 | 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); | glMatrixMode(GL_PROJECTION); | ||||
glLoadIdentity(); | glLoadIdentity(); | ||||
glOrtho(0, width, height, 0, 0, 1); | glOrtho(0, width, height, 0, 0, 1); | ||||
@@ -271,82 +256,5 @@ puglDefaultReshape(PuglView* view, int width, int height) | |||||
glMatrixMode(GL_MODELVIEW); | glMatrixMode(GL_MODELVIEW); | ||||
glLoadIdentity(); | 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 | #endif | ||||
} |
@@ -218,7 +218,7 @@ puglDisplay(PuglView* view) | |||||
if (puglview->reshapeFunc) { | if (puglview->reshapeFunc) { | ||||
puglview->reshapeFunc(puglview, width, height); | puglview->reshapeFunc(puglview, width, height); | ||||
} else { | } else { | ||||
puglDefaultReshape(puglview, width, height); | |||||
puglDefaultReshape(width, height); | |||||
} | } | ||||
puglLeaveContext(puglview, false); | puglLeaveContext(puglview, false); | ||||
@@ -427,18 +427,13 @@ puglInitInternals() | |||||
void | void | ||||
puglEnterContext(PuglView* view) | 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 | void | ||||
puglLeaveContext(PuglView* view, bool flush) | puglLeaveContext(PuglView* view, bool flush) | ||||
{ | { | ||||
#ifdef PUGL_HAVE_GL | |||||
if (view->ctx_type == PUGL_GL && flush) { | |||||
if (flush) { | |||||
if (view->impl->glview->doubleBuffered) { | if (view->impl->glview->doubleBuffered) { | ||||
[[view->impl->glview openGLContext] flushBuffer]; | [[view->impl->glview openGLContext] flushBuffer]; | ||||
} else { | } else { | ||||
@@ -446,7 +441,6 @@ puglLeaveContext(PuglView* view, bool flush) | |||||
} | } | ||||
//[NSOpenGLContext clearCurrentContext]; | //[NSOpenGLContext clearCurrentContext]; | ||||
} | } | ||||
#endif | |||||
} | } | ||||
int | int | ||||
@@ -465,7 +459,7 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
impl->glview->puglview = view; | impl->glview->puglview = view; | ||||
if (view->resizable) { | |||||
if (view->user_resizable) { | |||||
[impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | [impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | ||||
} | } | ||||
@@ -566,11 +560,14 @@ puglGetNativeWindow(PuglView* view) | |||||
return (PuglNativeWindow)view->impl->glview; | 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)view; | ||||
(void)min_width; | |||||
(void)min_height; | |||||
(void)aspect; | |||||
} | } |
@@ -76,25 +76,17 @@ puglInitInternals() | |||||
void | void | ||||
puglEnterContext(PuglView* view) | 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 | void | ||||
puglLeaveContext(PuglView* view, bool flush) | 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 | int | ||||
@@ -137,7 +129,7 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
} | } | ||||
int winFlags = WS_POPUPWINDOW | WS_CAPTION; | int winFlags = WS_POPUPWINDOW | WS_CAPTION; | ||||
if (view->resizable) { | |||||
if (view->user_resizable) { | |||||
winFlags |= WS_SIZEBOX; | winFlags |= WS_SIZEBOX; | ||||
if (view->min_width > 0 && view->min_height > 0) { | if (view->min_width > 0 && view->min_height > 0) { | ||||
// Adjust the minimum window size to accomodate requested view size | // Adjust the minimum window size to accomodate requested view size | ||||
@@ -231,7 +223,7 @@ puglReshape(PuglView* view, int width, int height) | |||||
if (view->reshapeFunc) { | if (view->reshapeFunc) { | ||||
view->reshapeFunc(view, width, height); | view->reshapeFunc(view, width, height); | ||||
} else { | } else { | ||||
puglDefaultReshape(view, width, height); | |||||
puglDefaultReshape(width, height); | |||||
} | } | ||||
view->width = width; | view->width = width; | ||||
@@ -477,13 +469,14 @@ puglGetNativeWindow(PuglView* view) | |||||
return (PuglNativeWindow)view->impl->hwnd; | 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 2012-2014 David Robillard <http://drobilla.net> | ||||
Copyright 2013 Robin Gareus <robin@gareus.org> | |||||
Copyright 2011-2012 Ben Loftis, Harrison Consoles | 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 | Permission to use, copy, modify, and/or distribute this software for any | ||||
purpose with or without fee is hereby granted, provided that the above | purpose with or without fee is hereby granted, provided that the above | ||||
@@ -24,22 +24,14 @@ | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <GL/gl.h> | |||||
#include <GL/glx.h> | |||||
#include <X11/Xatom.h> | #include <X11/Xatom.h> | ||||
#include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||
#include <X11/Xutil.h> | #include <X11/Xutil.h> | ||||
#include <X11/keysym.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 | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
#define SOFD_HAVE_X11 | #define SOFD_HAVE_X11 | ||||
@@ -47,212 +39,145 @@ | |||||
#include "../sofd/libsofd.c" | #include "../sofd/libsofd.c" | ||||
#endif | #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 { | struct PuglInternalsImpl { | ||||
Display* display; | Display* display; | ||||
int screen; | int screen; | ||||
Window win; | Window win; | ||||
XIM xim; | |||||
XIC xic; | |||||
#ifdef PUGL_HAVE_CAIRO | |||||
cairo_t* cr; | |||||
cairo_surface_t* surface; | |||||
#endif | |||||
#ifdef PUGL_HAVE_GL | |||||
GLXContext ctx; | GLXContext ctx; | ||||
Bool doubleBuffered; | 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 | void | ||||
puglEnterContext(PuglView* view) | 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 | void | ||||
puglLeaveContext(PuglView* view, bool flush) | 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 | int | ||||
puglCreateWindow(PuglView* view, const char* title) | 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->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) { | if (!vi) { | ||||
XCloseDisplay(impl->display); | XCloseDisplay(impl->display); | ||||
impl->display = NULL; | |||||
free(impl); | |||||
return 1; | return 1; | ||||
} | } | ||||
#ifdef PUGL_HAVE_GL | |||||
#ifdef PUGL_VERBOSE | |||||
int glxMajor, glxMinor; | int glxMajor, glxMinor; | ||||
glXQueryVersion(impl->display, &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 | #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 xParent = view->parent | ||||
? (Window)view->parent | ? (Window)view->parent | ||||
: RootWindow(impl->display, impl->screen); | : RootWindow(impl->display, impl->screen); | ||||
@@ -262,76 +187,55 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
XSetWindowAttributes attr; | XSetWindowAttributes attr; | ||||
memset(&attr, 0, sizeof(XSetWindowAttributes)); | 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->win = XCreateWindow( | ||||
impl->display, xParent, | impl->display, xParent, | ||||
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | 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); | XCloseDisplay(impl->display); | ||||
impl->display = NULL; | |||||
free(impl); | |||||
return 1; | 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) { | if (title) { | ||||
XStoreName(impl->display, impl->win, 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); | Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | ||||
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | ||||
} | } | ||||
#ifdef PUGL_VERBOSE | |||||
if (glXIsDirect(impl->display, impl->ctx)) { | 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 { | } else { | ||||
PUGL_LOG("No DRI available\n"); | |||||
printf("puGL: No DRI available\n"); | |||||
} | } | ||||
#endif | |||||
XFree(vi); | 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 | void | ||||
@@ -340,18 +244,29 @@ puglDestroy(PuglView* view) | |||||
if (!view) { | if (!view) { | ||||
return; | return; | ||||
} | } | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | #ifndef DGL_FILE_BROWSER_DISABLED | ||||
x_fib_close(view->impl->display); | x_fib_close(view->impl->display); | ||||
#endif | #endif | ||||
destroyContext(view); | |||||
glXDestroyContext(view->impl->display, view->impl->ctx); | |||||
XDestroyWindow(view->impl->display, view->impl->win); | XDestroyWindow(view->impl->display, view->impl->win); | ||||
XCloseDisplay(view->impl->display); | XCloseDisplay(view->impl->display); | ||||
free(view->impl); | free(view->impl); | ||||
free(view); | 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 | static void | ||||
puglReshape(PuglView* view, int width, int height) | puglReshape(PuglView* view, int width, int height) | ||||
{ | { | ||||
@@ -360,7 +275,7 @@ puglReshape(PuglView* view, int width, int height) | |||||
if (view->reshapeFunc) { | if (view->reshapeFunc) { | ||||
view->reshapeFunc(view, width, height); | view->reshapeFunc(view, width, height); | ||||
} else { | } else { | ||||
puglDefaultReshape(view, width, height); | |||||
puglDefaultReshape(width, height); | |||||
} | } | ||||
puglLeaveContext(view, false); | puglLeaveContext(view, false); | ||||
@@ -375,7 +290,6 @@ puglDisplay(PuglView* view) | |||||
puglEnterContext(view); | puglEnterContext(view); | ||||
view->redisplay = false; | view->redisplay = false; | ||||
if (view->displayFunc) { | if (view->displayFunc) { | ||||
view->displayFunc(view); | view->displayFunc(view); | ||||
} | } | ||||
@@ -383,6 +297,36 @@ puglDisplay(PuglView* view) | |||||
puglLeaveContext(view, true); | 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 | static PuglKey | ||||
keySymToSpecial(KeySym sym) | keySymToSpecial(KeySym sym) | ||||
{ | { | ||||
@@ -477,6 +421,9 @@ send_event: | |||||
PuglStatus | PuglStatus | ||||
puglProcessEvents(PuglView* view) | puglProcessEvents(PuglView* view) | ||||
{ | { | ||||
int conf_width = -1; | |||||
int conf_height = -1; | |||||
XEvent event; | XEvent event; | ||||
while (XPending(view->impl->display) > 0) { | while (XPending(view->impl->display) > 0) { | ||||
XNextEvent(view->impl->display, &event); | XNextEvent(view->impl->display, &event); | ||||
@@ -511,22 +458,26 @@ puglProcessEvents(PuglView* view) | |||||
} | } | ||||
switch (event.type) { | switch (event.type) { | ||||
case UnmapNotify: | |||||
if (view->motionFunc) { | |||||
view->motionFunc(view, -1, -1); | |||||
} | |||||
break; | |||||
case MapNotify: | case MapNotify: | ||||
puglReshape(view, view->width, view->height); | puglReshape(view, view->width, view->height); | ||||
break; | break; | ||||
case ConfigureNotify: | case ConfigureNotify: | ||||
if ((event.xconfigure.width != view->width) || | if ((event.xconfigure.width != view->width) || | ||||
(event.xconfigure.height != view->height)) { | (event.xconfigure.height != view->height)) { | ||||
puglReshape(view, | |||||
event.xconfigure.width, | |||||
event.xconfigure.height); | |||||
conf_width = event.xconfigure.width; | |||||
conf_height = event.xconfigure.height; | |||||
} | } | ||||
break; | break; | ||||
case Expose: | case Expose: | ||||
if (event.xexpose.count != 0) { | if (event.xexpose.count != 0) { | ||||
break; | break; | ||||
} | } | ||||
puglDisplay(view); | |||||
view->redisplay = true; | |||||
break; | break; | ||||
case MotionNotify: | case MotionNotify: | ||||
setModifiers(view, event.xmotion.state, event.xmotion.time); | 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) { | if (view->redisplay) { | ||||
puglDisplay(view); | puglDisplay(view); | ||||
} | } | ||||
@@ -615,22 +574,35 @@ puglPostRedisplay(PuglView* view) | |||||
view->redisplay = true; | view->redisplay = true; | ||||
} | } | ||||
void | |||||
puglPostResize(PuglView* view) | |||||
{ | |||||
view->pending_resize = true; | |||||
} | |||||
PuglNativeWindow | PuglNativeWindow | ||||
puglGetNativeWindow(PuglView* view) | puglGetNativeWindow(PuglView* view) | ||||
{ | { | ||||
return view->impl->win; | 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 | 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'. | The first tick is tick '0'. | ||||
*/ | */ | ||||
int32_t tick; | int32_t tick; | ||||
@@ -567,7 +567,7 @@ struct TimePosition { | |||||
float beatType; | 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. | Usually a moderately large integer with many denominators, such as 1920.0. | ||||
*/ | */ | ||||
double ticksPerBeat; | double ticksPerBeat; | ||||
@@ -590,6 +590,22 @@ struct TimePosition { | |||||
beatType(0.0f), | beatType(0.0f), | ||||
ticksPerBeat(0.0), | ticksPerBeat(0.0), | ||||
beatsPerMinute(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; | } bbt; | ||||
/** | /** | ||||
@@ -599,6 +615,16 @@ struct TimePosition { | |||||
: playing(false), | : playing(false), | ||||
frame(0), | frame(0), | ||||
bbt() {} | 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) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -20,7 +20,8 @@ | |||||
#include "extra/LeakDetector.hpp" | #include "extra/LeakDetector.hpp" | ||||
#include "src/DistrhoPluginChecks.h" | #include "src/DistrhoPluginChecks.h" | ||||
#ifndef HAVE_DGL | |||||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
# include "../dgl/Base.hpp" | |||||
# include "extra/ExternalWindow.hpp" | # include "extra/ExternalWindow.hpp" | ||||
typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; | typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; | ||||
#elif DISTRHO_UI_USE_NANOVG | #elif DISTRHO_UI_USE_NANOVG | ||||
@@ -54,13 +55,28 @@ public: | |||||
UI class constructor. | UI class constructor. | ||||
The UI should be initialized to a default state that matches the plugin side. | 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. | Destructor. | ||||
*/ | */ | ||||
virtual ~UI(); | 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 */ | * Host state */ | ||||
@@ -166,7 +182,7 @@ protected: | |||||
*/ | */ | ||||
virtual void sampleRateChanged(double newSampleRate); | virtual void sampleRateChanged(double newSampleRate); | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
/* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
* UI Callbacks (optional) */ | * UI Callbacks (optional) */ | ||||
@@ -176,13 +192,13 @@ protected: | |||||
*/ | */ | ||||
virtual void uiIdle() {} | virtual void uiIdle() {} | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
/** | /** | ||||
File browser selected function. | File browser selected function. | ||||
@see Window::fileBrowserSelected(const char*) | @see Window::fileBrowserSelected(const char*) | ||||
*/ | */ | ||||
virtual void uiFileBrowserSelected(const char* filename); | virtual void uiFileBrowserSelected(const char* filename); | ||||
#endif | |||||
# endif | |||||
/** | /** | ||||
OpenGL window reshape function, called when parent window is resized. | OpenGL window reshape function, called when parent window is resized. | ||||
@@ -210,7 +226,7 @@ private: | |||||
friend class UIExporter; | friend class UIExporter; | ||||
friend class UIExporterWindow; | friend class UIExporterWindow; | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
// these should not be used | // these should not be used | ||||
void setAbsoluteX(int) const noexcept {} | void setAbsoluteX(int) const noexcept {} | ||||
void setAbsoluteY(int) const noexcept {} | void setAbsoluteY(int) const noexcept {} | ||||
@@ -40,6 +40,8 @@ public: | |||||
: width(w), | : width(w), | ||||
height(h), | height(h), | ||||
title(t), | title(t), | ||||
transientWinId(0), | |||||
visible(false), | |||||
pid(0) {} | pid(0) {} | ||||
virtual ~ExternalWindow() | virtual ~ExternalWindow() | ||||
@@ -62,9 +64,14 @@ public: | |||||
return title; | 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 | bool isRunning() noexcept | ||||
@@ -84,6 +91,27 @@ public: | |||||
return true; | 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: | protected: | ||||
bool startExternalProcess(const char* args[]) | 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() | void terminateAndWaitForProcess() | ||||
{ | { | ||||
if (pid <= 0) | 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) | DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow) | ||||
}; | }; | ||||
@@ -66,6 +66,15 @@ | |||||
# define nullptr NULL | # define nullptr NULL | ||||
#endif | #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* */ | ||||
#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__); | #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; } | #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 | // 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 | # undef DISTRHO_PLUGIN_WANT_FULL_STATE | ||||
# define DISTRHO_PLUGIN_WANT_FULL_STATE 1 | # define DISTRHO_PLUGIN_WANT_FULL_STATE 1 | ||||
#endif | #endif | ||||
@@ -16,11 +16,6 @@ | |||||
#include "DistrhoPluginInternal.hpp" | #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 | #if DISTRHO_PLUGIN_HAS_UI | ||||
# include "DistrhoUIInternal.hpp" | # include "DistrhoUIInternal.hpp" | ||||
#else | #else | ||||
@@ -286,9 +286,9 @@ public: | |||||
# if DISTRHO_PLUGIN_WANT_STATE | # if DISTRHO_PLUGIN_WANT_STATE | ||||
char* dssi_configure(const char* const key, const char* const value) | 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; | 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; | return nullptr; | ||||
fPlugin.setState(key, value); | fPlugin.setState(key, value); | ||||
@@ -169,7 +169,7 @@ public: | |||||
void lv2_activate() | void lv2_activate() | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | #if DISTRHO_PLUGIN_WANT_TIMEPOS | ||||
std::memset(&fTimePosition, 0, sizeof(TimePosition)); | |||||
fTimePosition.clear(); | |||||
// hosts may not send all values, resulting on some invalid data | // hosts may not send all values, resulting on some invalid data | ||||
fTimePosition.bbt.bar = 1; | fTimePosition.bbt.bar = 1; | ||||
@@ -613,7 +613,6 @@ public: | |||||
fEventsOutData.initIfNeeded(fURIDs.atomSequence); | fEventsOutData.initIfNeeded(fURIDs.atomSequence); | ||||
LV2_Atom_Event* aev; | LV2_Atom_Event* aev; | ||||
uint32_t offset = fEventsOutData.offset; | |||||
const uint32_t capacity = fEventsOutData.capacity; | const uint32_t capacity = fEventsOutData.capacity; | ||||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | 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) | // set msg size (key + value + separator + 2x null terminator) | ||||
const size_t msgSize(key.length()+value.length()+3); | 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; | break; | ||||
} | |||||
// reserve msg space | // reserve msg space | ||||
// FIXME create a large enough buffer beforehand | // FIXME create a large enough buffer beforehand | ||||
@@ -644,15 +646,15 @@ public: | |||||
std::memset(msgBuf, 0, msgSize); | std::memset(msgBuf, 0, msgSize); | ||||
// write key and value in atom bufer | // 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 | // 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->time.frames = 0; | ||||
aev->body.type = fURIDs.distrhoState; | aev->body.type = fURIDs.distrhoState; | ||||
aev->body.size = msgSize; | 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)); | fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | ||||
@@ -139,6 +139,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
# endif | # endif | ||||
manifestString += "\n"; | manifestString += "\n"; | ||||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | # if DISTRHO_PLUGIN_HAS_EMBED_UI | ||||
// TODO: pluginUI.isUserResizable() | |||||
manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | ||||
manifestString += " ui:resize ,\n"; | manifestString += " ui:resize ,\n"; | ||||
manifestString += " ui:touch ;\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_INSTANCE_ACCESS_URI "> ,\n"; | ||||
manifestString += " <" LV2_OPTIONS__options "> ,\n"; | manifestString += " <" LV2_OPTIONS__options "> ,\n"; | ||||
manifestString += " <" LV2_URID__map "> .\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"; | manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | ||||
# endif | |||||
# endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
manifestString += "\n"; | manifestString += "\n"; | ||||
#endif | #endif | ||||
@@ -192,7 +194,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
String pluginString; | String pluginString; | ||||
// header | // 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"; | pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | ||||
#endif | #endif | ||||
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | ||||
@@ -628,6 +630,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
# endif | # endif | ||||
uiString += "\n"; | uiString += "\n"; | ||||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | # if DISTRHO_PLUGIN_HAS_EMBED_UI | ||||
// TODO: pluginUI.isUserResizable() | |||||
uiString += " lv2:optionalFeature ui:noUserResize ,\n"; | uiString += " lv2:optionalFeature ui:noUserResize ,\n"; | ||||
uiString += " ui:resize ,\n"; | uiString += " ui:resize ,\n"; | ||||
uiString += " ui:touch ;\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) | void strncpy(char* const dst, const char* const src, const size_t size) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | 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) | void snprintf_param(char* const dst, const float value, const size_t size) | ||||
@@ -965,7 +973,7 @@ public: | |||||
if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) | if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) | ||||
{ | { | ||||
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; | 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); | const double rest = std::fmod(barBeats, 1.0); | ||||
fTimePosition.bbt.bar = int(vstTimeInfo->ppqPos)/ppqPerBar + 1; | fTimePosition.bbt.bar = int(vstTimeInfo->ppqPos)/ppqPerBar + 1; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -15,8 +15,7 @@ | |||||
*/ | */ | ||||
#include "DistrhoUIInternal.hpp" | #include "DistrhoUIInternal.hpp" | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
# include "src/WidgetPrivateData.hpp" | # include "src/WidgetPrivateData.hpp" | ||||
#endif | #endif | ||||
@@ -27,29 +26,30 @@ START_NAMESPACE_DISTRHO | |||||
double d_lastUiSampleRate = 0.0; | double d_lastUiSampleRate = 0.0; | ||||
void* d_lastUiDspPtr = nullptr; | void* d_lastUiDspPtr = nullptr; | ||||
#ifdef HAVE_DGL | |||||
Window* d_lastUiWindow = nullptr; | |||||
#endif | |||||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
uintptr_t g_nextWindowId = 0; | uintptr_t g_nextWindowId = 0; | ||||
const char* g_nextBundlePath = nullptr; | const char* g_nextBundlePath = nullptr; | ||||
#else | |||||
Window* d_lastUiWindow = nullptr; | |||||
#endif | |||||
/* ------------------------------------------------------------------------------------------------------------ | /* ------------------------------------------------------------------------------------------------------------ | ||||
* UI */ | * 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), | : UIWidget(*d_lastUiWindow), | ||||
pData(new PrivateData()) | |||||
pData(new PrivateData(userResizable)) | |||||
{ | { | ||||
((UIWidget*)this)->pData->needsFullViewport = false; | ((UIWidget*)this)->pData->needsFullViewport = false; | ||||
if (width > 0 && height > 0) | if (width > 0 && height > 0) | ||||
setSize(width, height); | setSize(width, height); | ||||
} | } | ||||
#else | |||||
UI::UI(uint width, uint height) | |||||
: UIWidget(width, height), | |||||
pData(new PrivateData()) {} | |||||
#endif | #endif | ||||
UI::~UI() | UI::~UI() | ||||
@@ -57,6 +57,25 @@ UI::~UI() | |||||
delete pData; | 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 */ | * Host state */ | ||||
@@ -121,15 +140,15 @@ uintptr_t UI::getNextWindowId() noexcept | |||||
void UI::sampleRateChanged(double) {} | void UI::sampleRateChanged(double) {} | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
/* ------------------------------------------------------------------------------------------------------------ | /* ------------------------------------------------------------------------------------------------------------ | ||||
* UI Callbacks (optional) */ | * UI Callbacks (optional) */ | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
void UI::uiFileBrowserSelected(const char*) | void UI::uiFileBrowserSelected(const char*) | ||||
{ | { | ||||
} | } | ||||
#endif | |||||
# endif | |||||
void UI::uiReshape(uint width, uint height) | void UI::uiReshape(uint width, uint height) | ||||
{ | { | ||||
@@ -148,9 +167,12 @@ void UI::uiReshape(uint width, uint height) | |||||
void UI::onResize(const ResizeEvent& ev) | void UI::onResize(const ResizeEvent& ev) | ||||
{ | { | ||||
if (pData->resizeInProgress) | |||||
return; | |||||
pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); | pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); | ||||
} | } | ||||
#endif | |||||
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
// ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
@@ -194,7 +194,7 @@ protected: | |||||
uint8_t mdata[4] = { | uint8_t mdata[4] = { | ||||
0, | 0, | ||||
channel + (velocity != 0 ? 0x90 : 0x80), | |||||
static_cast<uint8_t>(channel + (velocity != 0 ? 0x90 : 0x80)), | |||||
note, | note, | ||||
velocity | velocity | ||||
}; | }; | ||||
@@ -19,7 +19,10 @@ | |||||
#include "../DistrhoUI.hpp" | #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/Application.hpp" | ||||
# include "../../dgl/Window.hpp" | # include "../../dgl/Window.hpp" | ||||
using DGL_NAMESPACE::Application; | using DGL_NAMESPACE::Application; | ||||
@@ -34,7 +37,7 @@ START_NAMESPACE_DISTRHO | |||||
extern double d_lastUiSampleRate; | extern double d_lastUiSampleRate; | ||||
extern void* d_lastUiDspPtr; | extern void* d_lastUiDspPtr; | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
extern Window* d_lastUiWindow; | extern Window* d_lastUiWindow; | ||||
#endif | #endif | ||||
extern uintptr_t g_nextWindowId; | extern uintptr_t g_nextWindowId; | ||||
@@ -60,6 +63,13 @@ struct UI::PrivateData { | |||||
void* dspPtr; | void* dspPtr; | ||||
#endif | #endif | ||||
// UI | |||||
const bool userResizable; | |||||
bool automaticallyScale; | |||||
bool resizeInProgress; | |||||
uint minWidth; | |||||
uint minHeight; | |||||
// Callbacks | // Callbacks | ||||
void* callbacksPtr; | void* callbacksPtr; | ||||
editParamFunc editParamCallbackFunc; | editParamFunc editParamCallbackFunc; | ||||
@@ -68,12 +78,17 @@ struct UI::PrivateData { | |||||
sendNoteFunc sendNoteCallbackFunc; | sendNoteFunc sendNoteCallbackFunc; | ||||
setSizeFunc setSizeCallbackFunc; | setSizeFunc setSizeCallbackFunc; | ||||
PrivateData() noexcept | |||||
PrivateData(const bool resizable) noexcept | |||||
: sampleRate(d_lastUiSampleRate), | : sampleRate(d_lastUiSampleRate), | ||||
parameterOffset(0), | parameterOffset(0), | ||||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
dspPtr(d_lastUiDspPtr), | dspPtr(d_lastUiDspPtr), | ||||
#endif | #endif | ||||
userResizable(resizable), | |||||
automaticallyScale(false), | |||||
resizeInProgress(false), | |||||
minWidth(0), | |||||
minHeight(0), | |||||
callbacksPtr(nullptr), | callbacksPtr(nullptr), | ||||
editParamCallbackFunc(nullptr), | editParamCallbackFunc(nullptr), | ||||
setParamCallbackFunc(nullptr), | setParamCallbackFunc(nullptr), | ||||
@@ -134,7 +149,20 @@ struct UI::PrivateData { | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Plugin Window, needed to take care of resize properly | // 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 | static inline | ||||
UI* createUiWrapper(void* const dspPtr, Window* const window) | UI* createUiWrapper(void* const dspPtr, Window* const window) | ||||
{ | { | ||||
@@ -155,9 +183,9 @@ public: | |||||
fIsReady(false) | fIsReady(false) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | 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()); | setSize(fUI->getWidth(), fUI->getHeight()); | ||||
} | } | ||||
@@ -182,11 +210,25 @@ protected: | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | 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); | fUI->uiReshape(width, height); | ||||
fIsReady = true; | fIsReady = true; | ||||
} | } | ||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
# ifndef DGL_FILE_BROWSER_DISABLED | |||||
// custom file-browser selected | // custom file-browser selected | ||||
void fileBrowserSelected(const char* filename) override | void fileBrowserSelected(const char* filename) override | ||||
{ | { | ||||
@@ -194,26 +236,13 @@ protected: | |||||
fUI->uiFileBrowserSelected(filename); | fUI->uiFileBrowserSelected(filename); | ||||
} | } | ||||
#endif | |||||
# endif | |||||
private: | private: | ||||
UI* const fUI; | UI* const fUI; | ||||
bool fIsReady; | 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 | // UI exporter class | ||||
@@ -230,13 +259,13 @@ public: | |||||
const setSizeFunc setSizeCall, | const setSizeFunc setSizeCall, | ||||
void* const dspPtr = nullptr, | void* const dspPtr = nullptr, | ||||
const char* const bundlePath = nullptr) | const char* const bundlePath = nullptr) | ||||
#ifdef HAVE_DGL | |||||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
: fUI(createUiWrapper(dspPtr, winId, bundlePath)), | |||||
#else | |||||
: glApp(), | : glApp(), | ||||
glWindow(glApp, winId, dspPtr), | glWindow(glApp, winId, dspPtr), | ||||
fChangingSize(false), | fChangingSize(false), | ||||
fUI(glWindow.getUI()), | fUI(glWindow.getUI()), | ||||
#else | |||||
: fUI(createUiWrapper(dspPtr, winId, bundlePath)), | |||||
#endif | #endif | ||||
fData((fUI != nullptr) ? fUI->pData : nullptr) | fData((fUI != nullptr) ? fUI->pData : nullptr) | ||||
{ | { | ||||
@@ -250,54 +279,65 @@ public: | |||||
fData->sendNoteCallbackFunc = sendNoteCall; | fData->sendNoteCallbackFunc = sendNoteCall; | ||||
fData->setSizeCallbackFunc = setSizeCall; | fData->setSizeCallbackFunc = setSizeCall; | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
// unused | // unused | ||||
return; (void)bundlePath; | return; (void)bundlePath; | ||||
#endif | #endif | ||||
} | } | ||||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
~UIExporter() | |||||
{ | |||||
delete fUI; | |||||
} | |||||
#endif | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
uint getWidth() const noexcept | uint getWidth() const noexcept | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
return glWindow.getWidth(); | |||||
#else | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | ||||
return fUI->getWidth(); | return fUI->getWidth(); | ||||
#endif | |||||
} | } | ||||
uint getHeight() const noexcept | uint getHeight() const noexcept | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
return glWindow.getHeight(); | |||||
#else | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | ||||
return fUI->getHeight(); | return fUI->getHeight(); | ||||
#endif | |||||
} | } | ||||
bool isVisible() const noexcept | bool isVisible() const noexcept | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
return glWindow.isVisible(); | |||||
#else | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | ||||
return fUI->isRunning(); | 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 | intptr_t getWindowId() const noexcept | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
return glWindow.getWindowId(); | 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) | void exec(IdleCallback* const cb) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | ||||
@@ -355,48 +427,64 @@ public: | |||||
if (glWindow.isReady()) | if (glWindow.isReady()) | ||||
fUI->uiIdle(); | fUI->uiIdle(); | ||||
} | } | ||||
#endif | |||||
bool idle() | bool idle() | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | ||||
#ifdef HAVE_DGL | |||||
glApp.idle(); | glApp.idle(); | ||||
if (glWindow.isReady()) | if (glWindow.isReady()) | ||||
fUI->uiIdle(); | fUI->uiIdle(); | ||||
return ! glApp.isQuiting(); | return ! glApp.isQuiting(); | ||||
#else | |||||
return fUI->isRunning(); | |||||
#endif | |||||
} | } | ||||
void quit() | void quit() | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
glWindow.close(); | glWindow.close(); | ||||
glApp.quit(); | 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) | void setWindowTitle(const char* const uiTitle) | ||||
{ | { | ||||
#ifdef HAVE_DGL | |||||
glWindow.setTitle(uiTitle); | |||||
#else | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | ||||
fUI->setTitle(uiTitle); | 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) | void setWindowSize(const uint width, const uint height, const bool updateUI = false) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | ||||
@@ -431,12 +519,8 @@ public: | |||||
bool handlePluginSpecial(const bool press, const Key key) | 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 | #endif | ||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
@@ -457,7 +541,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
#ifdef HAVE_DGL | |||||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
// DGL Application and Window for this widget | // DGL Application and Window for this widget | ||||
@@ -117,13 +117,13 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) | |||||
@endcode | @endcode | ||||
*/ | */ | ||||
#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ | #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)); \ | !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ | ||||
(iter) = lv2_atom_sequence_next(iter)) | (iter) = lv2_atom_sequence_next(iter)) | ||||
/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ | /** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ | ||||
#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ | #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)); \ | !lv2_atom_sequence_is_end(body, size, (iter)); \ | ||||
(iter) = lv2_atom_sequence_next(iter)) | (iter) = lv2_atom_sequence_next(iter)) | ||||
@@ -214,13 +214,13 @@ lv2_atom_tuple_next(const LV2_Atom* i) | |||||
@endcode | @endcode | ||||
*/ | */ | ||||
#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ | #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)); \ | !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->size, (iter)); \ | ||||
(iter) = lv2_atom_tuple_next(iter)) | (iter) = lv2_atom_tuple_next(iter)) | ||||
/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ | /** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ | ||||
#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ | #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)); \ | !lv2_atom_tuple_is_end(body, size, (iter)); \ | ||||
(iter) = lv2_atom_tuple_next(iter)) | (iter) = lv2_atom_tuple_next(iter)) | ||||
@@ -270,13 +270,13 @@ lv2_atom_object_next(const LV2_Atom_Property_Body* i) | |||||
@endcode | @endcode | ||||
*/ | */ | ||||
#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ | #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)); \ | !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ | ||||
(iter) = lv2_atom_object_next(iter)) | (iter) = lv2_atom_object_next(iter)) | ||||
/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ | /** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ | ||||
#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ | #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)); \ | !lv2_atom_object_is_end(body, size, (iter)); \ | ||||
(iter) = lv2_atom_object_next(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 -*- | # -*- coding: utf-8 -*- | ||||
# DISTRHO Plugin Framework (DPF) | # 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 | # 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 | # 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 = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] | ||||
shortFilename = shortFilename.replace("-", "_") | shortFilename = shortFilename.replace("-", "_") | ||||
png = Image.open(filename) | |||||
png = Image.open(filename) | |||||
if png.getpalette(): | |||||
png = png.convert() | |||||
pngNumpy = numpy.array(png) | pngNumpy = numpy.array(png) | ||||
pngData = pngNumpy.tolist() | pngData = pngNumpy.tolist() | ||||
#pngData.reverse() | #pngData.reverse() | ||||
@@ -60,8 +60,8 @@ public: | |||||
}; | }; | ||||
MVerb(){ | MVerb(){ | ||||
DampingFreq = 18000.; | |||||
BandwidthFreq = 18000.; | |||||
DampingFreq = 0.9; | |||||
BandwidthFreq = 0.9; | |||||
SampleRate = 44100.; | SampleRate = 44100.; | ||||
Decay = 0.5; | Decay = 0.5; | ||||
Gain = 1.; | Gain = 1.; | ||||