diff --git a/data/update-dpf b/data/update-dpf index 8746b4413..2b6a1288a 100755 --- a/data/update-dpf +++ b/data/update-dpf @@ -53,7 +53,7 @@ rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/src/.clang-tidy rm ${CARLA_DIR}/source/modules/distrho/DistrhoInfo.hpp rm ${CARLA_DIR}/source/modules/distrho/DistrhoUI_win32.cpp -rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPlugin{AU,CLAP,Export,JACK,LADSPA+DSSI,LV2,LV2export,Stub,VST2,VST3}.cpp +rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPlugin{AU,CLAP,Export,JACK,LADSPA+DSSI,LV2,LV2export,MAPI,Stub,VST2,VST3}.cpp rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPluginVST.hpp rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoUI{DSSI,LV2,Stub,VST3}.cpp diff --git a/source/modules/dgl/Application.hpp b/source/modules/dgl/Application.hpp index c028b53c9..fac5fccc8 100644 --- a/source/modules/dgl/Application.hpp +++ b/source/modules/dgl/Application.hpp @@ -83,10 +83,21 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_off) class DISTRHO_API Application { public: + /** + Type of application to setup, either "classic" or "modern". + + What this means depends on the OS. + */ + enum Type { + kTypeAuto, + kTypeClassic, + kTypeModern, + }; + /** Constructor for standalone or plugin application. */ - Application(bool isStandalone = true); + Application(bool isStandalone = true, Type type = kTypeAuto); /** Constructor for a standalone application. @@ -141,6 +152,12 @@ public: */ double getTime() const; + /** + Return the application type, either kTypeClassic or kTypeModern. + This function never return kTypeAuto. + */ + Type getType() const noexcept; + /** Add a callback function to be triggered on every idle cycle. You can add more than one, and remove them at anytime with removeIdleCallback(). diff --git a/source/modules/dgl/Base.hpp b/source/modules/dgl/Base.hpp index c60a73778..cc2200a7a 100644 --- a/source/modules/dgl/Base.hpp +++ b/source/modules/dgl/Base.hpp @@ -49,6 +49,10 @@ # error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out) #endif +#ifndef DGL_ALLOW_DEPRECATED_METHODS +# define DGL_ALLOW_DEPRECATED_METHODS 1 +#endif + // -------------------------------------------------------------------------------------------------------------------- // Define namespace diff --git a/source/modules/dgl/Color.hpp b/source/modules/dgl/Color.hpp index 0b8aaedec..ce3c6fc41 100644 --- a/source/modules/dgl/Color.hpp +++ b/source/modules/dgl/Color.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -115,6 +115,17 @@ struct Color { */ static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; + /** + Create a color from a RGB unsigned integer. + Basically doing: + ``` + uint8_t red = (color >> 24) & 0xff; + uint8_t green = (color >> 16) & 0xff; + uint8_t blue = (color >> 8) & 0xff; + ``` + */ + static Color fromRGB(uint color, float alpha = 1.0f) noexcept; + /** Linearly interpolate this color against another. */ diff --git a/source/modules/dgl/Geometry.hpp b/source/modules/dgl/Geometry.hpp index 9ac9483be..c39eb8157 100644 --- a/source/modules/dgl/Geometry.hpp +++ b/source/modules/dgl/Geometry.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -360,25 +360,23 @@ public: */ bool isNotNull() const noexcept; -#ifndef DPF_TEST_POINT_CPP /** Draw this line using the provided graphics context, optionally specifying line width. */ void draw(const GraphicsContext& context, T width = 1); -#endif Line& operator=(const Line& line) noexcept; bool operator==(const Line& line) const noexcept; bool operator!=(const Line& line) const noexcept; -#ifndef DPF_TEST_POINT_CPP + #ifdef DGL_ALLOW_DEPRECATED_METHODS /** Draw this line using the current OpenGL state.@n DEPRECATED Please use draw(const GraphicsContext&) instead. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); -#endif + #endif private: Point posStart, posEnd; @@ -489,7 +487,7 @@ public: bool operator==(const Circle& cir) const noexcept; bool operator!=(const Circle& cir) const noexcept; -#ifndef DPF_TEST_POINT_CPP + #ifdef DGL_ALLOW_DEPRECATED_METHODS /** Draw this circle using the current OpenGL state.@n DEPRECATED Please use draw(const GraphicsContext&) instead. @@ -503,7 +501,7 @@ public: */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); -#endif + #endif private: Point fPos; @@ -582,7 +580,7 @@ public: bool operator==(const Triangle& tri) const noexcept; bool operator!=(const Triangle& tri) const noexcept; -#ifndef DPF_TEST_POINT_CPP + #ifdef DGL_ALLOW_DEPRECATED_METHODS /** Draw this triangle using the current OpenGL state.@n DEPRECATED Please use draw(const GraphicsContext&) instead. @@ -596,7 +594,7 @@ public: */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); -#endif + #endif private: Point pos1, pos2, pos3; @@ -813,6 +811,7 @@ public: bool operator==(const Rectangle& size) const noexcept; bool operator!=(const Rectangle& size) const noexcept; + #ifdef DGL_ALLOW_DEPRECATED_METHODS /** Draw this rectangle using the current OpenGL state.@n DEPRECATED Please use draw(const GraphicsContext&) instead. @@ -826,6 +825,7 @@ public: */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); + #endif private: Point pos; diff --git a/source/modules/dgl/Image.hpp b/source/modules/dgl/Image.hpp index 2e5153615..6e5f3ced9 100644 --- a/source/modules/dgl/Image.hpp +++ b/source/modules/dgl/Image.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -17,17 +17,21 @@ #ifndef DGL_IMAGE_HPP_INCLUDED #define DGL_IMAGE_HPP_INCLUDED -#ifdef DGL_CAIRO -#include "Cairo.hpp" +#if defined(DGL_CAIRO) +# include "Cairo.hpp" +#elif defined(DGL_OPENGL) +# include "OpenGL.hpp" +#elif defined(DGL_VULKAN) +# include "Vulkan.hpp" #else -#include "OpenGL.hpp" +# include "Base.hpp" #endif START_NAMESPACE_DGL -#ifdef DGL_CAIRO +#if defined(DGL_CAIRO) typedef CairoImage Image; -#else +#elif defined(DGL_OPENGL) typedef OpenGLImage Image; #endif diff --git a/source/modules/dgl/Makefile b/source/modules/dgl/Makefile index 50eab4b88..22b9e7604 100644 --- a/source/modules/dgl/Makefile +++ b/source/modules/dgl/Makefile @@ -32,6 +32,7 @@ OBJS = \ $(OBJDIR)/ImageBaseWidgets.cpp.o \ $(OBJDIR)/NanoVG.cpp.o \ $(OBJDIR)/OpenGL.cpp.o \ + $(OBJDIR)/OpenGL2.cpp.o \ $(OBJDIR)/SubWidget.cpp.o \ $(OBJDIR)/SubWidgetPrivateData.cpp.o \ $(OBJDIR)/TopLevelWidget.cpp.o \ @@ -51,6 +52,7 @@ OBJS_wine = \ $(OBJDIR)/ImageBase.cpp-wine.o \ $(OBJDIR)/ImageBaseWidgets.cpp-wine.o \ $(OBJDIR)/OpenGL.cpp-wine.o \ + $(OBJDIR)/OpenGL2.cpp-wine.o \ $(OBJDIR)/SubWidget.cpp-wine.o \ $(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \ $(OBJDIR)/TopLevelWidget.cpp-wine.o \ diff --git a/source/modules/dgl/OpenGL-include.hpp b/source/modules/dgl/OpenGL-include.hpp index 117e228a9..d0b5dc188 100644 --- a/source/modules/dgl/OpenGL-include.hpp +++ b/source/modules/dgl/OpenGL-include.hpp @@ -53,6 +53,12 @@ #ifdef DISTRHO_OS_MAC # ifdef DGL_USE_OPENGL3 +// NOTE GLES with macOS is not supported +# ifdef DGL_USE_GLES +# undef DGL_USE_GLES +# undef DGL_USE_GLES2 +# undef DGL_USE_GLES3 +# endif # include # include # else diff --git a/source/modules/dgl/OpenGL.hpp b/source/modules/dgl/OpenGL.hpp index 4d2e9ceb0..46d5aecbf 100644 --- a/source/modules/dgl/OpenGL.hpp +++ b/source/modules/dgl/OpenGL.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -24,34 +24,131 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +#ifdef DGL_USE_OPENGL3 /** - OpenGL Graphics context. + OpenGL3 Graphics context. + + This provides access to the program, shaders and uniforms used by the underlying DPF implementation. */ -struct OpenGLGraphicsContext : GraphicsContext +struct OpenGL3GraphicsContext : GraphicsContext { -#ifdef DGL_USE_OPENGL3 -#endif + /** + The OpenGL3 program used for this context. + It is activated automatically before any widget onDisplay() is called. + If changing the current OpenGL program make sure to revert back to this one at the end of your pipeline. + + @code + // use custom program + glUseProgram(context.program); + + // custom stuff here + + // revert back + glUseProgram(context.program); + @endcode + */ + GLuint program; + + /** + A vec4 uniform used to set the next drawing color. + + @code + const GLfloat color[4] = { red, green, blue, alpha }; + glUniform4fv(context.color, 1, color); + @endcode + */ + GLuint color; + + /** + A vertex shader attribute directly linked to gl_Position. + Use this to set the bounds for drawing, normalized as -1.0 to +1.0. + The @a width and @a height provide the total window size for convenience. + + @code + const GLfloat triangle[] = { x1, y1, x2, y2, x3, y3 }; + glEnableVertexAttribArray(context.bounds); + glVertexAttribPointer(context.bounds, 2, GL_FLOAT, GL_FALSE, 0, triangle); + @endcode + */ + GLuint bounds; + + /** + A vertex shader attribute directly linked to GL_TEXTURE0 map. + // TODO find the correct wording, map??. + + @code + const GLfloat map[] = { 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f }; + glEnableVertexAttribArray(context.textureMap); + glVertexAttribPointer(context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, map); + @endcode + */ + GLuint textureMap; + + /** + A boolean uniform used to indicate if next drawing should @a texture or @a color. + Set to 0 for color mode, 1 for texture. + Default mode is color, if changed make sure to revert to color mode at the end of your pipeline. + + @code + // setup for drawing based on texture + glUniform1i(context.usingTexture, 1); + + // bind texture + glBindTexture(GL_TEXTURE_2D, myTextureId); + // etc.. + + // glDrawElements or similar + + // unbind texture + glBindTexture(GL_TEXTURE_2D, 0); + // etc.. + + // revert to color mode + glUniform1i(context.usingTexture, 0); + @endcode + */ + GLuint usingTexture; + + /** + Set of buffers created with glGenBuffers. + Used internally in DPF to draw generic shapes, can be reused in custom code. + Unbound by default, make sure to leave them unbound at the end of your pipeline. + */ + GLuint buffers[2]; + + /** + Total width of the window used for this context. + */ + uint width; + + /** + Total height of the window used for this context. + */ + uint height; }; +#endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- static inline ImageFormat asDISTRHOImageFormat(const GLenum format) { switch (format) { -#ifdef DGL_USE_OPENGL3 + #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) case GL_RED: -#else + #else case GL_LUMINANCE: -#endif + #endif return kImageFormatGrayscale; + #ifndef DGL_USE_GLES case GL_BGR: return kImageFormatBGR; case GL_BGRA: return kImageFormatBGRA; + #endif case GL_RGB: return kImageFormatRGB; case GL_RGBA: @@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format) case kImageFormatNull: break; case kImageFormatGrayscale: -#ifdef DGL_USE_OPENGL3 + #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) return GL_RED; -#else + #else return GL_LUMINANCE; -#endif + #endif case kImageFormatBGR: + #ifndef DGL_USE_GLES return GL_BGR; + #else + return 0; + #endif case kImageFormatBGRA: + #ifndef DGL_USE_GLES return GL_BGRA; + #else + return 0; + #endif case kImageFormatRGB: return GL_RGB; case kImageFormatRGBA: return GL_RGBA; } - return 0x0; + return 0; } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** OpenGL Image class. @@ -144,6 +249,18 @@ public: */ void drawAt(const GraphicsContext& context, const Point& pos) override; + #ifdef DGL_USE_GLES + /** + Get the image format. + */ + ImageFormat getFormat() const noexcept; + + /** + Get the raw image data. + */ + const char* getRawData() const noexcept; + #endif + /** TODO document this. */ @@ -157,6 +274,7 @@ public: inline void drawAt(const GraphicsContext& context, int x, int y) { drawAt(context, Point(x, y)); } + #ifdef DGL_ALLOW_DEPRECATED_METHODS /** Constructor using raw image data, specifying an OpenGL image format. @note @a rawData must remain valid for the lifetime of this Image. @@ -200,14 +318,19 @@ public: */ DISTRHO_DEPRECATED GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } + #endif // DGL_ALLOW_DEPRECATED_METHODS private: bool setupCalled; bool textureInit; GLuint textureId; + #ifdef DGL_USE_GLES + mutable char* convertedData; + mutable const char* rawDataLast; + #endif }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- typedef ImageBaseAboutWindow OpenGLImageAboutWindow; typedef ImageBaseButton OpenGLImageButton; @@ -215,7 +338,7 @@ typedef ImageBaseKnob OpenGLImageKnob; typedef ImageBaseSlider OpenGLImageSlider; typedef ImageBaseSwitch OpenGLImageSwitch; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/source/modules/dgl/src/Application.cpp b/source/modules/dgl/src/Application.cpp index 09f57e1ec..705b10fbb 100644 --- a/source/modules/dgl/src/Application.cpp +++ b/source/modules/dgl/src/Application.cpp @@ -98,8 +98,8 @@ static void app_idle(void* const app) } #endif -Application::Application(const bool isStandalone) - : pData(new PrivateData(isStandalone)) +Application::Application(const bool isStandalone, const Type type) + : pData(new PrivateData(isStandalone, type)) { // build config sentinels #ifdef DPF_DEBUG @@ -126,7 +126,7 @@ Application::Application(const bool isStandalone) } Application::Application(int argc, char* argv[]) - : pData(new PrivateData(true)) + : pData(new PrivateData(true, kTypeAuto)) { #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) @@ -213,6 +213,11 @@ double Application::getTime() const return pData->getTime(); } +Application::Type Application::getType() const noexcept +{ + return pData->isModern ? kTypeModern : kTypeClassic; +} + void Application::addIdleCallback(IdleCallback* const callback) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) diff --git a/source/modules/dgl/src/ApplicationPrivateData.cpp b/source/modules/dgl/src/ApplicationPrivateData.cpp index 0e353a7ec..ca0d27f9e 100644 --- a/source/modules/dgl/src/ApplicationPrivateData.cpp +++ b/source/modules/dgl/src/ApplicationPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -53,13 +53,14 @@ const char* Application::getClassName() const noexcept // -------------------------------------------------------------------------------------------------------------------- -Application::PrivateData::PrivateData(const bool standalone) +Application::PrivateData::PrivateData(const bool standalone, const Type type) : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, - standalone ? PUGL_WORLD_THREADS : 0x0)), + (standalone ? PUGL_WORLD_THREADS : 0))), + isModern(false), isStandalone(standalone), + isStarting(true), isQuitting(false), isQuittingInNextCycle(false), - isStarting(true), needsRepaint(false), visibleWindows(0), mainThreadHandle(getCurrentThreadHandle()), @@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone) { DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); - #ifdef DGL_USING_SDL - SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO); - #else - puglSetWorldHandle(world, this); #ifdef __EMSCRIPTEN__ puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); #else puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); #endif - #endif } Application::PrivateData::~PrivateData() @@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData() windows.clear(); idleCallbacks.clear(); - #ifdef DGL_USING_SDL - SDL_Quit(); - #else if (world != nullptr) puglFreeWorld(world); - #endif } // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/dgl/src/ApplicationPrivateData.hpp b/source/modules/dgl/src/ApplicationPrivateData.hpp index 59afaae33..b5ef6621f 100644 --- a/source/modules/dgl/src/ApplicationPrivateData.hpp +++ b/source/modules/dgl/src/ApplicationPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -51,18 +51,21 @@ struct Application::PrivateData { /** Pugl world instance. */ PuglWorld* const world; + /** Whether the applicating uses modern backend, otherwise classic. */ + const bool isModern; + /** Whether the application is running as standalone, otherwise it is part of a plugin. */ const bool isStandalone; + /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ + bool isStarting; + /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ bool isQuitting; /** Helper for safely close everything from main thread. */ bool isQuittingInNextCycle; - /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ - bool isStarting; - /** When true force all windows to be repainted on next idle. */ bool needsRepaint; @@ -80,7 +83,7 @@ struct Application::PrivateData { std::list idleCallbacks; /** Constructor and destructor */ - explicit PrivateData(bool standalone); + explicit PrivateData(bool standalone, Type type); ~PrivateData(); /** Flag one window as shown, which increments @a visibleWindows. diff --git a/source/modules/dgl/src/Color.cpp b/source/modules/dgl/src/Color.cpp index 525c651fd..109910b0b 100644 --- a/source/modules/dgl/src/Color.cpp +++ b/source/modules/dgl/src/Color.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -242,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept return Color(r, g, b, alpha); } +Color Color::fromRGB(const uint color, const float alpha) noexcept +{ + return Color(static_cast(color >> 24) & 0xff, + static_cast(color >> 16) & 0xff, + static_cast(color >> 8) & 0xff, + alpha); +} + void Color::interpolate(const Color& other, float u) noexcept { fixRange(u); diff --git a/source/modules/dgl/src/EventHandlers.cpp b/source/modules/dgl/src/EventHandlers.cpp index 9ae82ae77..30456b0c4 100644 --- a/source/modules/dgl/src/EventHandlers.cpp +++ b/source/modules/dgl/src/EventHandlers.cpp @@ -108,13 +108,6 @@ struct ButtonEventHandler::PrivateData { if (! enabledInput) return false; - // keep pressed - if (button != -1) - { - lastMotionPos = ev.pos; - return true; - } - bool ret = false; if (widget->contains(ev.pos)) @@ -143,7 +136,7 @@ struct ButtonEventHandler::PrivateData { } lastMotionPos = ev.pos; - return ret; + return ret || button != -1; } void setActive(const bool active2, const bool sendCallback) noexcept diff --git a/source/modules/dgl/src/NanoVG.cpp b/source/modules/dgl/src/NanoVG.cpp index 47781ccea..56546d76b 100644 --- a/source/modules/dgl/src/NanoVG.cpp +++ b/source/modules/dgl/src/NanoVG.cpp @@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) //#define STB_IMAGE_STATIC #if defined(DGL_USE_GLES2) # define NANOVG_GLES2_IMPLEMENTATION +#elif defined(DGL_USE_GLES3) +# define NANOVG_GLES3_IMPLEMENTATION #elif defined(DGL_USE_OPENGL3) # define NANOVG_GL3_IMPLEMENTATION #else diff --git a/source/modules/dgl/src/OpenGL.cpp b/source/modules/dgl/src/OpenGL.cpp index fa387e416..989db626f 100644 --- a/source/modules/dgl/src/OpenGL.cpp +++ b/source/modules/dgl/src/OpenGL.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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,6 @@ #endif #include "../OpenGL.hpp" -#include "../Color.hpp" #include "../ImageWidgets.hpp" #include "SubWidgetPrivateData.hpp" @@ -28,416 +27,20 @@ #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" -// templated classes -#include "ImageBaseWidgets.cpp" - START_NAMESPACE_DGL -// ----------------------------------------------------------------------- - -#ifdef DGL_USE_OPENGL3 -static void notImplemented(const char* const name) -{ - d_stderr2("OpenGL3 function not implemented: %s", name); -} -#else -# define DGL_USE_COMPAT_OPENGL -#endif - -// ----------------------------------------------------------------------- -// Color - -void Color::setFor(const GraphicsContext&, const bool includeAlpha) -{ -#ifdef DGL_USE_COMPAT_OPENGL - if (includeAlpha) - glColor4f(red, green, blue, alpha); - else - glColor3f(red, green, blue); -#else - notImplemented("Color::setFor"); - // unused - (void)includeAlpha; -#endif -} - -// ----------------------------------------------------------------------- -// Line - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawLine(const Point& posStart, const Point& posEnd) -{ - DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); - - glBegin(GL_LINES); - - { - glVertex2d(posStart.getX(), posStart.getY()); - glVertex2d(posEnd.getX(), posEnd.getY()); - } - - glEnd(); -} -#endif - -template -void Line::draw(const GraphicsContext&, const T width) -{ -#ifdef DGL_USE_COMPAT_OPENGL - DISTRHO_SAFE_ASSERT_RETURN(width != 0,); - - glLineWidth(static_cast(width)); - drawLine(posStart, posEnd); -#else - notImplemented("Line::draw"); -#endif -} - -// deprecated calls -template -void Line::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawLine(posStart, posEnd); -#else - notImplemented("Line::draw"); -#endif -} - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -// ----------------------------------------------------------------------- -// Circle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawCircle(const Point& pos, - const uint numSegments, - const float size, - const float sin, - const float cos, - const bool outline) -{ - DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); - - const T origx = pos.getX(); - const T origy = pos.getY(); - double t, x = size, y = 0.0; - - glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); - - for (uint i=0; i -void Circle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); -#else - notImplemented("Circle::draw"); -#endif -} - -template -void Circle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); -#else - notImplemented("Circle::drawOutline"); -#endif -} - -// deprecated calls -template -void Circle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); -#else - notImplemented("Circle::draw"); -#endif -} - -template -void Circle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); -#else - notImplemented("Circle::drawOutline"); -#endif -} - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -// ----------------------------------------------------------------------- -// Triangle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawTriangle(const Point& pos1, - const Point& pos2, - const Point& pos3, - const bool outline) -{ - DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); - - glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); - - { - glVertex2d(pos1.getX(), pos1.getY()); - glVertex2d(pos2.getX(), pos2.getY()); - glVertex2d(pos3.getX(), pos3.getY()); - } - - glEnd(); -} -#endif - -template -void Triangle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, false); -#else - notImplemented("Triangle::draw"); -#endif -} - -template -void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, true); -#else - notImplemented("Triangle::drawOutline"); -#endif -} - -// deprecated calls -template -void Triangle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, false); -#else - notImplemented("Triangle::draw"); -#endif -} - -template -void Triangle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, true); -#else - notImplemented("Triangle::drawOutline"); -#endif -} - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -// ----------------------------------------------------------------------- -// Rectangle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawRectangle(const Rectangle& rect, const bool outline) -{ - DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); - - glBegin(outline ? GL_LINE_LOOP : GL_QUADS); - - { - const T x = rect.getX(); - const T y = rect.getY(); - const T w = rect.getWidth(); - const T h = rect.getHeight(); - - glTexCoord2f(0.0f, 0.0f); - glVertex2d(x, y); - - glTexCoord2f(1.0f, 0.0f); - glVertex2d(x+w, y); - - glTexCoord2f(1.0f, 1.0f); - glVertex2d(x+w, y+h); - - glTexCoord2f(0.0f, 1.0f); - glVertex2d(x, y+h); - } - - glEnd(); -} -#endif - -template -void Rectangle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, false); -#else - notImplemented("Rectangle::draw"); -#endif -} - -template -void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, true); -#else - notImplemented("Rectangle::drawOutline"); -#endif -} - -// deprecated calls -template -void Rectangle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, false); -#else - notImplemented("Rectangle::draw"); -#endif -} - -template -void Rectangle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, true); -#else - notImplemented("Rectangle::drawOutline"); -#endif -} - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // OpenGLImage -static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) -{ - DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textureId); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(image.getWidth()), - static_cast(image.getHeight()), - 0, - asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData()); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, const GLuint textureId, bool& setupCalled) -{ - if (textureId == 0 || image.isInvalid()) - return; - - if (! setupCalled) - { - setupOpenGLImage(image, textureId); - setupCalled = true; - } - -#ifdef DGL_USE_COMPAT_OPENGL - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -#endif - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textureId); - -#ifdef DGL_USE_COMPAT_OPENGL - glBegin(GL_QUADS); - - { - const int x = pos.getX(); - const int y = pos.getY(); - const int w = static_cast(image.getWidth()); - const int h = static_cast(image.getHeight()); - - glTexCoord2f(0.0f, 0.0f); - glVertex2d(x, y); - - glTexCoord2f(1.0f, 0.0f); - glVertex2d(x+w, y); - - glTexCoord2f(1.0f, 1.0f); - glVertex2d(x+w, y+h); - - glTexCoord2f(0.0f, 1.0f); - glVertex2d(x, y+h); - } - - glEnd(); -#endif - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - OpenGLImage::OpenGLImage() : ImageBase(), setupCalled(false), textureInit(false), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { } @@ -446,6 +49,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, co setupCalled(false), textureInit(true), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); @@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const Ima setupCalled(false), textureInit(true), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); @@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image) setupCalled(false), textureInit(true), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); @@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage() { if (textureId != 0) glDeleteTextures(1, &textureId); + + #ifdef DGL_USE_GLES + std::free(convertedData); + #endif } void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept @@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, c ImageBase::loadFromMemory(rdata, s, fmt); } -void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) -{ - drawOpenGLImage(*this, pos, textureId, setupCalled); -} - OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept { rawData = image.rawData; @@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept return *this; } -// deprecated calls +#ifdef DGL_ALLOW_DEPRECATED_METHODS OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), setupCalled(false), textureInit(true), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); @@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const GLe setupCalled(false), textureInit(true), textureId(0) + #ifdef DGL_USE_GLES + , convertedData(nullptr) + , rawDataLast(nullptr) + #endif { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } - -void OpenGLImage::draw() -{ - drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); -} - -void OpenGLImage::drawAt(const int x, const int y) -{ - drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); -} - -void OpenGLImage::drawAt(const Point& pos) -{ - drawOpenGLImage(*this, pos, textureId, setupCalled); -} - -// ----------------------------------------------------------------------- -// ImageBaseAboutWindow - -#if 0 -template <> -void ImageBaseAboutWindow::onDisplay() -{ - const GraphicsContext& context(getGraphicsContext()); - img.draw(context); -} -#endif - -template class ImageBaseAboutWindow; - -// ----------------------------------------------------------------------- -// ImageBaseButton - -template class ImageBaseButton; - -// ----------------------------------------------------------------------- -// ImageBaseKnob - -template <> -void ImageBaseKnob::PrivateData::init() -{ - glTextureId = 0; - glGenTextures(1, &glTextureId); -} - -template <> -void ImageBaseKnob::PrivateData::cleanup() -{ - if (glTextureId == 0) - return; - - glDeleteTextures(1, &glTextureId); - glTextureId = 0; -} - -template <> -void ImageBaseKnob::onDisplay() -{ - const GraphicsContext& context(getGraphicsContext()); - const float normValue = getNormalizedValue(); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, pData->glTextureId); - - if (! pData->isReady) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - uint imageDataOffset = 0; - - if (pData->rotationAngle == 0) - { - DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); - DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); - - const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); - const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); - - // TODO kImageFormatGreyscale - const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || - pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); - /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); - } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(getWidth()), static_cast(getHeight()), 0, - asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); - - pData->isReady = true; - } - - const int w = static_cast(getWidth()); - const int h = static_cast(getHeight()); - - if (pData->rotationAngle != 0) - { -#ifdef DGL_USE_COMPAT_OPENGL - glPushMatrix(); -#endif - - const int w2 = w/2; - const int h2 = h/2; - -#ifdef DGL_USE_COMPAT_OPENGL - glTranslatef(static_cast(w2), static_cast(h2), 0.0f); - glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); #endif - Rectangle(-w2, -h2, w, h).draw(context); - -#ifdef DGL_USE_COMPAT_OPENGL - glPopMatrix(); -#endif - } - else - { - Rectangle(0, 0, w, h).draw(context); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -template class ImageBaseKnob; - -// ----------------------------------------------------------------------- -// ImageBaseSlider - -template class ImageBaseSlider; - -// ----------------------------------------------------------------------- -// ImageBaseSwitch - -template class ImageBaseSwitch; - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) { @@ -736,7 +216,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- void TopLevelWidget::PrivateData::display() { @@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display() selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor); } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- void Window::PrivateData::renderToPicture(const char* const filename, const GraphicsContext&, @@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename, fclose(f); } -// ----------------------------------------------------------------------- - -const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept -{ - return (const GraphicsContext&)graphicsContext; -} - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/source/modules/dgl/src/OpenGL2.cpp b/source/modules/dgl/src/OpenGL2.cpp new file mode 100644 index 000000000..78e3be3ef --- /dev/null +++ b/source/modules/dgl/src/OpenGL2.cpp @@ -0,0 +1,570 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2025 Filipe Coelho + * + * 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. + */ + +#ifdef _MSC_VER +// instantiated template classes whose methods are defined elsewhere +# pragma warning(disable:4661) +#endif + +#include "../OpenGL.hpp" +#include "../Color.hpp" +#include "../ImageWidgets.hpp" + +// #include "SubWidgetPrivateData.hpp" +// #include "TopLevelWidgetPrivateData.hpp" +// #include "WidgetPrivateData.hpp" +#include "WindowPrivateData.hpp" + +// templated classes +#include "ImageBaseWidgets.cpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- +// Check for correct build config + +#ifndef DGL_OPENGL +# error Build config error, OpenGL was NOT requested while building OpenGL2 code +#endif +#ifdef DGL_CAIRO +# error Build config error, Cairo requested while building OpenGL2 code +#endif +#ifdef DGL_VULKAN +# error Build config error, Vulkan requested while building OpenGL2 code +#endif +#ifdef DGL_USE_GLES2 +# error Build config error, GLESv2 requested while building OpenGL2 code +#endif +#ifdef DGL_USE_GLES3 +# error Build config error, GLESv3 requested while building OpenGL2 code +#endif +#ifdef DGL_USE_OPENGL3 +# error Build config error, OpenGL3 requested while building OpenGL2 code +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext&, const bool includeAlpha) +{ + if (includeAlpha) + glColor4f(red, green, blue, alpha); + else + glColor3f(red, green, blue); +} + +// -------------------------------------------------------------------------------------------------------------------- +// Line + +template +static void drawLine(const Point& posStart, const Point& posEnd) +{ + DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); + + glBegin(GL_LINES); + + { + glVertex2d(posStart.getX(), posStart.getY()); + glVertex2d(posEnd.getX(), posEnd.getY()); + } + + glEnd(); +} + +template +void Line::draw(const GraphicsContext&, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + glLineWidth(static_cast(width)); + drawLine(posStart, posEnd); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Line::draw() +{ + drawLine(posStart, posEnd); +} +#endif + +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +// -------------------------------------------------------------------------------------------------------------------- +// Circle + +template +static void drawCircle(const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); + + const double origx = static_cast(pos.getX()); + const double origy = static_cast(pos.getY()); + double t; + double x = size; + double y = 0.0; + + glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); + + for (uint i = 0; i < numSegments; ++i) + { + glVertex2d(x + origx, y + origy); + + t = x; + x = cos * x - sin * y; + y = sin * t + cos * y; + } + + glEnd(); +} + +template +static void drawCircle(const GraphicsContext&, + const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + drawCircle(pos, numSegments, size, sin, cos, outline); +} + +template +void Circle::draw(const GraphicsContext& context) +{ + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Circle::draw() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +} +#endif + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +// -------------------------------------------------------------------------------------------------------------------- +// Triangle + +template +static void drawTriangle(const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); + + glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); + + { + glVertex2d(pos1.getX(), pos1.getY()); + glVertex2d(pos2.getX(), pos2.getY()); + glVertex2d(pos3.getX(), pos3.getY()); + } + + glEnd(); +} + +template +static void drawTriangle(const GraphicsContext&, + const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + drawTriangle(pos1, pos2, pos3, outline); +} + +template +void Triangle::draw(const GraphicsContext&) +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawTriangle(pos1, pos2, pos3, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Triangle::draw() +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline() +{ + drawTriangle(pos1, pos2, pos3, true); +} +#endif + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +// -------------------------------------------------------------------------------------------------------------------- +// Rectangle + +template +static void drawRectangle(const Rectangle& rect, const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); + + glBegin(outline ? GL_LINE_LOOP : GL_QUADS); + + { + const T x = rect.getX(); + const T y = rect.getY(); + const T w = rect.getWidth(); + const T h = rect.getHeight(); + + glTexCoord2f(0.0f, 0.0f); + glVertex2d(x, y); + + glTexCoord2f(1.0f, 0.0f); + glVertex2d(x+w, y); + + glTexCoord2f(1.0f, 1.0f); + glVertex2d(x+w, y+h); + + glTexCoord2f(0.0f, 1.0f); + glVertex2d(x, y+h); + } + + glEnd(); +} + +template +void Rectangle::draw(const GraphicsContext& context) +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawRectangle(*this, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Rectangle::draw() +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline() +{ + drawRectangle(*this, true); +} +#endif + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + +// -------------------------------------------------------------------------------------------------------------------- +// OpenGLImage + +static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) +{ + DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + static_cast(image.getWidth()), + static_cast(image.getHeight()), + 0, + asOpenGLImageFormat(image.getFormat()), + GL_UNSIGNED_BYTE, + image.getRawData()); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, const GLuint textureId, bool& setupCalled) +{ + if (textureId == 0 || image.isInvalid()) + return; + + if (! setupCalled) + { + setupOpenGLImage(image, textureId); + setupCalled = true; + } + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + + glBegin(GL_QUADS); + + { + const int x = pos.getX(); + const int y = pos.getY(); + const int w = static_cast(image.getWidth()); + const int h = static_cast(image.getHeight()); + + glTexCoord2f(0.0f, 0.0f); + glVertex2d(x, y); + + glTexCoord2f(1.0f, 0.0f); + glVertex2d(x+w, y); + + glTexCoord2f(1.0f, 1.0f); + glVertex2d(x+w, y+h); + + glTexCoord2f(0.0f, 1.0f); + glVertex2d(x, y+h); + } + + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) +{ + drawOpenGLImage(*this, pos, textureId, setupCalled); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +void OpenGLImage::draw() +{ + drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); +} + +void OpenGLImage::drawAt(const int x, const int y) +{ + drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); +} + +void OpenGLImage::drawAt(const Point& pos) +{ + drawOpenGLImage(*this, pos, textureId, setupCalled); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseAboutWindow + +#if 0 +template <> +void ImageBaseAboutWindow::onDisplay() +{ +const GraphicsContext& context(getGraphicsContext()); +img.draw(context); +} +#endif + +template class ImageBaseAboutWindow; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseButton + +template class ImageBaseButton; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + glTextureId = 0; + glGenTextures(1, &glTextureId); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + if (glTextureId == 0) + return; + + glDeleteTextures(1, &glTextureId); + glTextureId = 0; +} + +template <> +void ImageBaseKnob::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + const float normValue = getNormalizedValue(); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, pData->glTextureId); + + if (! pData->isReady) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + uint imageDataOffset = 0; + + if (pData->rotationAngle == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); + DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); + + const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); + const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); + + // TODO kImageFormatGreyscale + const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || + pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); + /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + static_cast(getWidth()), static_cast(getHeight()), 0, + asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); + + pData->isReady = true; + } + + const int w = static_cast(getWidth()); + const int h = static_cast(getHeight()); + + if (pData->rotationAngle != 0) + { + glPushMatrix(); + + const int w2 = w/2; + const int h2 = h/2; + + glTranslatef(static_cast(w2), static_cast(h2), 0.0f); + glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); + + Rectangle(-w2, -h2, w, h).draw(context); + + glPopMatrix(); + } + else + { + Rectangle(0, 0, w, h).draw(context); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +template class ImageBaseKnob; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + +// -------------------------------------------------------------------------------------------------------------------- + +void Window::PrivateData::createContextIfNeeded() +{ +} + +void Window::PrivateData::destroyContext() +{ +} + +void Window::PrivateData::startContext() +{ +} + +void Window::PrivateData::endContext() +{ +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/source/modules/dgl/src/OpenGL3.cpp b/source/modules/dgl/src/OpenGL3.cpp new file mode 100644 index 000000000..90497ec10 --- /dev/null +++ b/source/modules/dgl/src/OpenGL3.cpp @@ -0,0 +1,1025 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2025 Filipe Coelho + * + * 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. + */ + +#ifdef _MSC_VER +// instantiated template classes whose methods are defined elsewhere +# pragma warning(disable:4661) +#endif + +#include "../OpenGL.hpp" +#include "../Color.hpp" +#include "../ImageWidgets.hpp" + +#include "WindowPrivateData.hpp" + +// templated classes +#include "ImageBaseWidgets.cpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- +// Check for correct build config + +#ifndef DGL_OPENGL +# error Build config error, OpenGL was NOT requested while building OpenGL3 code +#endif +#ifndef DGL_USE_OPENGL3 +# error Build config error, OpenGL3 not requested while building OpenGL3 code +#endif +#ifdef DGL_CAIRO +# error Build config error, Cairo requested while building OpenGL3 code +#endif +#ifdef DGL_VULKAN +# error Build config error, Vulkan requested while building OpenGL3 code +#endif +#if defined(DGL_USE_GLES2) && defined(DGL_USE_GLES3) +# error Build config error, both GLESv2 and GLESv3 requested at the same time +#endif +#if defined(DGL_USE_GLES2) && !defined(DGL_USE_GLES) +# error Build config error, DGL_USE_GLES2 is defined but DGL_USE_GLES is not +#endif +#if defined(DGL_USE_GLES3) && !defined(DGL_USE_GLES) +# error Build config error, DGL_USE_GLES3 is defined but DGL_USE_GLES is not +#endif +#if defined(DGL_USE_GLES) && !(defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3)) +# error Build config error, DGL_USE_GLES is defined which requires either DGL_USE_GLES2 or DGL_USE_GLES3 +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Load OpenGL3 symbols on Windows + +#if defined(DISTRHO_OS_WINDOWS) +# include +# define DGL_EXT(PROC, func) static PROC func; +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +# undef DGL_EXT +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +static void notImplemented(const char* const name) +{ + d_stderr2("OpenGL3 function not implemented: %s", name); +} + +// -------------------------------------------------------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext& context, const bool includeAlpha) +{ + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat color[4] = { red, green, blue, includeAlpha ? alpha : 1.f }; + glUniform4fv(gl3context.color, 1, color); +} + +// -------------------------------------------------------------------------------------------------------------------- +// Line + +template +void Line::draw(const GraphicsContext& context, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x1 = (static_cast(posStart.x) / gl3context.width) * 2 - 1; + const GLfloat y1 = (static_cast(posStart.y) / gl3context.height) * -2 + 1; + const GLfloat x2 = (static_cast(posEnd.x) / gl3context.width) * 2 - 1; + const GLfloat y2 = (static_cast(posEnd.y) / gl3context.height) * -2 + 1; + + glLineWidth(static_cast(width)); + + const GLfloat vertices[] = { x1, y1, x2, y2, }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + const GLubyte order[] = { 0, 1 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Line::draw() +{ + notImplemented("Line::draw"); +} +#endif + +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +// -------------------------------------------------------------------------------------------------------------------- +// Circle + +template +static void drawCircle(const GraphicsContext& context, + const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + #define MAX_CIRCLE_SEGMENTS 512 + DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); + DISTRHO_SAFE_ASSERT_RETURN(numSegments <= MAX_CIRCLE_SEGMENTS,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const double origx = static_cast(pos.getX()); + const double origy = static_cast(pos.getY()); + double t; + double x = size; + double y = 0.0; + + GLfloat vertices[(MAX_CIRCLE_SEGMENTS + 1) * 2]; + for (uint i = 0; i < numSegments; ++i) + { + vertices[i * 2 + 0] = ((x + origx) / gl3context.width) * 2 - 1; + vertices[i * 2 + 1] = ((y + origy) / gl3context.height) * -2 + 1; + + t = x; + x = cos * x - sin * y; + y = sin * t + cos * y; + } + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (numSegments + 1), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + + if (outline) + { + GLushort order[MAX_CIRCLE_SEGMENTS * 2]; + for (uint i = 0; i < numSegments; ++i) + { + order[i * 2 + 0] = i; + order[i * 2 + 1] = i + 1; + } + order[numSegments * 2 - 1] = 0; + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numSegments * 2, order, GL_DYNAMIC_DRAW); + glDrawElements(GL_LINES, numSegments * 2, GL_UNSIGNED_SHORT, nullptr); + } + else + { + // center position + vertices[numSegments * 2 + 0] = (origx / gl3context.width) * 2 - 1; + vertices[numSegments * 2 + 1] = (origy / gl3context.height) * -2 + 1; + + GLushort order[MAX_CIRCLE_SEGMENTS * 3]; + for (uint i = 0; i < numSegments; ++i) + { + order[i * 3 + 0] = i; + order[i * 3 + 1] = i + 1; + order[i * 3 + 2] = numSegments; + } + order[numSegments * 3 - 2] = 0; + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numSegments * 3, order, GL_DYNAMIC_DRAW); + glDrawElements(GL_TRIANGLES, numSegments * 3, GL_UNSIGNED_SHORT, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Circle::draw(const GraphicsContext& context) +{ + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Circle::draw() +{ + notImplemented("Circle::draw"); +} + +template +void Circle::drawOutline() +{ + notImplemented("Circle::drawOutline"); +} +#endif + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +// -------------------------------------------------------------------------------------------------------------------- +// Triangle + +template +static void drawTriangle(const GraphicsContext& context, + const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x1 = (static_cast(pos1.getX()) / gl3context.width) * 2 - 1; + const GLfloat y1 = (static_cast(pos1.getY()) / gl3context.height) * -2 + 1; + const GLfloat x2 = (static_cast(pos2.getX()) / gl3context.width) * 2 - 1; + const GLfloat y2 = (static_cast(pos2.getY()) / gl3context.height) * -2 + 1; + const GLfloat x3 = (static_cast(pos3.getX()) / gl3context.width) * 2 - 1; + const GLfloat y3 = (static_cast(pos3.getY()) / gl3context.height) * -2 + 1; + + const GLfloat vertices[] = { x1, y1, x2, y2, x3, y3 }; + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + if (outline) + { + static constexpr const GLubyte order[] = { 0, 1, 1, 2, 2, 0 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else + { + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Triangle::draw(const GraphicsContext& context) +{ + drawTriangle(context, pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawTriangle(context, pos1, pos2, pos3, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Triangle::draw() +{ + notImplemented("Triangle::draw"); +} + +template +void Triangle::drawOutline() +{ + notImplemented("Triangle::drawOutline"); +} +#endif + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +// -------------------------------------------------------------------------------------------------------------------- +// Rectangle + +template +static void drawRectangle(const GraphicsContext& context, const Rectangle& rect, const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x = (static_cast(rect.getX()) / gl3context.width) * 2 - 1; + const GLfloat y = (static_cast(rect.getY()) / gl3context.height) * -2 + 1; + const GLfloat w = (static_cast(rect.getWidth()) / gl3context.width) * 2; + const GLfloat h = (static_cast(rect.getHeight()) / gl3context.height) * -2; + + const GLfloat vertices[] = { x, y, x, y + h, x + w, y + h, x + w, y }; + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + + if (outline) + { + static constexpr const GLubyte order[] = { 0, 1, 1, 2, 2, 3, 3, 0 }; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + } + else + { + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Rectangle::draw(const GraphicsContext& context) +{ + drawRectangle(context, *this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawRectangle(context, *this, true); +} + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +template +void Rectangle::draw() +{ + notImplemented("Rectangle::draw"); +} + +template +void Rectangle::drawOutline() +{ + notImplemented("Rectangle::drawOutline"); +} +#endif + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + +// -------------------------------------------------------------------------------------------------------------------- +// OpenGLImage + +static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) +{ + DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); + + const ImageFormat imageFormat = image.getFormat(); + GLint intformat; + + #ifdef DGL_USE_GLES + // GLES does not support BGR + DISTRHO_SAFE_ASSERT_RETURN(imageFormat != kImageFormatBGR && imageFormat != kImageFormatBGRA,); + #endif + + glBindTexture(GL_TEXTURE_2D, textureId); + + switch (imageFormat) + { + case kImageFormatBGR: + case kImageFormatRGB: + intformat = GL_RGB; + break; + case kImageFormatGrayscale: + #ifdef DGL_USE_GLES2 + intformat = GL_LUMINANCE; + #else + intformat = GL_RED; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + #endif + break; + default: + intformat = GL_RGBA; + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + #ifndef DGL_USE_GLES + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + #endif + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, + 0, + intformat, + static_cast(image.getWidth()), + static_cast(image.getHeight()), + 0, + asOpenGLImageFormat(imageFormat), + GL_UNSIGNED_BYTE, + image.getRawData()); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void OpenGLImage::drawAt(const GraphicsContext& context, const Point& pos) +{ + if (textureId == 0 || isInvalid()) + return; + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + if (! setupCalled) + { + setupOpenGLImage(*this, textureId); + setupCalled = true; + } + + const GLfloat x = (static_cast(pos.getX()) / gl3context.width) * 2 - 1; + const GLfloat y = (static_cast(pos.getY()) / gl3context.height) * -2 + 1; + const GLfloat w = (static_cast(getWidth()) / gl3context.width) * 2; + const GLfloat h = (static_cast(getHeight()) / gl3context.height) * -2; + + const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + glUniform4fv(gl3context.color, 1, color); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + glUniform1i(gl3context.usingTexture, 1); + + const GLfloat vertices[] = { + x, y, x, y + h, x + w, y + h, x + w, y, + 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glEnableVertexAttribArray(gl3context.textureMap); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glVertexAttribPointer(gl3context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(sizeof(GLfloat) * 8)); + + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.textureMap); + glDisableVertexAttribArray(gl3context.bounds); + glUniform1i(gl3context.usingTexture, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +#ifdef DGL_USE_GLES +ImageFormat OpenGLImage::getFormat() const noexcept +{ + switch (format) + { + case kImageFormatBGR: + return kImageFormatRGB; + case kImageFormatBGRA: + return kImageFormatRGBA; + default: + return format; + } +} + +const char* OpenGLImage::getRawData() const noexcept +{ + if (rawDataLast != rawData) + { + if (format == kImageFormatBGR || format == kImageFormatBGRA) + { + const uint w = size.getWidth(); + const uint h = size.getHeight(); + + std::free(convertedData); + + if (format == kImageFormatBGR) + { + convertedData = static_cast(std::malloc(sizeof(char) * 3 * w * h)); + for (int i = 0; i < w * h; ++i) + { + convertedData[i * 3 + 0] = rawData[i * 3 + 2]; + convertedData[i * 3 + 1] = rawData[i * 3 + 1]; + convertedData[i * 3 + 2] = rawData[i * 3 + 0]; + } + } + else + { + convertedData = static_cast(std::malloc(sizeof(char) * 4 * w * h)); + for (int i = 0; i < w * h; ++i) + { + convertedData[i * 4 + 0] = rawData[i * 4 + 2]; + convertedData[i * 4 + 1] = rawData[i * 4 + 1]; + convertedData[i * 4 + 2] = rawData[i * 4 + 0]; + convertedData[i * 4 + 3] = rawData[i * 4 + 3]; + } + } + } + else + { + std::free(convertedData); + convertedData = nullptr; + } + + rawDataLast = rawData; + } + + return + #ifdef DGL_USE_GLES + convertedData != nullptr ? convertedData : + #endif + rawData; +} +#endif + +#ifdef DGL_ALLOW_DEPRECATED_METHODS +void OpenGLImage::draw() +{ + notImplemented("OpenGLImage::draw"); +} + +void OpenGLImage::drawAt(const int x, const int y) +{ + notImplemented("OpenGLImage::drawAt"); +} + +void OpenGLImage::drawAt(const Point& pos) +{ + notImplemented("OpenGLImage::drawAt"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseAboutWindow + +#if 0 +template <> +void ImageBaseAboutWindow::onDisplay() +{ +const GraphicsContext& context(getGraphicsContext()); +img.draw(context); +} +#endif + +template class ImageBaseAboutWindow; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseButton + +template class ImageBaseButton; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + glTextureId = 0; + glGenTextures(1, &glTextureId); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + if (glTextureId == 0) + return; + + glDeleteTextures(1, &glTextureId); + glTextureId = 0; +} + +template <> +void ImageBaseKnob::onDisplay() +{ + const OpenGL3GraphicsContext& gl3context = static_cast(getGraphicsContext()); + + if (gl3context.program == 0) + return; + + const ImageFormat imageFormat = pData->image.getFormat(); + const float normValue = getNormalizedValue(); + + #ifdef DGL_USE_GLES + // GLES does not support BGR + DISTRHO_SAFE_ASSERT_RETURN(imageFormat != kImageFormatBGR && imageFormat != kImageFormatBGRA,); + #endif + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + glUniform4fv(gl3context.color, 1, color); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, pData->glTextureId); + glUniform1i(gl3context.usingTexture, 1); + + if (! pData->isReady) + { + GLint intformat; + + switch (imageFormat) + { + case kImageFormatBGR: + case kImageFormatRGB: + intformat = GL_RGB; + break; + case kImageFormatGrayscale: + #ifdef DGL_USE_GLES2 + intformat = GL_LUMINANCE; + #else + intformat = GL_RED; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + #endif + break; + default: + intformat = GL_RGBA; + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + #ifndef DGL_USE_GLES + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + #endif + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + uint imageDataOffset = 0; + + if (pData->rotationAngle == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); + DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); + + const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); + const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); + + // TODO kImageFormatGreyscale + const uint layerDataSize = v1 * v2 * ((imageFormat == kImageFormatBGRA || + imageFormat == kImageFormatRGBA) ? 4 : 3); + /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount - 1)); + } + + glTexImage2D(GL_TEXTURE_2D, + 0, + intformat, + static_cast(getWidth()), + static_cast(getHeight()), + 0, + asOpenGLImageFormat(imageFormat), + GL_UNSIGNED_BYTE, + pData->image.getRawData() + imageDataOffset); + + pData->isReady = true; + } + + GLfloat x, y, w, h; + + if (pData->rotationAngle != 0) + { + // TODO + x = -1; + y = 1; + w = (static_cast(getWidth()) / gl3context.width) * 2; + h = (static_cast(getHeight()) / gl3context.height) * -2; + } + else + { + x = -1; + y = 1; + w = (static_cast(getWidth()) / gl3context.width) * 2; + h = (static_cast(getHeight()) / gl3context.height) * -2; + } + + const GLfloat vertices[] = { + x, y, x, y + h, x + w, y + h, x + w, y, + 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glEnableVertexAttribArray(gl3context.textureMap); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glVertexAttribPointer(gl3context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(sizeof(GLfloat) * 8)); + + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.textureMap); + glDisableVertexAttribArray(gl3context.bounds); + glUniform1i(gl3context.usingTexture, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glDisable(GL_BLEND); +} + +template class ImageBaseKnob; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + +// -------------------------------------------------------------------------------------------------------------------- + +static void shaderCreationFail(const GLuint shaderErr, const GLuint shader2 = 0) +{ + if (shaderErr != 0) + { + GLint len = 0; + glGetShaderiv(shaderErr, GL_INFO_LOG_LENGTH, &len); + + std::vector errorLog(len); + glGetShaderInfoLog(shaderErr, len, &len, errorLog.data()); + + d_stderr2("OpenGL3 shader compilation error: %s", errorLog.data()); + + glDeleteShader(shaderErr); + } + + glDeleteShader(shader2); +} + +static void contextCreationFail(const GLuint program, const GLuint shader1, const GLuint shader2) +{ + glDeleteProgram(program); + glDeleteShader(shader1); + glDeleteShader(shader2); +} + +void Window::PrivateData::createContextIfNeeded() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + + if (gl3context.program != 0) + return; + +#if defined(DISTRHO_OS_WINDOWS) +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +# endif + static bool needsInit = true; +# define DGL_EXT(PROC, func) \ + if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ + DISTRHO_SAFE_ASSERT_RETURN(func != nullptr,); +# define DGL_EXT2(PROC, func, fallback) \ + if (needsInit) { \ + func = (PROC) wglGetProcAddress ( #func ); \ + if (func == nullptr) func = (PROC) wglGetProcAddress ( #fallback ); \ + } DISTRHO_SAFE_ASSERT_RETURN(func != nullptr,); +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +# undef DGL_EXT +# undef DGL_EXT2 + needsInit = false; +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic pop +# endif +#endif + + int status; + + const GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER); + DISTRHO_SAFE_ASSERT_RETURN(fragment != 0, shaderCreationFail(fragment)); + + const GLuint vertex = glCreateShader(GL_VERTEX_SHADER); + DISTRHO_SAFE_ASSERT_RETURN(vertex != 0, shaderCreationFail(vertex, fragment)); + + const GLuint program = glCreateProgram(); + DISTRHO_SAFE_ASSERT_RETURN(program != 0,); + + #if defined(DGL_USE_GLES2) + #define DGL_SHADER_HEADER "#version 100\n" + #elif defined(DGL_USE_GLES3) + #define DGL_SHADER_HEADER "#version 300 es\n" + #else + #define DGL_SHADER_HEADER "#version 150 core\n" + #endif + + { + static constexpr const char* const src = DGL_SHADER_HEADER + "precision mediump float;" + "uniform vec4 color;" + "uniform sampler2D stex;" + "uniform bool texok;" + #ifdef DGL_USE_GLES3 + "in vec2 vtex;" + "out vec4 FragColor;" + "void main() { FragColor = texok ? texture(stex, vtex) : color; }"; + #else + "varying vec2 vtex;" + "void main() { gl_FragColor = texok ? texture2D(stex, vtex) : color; }"; + #endif + + glShaderSource(fragment, 1, &src, nullptr); + glCompileShader(fragment); + + glGetShaderiv(fragment, GL_COMPILE_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + } + + { + static constexpr const char* const src = DGL_SHADER_HEADER + #ifdef DGL_USE_GLES3 + "in vec4 pos;" + "in vec2 tex;" + "out vec2 vtex;" + #else + "attribute vec4 pos;" + "attribute vec2 tex;" + "varying vec2 vtex;" + #endif + "void main() { gl_Position = pos; vtex = tex; }"; + + glShaderSource(vertex, 1, &src, nullptr); + glCompileShader(vertex); + + glGetShaderiv(vertex, GL_COMPILE_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + } + + glAttachShader(program, fragment); + glAttachShader(program, vertex); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + + glGenBuffers(2, gl3context.buffers); + DISTRHO_SAFE_ASSERT_RETURN(gl3context.buffers[0] != 0, contextCreationFail(program, fragment, vertex)); + DISTRHO_SAFE_ASSERT_RETURN(gl3context.buffers[1] != 0, contextCreationFail(program, fragment, vertex)); + + glDeleteShader(fragment); + glDeleteShader(vertex); + + gl3context.program = program; + gl3context.color = glGetUniformLocation(gl3context.program, "color"); + gl3context.usingTexture = glGetUniformLocation(gl3context.program, "texok"); + gl3context.bounds = glGetAttribLocation(gl3context.program, "pos"); + gl3context.textureMap = glGetAttribLocation(gl3context.program, "tex"); +} + +void Window::PrivateData::destroyContext() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + + if (gl3context.program == 0) + return; + + glDeleteBuffers(2, gl3context.buffers); + glDeleteProgram(gl3context.program); + gl3context.program = 0; +} + +void Window::PrivateData::startContext() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); + + gl3context.width = size.width; + gl3context.height = size.height; + glUseProgram(gl3context.program); +} + +void Window::PrivateData::endContext() +{ + glUseProgram(0); +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/source/modules/dgl/src/Window.cpp b/source/modules/dgl/src/Window.cpp index 57ae25704..6f7562e21 100644 --- a/source/modules/dgl/src/Window.cpp +++ b/source/modules/dgl/src/Window.cpp @@ -28,7 +28,11 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win) : window(win), ppData(nullptr), active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)), - reenter(false) {} + reenter(false) +{ + if (active) + window.pData->createContextIfNeeded(); +} Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin) : window(win), @@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi { puglBackendLeave(ppData->view); active = puglBackendEnter(window.pData->view); + if (active) + window.pData->createContextIfNeeded(); } } @@ -180,22 +186,22 @@ int Window::getOffsetX() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); - return puglGetFrame(pData->view).x; + return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).x; } int Window::getOffsetY() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); - return puglGetFrame(pData->view).y; + return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y; } Point Window::getOffset() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point()); - const PuglRect rect = puglGetFrame(pData->view); - return Point(rect.x, rect.y); + const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION); + return Point(pos.x, pos.y); } void Window::setOffsetX(const int x) @@ -214,7 +220,7 @@ void Window::setOffset(const int x, const int y) DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,); if (pData->view != nullptr) - puglSetPosition(pData->view, x, y); + puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y); } void Window::setOffset(const Point& offset) @@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); - const double width = puglGetFrame(pData->view).width; - DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0); - return static_cast(width + 0.5); + const PuglSpan width = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).width; + DISTRHO_SAFE_ASSERT(width > 0); + return width; } uint Window::getHeight() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); - const double height = puglGetFrame(pData->view).height; - DISTRHO_SAFE_ASSERT_RETURN(height > 0.0, 0); - return static_cast(height + 0.5); + const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height; + DISTRHO_SAFE_ASSERT(height > 0); + return height; } Size Window::getSize() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size()); - const PuglRect rect = puglGetFrame(pData->view); - DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size()); - DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size()); - return Size(static_cast(rect.width + 0.5), - static_cast(rect.height + 0.5)); + const PuglArea size = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE); + DISTRHO_SAFE_ASSERT(size.width > 0); + DISTRHO_SAFE_ASSERT(size.height > 0); + return Size(size.width, size.height); } void Window::setWidth(const uint width) @@ -443,7 +448,7 @@ void Window::repaint() noexcept if (pData->usesScheduledRepaints) pData->appData->needsRepaint = true; - puglPostRedisplay(pData->view); + puglObscureView(pData->view); } void Window::repaint(const Rectangle& rect) noexcept @@ -454,22 +459,22 @@ void Window::repaint(const Rectangle& rect) noexcept if (pData->usesScheduledRepaints) pData->appData->needsRepaint = true; - PuglRect prect = { - static_cast(rect.getX()), - static_cast(rect.getY()), - static_cast(rect.getWidth()), - static_cast(rect.getHeight()), - }; + int x = static_cast(rect.getX()); + int y = static_cast(rect.getY()); + uint width = rect.getWidth(); + uint height = rect.getHeight(); + if (pData->autoScaling) { const double autoScaleFactor = pData->autoScaleFactor; - prect.x = static_cast(prect.x * autoScaleFactor); - prect.y = static_cast(prect.y * autoScaleFactor); - prect.width = static_cast(prect.width * autoScaleFactor + 0.5); - prect.height = static_cast(prect.height * autoScaleFactor + 0.5); + x = d_roundToIntPositive(x * autoScaleFactor); + y = d_roundToIntPositive(y * autoScaleFactor); + width = d_roundToUnsignedInt(width * autoScaleFactor); + height = d_roundToUnsignedInt(height * autoScaleFactor); } - puglPostRedisplayRect(pData->view, prect); + + puglObscureRegion(pData->view, x, y, width, height); } void Window::renderToPicture(const char* const filename) @@ -523,8 +528,8 @@ void Window::setGeometryConstraints(uint minimumWidth, { const Size size(getSize()); - setSize(static_cast(size.getWidth() * scaleFactor + 0.5), - static_cast(size.getHeight() * scaleFactor + 0.5)); + setSize(d_roundToUnsignedInt(size.getWidth() * scaleFactor), + d_roundToUnsignedInt(size.getHeight() * scaleFactor)); } } diff --git a/source/modules/dgl/src/WindowPrivateData.cpp b/source/modules/dgl/src/WindowPrivateData.cpp index be1a51df4..d7a3c934f 100644 --- a/source/modules/dgl/src/WindowPrivateData.cpp +++ b/source/modules/dgl/src/WindowPrivateData.cpp @@ -91,10 +91,10 @@ static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintp if (PuglView* const view = puglNewView(world)) { - puglSetParentWindow(view, parentWindowHandle); + puglSetParent(view, parentWindowHandle); if (parentWindowHandle != 0) - puglSetPosition(view, 0, 0); + puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0); return view; } @@ -268,6 +268,9 @@ Window::PrivateData::~PrivateData() isVisible = false; } + #ifndef DPF_TEST_WINDOW_CPP + destroyContext(); + #endif puglFreeView(view); } @@ -300,8 +303,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); - // setting default size triggers system-level calls, do it last - puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast(width), static_cast(height)); + // setting size triggers system-level calls, do it last + puglSetSizeAndDefault(view, width, height); } bool Window::PrivateData::initPost() @@ -366,10 +369,6 @@ void Window::PrivateData::show() isClosed = false; appData->oneWindowShown(); - // FIXME -// PuglRect rect = puglGetFrame(view); -// puglSetWindowSize(view, static_cast(rect.width), static_cast(rect.height)); - #if defined(DISTRHO_OS_WINDOWS) puglWin32ShowCentered(view); #elif defined(DISTRHO_OS_MAC) @@ -453,6 +452,13 @@ void Window::PrivateData::setResizable(const bool resizable) puglSetResizable(view, resizable); } +// -------------------------------------------------------------------------------------------------------------------- + +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept +{ + return reinterpret_cast(graphicsContext); +} + // ----------------------------------------------------------------------- void Window::PrivateData::idleCallback() @@ -538,9 +544,9 @@ bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPA if (webViewHandle != nullptr) webViewDestroy(webViewHandle); - const PuglRect rect = puglGetFrame(view); - uint initialWidth = static_cast(rect.width) - options.offset.x; - uint initialHeight = static_cast(rect.height) - options.offset.y; + const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); + uint initialWidth = size.width - options.offset.x; + uint initialHeight = size.height - options.offset.y; webViewOffset = Point(options.offset.x, options.offset.y); @@ -640,6 +646,10 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) DGL_DBGp("PUGL: onReshape : %d %d\n", width, height); + #ifndef DPF_TEST_WINDOW_CPP + createContextIfNeeded(); + #endif + if (autoScaling) { const double scaleHorizontal = width / static_cast(minWidth); @@ -682,7 +692,7 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) #endif // always repaint after a resize - puglPostRedisplay(view); + puglObscureView(view); } void Window::PrivateData::onPuglExpose() @@ -692,6 +702,8 @@ void Window::PrivateData::onPuglExpose() puglOnDisplayPrepare(view); #ifndef DPF_TEST_WINDOW_CPP + startContext(); + FOR_EACH_TOP_LEVEL_WIDGET(it) { TopLevelWidget* const widget(*it); @@ -702,11 +714,13 @@ void Window::PrivateData::onPuglExpose() if (char* const filename = filenameToRenderInto) { - const PuglRect rect = puglGetFrame(view); + const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); filenameToRenderInto = nullptr; - renderToPicture(filename, getGraphicsContext(), static_cast(rect.width), static_cast(rect.height)); + renderToPicture(filename, getGraphicsContext(), size.width, size.height); std::free(filename); } + + endContext(); #endif } @@ -978,7 +992,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); #endif #ifdef DGL_USING_X11 - puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); + puglX11SetWindowType(view, pData->appData->isStandalone); #endif } break; diff --git a/source/modules/dgl/src/WindowPrivateData.hpp b/source/modules/dgl/src/WindowPrivateData.hpp index 31b1481ad..388318483 100644 --- a/source/modules/dgl/src/WindowPrivateData.hpp +++ b/source/modules/dgl/src/WindowPrivateData.hpp @@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback { PuglView* view; /** Reserved space for graphics context. */ - mutable uint8_t graphicsContext[sizeof(void*)]; + mutable uint8_t graphicsContext[sizeof(int) * 9]; + void createContextIfNeeded(); + void destroyContext(); + void startContext(); + void endContext(); /** The top-level widgets associated with this Window. */ std::list topLevelWidgets; diff --git a/source/modules/dgl/src/nanovg/nanovg_gl.h b/source/modules/dgl/src/nanovg/nanovg_gl.h index 27e128355..feb502784 100644 --- a/source/modules/dgl/src/nanovg/nanovg_gl.h +++ b/source/modules/dgl/src/nanovg/nanovg_gl.h @@ -173,7 +173,7 @@ struct GLNVGtexture { int width, height; int type; int flags; -#if defined NANOVG_GLES2 +#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) unsigned char* data; #endif }; @@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im switch (type) { case NVG_TEXTURE_BGR: -#if NANOVG_GLES2 - // GLES2 cannot handle GL_BGR, do local conversion to GL_RGB +#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) + // GLES cannot handle GL_BGR, do local conversion to GL_RGB tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h); - for (uint32_t i=0; idata[i*3+0] = data[i*3+2]; tex->data[i*3+1] = data[i*3+1]; @@ -829,34 +829,34 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im #endif break; case NVG_TEXTURE_BGRA: -#if NANOVG_GLES2 - // GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA +#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) + // GLES cannot handle GL_BGRA, do local conversion to GL_RGBA tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h); - for (uint32_t i=0; idata[i*3+0] = data[i*3+3]; - tex->data[i*3+1] = data[i*3+2]; - tex->data[i*3+2] = data[i*3+1]; - tex->data[i*3+3] = data[i*3+0]; + tex->data[i*4+0] = data[i*4+2]; + tex->data[i*4+1] = data[i*4+1]; + tex->data[i*4+2] = data[i*4+0]; + tex->data[i*4+3] = data[i*4+3]; } data = tex->data; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); #endif break; case NVG_TEXTURE_RGB: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); break; case NVG_TEXTURE_RGBA: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; default: -#if defined(NANOVG_GLES2) || defined (NANOVG_GL2) +#if defined(NANOVG_GL2) || defined(NANOVG_GLES2) glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); -#elif defined(NANOVG_GLES3) - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); #else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); #endif break; @@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w switch (tex->type) { case NVG_TEXTURE_BGR: +#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); break; - case NVG_TEXTURE_BGRA: - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); - break; +#endif case NVG_TEXTURE_RGB: glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); break; + case NVG_TEXTURE_BGRA: +#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) + glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); + break; +#endif case NVG_TEXTURE_RGBA: glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); break; default: -#if defined(NANOVG_GLES2) || defined(NANOVG_GL2) +#if defined(NANOVG_GL2) || defined(NANOVG_GLES2) glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); #else glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); diff --git a/source/modules/dgl/src/pugl-upstream/include/pugl/attributes.h b/source/modules/dgl/src/pugl-upstream/include/pugl/attributes.h index 75322ee08..1e2264ccf 100644 --- a/source/modules/dgl/src/pugl-upstream/include/pugl/attributes.h +++ b/source/modules/dgl/src/pugl-upstream/include/pugl/attributes.h @@ -26,17 +26,6 @@ # endif #endif -// Deprecated API -#ifndef PUGL_DISABLE_DEPRECATED -# if defined(__clang__) -# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("", rep))) -# elif defined(__GNUC__) -# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("Use " rep))) -# else -# define PUGL_DEPRECATED_BY(rep) -# endif -#endif - // GCC function attributes #if defined(__GNUC__) # define PUGL_CONST_FUNC __attribute__((const)) @@ -47,13 +36,9 @@ #endif /// A const function in the public API that only reads parameters -#define PUGL_CONST_API \ - PUGL_API \ - PUGL_CONST_FUNC +#define PUGL_CONST_API PUGL_API PUGL_CONST_FUNC /// A malloc function in the public API that returns allocated memory -#define PUGL_MALLOC_API \ - PUGL_API \ - PUGL_MALLOC_FUNC +#define PUGL_MALLOC_API PUGL_API PUGL_MALLOC_FUNC #endif // PUGL_ATTRIBUTES_H diff --git a/source/modules/dgl/src/pugl-upstream/include/pugl/gl.h b/source/modules/dgl/src/pugl-upstream/include/pugl/gl.h index ac7c557de..f4d5f3daf 100644 --- a/source/modules/dgl/src/pugl-upstream/include/pugl/gl.h +++ b/source/modules/dgl/src/pugl-upstream/include/pugl/gl.h @@ -4,8 +4,8 @@ #ifndef PUGL_GL_H #define PUGL_GL_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include // IWYU pragma: begin_exports @@ -42,8 +42,7 @@ typedef void (*PuglGlFunc)(void); /** Return the address of an OpenGL extension function. */ -PUGL_API -PuglGlFunc +PUGL_API PuglGlFunc puglGetProcAddress(const char* name); /** @@ -53,8 +52,7 @@ puglGetProcAddress(const char* name); doing things like loading textures. Note that this must not be used for drawing, which may only be done while processing an expose event. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglEnterContext(PuglView* view); /** @@ -62,8 +60,7 @@ puglEnterContext(PuglView* view); This must only be called after puglEnterContext(). */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglLeaveContext(PuglView* view); /** @@ -71,8 +68,7 @@ puglLeaveContext(PuglView* view); Pass the returned value to puglSetBackend() to draw to a view with OpenGL. */ -PUGL_CONST_API -const PuglBackend* +PUGL_CONST_API const PuglBackend* puglGlBackend(void); PUGL_END_DECLS diff --git a/source/modules/dgl/src/pugl-upstream/include/pugl/pugl.h b/source/modules/dgl/src/pugl-upstream/include/pugl/pugl.h index ac5f51c5a..df1e3a290 100644 --- a/source/modules/dgl/src/pugl-upstream/include/pugl/pugl.h +++ b/source/modules/dgl/src/pugl-upstream/include/pugl/pugl.h @@ -4,7 +4,7 @@ #ifndef PUGL_PUGL_H #define PUGL_PUGL_H -#include "pugl/attributes.h" +#include #include #include @@ -46,22 +46,17 @@ typedef int16_t PuglCoord; */ typedef uint16_t PuglSpan; -/** - A rectangle in a view or on the screen. - - This type is used to describe two things: the position and size of a view - (for configuring), or a rectangle within a view (for exposing). - - The coordinate (0, 0) represents the top-left pixel of the parent window (or - display if there isn't one), or the top-left pixel of the view, - respectively. -*/ +/// A 2-dimensional position within/of a view typedef struct { PuglCoord x; PuglCoord y; - PuglSpan width; - PuglSpan height; -} PuglRect; +} PuglPoint; + +/// A 2-dimensional size within/of a view +typedef struct { + PuglSpan width; + PuglSpan height; +} PuglArea; /// A string property for configuration typedef enum { @@ -298,9 +293,9 @@ typedef PuglAnyEvent PuglCloseEvent; This event is sent to every view near the end of a main loop iteration when any pending exposures are about to be redrawn. It is typically used to mark - regions to expose with puglPostRedisplay() or puglPostRedisplayRect(). For - example, to continuously animate, a view calls puglPostRedisplay() when an - update event is received, and it will then shortly receive an expose event. + regions to expose with puglObscureView() or puglObscureRegion(). For + example, to continuously animate, obscure the view when an update event is + received, and it will receive an expose event shortly afterwards. */ typedef PuglAnyEvent PuglUpdateEvent; @@ -341,76 +336,77 @@ typedef struct { mapping used here is arbitrary and specific to Pugl. */ typedef enum { - PUGL_KEY_BACKSPACE = 0x00000008U, ///< Backspace - PUGL_KEY_TAB = 0x00000009U, ///< Tab - PUGL_KEY_ENTER = 0x0000000DU, ///< Enter - PUGL_KEY_ESCAPE = 0x0000001BU, ///< Escape - PUGL_KEY_DELETE = 0x0000007FU, ///< Delete - PUGL_KEY_SPACE = 0x00000020U, ///< Space - PUGL_KEY_F1 = 0x0000E000U, ///< F1 - PUGL_KEY_F2, ///< F2 - PUGL_KEY_F3, ///< F3 - PUGL_KEY_F4, ///< F4 - PUGL_KEY_F5, ///< F5 - PUGL_KEY_F6, ///< F6 - PUGL_KEY_F7, ///< F7 - PUGL_KEY_F8, ///< F8 - PUGL_KEY_F9, ///< F9 - PUGL_KEY_F10, ///< F10 - PUGL_KEY_F11, ///< F11 - PUGL_KEY_F12, ///< F12 - PUGL_KEY_PAGE_UP = 0xE031, ///< Page Up - PUGL_KEY_PAGE_DOWN, ///< Page Down - PUGL_KEY_END, ///< End - PUGL_KEY_HOME, ///< Home - PUGL_KEY_LEFT, ///< Left - PUGL_KEY_UP, ///< Up - PUGL_KEY_RIGHT, ///< Right - PUGL_KEY_DOWN, ///< Down - PUGL_KEY_PRINT_SCREEN = 0xE041U, ///< Print Screen - PUGL_KEY_INSERT, ///< Insert - PUGL_KEY_PAUSE, ///< Pause/Break - PUGL_KEY_MENU, ///< Menu - PUGL_KEY_NUM_LOCK, ///< Num Lock - PUGL_KEY_SCROLL_LOCK, ///< Scroll Lock - PUGL_KEY_CAPS_LOCK, ///< Caps Lock - PUGL_KEY_SHIFT_L = 0xE051U, ///< Left Shift - PUGL_KEY_SHIFT_R, ///< Right Shift - PUGL_KEY_CTRL_L, ///< Left Control - PUGL_KEY_CTRL_R, ///< Right Control - PUGL_KEY_ALT_L, ///< Left Alt - PUGL_KEY_ALT_R, ///< Right Alt / AltGr - PUGL_KEY_SUPER_L, ///< Left Super - PUGL_KEY_SUPER_R, ///< Right Super - PUGL_KEY_PAD_0 = 0xE060U, ///< Keypad 0 - PUGL_KEY_PAD_1, ///< Keypad 1 - PUGL_KEY_PAD_2, ///< Keypad 2 - PUGL_KEY_PAD_3, ///< Keypad 3 - PUGL_KEY_PAD_4, ///< Keypad 4 - PUGL_KEY_PAD_5, ///< Keypad 5 - PUGL_KEY_PAD_6, ///< Keypad 6 - PUGL_KEY_PAD_7, ///< Keypad 7 - PUGL_KEY_PAD_8, ///< Keypad 8 - PUGL_KEY_PAD_9, ///< Keypad 9 - PUGL_KEY_PAD_ENTER, ///< Keypad Enter - PUGL_KEY_PAD_PAGE_UP = 0xE071U, ///< Keypad Page Up - PUGL_KEY_PAD_PAGE_DOWN, ///< Keypad Page Down - PUGL_KEY_PAD_END, ///< Keypad End - PUGL_KEY_PAD_HOME, ///< Keypad Home - PUGL_KEY_PAD_LEFT, ///< Keypad Left - PUGL_KEY_PAD_UP, ///< Keypad Up - PUGL_KEY_PAD_RIGHT, ///< Keypad Right - PUGL_KEY_PAD_DOWN, ///< Keypad Down - PUGL_KEY_PAD_CLEAR = 0xE09DU, ///< Keypad Clear/Begin - PUGL_KEY_PAD_INSERT, ///< Keypad Insert - PUGL_KEY_PAD_DELETE, ///< Keypad Delete - PUGL_KEY_PAD_EQUAL, ///< Keypad Equal - PUGL_KEY_PAD_MULTIPLY = 0xE0AAU, ///< Keypad Multiply - PUGL_KEY_PAD_ADD, ///< Keypad Add - PUGL_KEY_PAD_SEPARATOR, ///< Keypad Separator - PUGL_KEY_PAD_SUBTRACT, ///< Keypad Subtract - PUGL_KEY_PAD_DECIMAL, ///< Keypad Decimal - PUGL_KEY_PAD_DIVIDE, ///< Keypad Divide + PUGL_KEY_NONE = 0U, ///< Sentinel value for no key + PUGL_KEY_BACKSPACE = 0x0008U, ///< Backspace + PUGL_KEY_TAB = 0x0009U, ///< Tab + PUGL_KEY_ENTER = 0x000DU, ///< Enter + PUGL_KEY_ESCAPE = 0x001BU, ///< Escape + PUGL_KEY_DELETE = 0x007FU, ///< Delete + PUGL_KEY_SPACE = 0x0020U, ///< Space + PUGL_KEY_F1 = 0xE000U, ///< F1 + PUGL_KEY_F2 = 0xE001U, ///< F2 + PUGL_KEY_F3 = 0xE002U, ///< F3 + PUGL_KEY_F4 = 0xE003U, ///< F4 + PUGL_KEY_F5 = 0xE004U, ///< F5 + PUGL_KEY_F6 = 0xE005U, ///< F6 + PUGL_KEY_F7 = 0xE006U, ///< F7 + PUGL_KEY_F8 = 0xE007U, ///< F8 + PUGL_KEY_F9 = 0xE008U, ///< F9 + PUGL_KEY_F10 = 0xE009U, ///< F10 + PUGL_KEY_F11 = 0xE010U, ///< F11 + PUGL_KEY_F12 = 0xE011U, ///< F12 + PUGL_KEY_PAGE_UP = 0xE031U, ///< Page Up + PUGL_KEY_PAGE_DOWN = 0xE032U, ///< Page Down + PUGL_KEY_END = 0xE033U, ///< End + PUGL_KEY_HOME = 0xE034U, ///< Home + PUGL_KEY_LEFT = 0xE035U, ///< Left + PUGL_KEY_UP = 0xE036U, ///< Up + PUGL_KEY_RIGHT = 0xE037U, ///< Right + PUGL_KEY_DOWN = 0xE038U, ///< Down + PUGL_KEY_PRINT_SCREEN = 0xE041U, ///< Print Screen + PUGL_KEY_INSERT = 0xE042U, ///< Insert + PUGL_KEY_PAUSE = 0xE043U, ///< Pause/Break + PUGL_KEY_MENU = 0xE044U, ///< Menu + PUGL_KEY_NUM_LOCK = 0xE045U, ///< Num Lock + PUGL_KEY_SCROLL_LOCK = 0xE046U, ///< Scroll Lock + PUGL_KEY_CAPS_LOCK = 0xE047U, ///< Caps Lock + PUGL_KEY_SHIFT_L = 0xE051U, ///< Left Shift + PUGL_KEY_SHIFT_R = 0xE052U, ///< Right Shift + PUGL_KEY_CTRL_L = 0xE053U, ///< Left Control + PUGL_KEY_CTRL_R = 0xE054U, ///< Right Control + PUGL_KEY_ALT_L = 0xE055U, ///< Left Alt + PUGL_KEY_ALT_R = 0xE056U, ///< Right Alt / AltGr + PUGL_KEY_SUPER_L = 0xE057U, ///< Left Super + PUGL_KEY_SUPER_R = 0xE058U, ///< Right Super + PUGL_KEY_PAD_0 = 0xE060U, ///< Keypad 0 + PUGL_KEY_PAD_1 = 0xE061U, ///< Keypad 1 + PUGL_KEY_PAD_2 = 0xE062U, ///< Keypad 2 + PUGL_KEY_PAD_3 = 0xE063U, ///< Keypad 3 + PUGL_KEY_PAD_4 = 0xE064U, ///< Keypad 4 + PUGL_KEY_PAD_5 = 0xE065U, ///< Keypad 5 + PUGL_KEY_PAD_6 = 0xE066U, ///< Keypad 6 + PUGL_KEY_PAD_7 = 0xE067U, ///< Keypad 7 + PUGL_KEY_PAD_8 = 0xE068U, ///< Keypad 8 + PUGL_KEY_PAD_9 = 0xE069U, ///< Keypad 9 + PUGL_KEY_PAD_ENTER = 0xE070U, ///< Keypad Enter + PUGL_KEY_PAD_PAGE_UP = 0xE071U, ///< Keypad Page Up + PUGL_KEY_PAD_PAGE_DOWN = 0xE072U, ///< Keypad Page Down + PUGL_KEY_PAD_END = 0xE073U, ///< Keypad End + PUGL_KEY_PAD_HOME = 0xE074U, ///< Keypad Home + PUGL_KEY_PAD_LEFT = 0xE075U, ///< Keypad Left + PUGL_KEY_PAD_UP = 0xE076U, ///< Keypad Up + PUGL_KEY_PAD_RIGHT = 0xE077U, ///< Keypad Right + PUGL_KEY_PAD_DOWN = 0xE078U, ///< Keypad Down + PUGL_KEY_PAD_CLEAR = 0xE09DU, ///< Keypad Clear/Begin + PUGL_KEY_PAD_INSERT = 0xE09EU, ///< Keypad Insert + PUGL_KEY_PAD_DELETE = 0xE09FU, ///< Keypad Delete + PUGL_KEY_PAD_EQUAL = 0xE0A0U, ///< Keypad Equal + PUGL_KEY_PAD_MULTIPLY = 0xE0AAU, ///< Keypad Multiply + PUGL_KEY_PAD_ADD = 0xE0ABU, ///< Keypad Add + PUGL_KEY_PAD_SEPARATOR = 0xE0ACU, ///< Keypad Separator + PUGL_KEY_PAD_SUBTRACT = 0xE0ADU, ///< Keypad Subtract + PUGL_KEY_PAD_DECIMAL = 0xE0AEU, ///< Keypad Decimal + PUGL_KEY_PAD_DIVIDE = 0xE0AFU, ///< Keypad Divide } PuglKey; /// Keyboard modifier flags @@ -443,8 +439,8 @@ typedef struct { Key press or release event. This event represents low-level key presses and releases. This can be used - for "direct" keyboard handing like key bindings, but must not be interpreted - as text input. + for "direct" keyboard handling like key bindings, but must not be + interpreted as text input. Keys are represented portably as Unicode code points, using the "natural" code point for the key where possible (see #PuglKey for details). The `key` @@ -709,9 +705,7 @@ typedef union { /** @} @defgroup pugl_status Status - Most functions return a status code which can be used to check for errors. - @{ */ @@ -723,6 +717,7 @@ typedef enum { PUGL_BAD_BACKEND, ///< Invalid or missing backend PUGL_BAD_CONFIGURATION, ///< Invalid view configuration PUGL_BAD_PARAMETER, ///< Invalid parameter + PUGL_BAD_CALL, ///< Invalid call PUGL_BACKEND_FAILED, ///< Backend initialization failed PUGL_REGISTRATION_FAILED, ///< Class registration failed PUGL_REALIZE_FAILED, ///< System view realization failed @@ -733,8 +728,7 @@ typedef enum { } PuglStatus; /// Return a string describing a status code -PUGL_CONST_API -const char* +PUGL_CONST_API const char* puglStrerror(PuglStatus status); /** @@ -790,13 +784,11 @@ typedef uint32_t PuglWorldFlags; @param flags Flags to control world features. @return A new world, which must be later freed with puglFreeWorld(). */ -PUGL_MALLOC_API -PuglWorld* +PUGL_MALLOC_API PuglWorld* puglNewWorld(PuglWorldType type, PuglWorldFlags flags); /// Free a world allocated with puglNewWorld() -PUGL_API -void +PUGL_API void puglFreeWorld(PuglWorld* world); /** @@ -807,17 +799,19 @@ puglFreeWorld(PuglWorld* world); The handle is opaque to Pugl and is not interpreted in any way. */ -PUGL_API -void +PUGL_API void puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle); /// Get the user data for the world -PUGL_API -PuglWorldHandle +PUGL_API PuglWorldHandle puglGetWorldHandle(PuglWorld* world); /** - Return a pointer to the native handle of the world. + Return the native world handle. + + The "native world" is a system-specific handle that's shared across an + entire program or module. It serves as a common denominator between pugl, + host applications, the windowing system API, and other APIs that use it. X11: Returns a pointer to the `Display`. @@ -825,8 +819,7 @@ puglGetWorldHandle(PuglWorld* world); Windows: Returns the `HMODULE` of the calling process. */ -PUGL_API -void* +PUGL_API void* puglGetNativeWorld(PuglWorld* world); /** @@ -835,8 +828,7 @@ puglGetNativeWorld(PuglWorld* world); The string value only needs to be valid for the duration of this call, it will be copied if necessary. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetWorldString(PuglWorld* world, PuglStringHint key, const char* value); /** @@ -845,8 +837,7 @@ puglSetWorldString(PuglWorld* world, PuglStringHint key, const char* value); The returned string should be accessed immediately, or copied. It may become invalid upon any call to any function that manipulates the same view. */ -PUGL_API -const char* +PUGL_API const char* puglGetWorldString(const PuglWorld* world, PuglStringHint key); /** @@ -856,8 +847,7 @@ puglGetWorldString(const PuglWorld* world, PuglStringHint key); time is only useful to compare against other times returned by this function, its absolute value has no meaning. */ -PUGL_API -double +PUGL_API double puglGetTime(const PuglWorld* world); /** @@ -882,8 +872,7 @@ puglGetTime(const PuglWorld* world); @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if no events are read, or an error. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglUpdate(PuglWorld* world, double timeout); /** @@ -919,6 +908,11 @@ typedef struct PuglBackendImpl PuglBackend; /** A native view handle. + A "native view" is a system-specific handle for a view (or "window"). A + #PuglView is itself a native view, which may be embedded in another. Like + the native world handle, this serves as a common denominator between this + and other APIs. + X11: This is a `Window`. MacOS: This is a pointer to an `NSView*`. @@ -959,9 +953,9 @@ typedef enum { /// A special view hint value typedef enum { - PUGL_DONT_CARE = -1, ///< Generic trinary: Use best default - PUGL_FALSE = 0, ///< Generic trinary: Explicitly false - PUGL_TRUE = 1, ///< Generic trinary: Explicitly true + PUGL_DONT_CARE = -1, ///< Generic trinary: unset + PUGL_FALSE = 0, ///< Generic trinary: false + PUGL_TRUE = 1, ///< Generic trinary: true PUGL_OPENGL_API = 2, ///< For #PUGL_CONTEXT_API PUGL_OPENGL_ES_API = 3, ///< For #PUGL_CONTEXT_API PUGL_OPENGL_CORE_PROFILE = 4, ///< For #PUGL_CONTEXT_PROFILE @@ -975,6 +969,49 @@ typedef enum { PUGL_VIEW_TYPE_DIALOG, ///< A dialog window } PuglViewType; +/** + A hint for configuring/constraining the position of a view. + + The system will attempt to make the view's window adhere to these, but they + are suggestions, not hard constraints. Applications should handle any view + position gracefully. + + An unset position has `INT16_MIN` (-32768) for both `x` and `y`. In + practice, set positions should be between -16000 and 16000 for portability. + Usually, the origin is the top left of the display, although negative + coordinates are possible, particularly on multi-display system. +*/ +typedef enum { + /** + Default position. + + This is used as the position during window creation as a default, if no + other position is specified. It isn't necessary to set a default position + (unlike the default size, which is required). If not even a default + position is set, then the window will be created at an arbitrary position. + This position is a best-effort attempt to do the most reasonable thing for + the initial display of the window, for example, by centering. Note that + it is implementation-defined, subject to change, platform-specific, and + for embedded views, may no longer make sense if the parent's size is + adjusted. Code that wants to make assumptions about the initial position + must set the default to a specific valid one, such as `{0, 0}`. + */ + PUGL_DEFAULT_POSITION, + + /** + Current position. + + This reflects the current position of the view, which may be different from + the default position if the view has been moved by the user, window + manager, or for any other reason. Typically, it overrides the + default position. + */ + PUGL_CURRENT_POSITION, +} PuglPositionHint; + +/// The number of #PuglPositionHint values +#define PUGL_NUM_POSITION_HINTS ((unsigned)PUGL_CURRENT_POSITION + 1U) + /** A hint for configuring/constraining the size of a view. @@ -991,6 +1028,15 @@ typedef enum { */ PUGL_DEFAULT_SIZE, + /** + Current size. + + This reflects the current size of the view, which may be different from + the default size if the view is resizable. Typically, it overrides the + default size. + */ + PUGL_CURRENT_SIZE, + /** Minimum size. @@ -1049,18 +1095,15 @@ typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); It must first be configured, then the system view can be created with puglRealize(). */ -PUGL_MALLOC_API -PuglView* +PUGL_MALLOC_API PuglView* puglNewView(PuglWorld* world); /// Free a view created with puglNewView() -PUGL_API -void +PUGL_API void puglFreeView(PuglView* view); /// Return the world that `view` is a part of -PUGL_API -PuglWorld* +PUGL_API PuglWorld* puglGetWorld(PuglView* view); /** @@ -1072,13 +1115,11 @@ puglGetWorld(PuglView* view); The handle is opaque to Pugl and is not interpreted in any way. */ -PUGL_API -void +PUGL_API void puglSetHandle(PuglView* view, PuglHandle handle); /// Get the user data for a view -PUGL_API -PuglHandle +PUGL_API PuglHandle puglGetHandle(PuglView* view); /** @@ -1098,18 +1139,15 @@ puglGetHandle(PuglView* view); applications must link against the appropriate backend library, or be sure to compile in the appropriate code if using a local copy of Pugl. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetBackend(PuglView* view, const PuglBackend* backend); /// Return the graphics backend used by a view -PUGL_API -const PuglBackend* +PUGL_API const PuglBackend* puglGetBackend(const PuglView* view); /// Set the function to call when an event occurs -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); /** @@ -1117,8 +1155,7 @@ puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); This only has an effect when called before puglRealize(). */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewHint(PuglView* view, PuglViewHint hint, int value); /** @@ -1128,8 +1165,7 @@ puglSetViewHint(PuglView* view, PuglViewHint hint, int value); hint which was initially set to PUGL_DONT_CARE, or has been adjusted from the suggested value. */ -PUGL_API -int +PUGL_API int puglGetViewHint(const PuglView* view, PuglViewHint hint); /** @@ -1139,8 +1175,7 @@ puglGetViewHint(const PuglView* view, PuglViewHint hint); string value only needs to be valid for the duration of this call, it will be copied if necessary. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewString(PuglView* view, PuglStringHint key, const char* value); /** @@ -1149,8 +1184,7 @@ puglSetViewString(PuglView* view, PuglStringHint key, const char* value); The returned string should be accessed immediately, or copied. It may become invalid upon any call to any function that manipulates the same view. */ -PUGL_API -const char* +PUGL_API const char* puglGetViewString(const PuglView* view, PuglStringHint key); /** @@ -1165,8 +1199,7 @@ puglGetViewString(const PuglView* view, PuglStringHint key); that is reasonably sized on a 96 DPI display, and the scale 2.0 should have text twice that large. */ -PUGL_API -double +PUGL_API double puglGetScaleFactor(const PuglView* view); /** @@ -1177,64 +1210,56 @@ puglGetScaleFactor(const PuglView* view); */ /** - Get the current position and size of the view. + Get a position hint for the view. - The position is in screen coordinates with an upper left origin. + This can be used to get the default or current position of a view, in screen + coordinates with an upper left origin. */ -PUGL_API -PuglRect -puglGetFrame(const PuglView* view); +PUGL_API PuglPoint +puglGetPositionHint(const PuglView* view, PuglPositionHint hint); /** - Set the current position and size of the view. + Set a position hint for the view. - The position is in screen coordinates with an upper left origin. + This can be used to set the default or current position of a view. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. -*/ -PUGL_API -PuglStatus -puglSetFrame(PuglView* view, PuglRect frame); - -/** - Set the current position of the view. + This should be called before puglRealize() so the initial window for the + view can be configured correctly. It may also be used dynamically after the + window is realized, for some hints. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. + @return An error code on failure, but always succeeds if the view is not yet + realized. */ -PUGL_API -PuglStatus -puglSetPosition(PuglView* view, int x, int y); +PUGL_API PuglStatus +puglSetPositionHint(PuglView* view, PuglPositionHint hint, int x, int y); /** - Set the current size of the view. + Get a size hint for the view. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. + This can be used to get the default, current, minimum, and maximum size of a + view, as well as the supported range of aspect ratios. */ -PUGL_API -PuglStatus -puglSetSize(PuglView* view, unsigned width, unsigned height); +PUGL_API PuglArea +puglGetSizeHint(const PuglView* view, PuglSizeHint hint); /** Set a size hint for the view. - This can be used to set the default, minimum, and maximum size of a view, - as well as the supported range of aspect ratios. + This can be used to set the default, current, minimum, and maximum size of a + view, as well as the supported range of aspect ratios. This should be called before puglRealize() so the initial window for the - view can be configured correctly. + view can be configured correctly. It may also be used dynamically after the + window is realized, for some hints. - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. + @return An error code on failure, but always succeeds if the view is not yet + realized. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetSizeHint(PuglView* view, PuglSizeHint hint, - PuglSpan width, - PuglSpan height); + unsigned width, + unsigned height); /** @} @@ -1244,18 +1269,21 @@ puglSetSizeHint(PuglView* view, */ /** - Set the parent window for embedding a view in an existing window. + Set the native view the view will be embedded in. This must be called before puglRealize(), reparenting is not supported. */ -PUGL_API -PuglStatus -puglSetParentWindow(PuglView* view, PuglNativeView parent); +PUGL_API PuglStatus +puglSetParent(PuglView* view, PuglNativeView parent); -/// Return the parent window this view is embedded in, or null -PUGL_API -PuglNativeView -puglGetParentWindow(const PuglView* view); +/** + Return the native view that the view is embedded in. + + @return The native view handle of the parent that `view` is embedded in, or + zero (if view is top-level or unrealized). +*/ +PUGL_API PuglNativeView +puglGetParent(const PuglView* view); /** Set the transient parent of the window. @@ -1267,8 +1295,7 @@ puglGetParentWindow(const PuglView* view); A view can either have a parent (for embedding) or a transient parent (for top-level windows like dialogs), but not both. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetTransientParent(PuglView* view, PuglNativeView parent); /** @@ -1277,8 +1304,7 @@ puglSetTransientParent(PuglView* view, PuglNativeView parent); @return The native handle to the window this view is a transient child of, or null. */ -PUGL_API -PuglNativeView +PUGL_API PuglNativeView puglGetTransientParent(const PuglView* view); /** @@ -1290,8 +1316,7 @@ puglGetTransientParent(const PuglView* view); The view should be fully configured using the above functions before this is called. This function may only be called once per view. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglRealize(PuglView* view); /** @@ -1300,8 +1325,7 @@ puglRealize(PuglView* view); This is the inverse of puglRealize(). After this call, the view no longer corresponds to a real system view, and can be realized again later. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglUnrealize(PuglView* view); /// A command to control the behaviour of puglShow() @@ -1340,22 +1364,26 @@ typedef enum { Show the view. If the view has not yet been realized, the first call to this function will - do so automatically. + do so automatically. If the view is currently hidden, it will be shown and + possibly raised to the top depending on the platform. - If the view is currently hidden, it will be shown and possibly raised to the - top depending on the platform. + @return #PUGL_SUCCESS, an error from puglRealize(), or #PUGL_FAILURE if the + window was shown but not raised. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglShow(PuglView* view, PuglShowCommand command); -/// Hide the current window -PUGL_API -PuglStatus +/** + Hide the view. + + This is the counterpart to puglShow(). If the view is currently visible, it + will be hidden, otherwise, this does nothing. +*/ +PUGL_API PuglStatus puglHide(PuglView* view); /** - Set a view state, if supported by the system. + Set a view style, if supported by the system. This can be used to manipulate the window into various special states, but note that not all states are supported on all systems. This function may @@ -1364,28 +1392,24 @@ puglHide(PuglView* view); used to determine if the state has actually been set. Any changes to the actual state of the view will arrive in later configure events. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewStyle(PuglView* view, PuglViewStyleFlags flags); /** - Return true if the view currently has a state flag set. + Return true if the view currently has a style flag set. The result is determined based on the state announced in the last configure event. */ -PUGL_API -PuglViewStyleFlags +PUGL_API PuglViewStyleFlags puglGetViewStyle(const PuglView* view); /// Return true iff the view is currently visible -PUGL_API -bool +PUGL_API bool puglGetVisible(const PuglView* view); /// Return the native window handle -PUGL_API -PuglNativeView +PUGL_API PuglNativeView puglGetNativeView(PuglView* view); /** @@ -1406,8 +1430,7 @@ puglGetNativeView(PuglView* view); All other backends: returns null. */ -PUGL_API -void* +PUGL_API void* puglGetContext(PuglView* view); /** @@ -1419,19 +1442,32 @@ puglGetContext(PuglView* view); platforms. If called elsewhere, an expose will be enqueued to be processed in the next event loop iteration. */ -PUGL_API -PuglStatus -puglPostRedisplay(PuglView* view); +PUGL_API PuglStatus +puglObscureView(PuglView* view); /** - Request a redisplay of the given rectangle within the view. + "Obscure" a region so it will be exposed in the next render. + + This will cause an expose event to be dispatched later. If called from + within the event handler, the expose should arrive at the end of the current + event loop iteration, though this is not strictly guaranteed on all + platforms. If called elsewhere, an expose will be enqueued to be processed + in the next event loop iteration. - This has the same semantics as puglPostRedisplay(), but allows giving a - precise region for redrawing only a portion of the view. + The region is clamped to the size of the view if necessary. + + @param view The view to expose later. + @param x The top-left X coordinate of the rectangle to obscure. + @param y The top-left Y coordinate of the rectangle to obscure. + @param width The width of the rectangle to obscure. + @param height The height coordinate of the rectangle to obscure. */ -PUGL_API -PuglStatus -puglPostRedisplayRect(PuglView* view, PuglRect rect); +PUGL_API PuglStatus +puglObscureRegion(PuglView* view, + int x, + int y, + unsigned width, + unsigned height); /** @} @@ -1470,13 +1506,15 @@ typedef enum { @return #PUGL_SUCCESS if the focus was successfully grabbed, or an error. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglGrabFocus(PuglView* view); -/// Return whether `view` has the keyboard input focus -PUGL_API -bool +/** + Return whether `view` has the keyboard input focus. + + @return True if the view is realized and focused for keyboard input. +*/ +PUGL_API bool puglHasFocus(const PuglView* view); /** @@ -1484,8 +1522,7 @@ puglHasFocus(const PuglView* view); A #PUGL_DATA_OFFER event will be sent if data is available. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglPaste(PuglView* view); /** @@ -1493,8 +1530,7 @@ puglPaste(PuglView* view); Returns zero if the clipboard is empty. */ -PUGL_API -uint32_t +PUGL_API uint32_t puglGetNumClipboardTypes(const PuglView* view); /** @@ -1506,8 +1542,7 @@ puglGetNumClipboardTypes(const PuglView* view); Returns null if `typeIndex` is out of bounds according to puglGetNumClipboardTypes(). */ -PUGL_API -const char* +PUGL_API const char* puglGetClipboardType(const PuglView* view, uint32_t typeIndex); /** @@ -1526,8 +1561,7 @@ puglGetClipboardType(const PuglView* view, uint32_t typeIndex); the `typeIndex` argument to the call of puglGetClipboardType() that returned the accepted type. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglAcceptOffer(PuglView* view, const PuglDataOfferEvent* offer, uint32_t typeIndex); @@ -1543,8 +1577,7 @@ puglAcceptOffer(PuglView* view, @param data The data to copy to the clipboard. @param len The length of data in bytes (including terminator if necessary). */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetClipboard(PuglView* view, const char* type, const void* data, @@ -1561,8 +1594,7 @@ puglSetClipboard(PuglView* view, @param[out] len Set to the length of the data in bytes. @return The clipboard contents, or null. */ -PUGL_API -const void* +PUGL_API const void* puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); /** @@ -1576,8 +1608,7 @@ puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); #PUGL_UNSUPPORTED if setting the cursor is not supported on this system, or another error if the cursor is known but loading it fails. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetCursor(PuglView* view, PuglCursor cursor); /** @@ -1606,8 +1637,7 @@ puglSetCursor(PuglView* view, PuglCursor cursor); @return #PUGL_FAILURE if timers are not supported by the system, #PUGL_UNKNOWN_ERROR if setting the timer failed. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglStartTimer(PuglView* view, uintptr_t id, double timeout); /** @@ -1619,8 +1649,7 @@ puglStartTimer(PuglView* view, uintptr_t id, double timeout); @return #PUGL_FAILURE if timers are not supported by this system, #PUGL_UNKNOWN_ERROR if stopping the timer failed. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglStopTimer(PuglView* view, uintptr_t id); /** @@ -1633,553 +1662,17 @@ puglStopTimer(PuglView* view, uintptr_t id); Currently, only #PUGL_CLIENT events are supported on all platforms. X11: A #PUGL_EXPOSE event can be sent, which is similar to calling - puglPostRedisplayRect(), but will always send a message to the X server, - even when called in an event handler. + puglObscureRegion(), but will always send a message to the X server, even + when called in an event handler. - @return #PUGL_UNSUPPORTED if sending events of this type is not supported, - #PUGL_UNKNOWN_ERROR if sending the event failed. + @return #PUGL_UNSUPPORTED if sending this type of event isn't supported in + general, #PUGL_FAILURE if the event can't be sent now. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event); /** @} -*/ - -#ifndef PUGL_DISABLE_DEPRECATED - -/** - @} - @defgroup pugl_deprecated Deprecated API - @{ -*/ - -PUGL_DEPRECATED_BY("PuglRealizeEvent") -typedef PuglRealizeEvent PuglCreateEvent; - -PUGL_DEPRECATED_BY("PuglUnrealizeEvent") -typedef PuglUnrealizeEvent PuglDestroyEvent; - -PUGL_DEPRECATED_BY("PuglRealizeEvent") -typedef PuglRealizeEvent PuglEventCreate; - -PUGL_DEPRECATED_BY("PuglUnrealizeEvent") -typedef PuglUnrealizeEvent PuglEventDestroy; - -PUGL_DEPRECATED_BY("PuglConfigureEvent") -typedef PuglConfigureEvent PuglEventConfigure; - -PUGL_DEPRECATED_BY("PuglUpdateEvent") -typedef PuglUpdateEvent PuglEventUpdate; - -PUGL_DEPRECATED_BY("PuglExposeEvent") -typedef PuglExposeEvent PuglEventExpose; - -PUGL_DEPRECATED_BY("PuglCloseEvent") -typedef PuglCloseEvent PuglEventClose; - -PUGL_DEPRECATED_BY("PuglFocusEvent") -typedef PuglFocusEvent PuglEventFocus; - -PUGL_DEPRECATED_BY("PuglKeyEvent") -typedef PuglKeyEvent PuglEventKey; - -PUGL_DEPRECATED_BY("PuglTextEvent") -typedef PuglTextEvent PuglEventText; - -PUGL_DEPRECATED_BY("PuglCrossingEvent") -typedef PuglCrossingEvent PuglEventCrossing; - -PUGL_DEPRECATED_BY("PuglButtonEvent") -typedef PuglButtonEvent PuglEventButton; - -PUGL_DEPRECATED_BY("PuglMotionEvent") -typedef PuglMotionEvent PuglEventMotion; - -PUGL_DEPRECATED_BY("PuglScrollEvent") -typedef PuglScrollEvent PuglEventScroll; - -PUGL_DEPRECATED_BY("PuglClientEvent") -typedef PuglClientEvent PuglEventClient; - -PUGL_DEPRECATED_BY("PuglTimerEvent") -typedef PuglTimerEvent PuglEventTimer; - -PUGL_DEPRECATED_BY("PuglLoopEnterEvent") -typedef PuglLoopEnterEvent PuglEventLoopEnter; - -PUGL_DEPRECATED_BY("PuglLoopLeaveEvent") -typedef PuglLoopLeaveEvent PuglEventLoopLeave; - -/** - A native window handle. - - X11: This is a `Window`. - - MacOS: This is a pointer to an `NSView*`. - - Windows: This is a `HWND`. -*/ -PUGL_DEPRECATED_BY("PuglNativeView") -typedef uintptr_t PuglNativeWindow; - -/** - Create a Pugl application and view. - - To create a window, call the various puglInit* functions as necessary, then - call puglRealize(). - - @deprecated Use puglNewApp() and puglNewView(). - - @param pargc Pointer to argument count (currently unused). - @param argv Arguments (currently unused). - @return A newly created view. -*/ -static inline PUGL_DEPRECATED_BY("puglNewView") -PuglView* -puglInit(const int* pargc, char** argv) -{ - (void)pargc; - (void)argv; - - return puglNewView(puglNewWorld(PUGL_MODULE, 0)); -} - -/** - Destroy an app and view created with `puglInit()`. - - @deprecated Use puglFreeApp() and puglFreeView(). -*/ -static inline PUGL_DEPRECATED_BY("puglFreeView") -void -puglDestroy(PuglView* view) -{ - PuglWorld* const world = puglGetWorld(view); - - puglFreeView(view); - puglFreeWorld(world); -} - -/** - Set the class name of the application. - - This is a stable identifier for the application, used as the window - class/instance name on X11 and Windows. It is not displayed to the user, - but can be used in scripts and by window managers, so it should be the same - for every instance of the application, but different from other - applications. -*/ -static inline PUGL_DEPRECATED_BY("puglSetWorldString") -PuglStatus -puglSetClassName(PuglWorld* world, const char* name) -{ - return puglSetWorldString(world, PUGL_CLASS_NAME, name); -} - -/// Get the class name of the application, or null -static inline PUGL_DEPRECATED_BY("puglGetWorldString") -const char* -puglGetClassName(const PuglWorld* world) -{ - return puglGetWorldString(world, PUGL_CLASS_NAME); -} - -/** - Set the window class name before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetClassName") -void -puglInitWindowClass(PuglView* view, const char* name) -{ - puglSetWorldString(puglGetWorld(view), PUGL_CLASS_NAME, name); -} - -/** - Set the window size before creating a window. - - @deprecated Use puglSetFrame(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetFrame") -void -puglInitWindowSize(PuglView* view, int width, int height) -{ - PuglRect frame = puglGetFrame(view); - - frame.width = (PuglSpan)width; - frame.height = (PuglSpan)height; - - puglSetFrame(view, frame); -} - -/** - Set the minimum window size before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetMinSize") -void -puglInitWindowMinSize(PuglView* view, int width, int height) -{ - puglSetSizeHint(view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the window aspect ratio range before creating a window. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currently work on MacOS (the minimum is used), so only setting a fixed - aspect ratio works properly across all platforms. -*/ -static inline PUGL_DEPRECATED_BY("puglSetAspectRatio") -void -puglInitWindowAspectRatio(PuglView* view, - int minX, - int minY, - int maxX, - int maxY) -{ - puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); - puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); -} - -/** - Set transient parent before creating a window. - - On X11, parent must be a Window. - On OSX, parent must be an NSView*. -*/ -static inline PUGL_DEPRECATED_BY("puglSetTransientParent") -void -puglInitTransientFor(PuglView* view, uintptr_t parent) -{ - puglSetTransientParent(view, (PuglNativeView)parent); -} - -/** - Set transient parent before creating a window. - - @deprecated Use puglSetTransientParent(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetTransientParent") -PuglStatus -puglSetTransientFor(PuglView* view, uintptr_t parent) -{ - return puglSetTransientParent(view, (PuglNativeView)parent); -} - -/** - Enable or disable resizing before creating a window. - - @deprecated Use puglSetViewHint() with #PUGL_RESIZABLE. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglInitResizable(PuglView* view, bool resizable) -{ - puglSetViewHint(view, PUGL_RESIZABLE, resizable); -} - -/** - Get the current size of the view. - - @deprecated Use puglGetFrame(). - -*/ -static inline PUGL_DEPRECATED_BY("puglGetFrame") -void -puglGetSize(PuglView* view, int* width, int* height) -{ - const PuglRect frame = puglGetFrame(view); - - *width = (int)frame.width; - *height = (int)frame.height; -} - -/** - Ignore synthetic repeated key events. - - @deprecated Use puglSetViewHint() with #PUGL_IGNORE_KEY_REPEAT. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglIgnoreKeyRepeat(PuglView* view, bool ignore) -{ - puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, ignore); -} - -/** - Set a hint before creating a window. - - @deprecated Use puglSetWindowHint(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglInitWindowHint(PuglView* view, PuglViewHint hint, int value) -{ - puglSetViewHint(view, hint, value); -} - -/** - Set the parent window before creating a window (for embedding). - - @deprecated Use puglSetWindowParent(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetParentWindow") -void -puglInitWindowParent(PuglView* view, PuglNativeView parent) -{ - puglSetParentWindow(view, parent); -} - -/** - Set the graphics backend to use. - - @deprecated Use puglSetBackend(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetBackend") -int -puglInitBackend(PuglView* view, const PuglBackend* backend) -{ - return (int)puglSetBackend(view, backend); -} - -/** - Set the title of the window. - - This only makes sense for non-embedded views that will have a corresponding - top-level window, and sets the title, typically displayed in the title bar - or in window switchers. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewString") -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) -{ - return puglSetViewString(view, PUGL_WINDOW_TITLE, title); -} - -/// Return the title of the window, or null -static inline PUGL_DEPRECATED_BY("puglGetViewString") -const char* -puglGetWindowTitle(const PuglView* view) -{ - return puglGetViewString(view, PUGL_WINDOW_TITLE); -} - -/** - Realize a view by creating a corresponding system view or window. - - The view should be fully configured using the above functions before this is - called. This function may only be called once per view. - - @deprecated Use puglRealize(), or just show the view. -*/ -static inline PUGL_DEPRECATED_BY("puglRealize") -PuglStatus -puglCreateWindow(PuglView* view, const char* title) -{ - puglSetViewString(view, PUGL_WINDOW_TITLE, title); - return puglRealize(view); -} - -/** - Block and wait for an event to be ready. - - This can be used in a loop to only process events via puglProcessEvents when - necessary. This function will block indefinitely if no events are - available, so is not appropriate for use in programs that need to perform - regular updates (e.g. animation). - - @deprecated Use puglPollEvents(). -*/ -PUGL_API -PUGL_DEPRECATED_BY("puglPollEvents") -PuglStatus -puglWaitForEvent(PuglView* view); - -/** - Process all pending window events. - - This handles input events as well as rendering, so it should be called - regularly and rapidly enough to keep the UI responsive. This function does - not block if no events are pending. - - @deprecated Use puglDispatchEvents(). -*/ -PUGL_API -PUGL_DEPRECATED_BY("puglDispatchEvents") -PuglStatus -puglProcessEvents(PuglView* view); - -/** - Poll for events that are ready to be processed. - - This polls for events that are ready for any view in the world, potentially - blocking depending on `timeout`. - - @param world The world to poll for events. - - @param timeout Maximum time to wait, in seconds. If zero, the call returns - immediately, if negative, the call blocks indefinitely. - - @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error. - - @deprecated Use puglUpdate(). -*/ -static inline PUGL_DEPRECATED_BY("puglUpdate") -PuglStatus -puglPollEvents(PuglWorld* world, double timeout) -{ - return puglUpdate(world, timeout); -} - -/** - Dispatch any pending events to views. - - This processes all pending events, dispatching them to the appropriate - views. View event handlers will be called in the scope of this call. This - function does not block, if no events are pending then it will return - immediately. - - @deprecated Use puglUpdate(). -*/ -static inline PUGL_DEPRECATED_BY("puglUpdate") -PuglStatus -puglDispatchEvents(PuglWorld* world) -{ - return puglUpdate(world, 0.0); -} - -static inline PUGL_DEPRECATED_BY("puglShow") -PuglStatus -puglShowWindow(PuglView* view) -{ - return puglShow(view, PUGL_SHOW_RAISE); -} - -static inline PUGL_DEPRECATED_BY("puglHide") -PuglStatus -puglHideWindow(PuglView* view) -{ - return puglHide(view); -} - -/** - Set the default size of the view. - - This should be called before puglRealize() to set the default size of the - view, which will be the initial size of the window if this is a top level - view. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetDefaultSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_DEFAULT_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the minimum size of the view. - - If an initial minimum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetMinSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the maximum size of the view. - - If an initial maximum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetMaxSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_MAX_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the view aspect ratio range. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currently work on MacOS (the minimum is used), so only setting a fixed - aspect ratio works properly across all platforms. - - If an initial aspect ratio is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY) -{ - const PuglStatus st0 = - puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); - - const PuglStatus st1 = - puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); - - return st0 ? st0 : st1; -} - -/// Return the native window handle -static inline PUGL_DEPRECATED_BY("puglGetNativeView") -PuglNativeView -puglGetNativeWindow(PuglView* view) -{ - return puglGetNativeView(view); -} - -/** - Request user attention. - - This hints to the system that the window or application requires attention - from the user. The exact effect depends on the platform, but is usually - something like a flashing task bar entry or bouncing application icon. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewStyle") -PuglStatus -puglRequestAttention(PuglView* view) -{ - return puglSetViewStyle(view, - puglGetViewStyle(view) | PUGL_VIEW_STYLE_DEMANDING); -} - -# define PUGL_KEY_SHIFT PUGL_KEY_SHIFT_L - -# define PUGL_KEY_CTRL PUGL_KEY_CTRL_L - -# define PUGL_KEY_ALT PUGL_KEY_ALT_L - -# define PUGL_KEY_SUPER PUGL_KEY_SUPER_L - -#endif // PUGL_DISABLE_DEPRECATED - -/** @} @} */ diff --git a/source/modules/dgl/src/pugl-upstream/include/pugl/stub.h b/source/modules/dgl/src/pugl-upstream/include/pugl/stub.h index 8bfe4d8ff..73f20aae5 100644 --- a/source/modules/dgl/src/pugl-upstream/include/pugl/stub.h +++ b/source/modules/dgl/src/pugl-upstream/include/pugl/stub.h @@ -4,8 +4,8 @@ #ifndef PUGL_STUB_H #define PUGL_STUB_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include PUGL_BEGIN_DECLS @@ -22,8 +22,7 @@ PUGL_BEGIN_DECLS This backend just creates a simple native window without setting up any portable graphics API. */ -PUGL_CONST_API -const PuglBackend* +PUGL_CONST_API const PuglBackend* puglStubBackend(void); /** diff --git a/source/modules/dgl/src/pugl-upstream/src/common.c b/source/modules/dgl/src/pugl-upstream/src/common.c index 46b2f3dbe..700ad9208 100644 --- a/source/modules/dgl/src/pugl-upstream/src/common.c +++ b/source/modules/dgl/src/pugl-upstream/src/common.c @@ -4,13 +4,11 @@ // Common implementations of public API functions in the core library #include "internal.h" - #include "platform.h" #include "types.h" -#include "pugl/pugl.h" +#include -#include #include #include #include @@ -27,6 +25,7 @@ puglStrerror(const PuglStatus status) case PUGL_BAD_BACKEND: return "Invalid or missing backend"; case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; case PUGL_BAD_PARAMETER: return "Invalid parameter"; + case PUGL_BAD_CALL: return "Invalid call"; case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; case PUGL_REGISTRATION_FAILED: return "Class registration failed"; case PUGL_REALIZE_FAILED: return "View creation failed"; @@ -106,27 +105,37 @@ puglGetWorldString(const PuglWorld* const world, const PuglStringHint key) } static void -puglSetDefaultHints(PuglHints hints) +puglSetDefaultHints(PuglView* const view) { - hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; - hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; - hints[PUGL_CONTEXT_VERSION_MINOR] = 0; - hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; - hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; - hints[PUGL_RED_BITS] = 8; - hints[PUGL_GREEN_BITS] = 8; - hints[PUGL_BLUE_BITS] = 8; - hints[PUGL_ALPHA_BITS] = 8; - hints[PUGL_DEPTH_BITS] = 0; - hints[PUGL_STENCIL_BITS] = 0; - hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; - hints[PUGL_SAMPLES] = 0; - hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; - hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; - hints[PUGL_RESIZABLE] = PUGL_FALSE; - hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; - hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; - hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; + view->hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; + view->hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; + view->hints[PUGL_CONTEXT_VERSION_MINOR] = 0; + view->hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; + view->hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; + view->hints[PUGL_RED_BITS] = 8; + view->hints[PUGL_GREEN_BITS] = 8; + view->hints[PUGL_BLUE_BITS] = 8; + view->hints[PUGL_ALPHA_BITS] = 8; + view->hints[PUGL_DEPTH_BITS] = 0; + view->hints[PUGL_STENCIL_BITS] = 0; + view->hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; + view->hints[PUGL_SAMPLES] = 0; + view->hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; + view->hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; + view->hints[PUGL_RESIZABLE] = PUGL_FALSE; + view->hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; + view->hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; + view->hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; + + for (unsigned i = 0U; i < PUGL_NUM_POSITION_HINTS; ++i) { + view->positionHints[i].x = INT16_MIN; + view->positionHints[i].y = INT16_MIN; + } + + for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { + view->sizeHints[i].width = 0U; + view->sizeHints[i].height = 0U; + } } PuglView* @@ -138,13 +147,8 @@ puglNewView(PuglWorld* const world) return NULL; } - view->world = world; - view->sizeHints[PUGL_MIN_SIZE].width = 1; - view->sizeHints[PUGL_MIN_SIZE].height = 1; - view->defaultX = INT_MIN; - view->defaultY = INT_MIN; - - puglSetDefaultHints(view->hints); + view->world = world; + puglSetDefaultHints(view); // Enlarge world view list const size_t newNumViews = world->numViews + 1U; @@ -287,43 +291,84 @@ puglGetViewString(const PuglView* const view, const PuglStringHint key) return view->strings[key]; } -PuglRect -puglGetFrame(const PuglView* view) +PuglPoint +puglGetPositionHint(const PuglView* const view, const PuglPositionHint hint) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Return the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; + if (hint == PUGL_CURRENT_POSITION) { + PuglPoint pos = {0, 0}; + if (view->lastConfigure.type == PUGL_CONFIGURE) { + pos.x = view->lastConfigure.x; + pos.y = view->lastConfigure.y; + } else { + const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; + if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { + pos.x = defaultPos.x; + pos.y = defaultPos.y; + } + } + return pos; } - // Get the default position if set, or fallback to (0, 0) - int x = view->defaultX; - int y = view->defaultY; - if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { - x = 0; - y = 0; + return view->positionHints[hint]; +} + +PuglStatus +puglSetPositionHint(PuglView* const view, + const PuglPositionHint hint, + const int x, + const int y) +{ + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; } - // Return the default frame, sanitized if necessary - const PuglRect frame = {(PuglCoord)x, - (PuglCoord)y, - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; + if (x <= INT16_MIN || x > INT16_MAX || y <= INT16_MIN || y > INT16_MAX) { + return PUGL_BAD_PARAMETER; + } + + view->positionHints[hint].x = (PuglCoord)x; + view->positionHints[hint].y = (PuglCoord)y; + + return (hint != PUGL_CURRENT_POSITION) ? PUGL_SUCCESS + : puglGetNativeView(view) ? puglSetWindowPosition(view, x, y) + : PUGL_FAILURE; +} + +PuglArea +puglGetSizeHint(const PuglView* const view, const PuglSizeHint hint) +{ + if (hint == PUGL_CURRENT_SIZE && view->lastConfigure.type == PUGL_CONFIGURE) { + const PuglArea area = {view->lastConfigure.width, + view->lastConfigure.height}; + return area; + } + + return view->sizeHints[hint]; } PuglStatus -puglSetParentWindow(PuglView* view, PuglNativeView parent) +puglSetParent(PuglView* view, PuglNativeView parent) { view->parent = parent; return PUGL_SUCCESS; } +PuglStatus +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const unsigned width, + const unsigned height) +{ + const PuglStatus st = puglStoreSizeHint(view, hint, width, height); + + return st ? st + : (hint != PUGL_CURRENT_SIZE) ? puglApplySizeHint(view, hint) + : puglGetNativeView(view) ? puglSetWindowSize(view, width, height) + : PUGL_FAILURE; +} + PuglNativeView -puglGetParentWindow(const PuglView* const view) +puglGetParent(const PuglView* const view) { return view->parent; } diff --git a/source/modules/dgl/src/pugl-upstream/src/internal.c b/source/modules/dgl/src/pugl-upstream/src/internal.c index 3c4d304c8..09b3680b3 100644 --- a/source/modules/dgl/src/pugl-upstream/src/internal.c +++ b/source/modules/dgl/src/pugl-upstream/src/internal.c @@ -5,19 +5,80 @@ #include "types.h" -#include "pugl/pugl.h" +#include #include #include +#include #include #include +static PuglPoint +make_point(const PuglCoord x, const PuglCoord y) +{ + const PuglPoint point = {x, y}; + return point; +} + +bool +puglIsValidPosition(const int x, const int y) +{ + // INT16_MIN is a sentinel, INT16_MAX is impossible with non-zero size + return x > INT16_MIN && x < INT16_MAX && y > INT16_MIN && y < INT16_MAX; +} + bool -puglIsValidSize(const PuglViewSize size) +puglIsValidSize(const unsigned width, const unsigned height) +{ + return width && height && width <= INT16_MAX && height <= INT16_MAX; +} + +bool +puglIsValidArea(const PuglArea size) { return size.width && size.height; } +PuglArea +puglGetInitialSize(const PuglView* const view) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured size + const PuglConfigureEvent config = view->lastConfigure; + const PuglArea size = {config.width, config.height}; + return size; + } + + // Use the default size hint set by the application + return view->sizeHints[PUGL_DEFAULT_SIZE]; +} + +PuglPoint +puglGetInitialPosition(const PuglView* const view, const PuglArea size) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured frame + return make_point(view->lastConfigure.x, view->lastConfigure.y); + } + + const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; + if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { + // Use the default position hint set by the application + return make_point(defaultPos.x, defaultPos.y); + } + + if (view->parent) { + // Default to the top/left origin of the parent + return make_point(0, 0); + } + + // Center frame on a transient ancestor, or failing that, the screen + const PuglPoint center = puglGetAncestorCenter(view); + const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)), + (PuglCoord)(center.y - (size.height / 2))}; + return pos; +} + void puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) { @@ -68,6 +129,25 @@ puglSetString(char** dest, const char* string) } } +PuglStatus +puglStoreSizeHint(PuglView* const view, + const PuglSizeHint hint, + const unsigned width, + const unsigned height) +{ + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } + + if (!puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + view->sizeHints[hint].width = (PuglSpan)width; + view->sizeHints[hint].height = (PuglSpan)height; + return PUGL_SUCCESS; +} + uint32_t puglDecodeUTF8(const uint8_t* buf) { @@ -159,11 +239,12 @@ puglPreRealize(PuglView* const view) } // Ensure that the default size is set to a valid size - if (!puglIsValidSize(view->sizeHints[PUGL_DEFAULT_SIZE])) { + if (!puglIsValidArea(view->sizeHints[PUGL_DEFAULT_SIZE])) { return PUGL_BAD_CONFIGURATION; } - return PUGL_SUCCESS; + return (view->world->state == PUGL_WORLD_EXPOSING) ? PUGL_BAD_CALL + : PUGL_SUCCESS; } PuglStatus @@ -173,7 +254,7 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER || type == PUGL_LOOP_LEAVE); - const PuglEvent event = {{type, 0}}; + const PuglEvent event = {{type, 0U}}; return puglDispatchEvent(view, &event); } @@ -240,8 +321,12 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) case PUGL_EXPOSE: assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED); if (!(st0 = view->backend->enter(view, &event->expose))) { - st0 = view->eventFunc(view, event); - st1 = view->backend->leave(view, &event->expose); + const PuglWorldState old_state = view->world->state; + + view->world->state = PUGL_WORLD_EXPOSING; + st0 = view->eventFunc(view, event); + view->world->state = old_state; + st1 = view->backend->leave(view, &event->expose); } break; diff --git a/source/modules/dgl/src/pugl-upstream/src/internal.h b/source/modules/dgl/src/pugl-upstream/src/internal.h index 3721c10e2..1ff9a2981 100644 --- a/source/modules/dgl/src/pugl-upstream/src/internal.h +++ b/source/modules/dgl/src/pugl-upstream/src/internal.h @@ -9,8 +9,8 @@ #include "attributes.h" #include "types.h" -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -18,9 +18,29 @@ PUGL_BEGIN_DECLS +/// Return true if `x`,`y` is a valid position +bool +puglIsValidPosition(int x, int y); + +/// Return true if `width`,`height` is a valid position +bool +puglIsValidSize(unsigned width, unsigned height); + /// Return true if `size` is a valid view size bool -puglIsValidSize(PuglViewSize size); +puglIsValidArea(PuglArea size); + +/// Return the center point of some "soft" ancestor (parent window or screen) +PuglPoint +puglGetAncestorCenter(const PuglView* view); + +/// Return the initial size of a view +PuglArea +puglGetInitialSize(const PuglView* view); + +/// Return the initial position of a view if known, or an invalid position +PuglPoint +puglGetInitialPosition(const PuglView* view, PuglArea size); /// Set hint to a default value if it is unset (PUGL_DONT_CARE) void @@ -34,8 +54,14 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len); void puglSetString(char** dest, const char* string); +/// Store `width` and `height` as the current value of a size `hint` +PuglStatus +puglStoreSizeHint(PuglView* view, + PuglSizeHint hint, + unsigned width, + unsigned height); + /// Handle a changed string property -PUGL_API PuglStatus puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); @@ -56,8 +82,7 @@ PuglStatus puglDispatchSimpleEvent(PuglView* view, PuglEventType type); /// Process configure event while already in the graphics context -PUGL_WARN_UNUSED_RESULT -PuglStatus +PUGL_WARN_UNUSED_RESULT PuglStatus puglConfigure(PuglView* view, const PuglEvent* event); /// Dispatch `event` to `view`, entering graphics context if necessary diff --git a/source/modules/dgl/src/pugl-upstream/src/mac.h b/source/modules/dgl/src/pugl-upstream/src/mac.h index 119e7c82b..305cf30bf 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac.h +++ b/source/modules/dgl/src/pugl-upstream/src/mac.h @@ -5,7 +5,7 @@ #ifndef PUGL_SRC_MAC_H #define PUGL_SRC_MAC_H -#include "pugl/pugl.h" +#include #import diff --git a/source/modules/dgl/src/pugl-upstream/src/mac.m b/source/modules/dgl/src/pugl-upstream/src/mac.m index 205e6950f..977cd7e48 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac.m +++ b/source/modules/dgl/src/pugl-upstream/src/mac.m @@ -7,9 +7,10 @@ #include "mac.h" #include "internal.h" +#include "macros.h" #include "platform.h" -#include "pugl/pugl.h" +#include #import @@ -121,7 +122,7 @@ viewScreen(const PuglView* view) } static NSRect -nsRectToPoints(PuglView* view, const NSRect rect) +nsRectToPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) } static NSRect -nsRectFromPoints(PuglView* view, const NSRect rect) +nsRectFromPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -143,21 +144,15 @@ nsRectFromPoints(PuglView* view, const NSRect rect) } static NSPoint -nsPointFromPoints(PuglView* view, const NSPoint point) +nsPointFromPoints(const PuglView* view, const NSPoint point) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor); } -static NSRect -rectToNsRect(const PuglRect rect) -{ - return NSMakeRect(rect.x, rect.y, rect.width, rect.height); -} - static NSSize -sizePoints(PuglView* view, const double width, const double height) +sizePoints(PuglView* view, const PuglSpan width, const PuglSpan height) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -188,7 +183,7 @@ getCurrentViewStyleFlags(PuglView* const view) } static PuglStatus -dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) +dispatchCurrentChildViewConfiguration(PuglView* const view) { const NSRect framePt = [view->impl->wrapperView frame]; const NSRect framePx = nsRectFromPoints(view, framePt); @@ -197,14 +192,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) return PUGL_SUCCESS; } - if (drawViewResize) { - const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size]; - [view->impl->drawView setFrameSize:sizePt]; - } - const PuglConfigureEvent ev = { PUGL_CONFIGURE, - 0, + 0U, (PuglCoord)framePx.origin.x, (PuglCoord)framePx.origin.y, (PuglSpan)framePx.size.width, @@ -254,7 +244,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) const PuglConfigureEvent ev = { PUGL_CONFIGURE, - 0, + 0U, (PuglCoord)contentPx.origin.x, (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), (PuglSpan)contentPx.size.width, @@ -322,7 +312,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) if (puglview->impl->window) { [puglview->impl->window dispatchCurrentConfiguration]; } else { - dispatchCurrentChildViewConfiguration(puglview, true); + dispatchCurrentChildViewConfiguration(puglview); } reshaped = false; } @@ -336,7 +326,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) const PuglExposeEvent ev = { PUGL_EXPOSE, - 0, + 0U, (PuglCoord)(rect.origin.x * scaleFactor), (PuglCoord)viewY, (PuglSpan)(rect.size.width * scaleFactor), @@ -350,9 +340,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) - (NSSize)intrinsicContentSize { - const PuglViewSize defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; + const PuglArea defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; - return puglIsValidSize(defaultSize) + return puglIsValidArea(defaultSize) ? sizePoints(puglview, defaultSize.width, defaultSize.height) : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); } @@ -468,7 +458,7 @@ keySymToSpecial(const NSEvent* const ev) return PUGL_KEY_PAD_9; } - return (PuglKey)0; + return PUGL_KEY_NONE; } - (void)updateTrackingAreas @@ -501,7 +491,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint rloc = [NSEvent mouseLocation]; const PuglCrossingEvent ev = { type, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -542,7 +532,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint rloc = [NSEvent mouseLocation]; const PuglMotionEvent ev = { PUGL_MOTION, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -577,7 +567,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint rloc = [NSEvent mouseLocation]; const PuglButtonEvent ev = { PUGL_BUTTON_PRESS, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -598,7 +588,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint rloc = [NSEvent mouseLocation]; const PuglButtonEvent ev = { PUGL_BUTTON_RELEASE, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -638,11 +628,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint wloc = [self eventLocation:event]; const NSPoint rloc = [NSEvent mouseLocation]; - double dx = -[event scrollingDeltaX] / 2.0; - double dy = [event scrollingDeltaY] / 2.0; + double dx = -[event scrollingDeltaX]; + double dy = [event scrollingDeltaY]; if ([event hasPreciseScrollingDeltas]) { - dx /= 10.0; - dy /= 10.0; + dx /= 20.0; + dy /= 20.0; } const PuglScrollDirection dir = @@ -657,7 +647,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const PuglScrollEvent ev = { PUGL_SCROLL, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -689,7 +679,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const PuglKeyEvent ev = { PUGL_KEY_PRESS, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -720,7 +710,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const PuglKeyEvent ev = { PUGL_KEY_RELEASE, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -832,7 +822,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) PuglTextEvent ev = { PUGL_TEXT, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -861,7 +851,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) - (void)flagsChanged:(NSEvent*)event { const uint32_t mods = getModifiers(event); - PuglKey special = (PuglKey)0; + PuglKey special = PUGL_KEY_NONE; const uint16_t keyCode = [event keyCode]; if (flagDiffers(mods, puglview->impl->mods, PUGL_MOD_SHIFT)) { @@ -882,7 +872,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) const bool release = [event type] == NSEventTypeKeyUp; const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS, - 0, + 0U, [event timestamp], wloc.x, wloc.y, @@ -924,7 +914,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) - (void)timerTick:(NSTimer*)userTimer { const NSNumber* userInfo = userTimer.userInfo; - const PuglTimerEvent ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue}; + const PuglTimerEvent ev = {PUGL_TIMER, 0U, userInfo.unsignedLongValue}; PuglEvent timerEvent; timerEvent.timer = ev; @@ -989,7 +979,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) { (void)notification; - PuglEvent ev = {{PUGL_FOCUS_IN, 0}}; + PuglEvent ev = {{PUGL_FOCUS_IN, 0U}}; ev.focus.mode = PUGL_CROSSING_NORMAL; puglDispatchEvent(window->puglview, &ev); } @@ -998,7 +988,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) { (void)notification; - PuglEvent ev = {{PUGL_FOCUS_OUT, 0}}; + PuglEvent ev = {{PUGL_FOCUS_OUT, 0U}}; ev.focus.mode = PUGL_CROSSING_NORMAL; puglDispatchEvent(window->puglview, &ev); } @@ -1117,17 +1107,18 @@ puglConstraint(const id item, constant:(CGFloat)constant]; } -static PuglStatus -updateSizeHint(PuglView* const view, const PuglSizeHint hint) +PuglStatus +puglApplySizeHint(PuglView* const view, const PuglSizeHint hint) { const PuglSpan width = view->sizeHints[hint].width; const PuglSpan height = view->sizeHints[hint].height; - if (!puglIsValidSize(view->sizeHints[hint])) { + if (!puglIsValidArea(view->sizeHints[hint])) { return PUGL_FAILURE; } switch (hint) { case PUGL_DEFAULT_SIZE: + case PUGL_CURRENT_SIZE: break; case PUGL_MIN_SIZE: @@ -1150,38 +1141,18 @@ updateSizeHint(PuglView* const view, const PuglSizeHint hint) return PUGL_SUCCESS; } -static void -updateSizeHints(PuglView* const view) +PuglStatus +puglUpdateSizeHints(PuglView* const view) { for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { - updateSizeHint(view, (PuglSizeHint)i); + puglApplySizeHint(view, (PuglSizeHint)i); } + return PUGL_SUCCESS; } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const int x = view->defaultX; - const int y = view->defaultY; - if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = {(PuglCoord)x, - (PuglCoord)y, - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; - } - - // Get a bounding rect from the transient parent or the screen const NSScreen* const screen = viewScreen(view); const NSRect boundsPt = rectFromScreen(screen, @@ -1189,17 +1160,11 @@ getInitialFrame(PuglView* const view) ? [[(const NSView*)view->transientParent window] frame] : [screen frame]); - // Center the frame around the center of the bounding rectangle - const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; - const NSRect boundsPx = nsRectFromPoints(view, boundsPt); - const double centerX = boundsPx.origin.x + boundsPx.size.width / 2; - const double centerY = boundsPx.origin.y + boundsPx.size.height / 2; - - const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)), - (PuglCoord)(centerY - (defaultSize.height / 2U)), - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; + const NSRect boundsPx = nsRectFromPoints(view, boundsPt); + const PuglPoint center = { + (PuglCoord)(boundsPx.origin.x + (boundsPx.size.width / 2.0)), + (PuglCoord)(boundsPx.origin.y + (boundsPx.size.height / 2.0))}; + return center; } PuglStatus @@ -1247,11 +1212,12 @@ puglRealize(PuglView* view) CVDisplayLinkRelease(link); } - // Get the initial frame to use from the defaults or last configuration - const PuglRect initialFrame = getInitialFrame(view); + // Get the initial size and position from the defaults or last configuration + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); // Convert frame to points - const NSRect framePx = rectToNsRect(initialFrame); + const NSRect framePx = NSMakeRect(pos.x, pos.y, size.width, size.height); const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, framePx.origin.y / scaleFactor, framePx.size.width / scaleFactor, @@ -1277,7 +1243,7 @@ puglRealize(PuglView* view) NSLayoutRelationGreaterThanOrEqual, view->sizeHints[PUGL_MIN_SIZE].height)]; - if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { + if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { [impl->wrapperView addConstraint:puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, @@ -1337,10 +1303,19 @@ puglRealize(PuglView* view) ((NSWindow*)window).delegate = [[PuglWindowDelegate alloc] initWithPuglWindow:window]; - // Set basic window hints and attributes - puglSetFrame(view, initialFrame); + // Set window frame + const NSRect screenPt = rectToScreen(screen, framePt); + const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; + [impl->window setFrame:winFrame display:NO]; + + // Resize views and move them to (0, 0) + const NSRect sizePx = {{0, 0}, {framePx.size.width, framePx.size.height}}; + const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; + [impl->wrapperView setFrame:sizePt]; + [impl->drawView setFrame:sizePt]; + puglSetTransientParent(view, view->transientParent); - updateSizeHints(view); + puglUpdateSizeHints(view); [window setContentView:impl->wrapperView]; [view->world->impl->app activateIgnoringOtherApps:YES]; @@ -1393,24 +1368,23 @@ puglUnrealize(PuglView* const view) PuglStatus puglShow(PuglView* view, const PuglShowCommand command) { - if (!view->impl->wrapperView) { - const PuglStatus st = puglRealize(view); - if (st) { - return st; - } + PuglInternals* impl = view->impl; + PuglStatus st = impl->wrapperView ? PUGL_SUCCESS : puglRealize(view); + if (st || !impl->wrapperView) { + return st; } - NSWindow* const window = [view->impl->wrapperView window]; + NSWindow* const window = [impl->wrapperView window]; if (![window isVisible]) { [window setIsVisible:YES]; - [view->impl->drawView setNeedsDisplay:YES]; + [impl->drawView setNeedsDisplay:YES]; } switch (command) { case PUGL_SHOW_PASSIVE: break; case PUGL_SHOW_RAISE: - [window orderFront:view->impl->wrapperView]; + [window orderFront:impl->wrapperView]; break; case PUGL_SHOW_FORCE_RAISE: [window orderFrontRegardless]; @@ -1583,6 +1557,10 @@ puglStopTimer(PuglView* view, uintptr_t id) PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event) { + if (!view->impl->window || view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_FAILURE; + } + if (event->type == PUGL_CLIENT) { PuglEvent copiedEvent = *event; @@ -1610,18 +1588,17 @@ puglSendEvent(PuglView* view, const PuglEvent* event) return PUGL_UNSUPPORTED; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* view) -{ - return puglPollEvents(view->world, -1.0); -} -#endif - PuglStatus puglUpdate(PuglWorld* world, const double timeout) { @autoreleasepool { + const PuglWorldState startState = world->state; + if (startState == PUGL_WORLD_IDLE) { + world->state = PUGL_WORLD_UPDATING; + } else if (startState != PUGL_WORLD_RECURSING) { + return PUGL_BAD_CALL; + } + if (world->type == PUGL_PROGRAM) { NSDate* date = ((timeout < 0) ? [NSDate distantFuture] @@ -1650,19 +1627,13 @@ puglUpdate(PuglWorld* world, const double timeout) [view->impl->drawView displayIfNeeded]; } + + world->state = startState; } return PUGL_SUCCESS; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* view) -{ - return puglDispatchEvents(view->world); -} -#endif - double puglGetTime(const PuglWorld* world) { @@ -1674,22 +1645,45 @@ puglGetTime(const PuglWorld* world) } PuglStatus -puglPostRedisplay(PuglView* view) +puglObscureView(PuglView* view) { + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } + [view->impl->drawView setNeedsDisplay:YES]; return PUGL_SUCCESS; } PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) +puglObscureRegion(PuglView* view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const NSRect rectPx = { - {(double)rect.x, - (double)view->lastConfigure.height - (rect.y + rect.height)}, - {(double)rect.width, (double)rect.height}, - }; + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } + + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + const PuglSpan viewHeight = view->lastConfigure.height; + + const int cx = MAX(0, x); + const int cy = MAX(0, viewHeight - y - (int)height); + const unsigned cw = MIN(view->lastConfigure.width, width); + const unsigned ch = MIN(view->lastConfigure.height, height); + + if (cw == view->lastConfigure.width && ch == view->lastConfigure.height) { + [view->impl->drawView setNeedsDisplay:YES]; + } else { + const NSRect rectPx = NSMakeRect(cx, cy, cw, ch); - [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; + [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; + } return PUGL_SUCCESS; } @@ -1735,107 +1729,35 @@ puglGetScaleFactor(const PuglView* const view) } PuglStatus -puglSetFrame(PuglView* view, const PuglRect frame) -{ - PuglInternals* const impl = view->impl; - const NSRect framePx = rectToNsRect(frame); - const NSRect framePt = nsRectToPoints(view, framePx); - - if (!impl->wrapperView) { - // Set defaults to be used when realized - view->defaultX = frame.x; - view->defaultY = frame.y; - view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height; - return PUGL_SUCCESS; - } - - if (impl->window) { - const NSRect screenPt = rectToScreen(viewScreen(view), framePt); - - // Move and resize window to fit new content rect - const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; - [impl->window setFrame:winFrame display:NO]; - - // Resize views - const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); - const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; - [impl->wrapperView setFrame:sizePt]; - [impl->drawView setFrame:sizePt]; - [impl->window dispatchCurrentConfiguration]; - return PUGL_SUCCESS; - } - - // Resize view - const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); - const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; - - [impl->wrapperView setFrame:framePt]; - [impl->drawView setFrame:sizePt]; - return dispatchCurrentChildViewConfiguration(view, false); -} - -PuglStatus -puglSetPosition(PuglView* const view, const int x, const int y) +puglSetWindowPosition(PuglView* const view, const int x, const int y) { - if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - PuglInternals* const impl = view->impl; - if (!impl->wrapperView) { - // Set defaults to be used when realized - view->defaultX = x; - view->defaultY = y; - return PUGL_SUCCESS; - } - const PuglRect frame = {(PuglCoord)x, - (PuglCoord)y, - view->lastConfigure.width, - view->lastConfigure.height}; + const NSRect framePx = + NSMakeRect(x, y, view->lastConfigure.width, view->lastConfigure.height); + + const NSRect framePt = nsRectToPoints(view, framePx); if (impl->window) { // Adjust top-level window frame - return puglSetFrame(view, frame); + const NSRect screenPt = rectToScreen(viewScreen(view), framePt); + [impl->window setFrameOrigin:screenPt.origin]; + return PUGL_SUCCESS; } // Set wrapper view origin - const NSRect framePx = rectToNsRect(frame); - const NSRect framePt = nsRectToPoints(view, framePx); [impl->wrapperView setFrameOrigin:framePt.origin]; - // Set draw view origin - const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height); - const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; - [impl->drawView setFrameOrigin:drawPt.origin]; - // Dispatch new configuration - return dispatchCurrentChildViewConfiguration(view, false); + return dispatchCurrentChildViewConfiguration(view); } PuglStatus -puglSetSize(PuglView* const view, const unsigned width, const unsigned height) +puglSetWindowSize(PuglView* const view, + const unsigned width, + const unsigned height) { - if (width > INT16_MAX || height > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - PuglInternals* const impl = view->impl; - if (!impl->wrapperView) { - // Set defaults to be used when realized - view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; - return PUGL_SUCCESS; - } - - if (impl->window) { - // Adjust top-level window frame - PuglRect frame = puglGetFrame(view); - frame.width = (PuglSpan)width; - frame.height = (PuglSpan)height; - return puglSetFrame(view, frame); - } // Set wrapper view size const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -1847,24 +1769,20 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; [impl->drawView setFrameSize:drawPt.size]; - // Dispatch new configuration - return dispatchCurrentChildViewConfiguration(view, false); -} + if (impl->window) { + const NSRect framePx = + NSMakeRect(view->lastConfigure.x, view->lastConfigure.y, width, height); + const NSRect framePt = nsRectToPoints(view, framePx); + const NSRect screenPt = rectToScreen(viewScreen(view), framePt); -PuglStatus -puglSetSizeHint(PuglView* const view, - const PuglSizeHint hint, - const PuglSpan width, - const PuglSpan height) -{ - if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { - return PUGL_BAD_PARAMETER; + // Resize window to fit new content rect + const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; + [impl->window setFrame:winFrame display:NO]; + [impl->window dispatchCurrentConfiguration]; } - view->sizeHints[hint].width = width; - view->sizeHints[hint].height = height; - - return view->impl->window ? updateSizeHint(view, hint) : PUGL_SUCCESS; + // Dispatch new configuration + return dispatchCurrentChildViewConfiguration(view); } PuglStatus @@ -1892,7 +1810,7 @@ puglPaste(PuglView* const view) { const PuglDataOfferEvent offer = { PUGL_DATA_OFFER, - 0, + 0U, puglGetTime(view->world), }; @@ -1939,7 +1857,7 @@ puglAcceptOffer(PuglView* const view, PuglWrapperView* const wrapper = view->impl->wrapperView; NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; if (!pasteboard) { - return PUGL_BAD_PARAMETER; + return PUGL_UNKNOWN_ERROR; } const NSArray* const types = [pasteboard types]; @@ -1950,13 +1868,12 @@ puglAcceptOffer(PuglView* const view, wrapper->dragOperation = NSDragOperationCopy; wrapper->dragTypeIndex = typeIndex; - const PuglDataEvent data = { - PUGL_DATA, 0U, puglGetTime(view->world), (uint32_t)typeIndex}; + const double now = puglGetTime(view->world); + const PuglDataEvent data = {PUGL_DATA, 0U, now, typeIndex}; PuglEvent dataEvent; dataEvent.data = data; - puglDispatchEvent(view, &dataEvent); - return PUGL_SUCCESS; + return puglDispatchEvent(view, &dataEvent); } const void* diff --git a/source/modules/dgl/src/pugl-upstream/src/mac_cairo.m b/source/modules/dgl/src/pugl-upstream/src/mac_cairo.m index e4d2f5f25..112b727e8 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac_cairo.m +++ b/source/modules/dgl/src/pugl-upstream/src/mac_cairo.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/cairo.h" +#include #include diff --git a/source/modules/dgl/src/pugl-upstream/src/mac_gl.m b/source/modules/dgl/src/pugl-upstream/src/mac_gl.m index f177bf981..57dfc4158 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac_gl.m +++ b/source/modules/dgl/src/pugl-upstream/src/mac_gl.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/gl.h" +#include #ifndef __MAC_10_10 # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core diff --git a/source/modules/dgl/src/pugl-upstream/src/mac_stub.m b/source/modules/dgl/src/pugl-upstream/src/mac_stub.m index 9ebedcad6..c7e4512c2 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac_stub.m +++ b/source/modules/dgl/src/pugl-upstream/src/mac_stub.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/stub.h" +#include #import diff --git a/source/modules/dgl/src/pugl-upstream/src/mac_vulkan.m b/source/modules/dgl/src/pugl-upstream/src/mac_vulkan.m index 4fdbaf691..f3563e291 100644 --- a/source/modules/dgl/src/pugl-upstream/src/mac_vulkan.m +++ b/source/modules/dgl/src/pugl-upstream/src/mac_vulkan.m @@ -8,9 +8,9 @@ #include "stub.h" #include "types.h" -#include "pugl/pugl.h" -#include "pugl/stub.h" -#include "pugl/vulkan.h" +#include +#include +#include #include #include diff --git a/source/modules/dgl/src/pugl-upstream/src/platform.h b/source/modules/dgl/src/pugl-upstream/src/platform.h index cfdb1b790..378c12284 100644 --- a/source/modules/dgl/src/pugl-upstream/src/platform.h +++ b/source/modules/dgl/src/pugl-upstream/src/platform.h @@ -1,20 +1,20 @@ -// Copyright 2012-2022 David Robillard +// Copyright 2012-2025 David Robillard // SPDX-License-Identifier: ISC -// The API that a platform implementation must define +// The internal API that a platform implementation must define #ifndef PUGL_PLATFORM_H #define PUGL_PLATFORM_H #include "types.h" -#include "pugl/pugl.h" +#include +#include PUGL_BEGIN_DECLS /// Allocate and initialise world internals (implemented once per platform) -PUGL_MALLOC_FUNC -PuglWorldInternals* +PUGL_MALLOC_FUNC PuglWorldInternals* puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); /// Destroy and free world internals (implemented once per platform) @@ -22,14 +22,29 @@ void puglFreeWorldInternals(PuglWorld* world); /// Allocate and initialise view internals (implemented once per platform) -PUGL_MALLOC_FUNC -PuglInternals* +PUGL_MALLOC_FUNC PuglInternals* puglInitViewInternals(PuglWorld* world); /// Destroy and free view internals (implemented once per platform) void puglFreeViewInternals(PuglView* view); +/// Adapt to the change of a size hint if necessary +PuglStatus +puglApplySizeHint(PuglView* view, PuglSizeHint hint); + +/// Adapt to all configured size hints +PuglStatus +puglUpdateSizeHints(PuglView* view); + +/// Set the current position of a view window +PuglStatus +puglSetWindowPosition(PuglView* view, int x, int y); + +/// Set the current size of a view window +PuglStatus +puglSetWindowSize(PuglView* view, unsigned width, unsigned height); + PUGL_END_DECLS #endif // PUGL_PLATFORM_H diff --git a/source/modules/dgl/src/pugl-upstream/src/stub.h b/source/modules/dgl/src/pugl-upstream/src/stub.h index 699bbb958..2926768d4 100644 --- a/source/modules/dgl/src/pugl-upstream/src/stub.h +++ b/source/modules/dgl/src/pugl-upstream/src/stub.h @@ -4,7 +4,8 @@ #ifndef PUGL_SRC_STUB_H #define PUGL_SRC_STUB_H -#include "pugl/pugl.h" +#include +#include #include diff --git a/source/modules/dgl/src/pugl-upstream/src/types.h b/source/modules/dgl/src/pugl-upstream/src/types.h index 9817474a5..7179193d5 100644 --- a/source/modules/dgl/src/pugl-upstream/src/types.h +++ b/source/modules/dgl/src/pugl-upstream/src/types.h @@ -6,7 +6,7 @@ #include "attributes.h" -#include "pugl/pugl.h" +#include #include #include @@ -21,24 +21,20 @@ typedef struct PuglInternalsImpl PuglInternals; /// View hints typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; -/// View position (both X and Y coordinates) or general point -typedef struct { - PuglCoord x; - PuglCoord y; -} PuglPoint; - -/// View size (both X and Y coordinates) -typedef struct { - PuglSpan width; - PuglSpan height; -} PuglViewSize; - /// Blob of arbitrary data typedef struct { void* data; ///< Dynamically allocated data size_t len; ///< Length of data in bytes } PuglBlob; +/// State of the world in the process of an update +typedef enum { + PUGL_WORLD_IDLE, ///< Idle, not in puglUpdate() + PUGL_WORLD_UPDATING, ///< Event processing stage of puglUpdate() + PUGL_WORLD_EXPOSING, ///< Exposing stage of puglUpdate() + PUGL_WORLD_RECURSING, ///< Currently in recursive loop (Windows) +} PuglWorldState; + /// Stage of a view along its lifespan typedef enum { PUGL_VIEW_STAGE_ALLOCATED, @@ -57,10 +53,9 @@ struct PuglViewImpl { uintptr_t transientParent; PuglConfigureEvent lastConfigure; PuglHints hints; - PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS]; + PuglPoint positionHints[PUGL_NUM_POSITION_HINTS]; + PuglArea sizeHints[PUGL_NUM_SIZE_HINTS]; char* strings[PUGL_NUM_STRING_HINTS]; - int defaultX; - int defaultY; PuglViewStage stage; bool resizing; }; @@ -74,6 +69,7 @@ struct PuglWorldImpl { PuglView** views; char* strings[PUGL_NUM_STRING_HINTS]; PuglWorldType type; + PuglWorldState state; }; /// Opaque surface used by graphics backend @@ -82,23 +78,21 @@ typedef void PuglSurface; /// Graphics backend interface struct PuglBackendImpl { /// Get visual information from display and setup view as necessary - PUGL_WARN_UNUSED_RESULT - PuglStatus (*configure)(PuglView*); + PUGL_WARN_UNUSED_RESULT PuglStatus (*configure)(PuglView*); /// Create surface and drawing context - PUGL_WARN_UNUSED_RESULT - PuglStatus (*create)(PuglView*); + PUGL_WARN_UNUSED_RESULT PuglStatus (*create)(PuglView*); /// Destroy surface and drawing context void (*destroy)(PuglView*); /// Enter drawing context, for drawing if expose is non-null - PUGL_WARN_UNUSED_RESULT - PuglStatus (*enter)(PuglView*, const PuglExposeEvent*); + PUGL_WARN_UNUSED_RESULT PuglStatus (*enter)(PuglView*, + const PuglExposeEvent*); /// Leave drawing context, after drawing if expose is non-null - PUGL_WARN_UNUSED_RESULT - PuglStatus (*leave)(PuglView*, const PuglExposeEvent*); + PUGL_WARN_UNUSED_RESULT PuglStatus (*leave)(PuglView*, + const PuglExposeEvent*); /// Return the puglGetContext() handle for the application, if any void* (*getContext)(PuglView*); diff --git a/source/modules/dgl/src/pugl-upstream/src/win.c b/source/modules/dgl/src/pugl-upstream/src/win.c index 73c8583ba..c56cc449a 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win.c +++ b/source/modules/dgl/src/pugl-upstream/src/win.c @@ -4,9 +4,10 @@ #include "win.h" #include "internal.h" +#include "macros.h" #include "platform.h" -#include "pugl/pugl.h" +#include #include #include @@ -42,13 +43,11 @@ #define PUGL_USER_TIMER_MIN 9470 #ifdef __cplusplus -# define PUGL_INIT_STRUCT \ - {} -#else # define PUGL_INIT_STRUCT \ { \ - 0 \ } +#else +# define PUGL_INIT_STRUCT {0} #endif typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void); @@ -58,8 +57,12 @@ typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); -static wchar_t* -puglUtf8ToWideChar(const char* const utf8) +#ifdef UNICODE + +typedef wchar_t ArgStringChar; + +static ArgStringChar* +puglArgStringNew(const char* const utf8) { const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); if (len > 0) { @@ -71,6 +74,30 @@ puglUtf8ToWideChar(const char* const utf8) return NULL; } +static void +puglArgStringFree(ArgStringChar* const utf8) +{ + free(utf8); +} + +#else // !defined(UNICODE) + +typedef const char ArgStringChar; + +static ArgStringChar* +puglArgStringNew(const char* const utf8) +{ + return utf8; +} + +static void +puglArgStringFree(ArgStringChar* const utf8) +{ + (void)utf8; +} + +#endif + static char* puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) { @@ -94,11 +121,7 @@ puglWinStatus(const BOOL success) static bool puglRegisterWindowClass(const char* name) { -#ifdef UNICODE - wchar_t* const wname = puglUtf8ToWideChar(name); -#else - const char* const wname = name; -#endif + ArgStringChar* const nameArg = puglArgStringNew(name); HMODULE module = NULL; if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | @@ -109,7 +132,7 @@ puglRegisterWindowClass(const char* name) } WNDCLASSEX wc = PUGL_INIT_STRUCT; - if (GetClassInfoEx(module, wname, &wc)) { + if (GetClassInfoEx(module, nameArg, &wc)) { return true; // Already registered } @@ -120,12 +143,10 @@ puglRegisterWindowClass(const char* name) wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wc.lpszClassName = wname; + wc.lpszClassName = nameArg; const bool success = !!RegisterClassEx(&wc); -#ifdef UNICODE - free(wname); -#endif + puglArgStringFree(nameArg); return success; } @@ -184,7 +205,7 @@ static double puglWinGetViewScaleFactor(const PuglView* const view) { const HMODULE shcore = - LoadLibraryExA("Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + LoadLibraryEx(TEXT("Shcore.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (!shcore) { return 1.0; } @@ -219,7 +240,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) if (type == PUGL_PROGRAM) { HMODULE user32 = - LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + LoadLibraryEx(TEXT("user32.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (user32) { PFN_SetProcessDPIAware SetProcessDPIAware = (PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); @@ -250,20 +271,6 @@ puglInitViewInternals(PuglWorld* PUGL_UNUSED(world)) return (PuglInternals*)calloc(1, sizeof(PuglInternals)); } -static PuglStatus -puglPollWinEvents(PuglWorld* world, const double timeout) -{ - (void)world; - - if (timeout < 0) { - WaitMessage(); - } else { - MsgWaitForMultipleObjects( - 0, NULL, FALSE, (DWORD)(timeout * 1e3), QS_ALLEVENTS); - } - return PUGL_SUCCESS; -} - PuglStatus puglRealize(PuglView* view) { @@ -287,9 +294,9 @@ puglRealize(PuglView* view) puglEnsureHint(view, PUGL_ALPHA_BITS, 8); // Get refresh rate for resize draw timer - DEVMODEA devMode; + DEVMODE devMode; memset(&devMode, 0, sizeof(devMode)); - EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode); view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; // Register window class if necessary @@ -308,16 +315,16 @@ puglRealize(PuglView* view) puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); puglSetTransientParent(view, view->transientParent); - view->impl->scaleFactor = puglWinGetViewScaleFactor(view); - view->impl->cursor = LoadCursor(NULL, IDC_ARROW); + impl->scaleFactor = puglWinGetViewScaleFactor(view); + impl->cursor = LoadCursor(NULL, IDC_ARROW); if (view->hints[PUGL_DARK_FRAME]) { const BOOL useDarkMode = TRUE; - if ((DwmSetWindowAttribute(view->impl->hwnd, + if ((DwmSetWindowAttribute(impl->hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkMode, sizeof(useDarkMode)) != S_OK)) { - DwmSetWindowAttribute(view->impl->hwnd, + DwmSetWindowAttribute(impl->hwnd, PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkMode, sizeof(useDarkMode)); @@ -343,51 +350,48 @@ puglUnrealize(PuglView* const view) view->backend->destroy(view); } - ReleaseDC(view->impl->hwnd, view->impl->hdc); - view->impl->hdc = NULL; - - DestroyWindow(view->impl->hwnd); - view->impl->hwnd = NULL; - memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent)); - return PUGL_SUCCESS; + ReleaseDC(impl->hwnd, impl->hdc); + impl->hdc = NULL; + + const PuglStatus st = puglWinStatus(DestroyWindow(impl->hwnd)); + impl->hwnd = NULL; + return st; } PuglStatus puglShow(PuglView* view, const PuglShowCommand command) { PuglInternals* impl = view->impl; + PuglStatus st = impl->hwnd ? PUGL_SUCCESS : puglRealize(view); - if (!impl->hwnd) { - const PuglStatus st = puglRealize(view); - if (st) { - return st; + if (!st) { + switch (command) { + case PUGL_SHOW_PASSIVE: + ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); + break; + case PUGL_SHOW_RAISE: + ShowWindow(impl->hwnd, SW_SHOWNORMAL); + st = SetActiveWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; + break; + case PUGL_SHOW_FORCE_RAISE: + ShowWindow(impl->hwnd, SW_SHOWNORMAL); + st = SetForegroundWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; + break; } } - switch (command) { - case PUGL_SHOW_PASSIVE: - ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); - break; - case PUGL_SHOW_RAISE: - ShowWindow(impl->hwnd, SW_SHOWNORMAL); - SetActiveWindow(impl->hwnd); - break; - case PUGL_SHOW_FORCE_RAISE: - ShowWindow(impl->hwnd, SW_SHOWNORMAL); - SetForegroundWindow(impl->hwnd); - break; - } - - return PUGL_SUCCESS; + return st; } PuglStatus puglHide(PuglView* view) { - PuglInternals* impl = view->impl; + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } - ShowWindow(impl->hwnd, SW_HIDE); + ShowWindow(view->impl->hwnd, SW_HIDE); return PUGL_SUCCESS; } @@ -408,14 +412,11 @@ puglFreeViewInternals(PuglView* view) void puglFreeWorldInternals(PuglWorld* world) { -#ifdef UNICODE - wchar_t* const wname = puglUtf8ToWideChar(world->strings[PUGL_CLASS_NAME]); - UnregisterClass(wname, NULL); - free(wname); -#else - UnregisterClass(world->strings[PUGL_CLASS_NAME], NULL); -#endif + const char* const className = world->strings[PUGL_CLASS_NAME]; + ArgStringChar* const classNameArg = puglArgStringNew(className); + UnregisterClass(classNameArg, NULL); + puglArgStringFree(classNameArg); free(world->impl); } @@ -426,14 +427,14 @@ keyInRange(const WPARAM winSym, const PuglKey puglMin) { return (winSym >= winMin && winSym <= winMax) - ? (PuglKey)(puglMin + (winSym - winMin)) - : (PuglKey)0; + ? (PuglKey)((WPARAM)puglMin + (winSym - winMin)) + : PUGL_KEY_NONE; } static PuglKey keySymToSpecial(const WPARAM sym, const bool ext) { - PuglKey key = (PuglKey)0; + PuglKey key = PUGL_KEY_NONE; if ((key = keyInRange(sym, VK_F1, VK_F12, PUGL_KEY_F1)) || (key = keyInRange(sym, VK_PRIOR, @@ -482,23 +483,26 @@ keySymToSpecial(const WPARAM sym, const bool ext) // clang-format on } - return (PuglKey)0; + return PUGL_KEY_NONE; +} + +static bool +is_toggled(int vkey) +{ + return (unsigned)GetKeyState(vkey) & 1U; } static uint32_t getModifiers(void) { - // clang-format off - return ( - ((GetKeyState(VK_SHIFT) < 0) ? (uint32_t)PUGL_MOD_SHIFT : 0U) | - ((GetKeyState(VK_CONTROL) < 0) ? (uint32_t)PUGL_MOD_CTRL : 0U) | - ((GetKeyState(VK_MENU) < 0) ? (uint32_t)PUGL_MOD_ALT : 0U) | - ((GetKeyState(VK_LWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | - ((GetKeyState(VK_RWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | - ((GetKeyState(VK_NUMLOCK) & 1U) ? (uint32_t)PUGL_MOD_NUM_LOCK : 0U) | - ((GetKeyState(VK_SCROLL) & 1U) ? (uint32_t)PUGL_MOD_SCROLL_LOCK : 0U) | - ((GetKeyState(VK_CAPITAL) & 1U) ? (uint32_t)PUGL_MOD_CAPS_LOCK : 0U)); - // clang-format on + return ((uint32_t)(((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0) | + ((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0) | + ((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0) | + ((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0) | + ((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0) | + (is_toggled(VK_NUMLOCK) ? PUGL_MOD_NUM_LOCK : 0) | + (is_toggled(VK_SCROLL) ? PUGL_MOD_SCROLL_LOCK : 0) | + (is_toggled(VK_CAPITAL) ? PUGL_MOD_CAPS_LOCK : 0))); } static void @@ -673,7 +677,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) const PuglCrossingEvent ev = { type, - 0, + 0U, GetMessageTime() / 1e3, (double)pos.x, (double)pos.y, @@ -683,7 +687,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) PUGL_CROSSING_NORMAL, }; - PuglEvent crossingEvent = {{type, 0}}; + PuglEvent crossingEvent = {{type, 0U}}; crossingEvent.crossing = ev; puglDispatchEvent(view, &crossingEvent); } @@ -693,8 +697,8 @@ constrainAspect(const PuglView* const view, RECT* const size, const WPARAM wParam) { - const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; - const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; + const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; + const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; const float minA = (float)minAspect.width / (float)minAspect.height; const float maxA = (float)maxAspect.width / (float)maxAspect.height; @@ -733,7 +737,7 @@ constrainAspect(const PuglView* const view, static LRESULT handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) { - PuglEvent event = {{PUGL_NOTHING, 0}}; + PuglEvent event = {{PUGL_NOTHING, 0U}}; RECT rect = {0, 0, 0, 0}; POINT pt = {0, 0}; MINMAXINFO* mmi = NULL; @@ -779,8 +783,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) handleConfigure(view, &event); break; case WM_SIZING: - if (puglIsValidSize(view->sizeHints[PUGL_MIN_ASPECT]) && - puglIsValidSize(view->sizeHints[PUGL_MAX_ASPECT])) { + if (puglIsValidArea(view->sizeHints[PUGL_MIN_ASPECT]) && + puglIsValidArea(view->sizeHints[PUGL_MAX_ASPECT])) { constrainAspect(view, (RECT*)lParam, wParam); return TRUE; } @@ -795,7 +799,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_TIMER: if (wParam >= PUGL_USER_TIMER_MIN) { - PuglEvent ev = {{PUGL_TIMER, 0}}; + PuglEvent ev = {{PUGL_TIMER, 0U}}; ev.timer.id = wParam - PUGL_USER_TIMER_MIN; puglDispatchEvent(view, &ev); } @@ -812,7 +816,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) mmi = (MINMAXINFO*)lParam; mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height; - if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { + if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { mmi->ptMaxTrackSize.x = view->sizeHints[PUGL_MAX_SIZE].width; mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; } @@ -948,8 +952,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) PuglStatus puglGrabFocus(PuglView* view) { - SetFocus(view->impl->hwnd); - return PUGL_SUCCESS; + return puglWinStatus(!!SetFocus(view->impl->hwnd)); } bool @@ -1023,20 +1026,32 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags) const bool newMaximized = styleIsMaximized(flags); if (oldMaximized != newMaximized) { ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); - puglPostRedisplay(view); + puglObscureView(view); } return PUGL_SUCCESS; } +PuglStatus +puglApplySizeHint(PuglView* const PUGL_UNUSED(view), + const PuglSizeHint PUGL_UNUSED(hint)) +{ + return PUGL_SUCCESS; +} + +PuglStatus +puglUpdateSizeHints(PuglView* const PUGL_UNUSED(view)) +{ + return PUGL_SUCCESS; +} + PuglStatus puglStartTimer(PuglView* view, uintptr_t id, double timeout) { const UINT msec = (UINT)floor(timeout * 1000.0); - return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL) - ? PUGL_SUCCESS - : PUGL_UNKNOWN_ERROR); + SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL); + return PUGL_SUCCESS; } PuglStatus @@ -1048,32 +1063,24 @@ puglStopTimer(PuglView* view, uintptr_t id) PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event) { + if (!view->impl->hwnd || view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_FAILURE; + } + if (event->type == PUGL_CLOSE) { - PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0); - return PUGL_SUCCESS; + return puglWinStatus(PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0)); } if (event->type == PUGL_CLIENT) { - PostMessage(view->impl->hwnd, - PUGL_LOCAL_CLIENT_MSG, - (WPARAM)event->client.data1, - (LPARAM)event->client.data2); - - return PUGL_SUCCESS; + return puglWinStatus(PostMessage(view->impl->hwnd, + PUGL_LOCAL_CLIENT_MSG, + (WPARAM)event->client.data1, + (LPARAM)event->client.data2)); } return PUGL_UNSUPPORTED; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* PUGL_UNUSED(view)) -{ - WaitMessage(); - return PUGL_SUCCESS; -} -#endif - static PuglStatus puglDispatchViewEvents(PuglView* view) { @@ -1118,21 +1125,31 @@ puglDispatchWinEvents(PuglWorld* world) PuglStatus puglUpdate(PuglWorld* world, double timeout) { - const double startTime = puglGetTime(world); - PuglStatus st = PUGL_SUCCESS; + static const double minWaitSeconds = 0.002; + + const double startTime = puglGetTime(world); + const PuglWorldState startState = world->state; + PuglStatus st = PUGL_SUCCESS; + + if (startState == PUGL_WORLD_IDLE) { + world->state = PUGL_WORLD_UPDATING; + } else if (startState != PUGL_WORLD_RECURSING) { + return PUGL_BAD_CALL; + } if (timeout < 0.0) { - st = puglPollWinEvents(world, timeout); - st = st ? st : puglDispatchWinEvents(world); - } else if (timeout <= 0.001) { + WaitMessage(); + st = puglDispatchWinEvents(world); + } else if (timeout < minWaitSeconds) { st = puglDispatchWinEvents(world); } else { - const double endTime = startTime + timeout - 0.001; - for (double t = startTime; t < endTime; t = puglGetTime(world)) { - if ((st = puglPollWinEvents(world, endTime - t)) || - (st = puglDispatchWinEvents(world))) { - break; - } + const double endTime = startTime + timeout - minWaitSeconds; + double t = startTime; + while (!st && t < endTime) { + const DWORD timeoutMs = (DWORD)((endTime - t) * 1e3); + MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutMs, QS_ALLEVENTS); + st = puglDispatchWinEvents(world); + t = puglGetTime(world); } } @@ -1144,17 +1161,10 @@ puglUpdate(PuglWorld* world, double timeout) UpdateWindow(world->views[i]->impl->hwnd); } + world->state = startState; return st; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* view) -{ - return puglUpdate(view->world, 0.0); -} -#endif - LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1188,23 +1198,35 @@ puglGetTime(const PuglWorld* world) } PuglStatus -puglPostRedisplay(PuglView* view) +puglObscureView(PuglView* view) { - InvalidateRect(view->impl->hwnd, NULL, false); - return PUGL_SUCCESS; + return view->world->state == PUGL_WORLD_EXPOSING + ? PUGL_BAD_CALL + : puglWinStatus(InvalidateRect(view->impl->hwnd, NULL, false)); } PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) +puglObscureRegion(PuglView* const view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const RECT r = {(long)floor(rect.x), - (long)floor(rect.y), - (long)ceil(rect.x + rect.width), - (long)ceil(rect.y + rect.height)}; + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } + + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } - InvalidateRect(view->impl->hwnd, &r, false); + const int cx = MAX(0, x); + const int cy = MAX(0, y); + const unsigned cw = MIN(view->lastConfigure.width, width); + const unsigned ch = MIN(view->lastConfigure.height, height); - return PUGL_SUCCESS; + const RECT r = {cx, cy, cx + (long)cw, cy + (long)ch}; + return puglWinStatus(InvalidateRect(view->impl->hwnd, &r, false)); } PuglNativeView @@ -1218,19 +1240,18 @@ puglViewStringChanged(PuglView* const view, const PuglStringHint key, const char* const value) { + PuglStatus st = PUGL_SUCCESS; if (!view->impl->hwnd) { - return PUGL_SUCCESS; + return st; } if (key == PUGL_WINDOW_TITLE) { - wchar_t* const wtitle = puglUtf8ToWideChar(value); - if (wtitle) { - SetWindowTextW(view->impl->hwnd, wtitle); - free(wtitle); - } + ArgStringChar* const titleArg = puglArgStringNew(value); + st = puglWinStatus(SetWindowText(view->impl->hwnd, titleArg)); + puglArgStringFree(titleArg); } - return PUGL_SUCCESS; + return st; } static RECT @@ -1256,44 +1277,8 @@ puglGetScaleFactor(const PuglView* const view) } PuglStatus -puglSetFrame(PuglView* view, const PuglRect frame) -{ - if (!view->impl->hwnd) { - // Set defaults to be used when realized - view->defaultX = frame.x; - view->defaultY = frame.y; - view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; - return PUGL_SUCCESS; - } - - const RECT rect = - adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height); - - return puglWinStatus( - SetWindowPos(view->impl->hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)); -} - -PuglStatus -puglSetPosition(PuglView* const view, const int x, const int y) +puglSetWindowPosition(PuglView* const view, const int x, const int y) { - if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - - if (!view->impl->hwnd) { - // Set defaults to be used when realized - view->defaultX = x; - view->defaultY = y; - return PUGL_SUCCESS; - } - const RECT rect = adjustedWindowRect( view, x, y, view->lastConfigure.width, view->lastConfigure.height); @@ -1305,19 +1290,10 @@ puglSetPosition(PuglView* const view, const int x, const int y) } PuglStatus -puglSetSize(PuglView* const view, const unsigned width, const unsigned height) +puglSetWindowSize(PuglView* const view, + const unsigned width, + const unsigned height) { - if (width > INT16_MAX || height > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - - if (!view->impl->hwnd) { - // Set defaults to be used when realized - view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; - return PUGL_SUCCESS; - } - const RECT rect = adjustedWindowRect(view, view->lastConfigure.x, view->lastConfigure.y, @@ -1336,21 +1312,6 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) flags)); } -PuglStatus -puglSetSizeHint(PuglView* const view, - const PuglSizeHint hint, - const PuglSpan width, - const PuglSpan height) -{ - if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { - return PUGL_BAD_PARAMETER; - } - - view->sizeHints[hint].width = width; - view->sizeHints[hint].height = height; - return PUGL_SUCCESS; -} - PuglStatus puglSetTransientParent(PuglView* view, PuglNativeView parent) { @@ -1394,15 +1355,14 @@ puglAcceptOffer(PuglView* const view, const PuglDataEvent data = { PUGL_DATA, - 0, + 0U, GetMessageTime() / 1e3, 0, }; PuglEvent dataEvent; dataEvent.data = data; - puglDispatchEvent(view, &dataEvent); - return PUGL_SUCCESS; + return puglDispatchEvent(view, &dataEvent); } const void* @@ -1412,8 +1372,21 @@ puglGetClipboard(PuglView* const view, { PuglInternals* const impl = view->impl; - if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT) || - !OpenClipboard(impl->hwnd)) { + if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT)) { + return NULL; + } + + // Try to open the clipboard several times since others may have locked it + BOOL opened = FALSE; + static const unsigned max_tries = 16U; + for (unsigned i = 0U; !opened && i < max_tries; ++i) { + opened = OpenClipboard(impl->hwnd); + if (!opened) { + Sleep(0); + } + } + + if (!opened) { return NULL; } @@ -1424,15 +1397,14 @@ puglGetClipboard(PuglView* const view, return NULL; } - free(view->impl->clipboard.data); - view->impl->clipboard.data = - puglWideCharToUtf8(wstr, &view->impl->clipboard.len); + free(impl->clipboard.data); + impl->clipboard.data = puglWideCharToUtf8(wstr, &impl->clipboard.len); GlobalUnlock(mem); CloseClipboard(); - *len = view->impl->clipboard.len; - return view->impl->clipboard.data; + *len = impl->clipboard.len; + return impl->clipboard.data; } PuglStatus @@ -1443,7 +1415,7 @@ puglSetClipboard(PuglView* const view, { PuglInternals* const impl = view->impl; - PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len); + PuglStatus st = puglSetBlob(&impl->clipboard, data, len); if (st) { return st; } @@ -1488,14 +1460,13 @@ puglPaste(PuglView* const view) { const PuglDataOfferEvent offer = { PUGL_DATA_OFFER, - 0, + 0U, GetMessageTime() / 1e3, }; PuglEvent offerEvent; offerEvent.offer = offer; - puglDispatchEvent(view, &offerEvent); - return PUGL_SUCCESS; + return puglDispatchEvent(view, &offerEvent); } static const TCHAR* const cursor_ids[] = { @@ -1563,42 +1534,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) return pfd; } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } + RECT rect = {0, 0, 0, 0}; + GetWindowRect(view->transientParent ? (HWND)view->transientParent + : GetDesktopWindow(), + &rect); - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get a bounding rect from the "nearest" parent or parent-like window - const HWND hwnd = puglWinGetWindow(view); - RECT rect = {0, 0, 0, 0}; - GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect); - - // Center the frame around the center of the bounding rectangle - const LONG centerX = rect.left + (rect.right - rect.left) / 2; - const LONG centerY = rect.top + (rect.bottom - rect.top) / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(rect.left + ((rect.right - rect.left) / 2)), + (PuglCoord)(rect.top + ((rect.bottom - rect.top) / 2))}; + return center; } PuglStatus @@ -1613,28 +1560,35 @@ puglWinCreateWindow(PuglView* const view, PuglNativeView parent = view->parent ? view->parent : view->transientParent; // Calculate initial window rectangle - const unsigned winFlags = puglWinGetWindowFlags(view); - const unsigned winExFlags = puglWinGetWindowExFlags(view); - const PuglRect frame = getInitialFrame(view); - RECT wr = {(long)frame.x, - (long)frame.y, - (long)frame.x + frame.width, - (long)frame.y + frame.height}; + const unsigned winFlags = puglWinGetWindowFlags(view); + const unsigned winExFlags = puglWinGetWindowExFlags(view); + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); + RECT wr = {(long)pos.x, + (long)pos.y, + (long)pos.x + size.width, + (long)pos.y + size.height}; AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); + ArgStringChar* const classNameArg = puglArgStringNew(className); + ArgStringChar* const titleArg = puglArgStringNew(title); + // Create window and get drawing context - if (!(*hwnd = CreateWindowExA(winExFlags, - className, - title, - winFlags, - wr.left, - wr.right, - wr.right - wr.left, - wr.bottom - wr.top, - (HWND)parent, - NULL, - NULL, - NULL))) { + *hwnd = CreateWindowEx(winExFlags, + classNameArg, + titleArg, + winFlags, + wr.left, + wr.right, + wr.right - wr.left, + wr.bottom - wr.top, + (HWND)parent, + NULL, + NULL, + NULL); + puglArgStringFree(titleArg); + puglArgStringFree(classNameArg); + if (!*hwnd) { return PUGL_REALIZE_FAILED; } @@ -1660,7 +1614,6 @@ puglWinConfigure(PuglView* view) { PuglInternals* const impl = view->impl; PuglStatus st = PUGL_SUCCESS; - if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { return st; } @@ -1673,20 +1626,18 @@ puglWinConfigure(PuglView* view) DestroyWindow(impl->hwnd); impl->hwnd = NULL; impl->hdc = NULL; - return PUGL_SET_FORMAT_FAILED; + st = PUGL_SET_FORMAT_FAILED; } - return PUGL_SUCCESS; + return st; } PuglStatus puglWinEnter(PuglView* view, const PuglExposeEvent* expose) { - if (expose) { - BeginPaint(view->impl->hwnd, &view->impl->paint); - } - - return PUGL_SUCCESS; + return expose + ? puglWinStatus(!!BeginPaint(view->impl->hwnd, &view->impl->paint)) + : PUGL_SUCCESS; } PuglStatus diff --git a/source/modules/dgl/src/pugl-upstream/src/win.h b/source/modules/dgl/src/pugl-upstream/src/win.h index 84f1d4e2a..b37db3fb3 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win.h +++ b/source/modules/dgl/src/pugl-upstream/src/win.h @@ -6,7 +6,7 @@ #include "internal.h" -#include "pugl/pugl.h" +#include #include @@ -37,28 +37,19 @@ struct PuglInternalsImpl { bool fullscreen; }; -PUGL_API -PuglWinPFD +PUGL_API PuglWinPFD puglWinGetPixelFormatDescriptor(const PuglHints hints); -PUGL_WARN_UNUSED_RESULT -PUGL_API -PuglStatus +PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus puglWinCreateWindow(PuglView* view, const char* title, HWND* hwnd, HDC* hdc); -PUGL_WARN_UNUSED_RESULT -PUGL_API -PuglStatus +PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus puglWinConfigure(PuglView* view); -PUGL_WARN_UNUSED_RESULT -PUGL_API -PuglStatus +PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus puglWinEnter(PuglView* view, const PuglExposeEvent* expose); -PUGL_WARN_UNUSED_RESULT -PUGL_API -PuglStatus +PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus puglWinLeave(PuglView* view, const PuglExposeEvent* expose); #endif // PUGL_SRC_WIN_H diff --git a/source/modules/dgl/src/pugl-upstream/src/win_cairo.c b/source/modules/dgl/src/pugl-upstream/src/win_cairo.c index 0aab254f4..873d395c6 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win_cairo.c +++ b/source/modules/dgl/src/pugl-upstream/src/win_cairo.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/cairo.h" +#include #include #include diff --git a/source/modules/dgl/src/pugl-upstream/src/win_gl.c b/source/modules/dgl/src/pugl-upstream/src/win_gl.c index 01321d4e5..4f33e3685 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win_gl.c +++ b/source/modules/dgl/src/pugl-upstream/src/win_gl.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/gl.h" +#include #include @@ -14,30 +14,42 @@ #include #include -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 - -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 - -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +typedef enum { + WGL_DRAW_TO_WINDOW_ARB = 0x2001, + WGL_ACCELERATION_ARB = 0x2003, + WGL_SUPPORT_OPENGL_ARB = 0x2010, + WGL_DOUBLE_BUFFER_ARB = 0x2011, + WGL_PIXEL_TYPE_ARB = 0x2013, + WGL_RED_BITS_ARB = 0x2015, + WGL_GREEN_BITS_ARB = 0x2017, + WGL_BLUE_BITS_ARB = 0x2019, + WGL_ALPHA_BITS_ARB = 0x201B, + WGL_DEPTH_BITS_ARB = 0x2022, + WGL_STENCIL_BITS_ARB = 0x2023, + WGL_SAMPLE_BUFFERS_ARB = 0x2041, + WGL_SAMPLES_ARB = 0x2042, +} PuglWinGlHintName; + +typedef enum { + WGL_FULL_ACCELERATION_ARB = 0x2027, + WGL_TYPE_RGBA_ARB = 0x202B, +} PuglWinGlHintValue; + +typedef enum { + WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091, + WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092, + WGL_CONTEXT_FLAGS_ARB = 0x2094, + WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126, +} PuglWinGlContextAttribName; + +typedef enum { + WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002, +} PuglWinGlContextProfileBit; + +typedef enum { + WGL_CONTEXT_DEBUG_BIT_ARB = 0x00000001, +} PuglWinGlContextDebugBit; typedef HGLRC(WINAPI* WglCreateContextAttribs)(HDC, HGLRC, const int*); diff --git a/source/modules/dgl/src/pugl-upstream/src/win_stub.c b/source/modules/dgl/src/pugl-upstream/src/win_stub.c index e98357c52..d2dc3f39c 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win_stub.c +++ b/source/modules/dgl/src/pugl-upstream/src/win_stub.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/stub.h" +#include static PuglStatus puglWinStubConfigure(PuglView* view) diff --git a/source/modules/dgl/src/pugl-upstream/src/win_vulkan.c b/source/modules/dgl/src/pugl-upstream/src/win_vulkan.c index 2927447ad..bd3bf45c1 100644 --- a/source/modules/dgl/src/pugl-upstream/src/win_vulkan.c +++ b/source/modules/dgl/src/pugl-upstream/src/win_vulkan.c @@ -7,7 +7,7 @@ #include "types.h" #include "win.h" -#include "pugl/vulkan.h" +#include #include #include diff --git a/source/modules/dgl/src/pugl-upstream/src/x11.c b/source/modules/dgl/src/pugl-upstream/src/x11.c index 217838f30..84a6fa3c4 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11.c +++ b/source/modules/dgl/src/pugl-upstream/src/x11.c @@ -11,7 +11,7 @@ #include "platform.h" #include "types.h" -#include "pugl/pugl.h" +#include #include #include @@ -69,13 +69,11 @@ #include #ifdef __cplusplus -# define PUGL_INIT_STRUCT \ - {} -#else # define PUGL_INIT_STRUCT \ { \ - 0 \ } +#else +# define PUGL_INIT_STRUCT {0} #endif enum WmClientStateMessageAction { @@ -396,8 +394,15 @@ findView(PuglWorld* const world, const Window window) return NULL; } -static PuglStatus -updateSizeHints(const PuglView* const view) +PuglStatus +puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint)) +{ + // No fine-grained updates, hints are always recalculated together + return puglUpdateSizeHints(view); +} + +PuglStatus +puglUpdateSizeHints(PuglView* const view) { if (!view->impl->win) { return PUGL_SUCCESS; @@ -407,40 +412,44 @@ updateSizeHints(const PuglView* const view) XSizeHints sizeHints = PUGL_INIT_STRUCT; if (!view->hints[PUGL_RESIZABLE]) { - const PuglRect frame = puglGetFrame(view); + PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); + if (!puglIsValidSize(size.width, size.height)) { + size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE); + } + sizeHints.flags = PBaseSize | PMinSize | PMaxSize; - sizeHints.base_width = (int)frame.width; - sizeHints.base_height = (int)frame.height; - sizeHints.min_width = (int)frame.width; - sizeHints.min_height = (int)frame.height; - sizeHints.max_width = (int)frame.width; - sizeHints.max_height = (int)frame.height; + sizeHints.base_width = (int)size.width; + sizeHints.base_height = (int)size.height; + sizeHints.min_width = (int)size.width; + sizeHints.min_height = (int)size.height; + sizeHints.max_width = (int)size.width; + sizeHints.max_height = (int)size.height; } else { // Avoid setting PBaseSize for top level views to avoid window manager bugs - const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; - if (puglIsValidSize(defaultSize) && view->parent) { + const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (puglIsValidArea(defaultSize) && view->parent) { sizeHints.flags |= PBaseSize; sizeHints.base_width = defaultSize.width; sizeHints.base_height = defaultSize.height; } - const PuglViewSize minSize = view->sizeHints[PUGL_MIN_SIZE]; - if (puglIsValidSize(minSize)) { + const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE]; + if (puglIsValidArea(minSize)) { sizeHints.flags |= PMinSize; sizeHints.min_width = minSize.width; sizeHints.min_height = minSize.height; } - const PuglViewSize maxSize = view->sizeHints[PUGL_MAX_SIZE]; - if (puglIsValidSize(maxSize)) { + const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE]; + if (puglIsValidArea(maxSize)) { sizeHints.flags |= PMaxSize; sizeHints.max_width = maxSize.width; sizeHints.max_height = maxSize.height; } - const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; - const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; - if (puglIsValidSize(minAspect) && puglIsValidSize(maxAspect)) { + const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; + const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; + if (puglIsValidArea(minAspect) && puglIsValidArea(maxAspect)) { sizeHints.flags |= PAspect; sizeHints.min_aspect.x = minAspect.width; sizeHints.min_aspect.y = minAspect.height; @@ -448,8 +457,8 @@ updateSizeHints(const PuglView* const view) sizeHints.max_aspect.y = maxAspect.height; } - const PuglViewSize fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; - if (puglIsValidSize(fixedAspect)) { + const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; + if (puglIsValidArea(fixedAspect)) { sizeHints.flags |= PAspect; sizeHints.min_aspect.x = fixedAspect.width; sizeHints.min_aspect.y = fixedAspect.height; @@ -513,48 +522,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) board->data.len = 0; } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get the best "parentish" window to position the window in - Display* const display = view->world->impl->display; - const Window parent = - (view->parent ? (Window)view->parent - : view->transientParent ? (Window)view->transientParent - : RootWindow(display, view->impl->screen)); - - // Get the position/size of the parent as bounds for the new window - XWindowAttributes parentAttrs = PUGL_INIT_STRUCT; - XGetWindowAttributes(display, parent, &parentAttrs); + Display* const display = view->world->impl->display; + const int screen = view->impl->screen; + XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT; + XGetWindowAttributes(display, + view->transientParent ? (Window)view->transientParent + : RootWindow(display, screen), + &ancestorAttrs); - // Center the frame within the parent bounds - const int centerX = parentAttrs.x + parentAttrs.width / 2; - const int centerY = parentAttrs.y + parentAttrs.height / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(ancestorAttrs.x + (ancestorAttrs.width / 2)), + (PuglCoord)(ancestorAttrs.y + (ancestorAttrs.height / 2))}; + return center; } PuglStatus @@ -609,16 +591,17 @@ puglRealize(PuglView* const view) attr.event_mask |= StructureNotifyMask; attr.event_mask |= VisibilityChangeMask; - // Calculate the initial window rectangle - const PuglRect initialFrame = getInitialFrame(view); + // Calculate the initial window frame + const PuglArea initialSize = puglGetInitialSize(view); + const PuglPoint initialPos = puglGetInitialPosition(view, initialSize); // Create the window impl->win = XCreateWindow(display, parent, - initialFrame.x, - initialFrame.y, - initialFrame.width, - initialFrame.height, + initialPos.x, + initialPos.y, + initialSize.width, + initialSize.height, 0, impl->vi->depth, InputOutput, @@ -650,7 +633,7 @@ puglRealize(PuglView* const view) if (XRRQueryExtension(display, &ignored, &ignored)) { // Set refresh rate hint to the real refresh rate XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent); - short current_rate = XRRConfigCurrentRate(conf); + const short current_rate = XRRConfigCurrentRate(conf); view->hints[PUGL_REFRESH_RATE] = current_rate; XRRFreeScreenConfigInfo(conf); @@ -663,7 +646,7 @@ puglRealize(PuglView* const view) XSetClassHint(display, impl->win, &classHint); puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); puglSetTransientParent(view, view->transientParent); - updateSizeHints(view); + puglUpdateSizeHints(view); // Set PID and hostname so the window manager can access our process char hostname[256] = PUGL_INIT_STRUCT; @@ -759,21 +742,22 @@ puglUnrealize(PuglView* const view) PuglStatus puglShow(PuglView* const view, const PuglShowCommand command) { - PuglStatus st = view->impl->win ? PUGL_SUCCESS : puglRealize(view); + PuglInternals* impl = view->impl; + PuglStatus st = impl->win ? PUGL_SUCCESS : puglRealize(view); if (!st) { switch (command) { case PUGL_SHOW_PASSIVE: - XMapWindow(view->world->impl->display, view->impl->win); + XMapWindow(view->world->impl->display, impl->win); break; case PUGL_SHOW_RAISE: case PUGL_SHOW_FORCE_RAISE: - XMapRaised(view->world->impl->display, view->impl->win); + XMapRaised(view->world->impl->display, impl->win); break; } if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) { - st = puglPostRedisplay(view); + st = puglObscureView(view); } } @@ -783,6 +767,10 @@ puglShow(PuglView* const view, const PuglShowCommand command) PuglStatus puglHide(PuglView* const view) { + if (view->world->state == PUGL_WORLD_EXPOSING) { + return PUGL_BAD_CALL; + } + XUnmapWindow(view->world->impl->display, view->impl->win); return PUGL_SUCCESS; } @@ -817,13 +805,13 @@ keyInRange(const KeySym xSym, const PuglKey puglMin) { return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin)) - : (PuglKey)0; + : PUGL_KEY_NONE; } static PuglKey keySymToSpecial(const KeySym sym) { - PuglKey key = (PuglKey)0; + PuglKey key = PUGL_KEY_NONE; if ((key = keyInRange(sym, XK_F1, XK_F12, PUGL_KEY_F1)) || (key = keyInRange(sym, XK_Page_Up, XK_End, PUGL_KEY_PAGE_UP)) || (key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) || @@ -856,7 +844,7 @@ keySymToSpecial(const KeySym sym) } // clang-format on - return (PuglKey)0; + return PUGL_KEY_NONE; } static int @@ -897,7 +885,8 @@ translateKey(PuglView* const view, XEvent* const xevent, PuglEvent* const event) event->key.key = (PuglKey)puglDecodeUTF8((const uint8_t*)ustr); } - if (xevent->type == KeyPress && !filter && !special && view->impl->xic) { + if (xevent->type == KeyPress && !filter && (!special || ufound > 0) && + view->impl->xic) { // Lookup shifted key for possible text event xevent->xkey.state = state; @@ -1028,7 +1017,7 @@ translateClientMessage(PuglView* const view, XClientMessageEvent message) { Display* const display = view->world->impl->display; const PuglX11Atoms* const atoms = &view->world->impl->atoms; - PuglEvent event = {{PUGL_NOTHING, 0}}; + PuglEvent event = {{PUGL_NOTHING, 0U}}; if (message.message_type == atoms->WM_PROTOCOLS) { const Atom protocol = (Atom)message.data.l[0]; @@ -1101,17 +1090,19 @@ getCurrentConfiguration(PuglView* const view) XWindowAttributes attrs; XGetWindowAttributes(display, view->impl->win, &attrs); - // Get window position relative to the root window + // Get window position (relative to the root window if not a child) Window ignoredChild = 0; - int rootX = 0; - int rootY = 0; - XTranslateCoordinates( - display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild); + int x = attrs.x; + int y = attrs.y; + if (!view->parent) { + XTranslateCoordinates( + display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild); + } // Build a configure event based on the current window configuration - PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}}; - configureEvent.configure.x = (PuglCoord)rootX; - configureEvent.configure.y = (PuglCoord)rootY; + PuglEvent configureEvent = {{PUGL_CONFIGURE, 0U}}; + configureEvent.configure.x = (PuglCoord)x; + configureEvent.configure.y = (PuglCoord)y; configureEvent.configure.width = (PuglSpan)attrs.width; configureEvent.configure.height = (PuglSpan)attrs.height; configureEvent.configure.style = getCurrentViewStyleFlags(view); @@ -1140,7 +1131,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) { const PuglInternals* const impl = view->impl; const PuglX11Atoms* const atoms = &view->world->impl->atoms; - PuglEvent event = {{PUGL_NOTHING, 0}}; + PuglEvent event = {{PUGL_NOTHING, 0U}}; if (message.atom == atoms->NET_WM_STATE) { // Get all the current states set in the window hints @@ -1190,7 +1181,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) static PuglEvent translateEvent(PuglView* const view, XEvent xevent) { - PuglEvent event = {{PUGL_NOTHING, 0}}; + PuglEvent event = {{PUGL_NOTHING, 0U}}; event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0; switch (xevent.type) { @@ -1211,6 +1202,9 @@ translateEvent(PuglView* const view, XEvent xevent) view->impl->mapped = false; event = makeConfigureEvent(view); break; + case DestroyNotify: + view->impl->win = None; + break; case ConfigureNotify: event = makeConfigureEvent(view); event.configure.width = (PuglSpan)xevent.xconfigure.width; @@ -1503,7 +1497,7 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) PuglInternals* const impl = view->impl; Display* const display = view->world->impl->display; XEvent xev = PUGL_INIT_STRUCT; - if (!impl->win) { + if (!impl->win || view->world->state == PUGL_WORLD_EXPOSING) { return PUGL_FAILURE; } @@ -1534,21 +1528,11 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) return PUGL_UNSUPPORTED; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* const view) -{ - XEvent xevent; - XPeekEvent(view->world->impl->display, &xevent); - return PUGL_SUCCESS; -} -#endif - static void mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) { if (!dst->type) { - if (src->width > 0.0 && src->height > 0.0) { + if (src->width && src->height) { *dst = *src; } } else { @@ -1612,7 +1596,7 @@ handleSelectionNotify(const PuglWorld* const world, Display* const display = view->world->impl->display; const Atom selection = event->selection; PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection); - PuglEvent puglEvent = {{PUGL_NOTHING, 0}}; + PuglEvent puglEvent = {{PUGL_NOTHING, 0U}}; if (event->target == atoms->TARGETS) { // Notification of available datatypes @@ -1622,7 +1606,7 @@ handleSelectionNotify(const PuglWorld* const world, view, event->requestor, event->property, &numFormats, &formats) && !setClipboardFormats(view, board, numFormats, formats)) { const PuglDataOfferEvent offer = { - PUGL_DATA_OFFER, 0, (double)event->time / 1e3}; + PUGL_DATA_OFFER, 0U, (double)event->time / 1e3}; puglEvent.offer = offer; board->acceptedFormatIndex = UINT32_MAX; @@ -1699,21 +1683,24 @@ handleSelectionRequest(const PuglWorld* const world, } /// Flush pending configure and expose events for all views -PUGL_WARN_UNUSED_RESULT -static PuglStatus +PUGL_WARN_UNUSED_RESULT static PuglStatus flushExposures(PuglWorld* const world) { PuglStatus st0 = PUGL_SUCCESS; PuglStatus st1 = PUGL_SUCCESS; PuglStatus st2 = PUGL_SUCCESS; + // Send update events so the application can trigger redraws for (size_t i = 0; i < world->numViews; ++i) { - PuglView* const view = world->views[i]; - - // Send update event so the application can trigger redraws - if (puglGetVisible(view)) { - puglDispatchSimpleEvent(view, PUGL_UPDATE); + if (puglGetVisible(world->views[i])) { + puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE); } + } + + // Expose any dirty views + world->state = PUGL_WORLD_EXPOSING; + for (size_t i = 0; i < world->numViews; ++i) { + PuglView* const view = world->views[i]; // Copy and reset pending events (in case their handlers write new ones) const PuglEvent configure = view->impl->pendingConfigure; @@ -1753,7 +1740,7 @@ handleTimerEvent(PuglWorld* const world, const XEvent xevent) for (size_t i = 0; i < world->impl->numTimers; ++i) { if (world->impl->timers[i].alarm == notify->alarm) { - PuglEvent event = {{PUGL_TIMER, 0}}; + PuglEvent event = {{PUGL_TIMER, 0U}}; event.timer.id = world->impl->timers[i].id; puglDispatchEvent(world->impl->timers[i].view, &event); } @@ -1851,26 +1838,24 @@ dispatchX11Events(PuglWorld* const world) return st; } -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* const view) -{ - return puglUpdate(view->world, 0.0); -} -#endif - PuglStatus puglUpdate(PuglWorld* const world, const double timeout) { - const double startTime = puglGetTime(world); - PuglStatus st0 = PUGL_SUCCESS; - PuglStatus st1 = PUGL_SUCCESS; + const double startTime = puglGetTime(world); + const PuglWorldState startState = world->state; + PuglStatus st0 = PUGL_SUCCESS; + PuglStatus st1 = PUGL_SUCCESS; - world->impl->dispatchingEvents = true; + if (startState == PUGL_WORLD_IDLE) { + world->state = PUGL_WORLD_UPDATING; + } else if (startState != PUGL_WORLD_RECURSING) { + return PUGL_BAD_CALL; + } if (timeout < 0.0) { - st0 = pollX11Socket(world, timeout); - st0 = st0 ? st0 : dispatchX11Events(world); + if (!(st0 = pollX11Socket(world, timeout))) { + st0 = dispatchX11Events(world); + } } else if (timeout <= 0.001) { st0 = dispatchX11Events(world); } else { @@ -1885,10 +1870,8 @@ puglUpdate(PuglWorld* const world, const double timeout) } } - st1 = flushExposures(world); - - world->impl->dispatchingEvents = false; - + st1 = flushExposures(world); + world->state = startState; return st0 ? st0 : st1; } @@ -1896,38 +1879,52 @@ double puglGetTime(const PuglWorld* const world) { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + return 0.0; + } + return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - world->startTime; } PuglStatus -puglPostRedisplay(PuglView* const view) +puglObscureView(PuglView* const view) { - PuglRect rect = puglGetFrame(view); - rect.x = 0; - rect.y = 0; - - return puglPostRedisplayRect(view, rect); + return puglObscureRegion( + view, 0, 0, view->lastConfigure.width, view->lastConfigure.height); } PuglStatus -puglPostRedisplayRect(PuglView* const view, const PuglRect rect) +puglObscureRegion(PuglView* const view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const PuglExposeEvent event = { - PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height}; + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + const PuglCoord cx = MAX((PuglCoord)0, (PuglCoord)x); + const PuglCoord cy = MAX((PuglCoord)0, (PuglCoord)y); + const PuglSpan cw = MIN(view->lastConfigure.width, (PuglSpan)width); + const PuglSpan ch = MIN(view->lastConfigure.height, (PuglSpan)height); + + const PuglExposeEvent event = {PUGL_EXPOSE, 0U, cx, cy, cw, ch}; - if (view->world->impl->dispatchingEvents) { + PuglStatus st = PUGL_SUCCESS; + if (view->world->state == PUGL_WORLD_UPDATING) { // Currently dispatching events, add/expand expose for the loop end mergeExposeEvents(&view->impl->pendingExpose.expose, &event); + } else if (view->world->state == PUGL_WORLD_EXPOSING) { + st = PUGL_BAD_CALL; } else if (view->impl->win) { // Not dispatching events, send an X expose so we wake up next time - PuglEvent exposeEvent = {{PUGL_EXPOSE, 0}}; + PuglEvent exposeEvent = {{PUGL_EXPOSE, 0U}}; exposeEvent.expose = event; - return puglSendEvent(view, &exposeEvent); + st = puglSendEvent(view, &exposeEvent); } - - return PUGL_SUCCESS; + return st; } PuglNativeView @@ -1977,79 +1974,21 @@ puglGetScaleFactor(const PuglView* const view) } PuglStatus -puglSetFrame(PuglView* const view, const PuglRect frame) -{ - if (!view->impl->win) { - // Set defaults to be used when realized - view->defaultX = frame.x; - view->defaultY = frame.y; - view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; - return PUGL_SUCCESS; - } - - return puglX11Status(XMoveResizeWindow(view->world->impl->display, - view->impl->win, - frame.x, - frame.y, - frame.width, - frame.height)); -} - -PuglStatus -puglSetPosition(PuglView* const view, const int x, const int y) +puglSetWindowPosition(PuglView* const view, const int x, const int y) { - Display* const display = view->world->impl->display; - - if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - - if (!view->impl->win) { - // Set defaults to be used when realized - view->defaultX = x; - view->defaultY = y; - return PUGL_SUCCESS; - } - - return puglX11Status(XMoveWindow(display, + return puglX11Status(XMoveWindow(view->world->impl->display, view->impl->win, (int)(x - view->impl->frameExtentLeft), (int)(y - view->impl->frameExtentTop))); } PuglStatus -puglSetSize(PuglView* const view, const unsigned width, const unsigned height) -{ - Display* const display = view->world->impl->display; - - if (width > INT16_MAX || height > INT16_MAX) { - return PUGL_BAD_PARAMETER; - } - - if (!view->impl->win) { - // Set defaults to be used when realized - view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; - view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; - return PUGL_SUCCESS; - } - - return puglX11Status(XResizeWindow(display, view->impl->win, width, height)); -} - -PuglStatus -puglSetSizeHint(PuglView* const view, - const PuglSizeHint hint, - const PuglSpan width, - const PuglSpan height) +puglSetWindowSize(PuglView* const view, + const unsigned width, + const unsigned height) { - if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { - return PUGL_BAD_PARAMETER; - } - - view->sizeHints[hint].width = width; - view->sizeHints[hint].height = height; - return updateSizeHints(view); + return puglX11Status( + XResizeWindow(view->world->impl->display, view->impl->win, width, height)); } PuglStatus diff --git a/source/modules/dgl/src/pugl-upstream/src/x11.h b/source/modules/dgl/src/pugl-upstream/src/x11.h index bc9363054..a52ea2811 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11.h +++ b/source/modules/dgl/src/pugl-upstream/src/x11.h @@ -7,8 +7,8 @@ #include "attributes.h" #include "types.h" -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -75,7 +75,6 @@ struct PuglWorldInternalsImpl { XID serverTimeCounter; int syncEventBase; bool syncSupported; - bool dispatchingEvents; }; struct PuglInternalsImpl { @@ -93,9 +92,7 @@ struct PuglInternalsImpl { bool mapped; }; -PUGL_WARN_UNUSED_RESULT -PUGL_API -PuglStatus +PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus puglX11Configure(PuglView* view); #endif // PUGL_SRC_X11_H diff --git a/source/modules/dgl/src/pugl-upstream/src/x11_cairo.c b/source/modules/dgl/src/pugl-upstream/src/x11_cairo.c index d6fbaec95..bc7b13399 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11_cairo.c +++ b/source/modules/dgl/src/pugl-upstream/src/x11_cairo.c @@ -5,8 +5,8 @@ #include "types.h" #include "x11.h" -#include "pugl/cairo.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -19,10 +19,10 @@ typedef struct { cairo_t* cr; } PuglX11CairoSurface; -static PuglViewSize +static PuglArea puglX11CairoGetViewSize(const PuglView* const view) { - PuglViewSize size = {0U, 0U}; + PuglArea size = {0U, 0U}; if (view->lastConfigure.type == PUGL_CONFIGURE) { // Use the size of the last configured frame @@ -97,11 +97,11 @@ puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose) PuglStatus st = PUGL_SUCCESS; if (expose) { - const PuglViewSize viewSize = puglX11CairoGetViewSize(view); - const PuglSpan right = (PuglSpan)(expose->x + expose->width); - const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); - const PuglSpan surfaceWidth = MAX(right, viewSize.width); - const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); + const PuglArea viewSize = puglX11CairoGetViewSize(view); + const PuglSpan right = (PuglSpan)(expose->x + expose->width); + const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); + const PuglSpan surfaceWidth = MAX(right, viewSize.width); + const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); if (!(st = puglX11CairoOpen(view, surfaceWidth, surfaceHeight))) { surface->cr = cairo_create(surface->front); if (cairo_status(surface->cr)) { diff --git a/source/modules/dgl/src/pugl-upstream/src/x11_gl.c b/source/modules/dgl/src/pugl-upstream/src/x11_gl.c index de22b345d..527335bcd 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11_gl.c +++ b/source/modules/dgl/src/pugl-upstream/src/x11_gl.c @@ -6,8 +6,8 @@ #include "types.h" #include "x11.h" -#include "pugl/gl.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -101,8 +101,7 @@ puglX11GlConfigure(PuglView* view) return PUGL_SUCCESS; } -PUGL_WARN_UNUSED_RESULT -static PuglStatus +PUGL_WARN_UNUSED_RESULT static PuglStatus puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) { PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; @@ -115,8 +114,7 @@ puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) : PUGL_FAILURE; } -PUGL_WARN_UNUSED_RESULT -static PuglStatus +PUGL_WARN_UNUSED_RESULT static PuglStatus puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) { Display* const display = view->world->impl->display; diff --git a/source/modules/dgl/src/pugl-upstream/src/x11_stub.c b/source/modules/dgl/src/pugl-upstream/src/x11_stub.c index 844b4dbd6..11143fecf 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11_stub.c +++ b/source/modules/dgl/src/pugl-upstream/src/x11_stub.c @@ -1,13 +1,13 @@ // Copyright 2012-2021 David Robillard // SPDX-License-Identifier: ISC -#include "pugl/stub.h" +#include #include "stub.h" #include "types.h" #include "x11.h" -#include "pugl/pugl.h" +#include const PuglBackend* puglStubBackend(void) diff --git a/source/modules/dgl/src/pugl-upstream/src/x11_vulkan.c b/source/modules/dgl/src/pugl-upstream/src/x11_vulkan.c index 834ac3723..078e524e3 100644 --- a/source/modules/dgl/src/pugl-upstream/src/x11_vulkan.c +++ b/source/modules/dgl/src/pugl-upstream/src/x11_vulkan.c @@ -8,8 +8,8 @@ #include "types.h" #include "x11.h" -#include "pugl/pugl.h" -#include "pugl/vulkan.h" +#include +#include #include #include diff --git a/source/modules/dgl/src/pugl.cpp b/source/modules/dgl/src/pugl.cpp index a2585c733..f618e0711 100644 --- a/source/modules/dgl/src/pugl.cpp +++ b/source/modules/dgl/src/pugl.cpp @@ -250,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) if (view->backend != nullptr) { - #ifdef DGL_OPENGL #if defined(DGL_USE_GLES2) puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); + #elif defined(DGL_USE_GLES3) + puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); + puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); + puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); #elif defined(DGL_USE_OPENGL3) puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); - #else + #elif defined(DGL_OPENGL) puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE); puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); #endif - #endif } else { @@ -277,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) void puglRaiseWindow(PuglView* const view) { -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) - if (NSWindow* const window = view->impl->window ? view->impl->window - : [view->impl->wrapperView window]) - [window orderFrontRegardless]; -#elif defined(DISTRHO_OS_WASM) + // this does the same as puglShow(view, PUGL_SHOW_FORCE_RAISE) + puglShow(view, PUGL_SHOW_RAISE) + #if defined(DISTRHO_OS_HAIKU) + #elif defined(DISTRHO_OS_MAC) + NSWindow* const window = [view->impl->wrapperView window]; + [window orderFrontRegardless]; + [window orderFront:view->impl->wrapperView]; + #elif defined(DISTRHO_OS_WASM) // nothing -#elif defined(DISTRHO_OS_WINDOWS) + #elif defined(DISTRHO_OS_WINDOWS) SetForegroundWindow(view->impl->hwnd); SetActiveWindow(view->impl->hwnd); -#elif defined(HAVE_X11) + #elif defined(HAVE_X11) XRaiseWindow(view->world->impl->display, view->impl->win); -#endif + #endif } // -------------------------------------------------------------------------------------------------------------------- @@ -306,29 +309,31 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast(height); } -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) + #if defined(DISTRHO_OS_HAIKU) + #elif defined(DISTRHO_OS_MAC) if (view->impl->window) { - if (const PuglStatus status = updateSizeHint(view, PUGL_MIN_SIZE)) - return status; - - if (const PuglStatus status = updateSizeHint(view, PUGL_FIXED_ASPECT)) + if (const PuglStatus status = puglUpdateSizeHints(view)) return status; } -#elif defined(DISTRHO_OS_WASM) + #elif defined(DISTRHO_OS_WASM) + const char* const className = view->world->strings[PUGL_CLASS_NAME]; + EM_ASM({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px'); + canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px'); + }, className, width, height); + #elif defined(DISTRHO_OS_WINDOWS) // nothing -#elif defined(DISTRHO_OS_WINDOWS) - // nothing -#elif defined(HAVE_X11) + #elif defined(HAVE_X11) if (view->impl->win) { - if (const PuglStatus status = updateSizeHints(view)) + if (const PuglStatus status = puglUpdateSizeHints(view)) return status; XFlush(view->world->impl->display); } -#endif + #endif return PUGL_SUCCESS; } @@ -340,99 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable) { puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) + #if defined(DISTRHO_OS_HAIKU) + #elif defined(DISTRHO_OS_MAC) if (PuglWindow* const window = view->impl->window) { const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) - | (resizable ? NSResizableWindowMask : 0x0); + | (resizable ? NSResizableWindowMask : 0); [window setStyleMask:style]; } // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? -#elif defined(DISTRHO_OS_WASM) - // nothing -#elif defined(DISTRHO_OS_WINDOWS) + #elif defined(DISTRHO_OS_WASM) + puglUpdateSizeHints(view); + #elif defined(DISTRHO_OS_WINDOWS) if (const HWND hwnd = view->impl->hwnd) { const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); SetWindowLong(hwnd, GWL_STYLE, winFlags); } -#elif defined(HAVE_X11) - updateSizeHints(view); -#endif + #elif defined(HAVE_X11) + puglUpdateSizeHints(view); + #endif } // -------------------------------------------------------------------------------------------------------------------- // set window size while also changing default -PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) +PuglStatus puglSetSizeAndDefault(PuglView* const view, const uint width, const uint height) { - if (width > INT16_MAX || height > INT16_MAX) - return PUGL_BAD_PARAMETER; - -#ifdef DGL_USING_X11 - // workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118 - // NOTE troublesome if used under KDE - if (view->impl->win && !view->parent && !view->transientParent && std::getenv("KDE_SESSION_VERSION") == nullptr) - { - view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0; - } - else -#endif // set default size first - { - view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast(width); - view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast(height); - } + view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_CURRENT_SIZE].width = width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = view->sizeHints[PUGL_CURRENT_SIZE].height = height; -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) + #if defined(DISTRHO_OS_HAIKU) + #elif defined(DISTRHO_OS_MAC) // matches upstream pugl if (view->impl->wrapperView) { - if (const PuglStatus status = puglSetSize(view, width, height)) - return status; - // nothing to do for PUGL_DEFAULT_SIZE hint + + if (const PuglStatus status = puglSetWindowSize(view, width, height)) + return status; } -#elif defined(DISTRHO_OS_WASM) - d_stdout("className is %s", view->world->strings[PUGL_CLASS_NAME]); + #elif defined(DISTRHO_OS_WASM) + if (const PuglStatus status = puglUpdateSizeHints(view)) + return status; + emscripten_set_canvas_element_size(view->world->strings[PUGL_CLASS_NAME], width, height); -#elif defined(DISTRHO_OS_WINDOWS) + #elif defined(DISTRHO_OS_WINDOWS) // matches upstream pugl, except we re-enter context after resize if (view->impl->hwnd) { - if (const PuglStatus status = puglSetSize(view, width, height)) - return status; - // nothing to do for PUGL_DEFAULT_SIZE hint + if (const PuglStatus status = puglSetWindowSize(view, width, height)) + return status; + // make sure to return context back to ourselves puglBackendEnter(view); } -#elif defined(HAVE_X11) + #elif defined(HAVE_X11) // matches upstream pugl, adds flush at the end if (view->impl->win) { - if (const PuglStatus status = puglSetSize(view, width, height)) + if (const PuglStatus status = puglUpdateSizeHints(view)) return status; - // updateSizeHints will use last known size, which is not yet updated - const PuglSpan lastWidth = view->lastConfigure.width; - const PuglSpan lastHeight = view->lastConfigure.height; - view->lastConfigure.width = static_cast(width); - view->lastConfigure.height = static_cast(height); - - updateSizeHints(view); - - view->lastConfigure.width = lastWidth; - view->lastConfigure.height = lastHeight; + if (const PuglStatus status = puglSetWindowSize(view, width, height)) + return status; // flush size changes XFlush(view->world->impl->display); } -#endif + #endif return PUGL_SUCCESS; } @@ -456,11 +441,12 @@ void puglOnDisplayPrepare(PuglView*) void puglFallbackOnResize(PuglView* const view, const uint width, const uint height) { #ifdef DGL_OPENGL + #if defined(DGL_USE_OPENGL3) + glViewport(0, 0, static_cast(width), static_cast(height)); + #else glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - #ifdef DGL_USE_OPENGL3 glViewport(0, 0, static_cast(width), static_cast(height)); - #else glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, static_cast(width), static_cast(height), 0.0, 0.0, 1.0); @@ -469,9 +455,10 @@ void puglFallbackOnResize(PuglView* const view, const uint width, const uint hei glLoadIdentity(); #endif #else - return; // unused (void)view; + (void)width; + (void)height; #endif } @@ -618,55 +605,64 @@ void puglWin32ShowCentered(PuglView* const view) PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) { - const bool wasDispatchingEvents = world->impl->dispatchingEvents; - world->impl->dispatchingEvents = true; + const PuglWorldState startState = world->state; + world->state = PUGL_WORLD_UPDATING; PuglStatus st = PUGL_SUCCESS; const double startTime = puglGetTime(world); - const double endTime = startTime + 0.03; + const double endTime = startTime + 0.03; for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) { - pollX11Socket(world, endTime - t); - st = dispatchX11Events(world); + if (!(st = pollX11Socket(world, endTime - t))) + st = dispatchX11Events(world); } - world->impl->dispatchingEvents = wasDispatchingEvents; + world->state = startState; return st; } // -------------------------------------------------------------------------------------------------------------------- -// X11 specific, set dialog window type and pid hints +// X11 specific, set dialog window type -void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) +void puglX11SetWindowType(const PuglView* const view, const bool isStandalone) { const PuglInternals* const impl = view->impl; Display* const display = view->world->impl->display; - const pid_t pid = getpid(); - const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False); - XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); - #if defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE) if (isStandalone) { - const Atom _nwi = XInternAtom(display, "_NET_WM_ICON", False); - XChangeProperty(display, impl->win, _nwi, XA_CARDINAL, 32, PropModeReplace, - (const uchar*)DGL_X11_WINDOW_ICON_NAME, DGL_X11_WINDOW_ICON_SIZE); + const Atom NET_WM_ICON = XInternAtom(display, "_NET_WM_ICON", False); + XChangeProperty(display, + impl->win, + NET_WM_ICON, + XA_CARDINAL, + 32, + PropModeReplace, + reinterpret_cast(DGL_X11_WINDOW_ICON_NAME), + DGL_X11_WINDOW_ICON_SIZE); } #endif - const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + const Atom NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); - Atom _wts[2]; - int numAtoms = 0; + Atom windowTypes[2]; + int numWindowTypes = 0; if (! isStandalone) - _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - - _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); - - XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); + windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + + windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); + + XChangeProperty(display, + impl->win, + NET_WM_WINDOW_TYPE, + XA_ATOM, + 32, + PropModeReplace, + reinterpret_cast(&windowTypes), + numWindowTypes); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/dgl/src/pugl.hpp b/source/modules/dgl/src/pugl.hpp index 76a266f9c..125707af9 100644 --- a/source/modules/dgl/src/pugl.hpp +++ b/source/modules/dgl/src/pugl.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2023 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -121,8 +121,8 @@ void puglWin32ShowCentered(PuglView* view); // X11 specific, update world without triggering exposure events PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); -// X11 specific, set dialog window type and pid hints -void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); +// X11 specific, set dialog window type +void puglX11SetWindowType(const PuglView* view, bool isStandalone); #endif diff --git a/source/modules/distrho/DistrhoPluginMain.cpp b/source/modules/distrho/DistrhoPluginMain.cpp index 254da24e5..d5650b088 100644 --- a/source/modules/distrho/DistrhoPluginMain.cpp +++ b/source/modules/distrho/DistrhoPluginMain.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -29,15 +29,14 @@ #elif defined(DISTRHO_PLUGIN_TARGET_LV2) # include "src/DistrhoPluginLV2.cpp" # include "src/DistrhoPluginLV2export.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_MAPI) +# include "src/DistrhoPluginMAPI.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST2) # include "src/DistrhoPluginVST2.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST3) # include "src/DistrhoPluginVST3.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_EXPORT) # include "src/DistrhoPluginExport.cpp" -#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) -DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); -DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } #elif defined(DISTRHO_PLUGIN_TARGET_STATIC) START_NAMESPACE_DISTRHO Plugin* createStaticPlugin() { return createPlugin(); } diff --git a/source/modules/distrho/DistrhoUI.hpp b/source/modules/distrho/DistrhoUI.hpp index ca4125786..77418403e 100644 --- a/source/modules/distrho/DistrhoUI.hpp +++ b/source/modules/distrho/DistrhoUI.hpp @@ -106,10 +106,11 @@ public: The following example code can be use to extract individual colors: ``` - const int red = (bgColor >> 24) & 0xff; - const int green = (bgColor >> 16) & 0xff; - const int blue = (bgColor >> 8) & 0xff; + int red = (bgColor >> 24) & 0xff; + int green = (bgColor >> 16) & 0xff; + int blue = (bgColor >> 8) & 0xff; ``` + @see Color::fromRGB */ uint getBackgroundColor() const noexcept; @@ -119,10 +120,11 @@ public: The following example code can be use to extract individual colors: ``` - const int red = (fgColor >> 24) & 0xff; - const int green = (fgColor >> 16) & 0xff; - const int blue = (fgColor >> 8) & 0xff; + int red = (fgColor >> 24) & 0xff; + int green = (fgColor >> 16) & 0xff; + int blue = (fgColor >> 8) & 0xff; ``` + @see Color::fromRGB */ uint getForegroundColor() const noexcept; diff --git a/source/modules/distrho/extra/ScopedPointer.hpp b/source/modules/distrho/extra/ScopedPointer.hpp index 6349199ca..5e04fb131 100644 --- a/source/modules/distrho/extra/ScopedPointer.hpp +++ b/source/modules/distrho/extra/ScopedPointer.hpp @@ -172,6 +172,11 @@ public: /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ ObjectType* operator->() const noexcept { return object; } + //============================================================================== + /** Removes the current object from this ScopedPointer and deletes it. + */ + void reset() noexcept { ObjectType* const o = object; object = nullptr; delete o; } + //============================================================================== /** Removes the current object from this ScopedPointer without deleting it. This will return the current object, and set the ScopedPointer to a null pointer. diff --git a/source/modules/distrho/extra/String.hpp b/source/modules/distrho/extra/String.hpp index b7fda9273..015ff49e0 100644 --- a/source/modules/distrho/extra/String.hpp +++ b/source/modules/distrho/extra/String.hpp @@ -269,7 +269,7 @@ public: /* * Get length of the string. */ - std::size_t length() const noexcept + size_t length() const noexcept { return fBufferLen; } @@ -295,7 +295,7 @@ public: */ bool contains(const char c) const noexcept { - for (std::size_t i=0; i(ret); + return static_cast(ret); } if (found != nullptr) @@ -460,7 +460,7 @@ public: * Find the last occurrence of character 'c' in the string. * Returns "length()" if the character is not found. */ - std::size_t rfind(const char c, bool* const found = nullptr) const noexcept + size_t rfind(const char c, bool* const found = nullptr) const noexcept { if (fBufferLen == 0 || c == '\0') { @@ -469,7 +469,7 @@ public: return fBufferLen; } - for (std::size_t i=fBufferLen; i > 0; --i) + for (size_t i=fBufferLen; i > 0; --i) { if (fBuffer[i-1] == c) { @@ -488,7 +488,7 @@ public: * Find the last occurrence of string 'strBuf' in the string. * Returns "length()" if the string is not found. */ - std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept + size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept { if (found != nullptr) *found = false; @@ -496,12 +496,12 @@ public: if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') return fBufferLen; - const std::size_t strBufLen(std::strlen(strBuf)); + const size_t strBufLen(std::strlen(strBuf)); - std::size_t ret = fBufferLen; + size_t ret = fBufferLen; const char* tmpBuf = fBuffer; - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) { @@ -532,7 +532,7 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] == before) fBuffer[i] = after; @@ -551,7 +551,7 @@ public: if (fBufferLen == 0) return *this; - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] == c) { @@ -567,7 +567,7 @@ public: /* * Truncate the string to size 'n'. */ - String& truncate(const std::size_t n) noexcept + String& truncate(const size_t n) noexcept { if (n >= fBufferLen) return *this; @@ -583,7 +583,7 @@ public: */ String& toBasic() noexcept { - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] >= '0' && fBuffer[i] <= '9') continue; @@ -607,7 +607,7 @@ public: { static constexpr const char kCharDiff = 'a' - 'A'; - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') fBuffer[i] = static_cast(fBuffer[i] + kCharDiff); @@ -623,7 +623,7 @@ public: { static constexpr const char kCharDiff = 'a' - 'A'; - for (std::size_t i=0; i < fBufferLen; ++i) + for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') fBuffer[i] = static_cast(fBuffer[i] - kCharDiff); @@ -688,31 +688,28 @@ public: // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html // Copyright (C) 2004-2008 René Nyffenegger - static String asBase64(const void* const data, const std::size_t dataSize) + static String asBase64(const void* const data, const size_t dataSize) { static constexpr const char* const kBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; - #ifndef _MSC_VER - const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast(dataSize/3)), 65536U); - #else - constexpr std::size_t kTmpBufSize = 65536U; - #endif + const size_t strBufSize = std::min(d_nextPowerOf2(static_cast(dataSize/3)), 65536U); + char* strBuf = static_cast(std::malloc(strBufSize)); + DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, String()); - const uchar* bytesToEncode((const uchar*)data); + strBuf[strBufSize] = '\0'; + size_t strBufIndex = 0; + + const uchar* bytesToEncode = static_cast(data); uint i=0, j=0; uint charArray3[3], charArray4[4]; - char strBuf[kTmpBufSize + 1]; - strBuf[kTmpBufSize] = '\0'; - std::size_t strBufIndex = 0; - String ret; - for (std::size_t s=0; s> 6); charArray4[3] = charArray3[2] & 0x3f; - for (i=0; i<4; ++i) + for (i = 0; i < 4; ++i) strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; - if (strBufIndex >= kTmpBufSize-7) + if (strBufIndex >= strBufSize - 7) { strBuf[strBufIndex] = '\0'; strBufIndex = 0; @@ -739,7 +736,7 @@ public: if (i != 0) { - for (j=i; j<3; ++j) + for (j = i; j < 3; ++j) charArray3[j] = '\0'; charArray4[0] = (charArray3[0] & 0xfc) >> 2; @@ -747,7 +744,7 @@ public: charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); charArray4[3] = charArray3[2] & 0x3f; - for (j=0; j<4 && i<3 && j(std::malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); @@ -1080,7 +1075,7 @@ public: private: char* fBuffer; // the actual string buffer - std::size_t fBufferLen; // string length + size_t fBufferLen; // string length bool fBufferAlloc; // wherever the buffer is allocated, not using _null() /* @@ -1101,7 +1096,7 @@ private: * - Allocates string only if 'strBuf' is not null and new string contents are different * - If 'strBuf' is null, 'size' must be 0 */ - void _dup(const char* const strBuf, const std::size_t size = 0) noexcept + void _dup(const char* const strBuf, const size_t size = 0) noexcept { if (strBuf != nullptr) { @@ -1158,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep if (strBefore.isEmpty()) return String(strBufAfter); - const std::size_t strBeforeLen = strBefore.length(); - const std::size_t strBufAfterLen = std::strlen(strBufAfter); - const std::size_t newBufSize = strBeforeLen + strBufAfterLen; + const size_t strBeforeLen = strBefore.length(); + const size_t strBufAfterLen = std::strlen(strBufAfter); + const size_t newBufSize = strBeforeLen + strBufAfterLen; char* const newBuf = static_cast(malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); @@ -1178,9 +1173,9 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep if (strBufBefore == nullptr || strBufBefore[0] == '\0') return strAfter; - const std::size_t strBufBeforeLen = std::strlen(strBufBefore); - const std::size_t strAfterLen = strAfter.length(); - const std::size_t newBufSize = strBufBeforeLen + strAfterLen; + const size_t strBufBeforeLen = std::strlen(strBufBefore); + const size_t strAfterLen = strAfter.length(); + const size_t newBufSize = strBufBeforeLen + strAfterLen; char* const newBuf = static_cast(malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); diff --git a/source/modules/distrho/src/DistrhoUIInternal.hpp b/source/modules/distrho/src/DistrhoUIInternal.hpp index ea3f56a2e..f7b578e6e 100644 --- a/source/modules/distrho/src/DistrhoUIInternal.hpp +++ b/source/modules/distrho/src/DistrhoUIInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 @@ -21,18 +21,18 @@ START_NAMESPACE_DISTRHO -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // UI exporter class class UIExporter { - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- // UI Widget and its private data UI* ui; UI::PrivateData* uiData; - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- public: UIExporter(void* const callbacksPtr, @@ -47,11 +47,12 @@ public: const char* const bundlePath = nullptr, void* const dspPtr = nullptr, const double scaleFactor = 0.0, + const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto, const uint32_t bgColor = 0, const uint32_t fgColor = 0xffffffff, const char* const appClassName = nullptr) : ui(nullptr), - uiData(new UI::PrivateData(appClassName)) + uiData(new UI::PrivateData(appClassName, appType)) { uiData->sampleRate = sampleRate; uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; diff --git a/source/modules/distrho/src/DistrhoUIPrivateData.hpp b/source/modules/distrho/src/DistrhoUIPrivateData.hpp index 416f08420..108e76b6c 100644 --- a/source/modules/distrho/src/DistrhoUIPrivateData.hpp +++ b/source/modules/distrho/src/DistrhoUIPrivateData.hpp @@ -67,14 +67,14 @@ START_NAMESPACE_DISTRHO int dpf_webview_start(int argc, char* argv[]); #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Plugin Application, will set class name based on plugin details class PluginApplication : public DGL_NAMESPACE::Application { public: - explicit PluginApplication(const char* className) - : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) + explicit PluginApplication(const char* className, const Application::Type type) + : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE, type) { #if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__) if (className == nullptr) @@ -108,7 +108,7 @@ public: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Plugin Window, will pass some Window events to UI class PluginWindow : public DGL_NAMESPACE::Window @@ -138,7 +138,10 @@ public: // this is called just before creating UI, ensuring proper context to it if (pData->initPost()) + { puglBackendEnter(pData->view); + pData->createContextIfNeeded(); + } } ~PluginWindow() override @@ -240,7 +243,7 @@ protected: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // UI callbacks typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); @@ -250,7 +253,7 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8 typedef void (*setSizeFunc) (void* ptr, uint width, uint height); typedef bool (*fileRequestFunc) (void* ptr, const char* key); -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // UI private data struct UI::PrivateData { @@ -289,8 +292,8 @@ struct UI::PrivateData { setSizeFunc setSizeCallbackFunc; fileRequestFunc fileRequestCallbackFunc; - PrivateData(const char* const appClassName) noexcept - : app(appClassName), + PrivateData(const char* const appClassName, const DGL_NAMESPACE::Application::Type appType) noexcept + : app(appClassName, appType), window(nullptr), #if DISTRHO_UI_USE_WEB_VIEW webview(nullptr), @@ -389,7 +392,7 @@ struct UI::PrivateData { #endif }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // UI private data fileRequestCallback, which requires PluginWindow definitions inline bool UI::PrivateData::fileRequestCallback(const char* const key) @@ -416,7 +419,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) return false; } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // PluginWindow onFileSelected that require UI::PrivateData definitions #if DISTRHO_UI_FILE_BROWSER @@ -454,7 +457,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) } #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/source/modules/distrho/src/DistrhoUtils.cpp b/source/modules/distrho/src/DistrhoUtils.cpp index ee0f5efa8..f029caccd 100644 --- a/source/modules/distrho/src/DistrhoUtils.cpp +++ b/source/modules/distrho/src/DistrhoUtils.cpp @@ -119,8 +119,10 @@ const char* getPluginFormatName() noexcept static inline void _createDirIfNeeded(const char* const dir) { - if (access(dir, F_OK) != 0) - mkdir(dir, 0755); + try { + if (access(dir, F_OK) != 0) + mkdir(dir, 0755); + } DISTRHO_SAFE_EXCEPTION("createDirIfNeeded"); } #endif @@ -255,7 +257,10 @@ static const char* _getDocumentsDir() // ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data if (dir.isEmpty()) - dir = _getDocumentsDir(); + { + dir = _getHomeDir(); + dir += "Documents/"; + } _createDirIfNeeded(dir); #endif