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