| @@ -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/DistrhoInfo.hpp | ||||
| rm ${CARLA_DIR}/source/modules/distrho/DistrhoUI_win32.cpp | 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/DistrhoPluginVST.hpp | ||||
| rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoUI{DSSI,LV2,Stub,VST3}.cpp | rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoUI{DSSI,LV2,Stub,VST3}.cpp | ||||
| @@ -83,10 +83,21 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_off) | |||||
| class DISTRHO_API Application | class DISTRHO_API Application | ||||
| { | { | ||||
| public: | 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. | Constructor for standalone or plugin application. | ||||
| */ | */ | ||||
| Application(bool isStandalone = true); | |||||
| Application(bool isStandalone = true, Type type = kTypeAuto); | |||||
| /** | /** | ||||
| Constructor for a standalone application. | Constructor for a standalone application. | ||||
| @@ -141,6 +152,12 @@ public: | |||||
| */ | */ | ||||
| double getTime() const; | 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. | Add a callback function to be triggered on every idle cycle. | ||||
| You can add more than one, and remove them at anytime with removeIdleCallback(). | You can add more than one, and remove them at anytime with removeIdleCallback(). | ||||
| @@ -49,6 +49,10 @@ | |||||
| # error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out) | # error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out) | ||||
| #endif | #endif | ||||
| #ifndef DGL_ALLOW_DEPRECATED_METHODS | |||||
| # define DGL_ALLOW_DEPRECATED_METHODS 1 | |||||
| #endif | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| // Define namespace | // Define namespace | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -115,6 +115,17 @@ struct Color { | |||||
| */ | */ | ||||
| static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; | 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. | Linearly interpolate this color against another. | ||||
| */ | */ | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -360,25 +360,23 @@ public: | |||||
| */ | */ | ||||
| bool isNotNull() const noexcept; | bool isNotNull() const noexcept; | ||||
| #ifndef DPF_TEST_POINT_CPP | |||||
| /** | /** | ||||
| Draw this line using the provided graphics context, optionally specifying line width. | Draw this line using the provided graphics context, optionally specifying line width. | ||||
| */ | */ | ||||
| void draw(const GraphicsContext& context, T width = 1); | void draw(const GraphicsContext& context, T width = 1); | ||||
| #endif | |||||
| Line<T>& operator=(const Line<T>& line) noexcept; | Line<T>& operator=(const Line<T>& line) noexcept; | ||||
| bool operator==(const Line<T>& line) const noexcept; | bool operator==(const Line<T>& line) const noexcept; | ||||
| bool operator!=(const Line<T>& line) const noexcept; | bool operator!=(const Line<T>& line) const noexcept; | ||||
| #ifndef DPF_TEST_POINT_CPP | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| /** | /** | ||||
| Draw this line using the current OpenGL state.@n | Draw this line using the current OpenGL state.@n | ||||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | DEPRECATED Please use draw(const GraphicsContext&) instead. | ||||
| */ | */ | ||||
| DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | ||||
| void draw(); | void draw(); | ||||
| #endif | |||||
| #endif | |||||
| private: | private: | ||||
| Point<T> posStart, posEnd; | Point<T> posStart, posEnd; | ||||
| @@ -489,7 +487,7 @@ public: | |||||
| bool operator==(const Circle<T>& cir) const noexcept; | bool operator==(const Circle<T>& cir) const noexcept; | ||||
| bool operator!=(const Circle<T>& cir) const noexcept; | bool operator!=(const Circle<T>& cir) const noexcept; | ||||
| #ifndef DPF_TEST_POINT_CPP | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| /** | /** | ||||
| Draw this circle using the current OpenGL state.@n | Draw this circle using the current OpenGL state.@n | ||||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | DEPRECATED Please use draw(const GraphicsContext&) instead. | ||||
| @@ -503,7 +501,7 @@ public: | |||||
| */ | */ | ||||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | ||||
| void drawOutline(); | void drawOutline(); | ||||
| #endif | |||||
| #endif | |||||
| private: | private: | ||||
| Point<T> fPos; | Point<T> fPos; | ||||
| @@ -582,7 +580,7 @@ public: | |||||
| bool operator==(const Triangle<T>& tri) const noexcept; | bool operator==(const Triangle<T>& tri) const noexcept; | ||||
| bool operator!=(const Triangle<T>& tri) const noexcept; | bool operator!=(const Triangle<T>& tri) const noexcept; | ||||
| #ifndef DPF_TEST_POINT_CPP | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| /** | /** | ||||
| Draw this triangle using the current OpenGL state.@n | Draw this triangle using the current OpenGL state.@n | ||||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | DEPRECATED Please use draw(const GraphicsContext&) instead. | ||||
| @@ -596,7 +594,7 @@ public: | |||||
| */ | */ | ||||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | ||||
| void drawOutline(); | void drawOutline(); | ||||
| #endif | |||||
| #endif | |||||
| private: | private: | ||||
| Point<T> pos1, pos2, pos3; | Point<T> pos1, pos2, pos3; | ||||
| @@ -813,6 +811,7 @@ public: | |||||
| bool operator==(const Rectangle<T>& size) const noexcept; | bool operator==(const Rectangle<T>& size) const noexcept; | ||||
| bool operator!=(const Rectangle<T>& size) const noexcept; | bool operator!=(const Rectangle<T>& size) const noexcept; | ||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| /** | /** | ||||
| Draw this rectangle using the current OpenGL state.@n | Draw this rectangle using the current OpenGL state.@n | ||||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | DEPRECATED Please use draw(const GraphicsContext&) instead. | ||||
| @@ -826,6 +825,7 @@ public: | |||||
| */ | */ | ||||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | ||||
| void drawOutline(); | void drawOutline(); | ||||
| #endif | |||||
| private: | private: | ||||
| Point<T> pos; | Point<T> pos; | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -17,17 +17,21 @@ | |||||
| #ifndef DGL_IMAGE_HPP_INCLUDED | #ifndef DGL_IMAGE_HPP_INCLUDED | ||||
| #define 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 | #else | ||||
| #include "OpenGL.hpp" | |||||
| # include "Base.hpp" | |||||
| #endif | #endif | ||||
| START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
| #ifdef DGL_CAIRO | |||||
| #if defined(DGL_CAIRO) | |||||
| typedef CairoImage Image; | typedef CairoImage Image; | ||||
| #else | |||||
| #elif defined(DGL_OPENGL) | |||||
| typedef OpenGLImage Image; | typedef OpenGLImage Image; | ||||
| #endif | #endif | ||||
| @@ -32,6 +32,7 @@ OBJS = \ | |||||
| $(OBJDIR)/ImageBaseWidgets.cpp.o \ | $(OBJDIR)/ImageBaseWidgets.cpp.o \ | ||||
| $(OBJDIR)/NanoVG.cpp.o \ | $(OBJDIR)/NanoVG.cpp.o \ | ||||
| $(OBJDIR)/OpenGL.cpp.o \ | $(OBJDIR)/OpenGL.cpp.o \ | ||||
| $(OBJDIR)/OpenGL2.cpp.o \ | |||||
| $(OBJDIR)/SubWidget.cpp.o \ | $(OBJDIR)/SubWidget.cpp.o \ | ||||
| $(OBJDIR)/SubWidgetPrivateData.cpp.o \ | $(OBJDIR)/SubWidgetPrivateData.cpp.o \ | ||||
| $(OBJDIR)/TopLevelWidget.cpp.o \ | $(OBJDIR)/TopLevelWidget.cpp.o \ | ||||
| @@ -51,6 +52,7 @@ OBJS_wine = \ | |||||
| $(OBJDIR)/ImageBase.cpp-wine.o \ | $(OBJDIR)/ImageBase.cpp-wine.o \ | ||||
| $(OBJDIR)/ImageBaseWidgets.cpp-wine.o \ | $(OBJDIR)/ImageBaseWidgets.cpp-wine.o \ | ||||
| $(OBJDIR)/OpenGL.cpp-wine.o \ | $(OBJDIR)/OpenGL.cpp-wine.o \ | ||||
| $(OBJDIR)/OpenGL2.cpp-wine.o \ | |||||
| $(OBJDIR)/SubWidget.cpp-wine.o \ | $(OBJDIR)/SubWidget.cpp-wine.o \ | ||||
| $(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \ | $(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \ | ||||
| $(OBJDIR)/TopLevelWidget.cpp-wine.o \ | $(OBJDIR)/TopLevelWidget.cpp-wine.o \ | ||||
| @@ -53,6 +53,12 @@ | |||||
| #ifdef DISTRHO_OS_MAC | #ifdef DISTRHO_OS_MAC | ||||
| # ifdef DGL_USE_OPENGL3 | # 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 <OpenGL/gl3.h> | # include <OpenGL/gl3.h> | ||||
| # include <OpenGL/gl3ext.h> | # include <OpenGL/gl3ext.h> | ||||
| # else | # else | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -24,34 +24,131 @@ | |||||
| START_NAMESPACE_DGL | 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 | static inline | ||||
| ImageFormat asDISTRHOImageFormat(const GLenum format) | ImageFormat asDISTRHOImageFormat(const GLenum format) | ||||
| { | { | ||||
| switch (format) | switch (format) | ||||
| { | { | ||||
| #ifdef DGL_USE_OPENGL3 | |||||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||||
| case GL_RED: | case GL_RED: | ||||
| #else | |||||
| #else | |||||
| case GL_LUMINANCE: | case GL_LUMINANCE: | ||||
| #endif | |||||
| #endif | |||||
| return kImageFormatGrayscale; | return kImageFormatGrayscale; | ||||
| #ifndef DGL_USE_GLES | |||||
| case GL_BGR: | case GL_BGR: | ||||
| return kImageFormatBGR; | return kImageFormatBGR; | ||||
| case GL_BGRA: | case GL_BGRA: | ||||
| return kImageFormatBGRA; | return kImageFormatBGRA; | ||||
| #endif | |||||
| case GL_RGB: | case GL_RGB: | ||||
| return kImageFormatRGB; | return kImageFormatRGB; | ||||
| case GL_RGBA: | case GL_RGBA: | ||||
| @@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format) | |||||
| case kImageFormatNull: | case kImageFormatNull: | ||||
| break; | break; | ||||
| case kImageFormatGrayscale: | case kImageFormatGrayscale: | ||||
| #ifdef DGL_USE_OPENGL3 | |||||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||||
| return GL_RED; | return GL_RED; | ||||
| #else | |||||
| #else | |||||
| return GL_LUMINANCE; | return GL_LUMINANCE; | ||||
| #endif | |||||
| #endif | |||||
| case kImageFormatBGR: | case kImageFormatBGR: | ||||
| #ifndef DGL_USE_GLES | |||||
| return GL_BGR; | return GL_BGR; | ||||
| #else | |||||
| return 0; | |||||
| #endif | |||||
| case kImageFormatBGRA: | case kImageFormatBGRA: | ||||
| #ifndef DGL_USE_GLES | |||||
| return GL_BGRA; | return GL_BGRA; | ||||
| #else | |||||
| return 0; | |||||
| #endif | |||||
| case kImageFormatRGB: | case kImageFormatRGB: | ||||
| return GL_RGB; | return GL_RGB; | ||||
| case kImageFormatRGBA: | case kImageFormatRGBA: | ||||
| return GL_RGBA; | return GL_RGBA; | ||||
| } | } | ||||
| return 0x0; | |||||
| return 0; | |||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| /** | /** | ||||
| OpenGL Image class. | OpenGL Image class. | ||||
| @@ -144,6 +249,18 @@ public: | |||||
| */ | */ | ||||
| void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | void drawAt(const GraphicsContext& context, const Point<int>& 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. | TODO document this. | ||||
| */ | */ | ||||
| @@ -157,6 +274,7 @@ public: | |||||
| inline void drawAt(const GraphicsContext& context, int x, int y) | inline void drawAt(const GraphicsContext& context, int x, int y) | ||||
| { drawAt(context, Point<int>(x, y)); } | { drawAt(context, Point<int>(x, y)); } | ||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| /** | /** | ||||
| Constructor using raw image data, specifying an OpenGL image format. | Constructor using raw image data, specifying an OpenGL image format. | ||||
| @note @a rawData must remain valid for the lifetime of this Image. | @note @a rawData must remain valid for the lifetime of this Image. | ||||
| @@ -200,14 +318,19 @@ public: | |||||
| */ | */ | ||||
| DISTRHO_DEPRECATED | DISTRHO_DEPRECATED | ||||
| GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | ||||
| #endif // DGL_ALLOW_DEPRECATED_METHODS | |||||
| private: | private: | ||||
| bool setupCalled; | bool setupCalled; | ||||
| bool textureInit; | bool textureInit; | ||||
| GLuint textureId; | GLuint textureId; | ||||
| #ifdef DGL_USE_GLES | |||||
| mutable char* convertedData; | |||||
| mutable const char* rawDataLast; | |||||
| #endif | |||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | ||||
| typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | ||||
| @@ -215,7 +338,7 @@ typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||||
| typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | ||||
| typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
| @@ -98,8 +98,8 @@ static void app_idle(void* const app) | |||||
| } | } | ||||
| #endif | #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 | // build config sentinels | ||||
| #ifdef DPF_DEBUG | #ifdef DPF_DEBUG | ||||
| @@ -126,7 +126,7 @@ Application::Application(const bool isStandalone) | |||||
| } | } | ||||
| Application::Application(int argc, char* argv[]) | 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 defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) | ||||
| if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | ||||
| @@ -213,6 +213,11 @@ double Application::getTime() const | |||||
| return pData->getTime(); | return pData->getTime(); | ||||
| } | } | ||||
| Application::Type Application::getType() const noexcept | |||||
| { | |||||
| return pData->isModern ? kTypeModern : kTypeClassic; | |||||
| } | |||||
| void Application::addIdleCallback(IdleCallback* const callback) | void Application::addIdleCallback(IdleCallback* const callback) | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -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, | : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | ||||
| standalone ? PUGL_WORLD_THREADS : 0x0)), | |||||
| (standalone ? PUGL_WORLD_THREADS : 0))), | |||||
| isModern(false), | |||||
| isStandalone(standalone), | isStandalone(standalone), | ||||
| isStarting(true), | |||||
| isQuitting(false), | isQuitting(false), | ||||
| isQuittingInNextCycle(false), | isQuittingInNextCycle(false), | ||||
| isStarting(true), | |||||
| needsRepaint(false), | needsRepaint(false), | ||||
| visibleWindows(0), | visibleWindows(0), | ||||
| mainThreadHandle(getCurrentThreadHandle()), | mainThreadHandle(getCurrentThreadHandle()), | ||||
| @@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone) | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | 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__ | #ifdef __EMSCRIPTEN__ | ||||
| puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | ||||
| #else | #else | ||||
| puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } | } | ||||
| Application::PrivateData::~PrivateData() | Application::PrivateData::~PrivateData() | ||||
| @@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData() | |||||
| windows.clear(); | windows.clear(); | ||||
| idleCallbacks.clear(); | idleCallbacks.clear(); | ||||
| #ifdef DGL_USING_SDL | |||||
| SDL_Quit(); | |||||
| #else | |||||
| if (world != nullptr) | if (world != nullptr) | ||||
| puglFreeWorld(world); | puglFreeWorld(world); | ||||
| #endif | |||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -51,18 +51,21 @@ struct Application::PrivateData { | |||||
| /** Pugl world instance. */ | /** Pugl world instance. */ | ||||
| PuglWorld* const world; | 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. */ | /** Whether the application is running as standalone, otherwise it is part of a plugin. */ | ||||
| const bool isStandalone; | 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. */ | /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | ||||
| bool isQuitting; | bool isQuitting; | ||||
| /** Helper for safely close everything from main thread. */ | /** Helper for safely close everything from main thread. */ | ||||
| bool isQuittingInNextCycle; | 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. */ | /** When true force all windows to be repainted on next idle. */ | ||||
| bool needsRepaint; | bool needsRepaint; | ||||
| @@ -80,7 +83,7 @@ struct Application::PrivateData { | |||||
| std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | ||||
| /** Constructor and destructor */ | /** Constructor and destructor */ | ||||
| explicit PrivateData(bool standalone); | |||||
| explicit PrivateData(bool standalone, Type type); | |||||
| ~PrivateData(); | ~PrivateData(); | ||||
| /** Flag one window as shown, which increments @a visibleWindows. | /** Flag one window as shown, which increments @a visibleWindows. | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -242,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept | |||||
| return Color(r, g, b, alpha); | return Color(r, g, b, alpha); | ||||
| } | } | ||||
| Color Color::fromRGB(const uint color, const float alpha) noexcept | |||||
| { | |||||
| return Color(static_cast<int>(color >> 24) & 0xff, | |||||
| static_cast<int>(color >> 16) & 0xff, | |||||
| static_cast<int>(color >> 8) & 0xff, | |||||
| alpha); | |||||
| } | |||||
| void Color::interpolate(const Color& other, float u) noexcept | void Color::interpolate(const Color& other, float u) noexcept | ||||
| { | { | ||||
| fixRange(u); | fixRange(u); | ||||
| @@ -108,13 +108,6 @@ struct ButtonEventHandler::PrivateData { | |||||
| if (! enabledInput) | if (! enabledInput) | ||||
| return false; | return false; | ||||
| // keep pressed | |||||
| if (button != -1) | |||||
| { | |||||
| lastMotionPos = ev.pos; | |||||
| return true; | |||||
| } | |||||
| bool ret = false; | bool ret = false; | ||||
| if (widget->contains(ev.pos)) | if (widget->contains(ev.pos)) | ||||
| @@ -143,7 +136,7 @@ struct ButtonEventHandler::PrivateData { | |||||
| } | } | ||||
| lastMotionPos = ev.pos; | lastMotionPos = ev.pos; | ||||
| return ret; | |||||
| return ret || button != -1; | |||||
| } | } | ||||
| void setActive(const bool active2, const bool sendCallback) noexcept | void setActive(const bool active2, const bool sendCallback) noexcept | ||||
| @@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||||
| //#define STB_IMAGE_STATIC | //#define STB_IMAGE_STATIC | ||||
| #if defined(DGL_USE_GLES2) | #if defined(DGL_USE_GLES2) | ||||
| # define NANOVG_GLES2_IMPLEMENTATION | # define NANOVG_GLES2_IMPLEMENTATION | ||||
| #elif defined(DGL_USE_GLES3) | |||||
| # define NANOVG_GLES3_IMPLEMENTATION | |||||
| #elif defined(DGL_USE_OPENGL3) | #elif defined(DGL_USE_OPENGL3) | ||||
| # define NANOVG_GL3_IMPLEMENTATION | # define NANOVG_GL3_IMPLEMENTATION | ||||
| #else | #else | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -20,7 +20,6 @@ | |||||
| #endif | #endif | ||||
| #include "../OpenGL.hpp" | #include "../OpenGL.hpp" | ||||
| #include "../Color.hpp" | |||||
| #include "../ImageWidgets.hpp" | #include "../ImageWidgets.hpp" | ||||
| #include "SubWidgetPrivateData.hpp" | #include "SubWidgetPrivateData.hpp" | ||||
| @@ -28,416 +27,20 @@ | |||||
| #include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
| #include "WindowPrivateData.hpp" | #include "WindowPrivateData.hpp" | ||||
| // templated classes | |||||
| #include "ImageBaseWidgets.cpp" | |||||
| START_NAMESPACE_DGL | 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<typename T> | |||||
| static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||||
| glBegin(GL_LINES); | |||||
| { | |||||
| glVertex2d(posStart.getX(), posStart.getY()); | |||||
| glVertex2d(posEnd.getX(), posEnd.getY()); | |||||
| } | |||||
| glEnd(); | |||||
| } | |||||
| #endif | |||||
| template<typename T> | |||||
| void Line<T>::draw(const GraphicsContext&, const T width) | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(width)); | |||||
| drawLine<T>(posStart, posEnd); | |||||
| #else | |||||
| notImplemented("Line::draw"); | |||||
| #endif | |||||
| } | |||||
| // deprecated calls | |||||
| template<typename T> | |||||
| void Line<T>::draw() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawLine<T>(posStart, posEnd); | |||||
| #else | |||||
| notImplemented("Line::draw"); | |||||
| #endif | |||||
| } | |||||
| template class Line<double>; | |||||
| template class Line<float>; | |||||
| template class Line<int>; | |||||
| template class Line<uint>; | |||||
| template class Line<short>; | |||||
| template class Line<ushort>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Circle | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| template<typename T> | |||||
| static void drawCircle(const Point<T>& 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<numSegments; ++i) | |||||
| { | |||||
| glVertex2d(x + origx, y + origy); | |||||
| t = x; | |||||
| x = cos * x - sin * y; | |||||
| y = sin * t + cos * y; | |||||
| } | |||||
| glEnd(); | |||||
| } | |||||
| #endif | |||||
| template<typename T> | |||||
| void Circle<T>::draw(const GraphicsContext&) | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
| #else | |||||
| notImplemented("Circle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
| #else | |||||
| notImplemented("Circle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| // deprecated calls | |||||
| template<typename T> | |||||
| void Circle<T>::draw() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
| #else | |||||
| notImplemented("Circle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Circle<T>::drawOutline() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
| #else | |||||
| notImplemented("Circle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| template class Circle<double>; | |||||
| template class Circle<float>; | |||||
| template class Circle<int>; | |||||
| template class Circle<uint>; | |||||
| template class Circle<short>; | |||||
| template class Circle<ushort>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Triangle | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| template<typename T> | |||||
| static void drawTriangle(const Point<T>& pos1, | |||||
| const Point<T>& pos2, | |||||
| const Point<T>& 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<typename T> | |||||
| void Triangle<T>::draw(const GraphicsContext&) | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||||
| #else | |||||
| notImplemented("Triangle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||||
| #else | |||||
| notImplemented("Triangle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| // deprecated calls | |||||
| template<typename T> | |||||
| void Triangle<T>::draw() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||||
| #else | |||||
| notImplemented("Triangle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Triangle<T>::drawOutline() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||||
| #else | |||||
| notImplemented("Triangle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| template class Triangle<double>; | |||||
| template class Triangle<float>; | |||||
| template class Triangle<int>; | |||||
| template class Triangle<uint>; | |||||
| template class Triangle<short>; | |||||
| template class Triangle<ushort>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // Rectangle | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| template<typename T> | |||||
| static void drawRectangle(const Rectangle<T>& 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<typename T> | |||||
| void Rectangle<T>::draw(const GraphicsContext&) | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawRectangle<T>(*this, false); | |||||
| #else | |||||
| notImplemented("Rectangle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawRectangle<T>(*this, true); | |||||
| #else | |||||
| notImplemented("Rectangle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| // deprecated calls | |||||
| template<typename T> | |||||
| void Rectangle<T>::draw() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawRectangle<T>(*this, false); | |||||
| #else | |||||
| notImplemented("Rectangle::draw"); | |||||
| #endif | |||||
| } | |||||
| template<typename T> | |||||
| void Rectangle<T>::drawOutline() | |||||
| { | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| drawRectangle<T>(*this, true); | |||||
| #else | |||||
| notImplemented("Rectangle::drawOutline"); | |||||
| #endif | |||||
| } | |||||
| template class Rectangle<double>; | |||||
| template class Rectangle<float>; | |||||
| template class Rectangle<int>; | |||||
| template class Rectangle<uint>; | |||||
| template class Rectangle<short>; | |||||
| template class Rectangle<ushort>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // OpenGLImage | // 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<GLsizei>(image.getWidth()), | |||||
| static_cast<GLsizei>(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<int>& 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<int>(image.getWidth()); | |||||
| const int h = static_cast<int>(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() | OpenGLImage::OpenGLImage() | ||||
| : ImageBase(), | : ImageBase(), | ||||
| setupCalled(false), | setupCalled(false), | ||||
| textureInit(false), | textureInit(false), | ||||
| textureId(0) | 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), | setupCalled(false), | ||||
| textureInit(true), | textureInit(true), | ||||
| textureId(0) | textureId(0) | ||||
| #ifdef DGL_USE_GLES | |||||
| , convertedData(nullptr) | |||||
| , rawDataLast(nullptr) | |||||
| #endif | |||||
| { | { | ||||
| glGenTextures(1, &textureId); | glGenTextures(1, &textureId); | ||||
| DISTRHO_SAFE_ASSERT(textureId != 0); | DISTRHO_SAFE_ASSERT(textureId != 0); | ||||
| @@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const Ima | |||||
| setupCalled(false), | setupCalled(false), | ||||
| textureInit(true), | textureInit(true), | ||||
| textureId(0) | textureId(0) | ||||
| #ifdef DGL_USE_GLES | |||||
| , convertedData(nullptr) | |||||
| , rawDataLast(nullptr) | |||||
| #endif | |||||
| { | { | ||||
| glGenTextures(1, &textureId); | glGenTextures(1, &textureId); | ||||
| DISTRHO_SAFE_ASSERT(textureId != 0); | DISTRHO_SAFE_ASSERT(textureId != 0); | ||||
| @@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image) | |||||
| setupCalled(false), | setupCalled(false), | ||||
| textureInit(true), | textureInit(true), | ||||
| textureId(0) | textureId(0) | ||||
| #ifdef DGL_USE_GLES | |||||
| , convertedData(nullptr) | |||||
| , rawDataLast(nullptr) | |||||
| #endif | |||||
| { | { | ||||
| glGenTextures(1, &textureId); | glGenTextures(1, &textureId); | ||||
| DISTRHO_SAFE_ASSERT(textureId != 0); | DISTRHO_SAFE_ASSERT(textureId != 0); | ||||
| @@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage() | |||||
| { | { | ||||
| if (textureId != 0) | if (textureId != 0) | ||||
| glDeleteTextures(1, &textureId); | glDeleteTextures(1, &textureId); | ||||
| #ifdef DGL_USE_GLES | |||||
| std::free(convertedData); | |||||
| #endif | |||||
| } | } | ||||
| void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | ||||
| @@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, c | |||||
| ImageBase::loadFromMemory(rdata, s, fmt); | ImageBase::loadFromMemory(rdata, s, fmt); | ||||
| } | } | ||||
| void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||||
| { | |||||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
| } | |||||
| OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | ||||
| { | { | ||||
| rawData = image.rawData; | rawData = image.rawData; | ||||
| @@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| // deprecated calls | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) | OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) | ||||
| : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), | : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), | ||||
| setupCalled(false), | setupCalled(false), | ||||
| textureInit(true), | textureInit(true), | ||||
| textureId(0) | textureId(0) | ||||
| #ifdef DGL_USE_GLES | |||||
| , convertedData(nullptr) | |||||
| , rawDataLast(nullptr) | |||||
| #endif | |||||
| { | { | ||||
| glGenTextures(1, &textureId); | glGenTextures(1, &textureId); | ||||
| DISTRHO_SAFE_ASSERT(textureId != 0); | DISTRHO_SAFE_ASSERT(textureId != 0); | ||||
| @@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLe | |||||
| setupCalled(false), | setupCalled(false), | ||||
| textureInit(true), | textureInit(true), | ||||
| textureId(0) | textureId(0) | ||||
| #ifdef DGL_USE_GLES | |||||
| , convertedData(nullptr) | |||||
| , rawDataLast(nullptr) | |||||
| #endif | |||||
| { | { | ||||
| glGenTextures(1, &textureId); | glGenTextures(1, &textureId); | ||||
| DISTRHO_SAFE_ASSERT(textureId != 0); | DISTRHO_SAFE_ASSERT(textureId != 0); | ||||
| } | } | ||||
| void OpenGLImage::draw() | |||||
| { | |||||
| drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||||
| } | |||||
| void OpenGLImage::drawAt(const int x, const int y) | |||||
| { | |||||
| drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||||
| } | |||||
| void OpenGLImage::drawAt(const Point<int>& pos) | |||||
| { | |||||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // ImageBaseAboutWindow | |||||
| #if 0 | |||||
| template <> | |||||
| void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||||
| { | |||||
| const GraphicsContext& context(getGraphicsContext()); | |||||
| img.draw(context); | |||||
| } | |||||
| #endif | |||||
| template class ImageBaseAboutWindow<OpenGLImage>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // ImageBaseButton | |||||
| template class ImageBaseButton<OpenGLImage>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // ImageBaseKnob | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||||
| { | |||||
| glTextureId = 0; | |||||
| glGenTextures(1, &glTextureId); | |||||
| } | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||||
| { | |||||
| if (glTextureId == 0) | |||||
| return; | |||||
| glDeleteTextures(1, &glTextureId); | |||||
| glTextureId = 0; | |||||
| } | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::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<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||||
| asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||||
| pData->isReady = true; | |||||
| } | |||||
| const int w = static_cast<int>(getWidth()); | |||||
| const int h = static_cast<int>(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<float>(w2), static_cast<float>(h2), 0.0f); | |||||
| glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||||
| #endif | #endif | ||||
| Rectangle<int>(-w2, -h2, w, h).draw(context); | |||||
| #ifdef DGL_USE_COMPAT_OPENGL | |||||
| glPopMatrix(); | |||||
| #endif | |||||
| } | |||||
| else | |||||
| { | |||||
| Rectangle<int>(0, 0, w, h).draw(context); | |||||
| } | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| glDisable(GL_TEXTURE_2D); | |||||
| } | |||||
| template class ImageBaseKnob<OpenGLImage>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // ImageBaseSlider | |||||
| template class ImageBaseSlider<OpenGLImage>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // ImageBaseSwitch | |||||
| template class ImageBaseSwitch<OpenGLImage>; | |||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | 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); | selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| void TopLevelWidget::PrivateData::display() | void TopLevelWidget::PrivateData::display() | ||||
| { | { | ||||
| @@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display() | |||||
| selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor); | selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| void Window::PrivateData::renderToPicture(const char* const filename, | void Window::PrivateData::renderToPicture(const char* const filename, | ||||
| const GraphicsContext&, | const GraphicsContext&, | ||||
| @@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename, | |||||
| fclose(f); | fclose(f); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||||
| { | |||||
| return (const GraphicsContext&)graphicsContext; | |||||
| } | |||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
| @@ -0,0 +1,570 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #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<typename T> | |||||
| static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||||
| glBegin(GL_LINES); | |||||
| { | |||||
| glVertex2d(posStart.getX(), posStart.getY()); | |||||
| glVertex2d(posEnd.getX(), posEnd.getY()); | |||||
| } | |||||
| glEnd(); | |||||
| } | |||||
| template<typename T> | |||||
| void Line<T>::draw(const GraphicsContext&, const T width) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(width)); | |||||
| drawLine<T>(posStart, posEnd); | |||||
| } | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| template<typename T> | |||||
| void Line<T>::draw() | |||||
| { | |||||
| drawLine<T>(posStart, posEnd); | |||||
| } | |||||
| #endif | |||||
| template class Line<double>; | |||||
| template class Line<float>; | |||||
| template class Line<int>; | |||||
| template class Line<uint>; | |||||
| template class Line<short>; | |||||
| template class Line<ushort>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Circle | |||||
| template<typename T> | |||||
| static void drawCircle(const Point<T>& 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<double>(pos.getX()); | |||||
| const double origy = static_cast<double>(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<typename T> | |||||
| static void drawCircle(const GraphicsContext&, | |||||
| const Point<T>& pos, | |||||
| const uint numSegments, | |||||
| const float size, | |||||
| const float sin, | |||||
| const float cos, | |||||
| const bool outline) | |||||
| { | |||||
| drawCircle<T>(pos, numSegments, size, sin, cos, outline); | |||||
| } | |||||
| template<typename T> | |||||
| void Circle<T>::draw(const GraphicsContext& context) | |||||
| { | |||||
| drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
| } | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| template<typename T> | |||||
| void Circle<T>::draw() | |||||
| { | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Circle<T>::drawOutline() | |||||
| { | |||||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||||
| } | |||||
| #endif | |||||
| template class Circle<double>; | |||||
| template class Circle<float>; | |||||
| template class Circle<int>; | |||||
| template class Circle<uint>; | |||||
| template class Circle<short>; | |||||
| template class Circle<ushort>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Triangle | |||||
| template<typename T> | |||||
| static void drawTriangle(const Point<T>& pos1, | |||||
| const Point<T>& pos2, | |||||
| const Point<T>& 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<typename T> | |||||
| static void drawTriangle(const GraphicsContext&, | |||||
| const Point<T>& pos1, | |||||
| const Point<T>& pos2, | |||||
| const Point<T>& pos3, | |||||
| const bool outline) | |||||
| { | |||||
| drawTriangle<T>(pos1, pos2, pos3, outline); | |||||
| } | |||||
| template<typename T> | |||||
| void Triangle<T>::draw(const GraphicsContext&) | |||||
| { | |||||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||||
| } | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| template<typename T> | |||||
| void Triangle<T>::draw() | |||||
| { | |||||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Triangle<T>::drawOutline() | |||||
| { | |||||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||||
| } | |||||
| #endif | |||||
| template class Triangle<double>; | |||||
| template class Triangle<float>; | |||||
| template class Triangle<int>; | |||||
| template class Triangle<uint>; | |||||
| template class Triangle<short>; | |||||
| template class Triangle<ushort>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Rectangle | |||||
| template<typename T> | |||||
| static void drawRectangle(const Rectangle<T>& 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<typename T> | |||||
| void Rectangle<T>::draw(const GraphicsContext& context) | |||||
| { | |||||
| drawRectangle<T>(*this, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||||
| drawRectangle<T>(*this, true); | |||||
| } | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| template<typename T> | |||||
| void Rectangle<T>::draw() | |||||
| { | |||||
| drawRectangle<T>(*this, false); | |||||
| } | |||||
| template<typename T> | |||||
| void Rectangle<T>::drawOutline() | |||||
| { | |||||
| drawRectangle<T>(*this, true); | |||||
| } | |||||
| #endif | |||||
| template class Rectangle<double>; | |||||
| template class Rectangle<float>; | |||||
| template class Rectangle<int>; | |||||
| template class Rectangle<uint>; | |||||
| template class Rectangle<short>; | |||||
| template class Rectangle<ushort>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // 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<GLsizei>(image.getWidth()), | |||||
| static_cast<GLsizei>(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<int>& 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<int>(image.getWidth()); | |||||
| const int h = static_cast<int>(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<int>& pos) | |||||
| { | |||||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
| } | |||||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||||
| void OpenGLImage::draw() | |||||
| { | |||||
| drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||||
| } | |||||
| void OpenGLImage::drawAt(const int x, const int y) | |||||
| { | |||||
| drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||||
| } | |||||
| void OpenGLImage::drawAt(const Point<int>& pos) | |||||
| { | |||||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||||
| } | |||||
| #endif | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // ImageBaseAboutWindow | |||||
| #if 0 | |||||
| template <> | |||||
| void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||||
| { | |||||
| const GraphicsContext& context(getGraphicsContext()); | |||||
| img.draw(context); | |||||
| } | |||||
| #endif | |||||
| template class ImageBaseAboutWindow<OpenGLImage>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // ImageBaseButton | |||||
| template class ImageBaseButton<OpenGLImage>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // ImageBaseKnob | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||||
| { | |||||
| glTextureId = 0; | |||||
| glGenTextures(1, &glTextureId); | |||||
| } | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||||
| { | |||||
| if (glTextureId == 0) | |||||
| return; | |||||
| glDeleteTextures(1, &glTextureId); | |||||
| glTextureId = 0; | |||||
| } | |||||
| template <> | |||||
| void ImageBaseKnob<OpenGLImage>::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<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||||
| asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||||
| pData->isReady = true; | |||||
| } | |||||
| const int w = static_cast<int>(getWidth()); | |||||
| const int h = static_cast<int>(getHeight()); | |||||
| if (pData->rotationAngle != 0) | |||||
| { | |||||
| glPushMatrix(); | |||||
| const int w2 = w/2; | |||||
| const int h2 = h/2; | |||||
| glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||||
| glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||||
| Rectangle<int>(-w2, -h2, w, h).draw(context); | |||||
| glPopMatrix(); | |||||
| } | |||||
| else | |||||
| { | |||||
| Rectangle<int>(0, 0, w, h).draw(context); | |||||
| } | |||||
| glBindTexture(GL_TEXTURE_2D, 0); | |||||
| glDisable(GL_TEXTURE_2D); | |||||
| } | |||||
| template class ImageBaseKnob<OpenGLImage>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // ImageBaseSlider | |||||
| template class ImageBaseSlider<OpenGLImage>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // ImageBaseSwitch | |||||
| template class ImageBaseSwitch<OpenGLImage>; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| void Window::PrivateData::createContextIfNeeded() | |||||
| { | |||||
| } | |||||
| void Window::PrivateData::destroyContext() | |||||
| { | |||||
| } | |||||
| void Window::PrivateData::startContext() | |||||
| { | |||||
| } | |||||
| void Window::PrivateData::endContext() | |||||
| { | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DGL | |||||
| @@ -28,7 +28,11 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win) | |||||
| : window(win), | : window(win), | ||||
| ppData(nullptr), | ppData(nullptr), | ||||
| active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)), | 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::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin) | ||||
| : window(win), | : window(win), | ||||
| @@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi | |||||
| { | { | ||||
| puglBackendLeave(ppData->view); | puglBackendLeave(ppData->view); | ||||
| active = puglBackendEnter(window.pData->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); | 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 | int Window::getOffsetY() const noexcept | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | ||||
| return puglGetFrame(pData->view).y; | |||||
| return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y; | |||||
| } | } | ||||
| Point<int> Window::getOffset() const noexcept | Point<int> Window::getOffset() const noexcept | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>()); | DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>()); | ||||
| const PuglRect rect = puglGetFrame(pData->view); | |||||
| return Point<int>(rect.x, rect.y); | |||||
| const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION); | |||||
| return Point<int>(pos.x, pos.y); | |||||
| } | } | ||||
| void Window::setOffsetX(const int x) | 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,); | DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,); | ||||
| if (pData->view != nullptr) | if (pData->view != nullptr) | ||||
| puglSetPosition(pData->view, x, y); | |||||
| puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y); | |||||
| } | } | ||||
| void Window::setOffset(const Point<int>& offset) | void Window::setOffset(const Point<int>& offset) | ||||
| @@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | 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<uint>(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 | uint Window::getHeight() const noexcept | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | 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<uint>(height + 0.5); | |||||
| const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height; | |||||
| DISTRHO_SAFE_ASSERT(height > 0); | |||||
| return height; | |||||
| } | } | ||||
| Size<uint> Window::getSize() const noexcept | Size<uint> Window::getSize() const noexcept | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>()); | DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>()); | ||||
| const PuglRect rect = puglGetFrame(pData->view); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size<uint>()); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size<uint>()); | |||||
| return Size<uint>(static_cast<uint>(rect.width + 0.5), | |||||
| static_cast<uint>(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<uint>(size.width, size.height); | |||||
| } | } | ||||
| void Window::setWidth(const uint width) | void Window::setWidth(const uint width) | ||||
| @@ -443,7 +448,7 @@ void Window::repaint() noexcept | |||||
| if (pData->usesScheduledRepaints) | if (pData->usesScheduledRepaints) | ||||
| pData->appData->needsRepaint = true; | pData->appData->needsRepaint = true; | ||||
| puglPostRedisplay(pData->view); | |||||
| puglObscureView(pData->view); | |||||
| } | } | ||||
| void Window::repaint(const Rectangle<uint>& rect) noexcept | void Window::repaint(const Rectangle<uint>& rect) noexcept | ||||
| @@ -454,22 +459,22 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||||
| if (pData->usesScheduledRepaints) | if (pData->usesScheduledRepaints) | ||||
| pData->appData->needsRepaint = true; | pData->appData->needsRepaint = true; | ||||
| PuglRect prect = { | |||||
| static_cast<PuglCoord>(rect.getX()), | |||||
| static_cast<PuglCoord>(rect.getY()), | |||||
| static_cast<PuglSpan>(rect.getWidth()), | |||||
| static_cast<PuglSpan>(rect.getHeight()), | |||||
| }; | |||||
| int x = static_cast<int>(rect.getX()); | |||||
| int y = static_cast<int>(rect.getY()); | |||||
| uint width = rect.getWidth(); | |||||
| uint height = rect.getHeight(); | |||||
| if (pData->autoScaling) | if (pData->autoScaling) | ||||
| { | { | ||||
| const double autoScaleFactor = pData->autoScaleFactor; | const double autoScaleFactor = pData->autoScaleFactor; | ||||
| prect.x = static_cast<PuglCoord>(prect.x * autoScaleFactor); | |||||
| prect.y = static_cast<PuglCoord>(prect.y * autoScaleFactor); | |||||
| prect.width = static_cast<PuglSpan>(prect.width * autoScaleFactor + 0.5); | |||||
| prect.height = static_cast<PuglSpan>(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) | void Window::renderToPicture(const char* const filename) | ||||
| @@ -523,8 +528,8 @@ void Window::setGeometryConstraints(uint minimumWidth, | |||||
| { | { | ||||
| const Size<uint> size(getSize()); | const Size<uint> size(getSize()); | ||||
| setSize(static_cast<uint>(size.getWidth() * scaleFactor + 0.5), | |||||
| static_cast<uint>(size.getHeight() * scaleFactor + 0.5)); | |||||
| setSize(d_roundToUnsignedInt(size.getWidth() * scaleFactor), | |||||
| d_roundToUnsignedInt(size.getHeight() * scaleFactor)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -91,10 +91,10 @@ static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintp | |||||
| if (PuglView* const view = puglNewView(world)) | if (PuglView* const view = puglNewView(world)) | ||||
| { | { | ||||
| puglSetParentWindow(view, parentWindowHandle); | |||||
| puglSetParent(view, parentWindowHandle); | |||||
| if (parentWindowHandle != 0) | if (parentWindowHandle != 0) | ||||
| puglSetPosition(view, 0, 0); | |||||
| puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0); | |||||
| return view; | return view; | ||||
| } | } | ||||
| @@ -268,6 +268,9 @@ Window::PrivateData::~PrivateData() | |||||
| isVisible = false; | isVisible = false; | ||||
| } | } | ||||
| #ifndef DPF_TEST_WINDOW_CPP | |||||
| destroyContext(); | |||||
| #endif | |||||
| puglFreeView(view); | puglFreeView(view); | ||||
| } | } | ||||
| @@ -300,8 +303,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||||
| // PUGL_SAMPLES ?? | // PUGL_SAMPLES ?? | ||||
| puglSetEventFunc(view, puglEventCallback); | puglSetEventFunc(view, puglEventCallback); | ||||
| // setting default size triggers system-level calls, do it last | |||||
| puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height)); | |||||
| // setting size triggers system-level calls, do it last | |||||
| puglSetSizeAndDefault(view, width, height); | |||||
| } | } | ||||
| bool Window::PrivateData::initPost() | bool Window::PrivateData::initPost() | ||||
| @@ -366,10 +369,6 @@ void Window::PrivateData::show() | |||||
| isClosed = false; | isClosed = false; | ||||
| appData->oneWindowShown(); | appData->oneWindowShown(); | ||||
| // FIXME | |||||
| // PuglRect rect = puglGetFrame(view); | |||||
| // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||||
| #if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
| puglWin32ShowCentered(view); | puglWin32ShowCentered(view); | ||||
| #elif defined(DISTRHO_OS_MAC) | #elif defined(DISTRHO_OS_MAC) | ||||
| @@ -453,6 +452,13 @@ void Window::PrivateData::setResizable(const bool resizable) | |||||
| puglSetResizable(view, resizable); | puglSetResizable(view, resizable); | ||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||||
| { | |||||
| return reinterpret_cast<const GraphicsContext&>(graphicsContext); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| void Window::PrivateData::idleCallback() | void Window::PrivateData::idleCallback() | ||||
| @@ -538,9 +544,9 @@ bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPA | |||||
| if (webViewHandle != nullptr) | if (webViewHandle != nullptr) | ||||
| webViewDestroy(webViewHandle); | webViewDestroy(webViewHandle); | ||||
| const PuglRect rect = puglGetFrame(view); | |||||
| uint initialWidth = static_cast<uint>(rect.width) - options.offset.x; | |||||
| uint initialHeight = static_cast<uint>(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<int>(options.offset.x, options.offset.y); | webViewOffset = Point<int>(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); | DGL_DBGp("PUGL: onReshape : %d %d\n", width, height); | ||||
| #ifndef DPF_TEST_WINDOW_CPP | |||||
| createContextIfNeeded(); | |||||
| #endif | |||||
| if (autoScaling) | if (autoScaling) | ||||
| { | { | ||||
| const double scaleHorizontal = width / static_cast<double>(minWidth); | const double scaleHorizontal = width / static_cast<double>(minWidth); | ||||
| @@ -682,7 +692,7 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||||
| #endif | #endif | ||||
| // always repaint after a resize | // always repaint after a resize | ||||
| puglPostRedisplay(view); | |||||
| puglObscureView(view); | |||||
| } | } | ||||
| void Window::PrivateData::onPuglExpose() | void Window::PrivateData::onPuglExpose() | ||||
| @@ -692,6 +702,8 @@ void Window::PrivateData::onPuglExpose() | |||||
| puglOnDisplayPrepare(view); | puglOnDisplayPrepare(view); | ||||
| #ifndef DPF_TEST_WINDOW_CPP | #ifndef DPF_TEST_WINDOW_CPP | ||||
| startContext(); | |||||
| FOR_EACH_TOP_LEVEL_WIDGET(it) | FOR_EACH_TOP_LEVEL_WIDGET(it) | ||||
| { | { | ||||
| TopLevelWidget* const widget(*it); | TopLevelWidget* const widget(*it); | ||||
| @@ -702,11 +714,13 @@ void Window::PrivateData::onPuglExpose() | |||||
| if (char* const filename = filenameToRenderInto) | if (char* const filename = filenameToRenderInto) | ||||
| { | { | ||||
| const PuglRect rect = puglGetFrame(view); | |||||
| const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||||
| filenameToRenderInto = nullptr; | filenameToRenderInto = nullptr; | ||||
| renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||||
| renderToPicture(filename, getGraphicsContext(), size.width, size.height); | |||||
| std::free(filename); | std::free(filename); | ||||
| } | } | ||||
| endContext(); | |||||
| #endif | #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))); | SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); | ||||
| #endif | #endif | ||||
| #ifdef DGL_USING_X11 | #ifdef DGL_USING_X11 | ||||
| puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |||||
| puglX11SetWindowType(view, pData->appData->isStandalone); | |||||
| #endif | #endif | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback { | |||||
| PuglView* view; | PuglView* view; | ||||
| /** Reserved space for graphics context. */ | /** 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. */ | /** The top-level widgets associated with this Window. */ | ||||
| std::list<TopLevelWidget*> topLevelWidgets; | std::list<TopLevelWidget*> topLevelWidgets; | ||||
| @@ -173,7 +173,7 @@ struct GLNVGtexture { | |||||
| int width, height; | int width, height; | ||||
| int type; | int type; | ||||
| int flags; | int flags; | ||||
| #if defined NANOVG_GLES2 | |||||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||||
| unsigned char* data; | unsigned char* data; | ||||
| #endif | #endif | ||||
| }; | }; | ||||
| @@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||||
| switch (type) | switch (type) | ||||
| { | { | ||||
| case NVG_TEXTURE_BGR: | 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); | tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h); | ||||
| for (uint32_t i=0; i<w*h; ++i) | |||||
| for (int i = 0; i < w * h; ++i) | |||||
| { | { | ||||
| tex->data[i*3+0] = data[i*3+2]; | tex->data[i*3+0] = data[i*3+2]; | ||||
| tex->data[i*3+1] = data[i*3+1]; | 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 | #endif | ||||
| break; | break; | ||||
| case NVG_TEXTURE_BGRA: | 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); | tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h); | ||||
| for (uint32_t i=0; i<w*h; ++i) | |||||
| for (int i = 0; i < w * h; ++i) | |||||
| { | { | ||||
| tex->data[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; | 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 | #else | ||||
| 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_BGRA, GL_UNSIGNED_BYTE, data); | ||||
| #endif | #endif | ||||
| break; | break; | ||||
| case NVG_TEXTURE_RGB: | 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; | break; | ||||
| case NVG_TEXTURE_RGBA: | case NVG_TEXTURE_RGBA: | ||||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
| break; | break; | ||||
| default: | 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); | 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 | #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); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
| #endif | #endif | ||||
| break; | break; | ||||
| @@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||||
| switch (tex->type) | switch (tex->type) | ||||
| { | { | ||||
| case NVG_TEXTURE_BGR: | 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); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); | ||||
| break; | 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: | case NVG_TEXTURE_RGB: | ||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); | ||||
| break; | 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: | case NVG_TEXTURE_RGBA: | ||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | ||||
| break; | break; | ||||
| default: | 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); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | ||||
| #else | #else | ||||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
| @@ -26,17 +26,6 @@ | |||||
| # endif | # endif | ||||
| #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 | // GCC function attributes | ||||
| #if defined(__GNUC__) | #if defined(__GNUC__) | ||||
| # define PUGL_CONST_FUNC __attribute__((const)) | # define PUGL_CONST_FUNC __attribute__((const)) | ||||
| @@ -47,13 +36,9 @@ | |||||
| #endif | #endif | ||||
| /// A const function in the public API that only reads parameters | /// 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 | /// 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 | #endif // PUGL_ATTRIBUTES_H | ||||
| @@ -4,8 +4,8 @@ | |||||
| #ifndef PUGL_GL_H | #ifndef PUGL_GL_H | ||||
| #define PUGL_GL_H | #define PUGL_GL_H | ||||
| #include "pugl/attributes.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| // IWYU pragma: begin_exports | // IWYU pragma: begin_exports | ||||
| @@ -42,8 +42,7 @@ typedef void (*PuglGlFunc)(void); | |||||
| /** | /** | ||||
| Return the address of an OpenGL extension function. | Return the address of an OpenGL extension function. | ||||
| */ | */ | ||||
| PUGL_API | |||||
| PuglGlFunc | |||||
| PUGL_API PuglGlFunc | |||||
| puglGetProcAddress(const char* name); | 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 | doing things like loading textures. Note that this must not be used for | ||||
| drawing, which may only be done while processing an expose event. | drawing, which may only be done while processing an expose event. | ||||
| */ | */ | ||||
| PUGL_API | |||||
| PuglStatus | |||||
| PUGL_API PuglStatus | |||||
| puglEnterContext(PuglView* view); | puglEnterContext(PuglView* view); | ||||
| /** | /** | ||||
| @@ -62,8 +60,7 @@ puglEnterContext(PuglView* view); | |||||
| This must only be called after puglEnterContext(). | This must only be called after puglEnterContext(). | ||||
| */ | */ | ||||
| PUGL_API | |||||
| PuglStatus | |||||
| PUGL_API PuglStatus | |||||
| puglLeaveContext(PuglView* view); | puglLeaveContext(PuglView* view); | ||||
| /** | /** | ||||
| @@ -71,8 +68,7 @@ puglLeaveContext(PuglView* view); | |||||
| Pass the returned value to puglSetBackend() to draw to a view with OpenGL. | 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); | puglGlBackend(void); | ||||
| PUGL_END_DECLS | PUGL_END_DECLS | ||||
| @@ -4,8 +4,8 @@ | |||||
| #ifndef PUGL_STUB_H | #ifndef PUGL_STUB_H | ||||
| #define PUGL_STUB_H | #define PUGL_STUB_H | ||||
| #include "pugl/attributes.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| PUGL_BEGIN_DECLS | PUGL_BEGIN_DECLS | ||||
| @@ -22,8 +22,7 @@ PUGL_BEGIN_DECLS | |||||
| This backend just creates a simple native window without setting up any | This backend just creates a simple native window without setting up any | ||||
| portable graphics API. | portable graphics API. | ||||
| */ | */ | ||||
| PUGL_CONST_API | |||||
| const PuglBackend* | |||||
| PUGL_CONST_API const PuglBackend* | |||||
| puglStubBackend(void); | puglStubBackend(void); | ||||
| /** | /** | ||||
| @@ -4,13 +4,11 @@ | |||||
| // Common implementations of public API functions in the core library | // Common implementations of public API functions in the core library | ||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "platform.h" | #include "platform.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <limits.h> | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| @@ -27,6 +25,7 @@ puglStrerror(const PuglStatus status) | |||||
| case PUGL_BAD_BACKEND: return "Invalid or missing backend"; | case PUGL_BAD_BACKEND: return "Invalid or missing backend"; | ||||
| case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; | case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; | ||||
| case PUGL_BAD_PARAMETER: return "Invalid parameter"; | case PUGL_BAD_PARAMETER: return "Invalid parameter"; | ||||
| case PUGL_BAD_CALL: return "Invalid call"; | |||||
| case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; | case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; | ||||
| case PUGL_REGISTRATION_FAILED: return "Class registration failed"; | case PUGL_REGISTRATION_FAILED: return "Class registration failed"; | ||||
| case PUGL_REALIZE_FAILED: return "View creation failed"; | case PUGL_REALIZE_FAILED: return "View creation failed"; | ||||
| @@ -106,27 +105,37 @@ puglGetWorldString(const PuglWorld* const world, const PuglStringHint key) | |||||
| } | } | ||||
| static void | 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* | PuglView* | ||||
| @@ -138,13 +147,8 @@ puglNewView(PuglWorld* const world) | |||||
| return NULL; | 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 | // Enlarge world view list | ||||
| const size_t newNumViews = world->numViews + 1U; | const size_t newNumViews = world->numViews + 1U; | ||||
| @@ -287,43 +291,84 @@ puglGetViewString(const PuglView* const view, const PuglStringHint key) | |||||
| return view->strings[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 | PuglStatus | ||||
| puglSetParentWindow(PuglView* view, PuglNativeView parent) | |||||
| puglSetParent(PuglView* view, PuglNativeView parent) | |||||
| { | { | ||||
| view->parent = parent; | view->parent = parent; | ||||
| return PUGL_SUCCESS; | 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 | PuglNativeView | ||||
| puglGetParentWindow(const PuglView* const view) | |||||
| puglGetParent(const PuglView* const view) | |||||
| { | { | ||||
| return view->parent; | return view->parent; | ||||
| } | } | ||||
| @@ -5,19 +5,80 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stdint.h> | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| 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 | 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; | 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 | void | ||||
| puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) | 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 | uint32_t | ||||
| puglDecodeUTF8(const uint8_t* buf) | puglDecodeUTF8(const uint8_t* buf) | ||||
| { | { | ||||
| @@ -159,11 +239,12 @@ puglPreRealize(PuglView* const view) | |||||
| } | } | ||||
| // Ensure that the default size is set to a valid size | // 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_BAD_CONFIGURATION; | ||||
| } | } | ||||
| return PUGL_SUCCESS; | |||||
| return (view->world->state == PUGL_WORLD_EXPOSING) ? PUGL_BAD_CALL | |||||
| : PUGL_SUCCESS; | |||||
| } | } | ||||
| PuglStatus | PuglStatus | ||||
| @@ -173,7 +254,7 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) | |||||
| type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER || | type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER || | ||||
| type == PUGL_LOOP_LEAVE); | type == PUGL_LOOP_LEAVE); | ||||
| const PuglEvent event = {{type, 0}}; | |||||
| const PuglEvent event = {{type, 0U}}; | |||||
| return puglDispatchEvent(view, &event); | return puglDispatchEvent(view, &event); | ||||
| } | } | ||||
| @@ -240,8 +321,12 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||||
| case PUGL_EXPOSE: | case PUGL_EXPOSE: | ||||
| assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED); | assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED); | ||||
| if (!(st0 = view->backend->enter(view, &event->expose))) { | 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; | break; | ||||
| @@ -9,8 +9,8 @@ | |||||
| #include "attributes.h" | #include "attributes.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/attributes.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stddef.h> | #include <stddef.h> | ||||
| @@ -18,9 +18,29 @@ | |||||
| PUGL_BEGIN_DECLS | 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 | /// Return true if `size` is a valid view size | ||||
| bool | 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) | /// Set hint to a default value if it is unset (PUGL_DONT_CARE) | ||||
| void | void | ||||
| @@ -34,8 +54,14 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len); | |||||
| void | void | ||||
| puglSetString(char** dest, const char* string); | 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 | /// Handle a changed string property | ||||
| PUGL_API | |||||
| PuglStatus | PuglStatus | ||||
| puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); | puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); | ||||
| @@ -56,8 +82,7 @@ PuglStatus | |||||
| puglDispatchSimpleEvent(PuglView* view, PuglEventType type); | puglDispatchSimpleEvent(PuglView* view, PuglEventType type); | ||||
| /// Process configure event while already in the graphics context | /// 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); | puglConfigure(PuglView* view, const PuglEvent* event); | ||||
| /// Dispatch `event` to `view`, entering graphics context if necessary | /// Dispatch `event` to `view`, entering graphics context if necessary | ||||
| @@ -5,7 +5,7 @@ | |||||
| #ifndef PUGL_SRC_MAC_H | #ifndef PUGL_SRC_MAC_H | ||||
| #define PUGL_SRC_MAC_H | #define PUGL_SRC_MAC_H | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #import <Cocoa/Cocoa.h> | #import <Cocoa/Cocoa.h> | ||||
| @@ -7,9 +7,10 @@ | |||||
| #include "mac.h" | #include "mac.h" | ||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "macros.h" | |||||
| #include "platform.h" | #include "platform.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #import <Cocoa/Cocoa.h> | #import <Cocoa/Cocoa.h> | ||||
| @@ -121,7 +122,7 @@ viewScreen(const PuglView* view) | |||||
| } | } | ||||
| static NSRect | static NSRect | ||||
| nsRectToPoints(PuglView* view, const NSRect rect) | |||||
| nsRectToPoints(const PuglView* view, const NSRect rect) | |||||
| { | { | ||||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | const double scaleFactor = [viewScreen(view) backingScaleFactor]; | ||||
| @@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) | |||||
| } | } | ||||
| static NSRect | static NSRect | ||||
| nsRectFromPoints(PuglView* view, const NSRect rect) | |||||
| nsRectFromPoints(const PuglView* view, const NSRect rect) | |||||
| { | { | ||||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | const double scaleFactor = [viewScreen(view) backingScaleFactor]; | ||||
| @@ -143,21 +144,15 @@ nsRectFromPoints(PuglView* view, const NSRect rect) | |||||
| } | } | ||||
| static NSPoint | static NSPoint | ||||
| nsPointFromPoints(PuglView* view, const NSPoint point) | |||||
| nsPointFromPoints(const PuglView* view, const NSPoint point) | |||||
| { | { | ||||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | const double scaleFactor = [viewScreen(view) backingScaleFactor]; | ||||
| return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor); | 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 | 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]; | const double scaleFactor = [viewScreen(view) backingScaleFactor]; | ||||
| @@ -188,7 +183,7 @@ getCurrentViewStyleFlags(PuglView* const view) | |||||
| } | } | ||||
| static PuglStatus | static PuglStatus | ||||
| dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| dispatchCurrentChildViewConfiguration(PuglView* const view) | |||||
| { | { | ||||
| const NSRect framePt = [view->impl->wrapperView frame]; | const NSRect framePt = [view->impl->wrapperView frame]; | ||||
| const NSRect framePx = nsRectFromPoints(view, framePt); | const NSRect framePx = nsRectFromPoints(view, framePt); | ||||
| @@ -197,14 +192,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| if (drawViewResize) { | |||||
| const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size]; | |||||
| [view->impl->drawView setFrameSize:sizePt]; | |||||
| } | |||||
| const PuglConfigureEvent ev = { | const PuglConfigureEvent ev = { | ||||
| PUGL_CONFIGURE, | PUGL_CONFIGURE, | ||||
| 0, | |||||
| 0U, | |||||
| (PuglCoord)framePx.origin.x, | (PuglCoord)framePx.origin.x, | ||||
| (PuglCoord)framePx.origin.y, | (PuglCoord)framePx.origin.y, | ||||
| (PuglSpan)framePx.size.width, | (PuglSpan)framePx.size.width, | ||||
| @@ -254,7 +244,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| const PuglConfigureEvent ev = { | const PuglConfigureEvent ev = { | ||||
| PUGL_CONFIGURE, | PUGL_CONFIGURE, | ||||
| 0, | |||||
| 0U, | |||||
| (PuglCoord)contentPx.origin.x, | (PuglCoord)contentPx.origin.x, | ||||
| (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), | (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), | ||||
| (PuglSpan)contentPx.size.width, | (PuglSpan)contentPx.size.width, | ||||
| @@ -322,7 +312,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| if (puglview->impl->window) { | if (puglview->impl->window) { | ||||
| [puglview->impl->window dispatchCurrentConfiguration]; | [puglview->impl->window dispatchCurrentConfiguration]; | ||||
| } else { | } else { | ||||
| dispatchCurrentChildViewConfiguration(puglview, true); | |||||
| dispatchCurrentChildViewConfiguration(puglview); | |||||
| } | } | ||||
| reshaped = false; | reshaped = false; | ||||
| } | } | ||||
| @@ -336,7 +326,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| const PuglExposeEvent ev = { | const PuglExposeEvent ev = { | ||||
| PUGL_EXPOSE, | PUGL_EXPOSE, | ||||
| 0, | |||||
| 0U, | |||||
| (PuglCoord)(rect.origin.x * scaleFactor), | (PuglCoord)(rect.origin.x * scaleFactor), | ||||
| (PuglCoord)viewY, | (PuglCoord)viewY, | ||||
| (PuglSpan)(rect.size.width * scaleFactor), | (PuglSpan)(rect.size.width * scaleFactor), | ||||
| @@ -350,9 +340,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||||
| - (NSSize)intrinsicContentSize | - (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) | ? sizePoints(puglview, defaultSize.width, defaultSize.height) | ||||
| : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); | : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); | ||||
| } | } | ||||
| @@ -468,7 +458,7 @@ keySymToSpecial(const NSEvent* const ev) | |||||
| return PUGL_KEY_PAD_9; | return PUGL_KEY_PAD_9; | ||||
| } | } | ||||
| return (PuglKey)0; | |||||
| return PUGL_KEY_NONE; | |||||
| } | } | ||||
| - (void)updateTrackingAreas | - (void)updateTrackingAreas | ||||
| @@ -501,7 +491,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const NSPoint rloc = [NSEvent mouseLocation]; | const NSPoint rloc = [NSEvent mouseLocation]; | ||||
| const PuglCrossingEvent ev = { | const PuglCrossingEvent ev = { | ||||
| type, | type, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -542,7 +532,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const NSPoint rloc = [NSEvent mouseLocation]; | const NSPoint rloc = [NSEvent mouseLocation]; | ||||
| const PuglMotionEvent ev = { | const PuglMotionEvent ev = { | ||||
| PUGL_MOTION, | PUGL_MOTION, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -577,7 +567,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const NSPoint rloc = [NSEvent mouseLocation]; | const NSPoint rloc = [NSEvent mouseLocation]; | ||||
| const PuglButtonEvent ev = { | const PuglButtonEvent ev = { | ||||
| PUGL_BUTTON_PRESS, | PUGL_BUTTON_PRESS, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -598,7 +588,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const NSPoint rloc = [NSEvent mouseLocation]; | const NSPoint rloc = [NSEvent mouseLocation]; | ||||
| const PuglButtonEvent ev = { | const PuglButtonEvent ev = { | ||||
| PUGL_BUTTON_RELEASE, | PUGL_BUTTON_RELEASE, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -638,11 +628,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const NSPoint wloc = [self eventLocation:event]; | const NSPoint wloc = [self eventLocation:event]; | ||||
| const NSPoint rloc = [NSEvent mouseLocation]; | 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]) { | if ([event hasPreciseScrollingDeltas]) { | ||||
| dx /= 10.0; | |||||
| dy /= 10.0; | |||||
| dx /= 20.0; | |||||
| dy /= 20.0; | |||||
| } | } | ||||
| const PuglScrollDirection dir = | const PuglScrollDirection dir = | ||||
| @@ -657,7 +647,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const PuglScrollEvent ev = { | const PuglScrollEvent ev = { | ||||
| PUGL_SCROLL, | PUGL_SCROLL, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -689,7 +679,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const PuglKeyEvent ev = { | const PuglKeyEvent ev = { | ||||
| PUGL_KEY_PRESS, | PUGL_KEY_PRESS, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -720,7 +710,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| const PuglKeyEvent ev = { | const PuglKeyEvent ev = { | ||||
| PUGL_KEY_RELEASE, | PUGL_KEY_RELEASE, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -832,7 +822,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||||
| PuglTextEvent ev = { | PuglTextEvent ev = { | ||||
| PUGL_TEXT, | PUGL_TEXT, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -861,7 +851,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||||
| - (void)flagsChanged:(NSEvent*)event | - (void)flagsChanged:(NSEvent*)event | ||||
| { | { | ||||
| const uint32_t mods = getModifiers(event); | const uint32_t mods = getModifiers(event); | ||||
| PuglKey special = (PuglKey)0; | |||||
| PuglKey special = PUGL_KEY_NONE; | |||||
| const uint16_t keyCode = [event keyCode]; | const uint16_t keyCode = [event keyCode]; | ||||
| if (flagDiffers(mods, puglview->impl->mods, PUGL_MOD_SHIFT)) { | 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 bool release = [event type] == NSEventTypeKeyUp; | ||||
| const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS, | const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS, | ||||
| 0, | |||||
| 0U, | |||||
| [event timestamp], | [event timestamp], | ||||
| wloc.x, | wloc.x, | ||||
| wloc.y, | wloc.y, | ||||
| @@ -924,7 +914,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||||
| - (void)timerTick:(NSTimer*)userTimer | - (void)timerTick:(NSTimer*)userTimer | ||||
| { | { | ||||
| const NSNumber* userInfo = userTimer.userInfo; | const NSNumber* userInfo = userTimer.userInfo; | ||||
| const PuglTimerEvent ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue}; | |||||
| const PuglTimerEvent ev = {PUGL_TIMER, 0U, userInfo.unsignedLongValue}; | |||||
| PuglEvent timerEvent; | PuglEvent timerEvent; | ||||
| timerEvent.timer = ev; | timerEvent.timer = ev; | ||||
| @@ -989,7 +979,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||||
| { | { | ||||
| (void)notification; | (void)notification; | ||||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0}}; | |||||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0U}}; | |||||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | ev.focus.mode = PUGL_CROSSING_NORMAL; | ||||
| puglDispatchEvent(window->puglview, &ev); | puglDispatchEvent(window->puglview, &ev); | ||||
| } | } | ||||
| @@ -998,7 +988,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||||
| { | { | ||||
| (void)notification; | (void)notification; | ||||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0}}; | |||||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0U}}; | |||||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | ev.focus.mode = PUGL_CROSSING_NORMAL; | ||||
| puglDispatchEvent(window->puglview, &ev); | puglDispatchEvent(window->puglview, &ev); | ||||
| } | } | ||||
| @@ -1117,17 +1107,18 @@ puglConstraint(const id item, | |||||
| constant:(CGFloat)constant]; | 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 width = view->sizeHints[hint].width; | ||||
| const PuglSpan height = view->sizeHints[hint].height; | const PuglSpan height = view->sizeHints[hint].height; | ||||
| if (!puglIsValidSize(view->sizeHints[hint])) { | |||||
| if (!puglIsValidArea(view->sizeHints[hint])) { | |||||
| return PUGL_FAILURE; | return PUGL_FAILURE; | ||||
| } | } | ||||
| switch (hint) { | switch (hint) { | ||||
| case PUGL_DEFAULT_SIZE: | case PUGL_DEFAULT_SIZE: | ||||
| case PUGL_CURRENT_SIZE: | |||||
| break; | break; | ||||
| case PUGL_MIN_SIZE: | case PUGL_MIN_SIZE: | ||||
| @@ -1150,38 +1141,18 @@ updateSizeHint(PuglView* const view, const PuglSizeHint hint) | |||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| static void | |||||
| updateSizeHints(PuglView* const view) | |||||
| PuglStatus | |||||
| puglUpdateSizeHints(PuglView* const view) | |||||
| { | { | ||||
| for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { | 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 NSScreen* const screen = viewScreen(view); | ||||
| const NSRect boundsPt = | const NSRect boundsPt = | ||||
| rectFromScreen(screen, | rectFromScreen(screen, | ||||
| @@ -1189,17 +1160,11 @@ getInitialFrame(PuglView* const view) | |||||
| ? [[(const NSView*)view->transientParent window] frame] | ? [[(const NSView*)view->transientParent window] frame] | ||||
| : [screen 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 | PuglStatus | ||||
| @@ -1247,11 +1212,12 @@ puglRealize(PuglView* view) | |||||
| CVDisplayLinkRelease(link); | 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 | // 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, | const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, | ||||
| framePx.origin.y / scaleFactor, | framePx.origin.y / scaleFactor, | ||||
| framePx.size.width / scaleFactor, | framePx.size.width / scaleFactor, | ||||
| @@ -1277,7 +1243,7 @@ puglRealize(PuglView* view) | |||||
| NSLayoutRelationGreaterThanOrEqual, | NSLayoutRelationGreaterThanOrEqual, | ||||
| view->sizeHints[PUGL_MIN_SIZE].height)]; | view->sizeHints[PUGL_MIN_SIZE].height)]; | ||||
| if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { | |||||
| if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { | |||||
| [impl->wrapperView | [impl->wrapperView | ||||
| addConstraint:puglConstraint(impl->wrapperView, | addConstraint:puglConstraint(impl->wrapperView, | ||||
| NSLayoutAttributeWidth, | NSLayoutAttributeWidth, | ||||
| @@ -1337,10 +1303,19 @@ puglRealize(PuglView* view) | |||||
| ((NSWindow*)window).delegate = | ((NSWindow*)window).delegate = | ||||
| [[PuglWindowDelegate alloc] initWithPuglWindow:window]; | [[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); | puglSetTransientParent(view, view->transientParent); | ||||
| updateSizeHints(view); | |||||
| puglUpdateSizeHints(view); | |||||
| [window setContentView:impl->wrapperView]; | [window setContentView:impl->wrapperView]; | ||||
| [view->world->impl->app activateIgnoringOtherApps:YES]; | [view->world->impl->app activateIgnoringOtherApps:YES]; | ||||
| @@ -1393,24 +1368,23 @@ puglUnrealize(PuglView* const view) | |||||
| PuglStatus | PuglStatus | ||||
| puglShow(PuglView* view, const PuglShowCommand command) | 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]) { | if (![window isVisible]) { | ||||
| [window setIsVisible:YES]; | [window setIsVisible:YES]; | ||||
| [view->impl->drawView setNeedsDisplay:YES]; | |||||
| [impl->drawView setNeedsDisplay:YES]; | |||||
| } | } | ||||
| switch (command) { | switch (command) { | ||||
| case PUGL_SHOW_PASSIVE: | case PUGL_SHOW_PASSIVE: | ||||
| break; | break; | ||||
| case PUGL_SHOW_RAISE: | case PUGL_SHOW_RAISE: | ||||
| [window orderFront:view->impl->wrapperView]; | |||||
| [window orderFront:impl->wrapperView]; | |||||
| break; | break; | ||||
| case PUGL_SHOW_FORCE_RAISE: | case PUGL_SHOW_FORCE_RAISE: | ||||
| [window orderFrontRegardless]; | [window orderFrontRegardless]; | ||||
| @@ -1583,6 +1557,10 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||||
| PuglStatus | PuglStatus | ||||
| puglSendEvent(PuglView* view, const PuglEvent* event) | puglSendEvent(PuglView* view, const PuglEvent* event) | ||||
| { | { | ||||
| if (!view->impl->window || view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| return PUGL_FAILURE; | |||||
| } | |||||
| if (event->type == PUGL_CLIENT) { | if (event->type == PUGL_CLIENT) { | ||||
| PuglEvent copiedEvent = *event; | PuglEvent copiedEvent = *event; | ||||
| @@ -1610,18 +1588,17 @@ puglSendEvent(PuglView* view, const PuglEvent* event) | |||||
| return PUGL_UNSUPPORTED; | return PUGL_UNSUPPORTED; | ||||
| } | } | ||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglWaitForEvent(PuglView* view) | |||||
| { | |||||
| return puglPollEvents(view->world, -1.0); | |||||
| } | |||||
| #endif | |||||
| PuglStatus | PuglStatus | ||||
| puglUpdate(PuglWorld* world, const double timeout) | puglUpdate(PuglWorld* world, const double timeout) | ||||
| { | { | ||||
| @autoreleasepool { | @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) { | if (world->type == PUGL_PROGRAM) { | ||||
| NSDate* date = | NSDate* date = | ||||
| ((timeout < 0) ? [NSDate distantFuture] | ((timeout < 0) ? [NSDate distantFuture] | ||||
| @@ -1650,19 +1627,13 @@ puglUpdate(PuglWorld* world, const double timeout) | |||||
| [view->impl->drawView displayIfNeeded]; | [view->impl->drawView displayIfNeeded]; | ||||
| } | } | ||||
| world->state = startState; | |||||
| } | } | ||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| return puglDispatchEvents(view->world); | |||||
| } | |||||
| #endif | |||||
| double | double | ||||
| puglGetTime(const PuglWorld* world) | puglGetTime(const PuglWorld* world) | ||||
| { | { | ||||
| @@ -1674,22 +1645,45 @@ puglGetTime(const PuglWorld* world) | |||||
| } | } | ||||
| PuglStatus | PuglStatus | ||||
| puglPostRedisplay(PuglView* view) | |||||
| puglObscureView(PuglView* view) | |||||
| { | { | ||||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| return PUGL_BAD_CALL; | |||||
| } | |||||
| [view->impl->drawView setNeedsDisplay:YES]; | [view->impl->drawView setNeedsDisplay:YES]; | ||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| PuglStatus | 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; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| @@ -1735,107 +1729,35 @@ puglGetScaleFactor(const PuglView* const view) | |||||
| } | } | ||||
| PuglStatus | 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; | 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) { | if (impl->window) { | ||||
| // Adjust top-level window frame | // 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 | // Set wrapper view origin | ||||
| const NSRect framePx = rectToNsRect(frame); | |||||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||||
| [impl->wrapperView setFrameOrigin:framePt.origin]; | [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 | // Dispatch new configuration | ||||
| return dispatchCurrentChildViewConfiguration(view, false); | |||||
| return dispatchCurrentChildViewConfiguration(view); | |||||
| } | } | ||||
| PuglStatus | 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; | 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 | // Set wrapper view size | ||||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | 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]; | const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; | ||||
| [impl->drawView setFrameSize:drawPt.size]; | [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 | PuglStatus | ||||
| @@ -1892,7 +1810,7 @@ puglPaste(PuglView* const view) | |||||
| { | { | ||||
| const PuglDataOfferEvent offer = { | const PuglDataOfferEvent offer = { | ||||
| PUGL_DATA_OFFER, | PUGL_DATA_OFFER, | ||||
| 0, | |||||
| 0U, | |||||
| puglGetTime(view->world), | puglGetTime(view->world), | ||||
| }; | }; | ||||
| @@ -1939,7 +1857,7 @@ puglAcceptOffer(PuglView* const view, | |||||
| PuglWrapperView* const wrapper = view->impl->wrapperView; | PuglWrapperView* const wrapper = view->impl->wrapperView; | ||||
| NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; | NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; | ||||
| if (!pasteboard) { | if (!pasteboard) { | ||||
| return PUGL_BAD_PARAMETER; | |||||
| return PUGL_UNKNOWN_ERROR; | |||||
| } | } | ||||
| const NSArray<NSPasteboardType>* const types = [pasteboard types]; | const NSArray<NSPasteboardType>* const types = [pasteboard types]; | ||||
| @@ -1950,13 +1868,12 @@ puglAcceptOffer(PuglView* const view, | |||||
| wrapper->dragOperation = NSDragOperationCopy; | wrapper->dragOperation = NSDragOperationCopy; | ||||
| wrapper->dragTypeIndex = typeIndex; | 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; | PuglEvent dataEvent; | ||||
| dataEvent.data = data; | dataEvent.data = data; | ||||
| puglDispatchEvent(view, &dataEvent); | |||||
| return PUGL_SUCCESS; | |||||
| return puglDispatchEvent(view, &dataEvent); | |||||
| } | } | ||||
| const void* | const void* | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "mac.h" | #include "mac.h" | ||||
| #include "stub.h" | #include "stub.h" | ||||
| #include "pugl/cairo.h" | |||||
| #include <pugl/cairo.h> | |||||
| #include <cairo-quartz.h> | #include <cairo-quartz.h> | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "mac.h" | #include "mac.h" | ||||
| #include "stub.h" | #include "stub.h" | ||||
| #include "pugl/gl.h" | |||||
| #include <pugl/gl.h> | |||||
| #ifndef __MAC_10_10 | #ifndef __MAC_10_10 | ||||
| # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core | # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "mac.h" | #include "mac.h" | ||||
| #include "stub.h" | #include "stub.h" | ||||
| #include "pugl/stub.h" | |||||
| #include <pugl/stub.h> | |||||
| #import <Cocoa/Cocoa.h> | #import <Cocoa/Cocoa.h> | ||||
| @@ -8,9 +8,9 @@ | |||||
| #include "stub.h" | #include "stub.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/stub.h" | |||||
| #include "pugl/vulkan.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <pugl/stub.h> | |||||
| #include <pugl/vulkan.h> | |||||
| #include <vulkan/vulkan_core.h> | #include <vulkan/vulkan_core.h> | ||||
| #include <vulkan/vulkan_macos.h> | #include <vulkan/vulkan_macos.h> | ||||
| @@ -1,20 +1,20 @@ | |||||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||||
| // Copyright 2012-2025 David Robillard <d@drobilla.net> | |||||
| // SPDX-License-Identifier: ISC | // 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 | #ifndef PUGL_PLATFORM_H | ||||
| #define PUGL_PLATFORM_H | #define PUGL_PLATFORM_H | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| PUGL_BEGIN_DECLS | PUGL_BEGIN_DECLS | ||||
| /// Allocate and initialise world internals (implemented once per platform) | /// Allocate and initialise world internals (implemented once per platform) | ||||
| PUGL_MALLOC_FUNC | |||||
| PuglWorldInternals* | |||||
| PUGL_MALLOC_FUNC PuglWorldInternals* | |||||
| puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); | puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); | ||||
| /// Destroy and free world internals (implemented once per platform) | /// Destroy and free world internals (implemented once per platform) | ||||
| @@ -22,14 +22,29 @@ void | |||||
| puglFreeWorldInternals(PuglWorld* world); | puglFreeWorldInternals(PuglWorld* world); | ||||
| /// Allocate and initialise view internals (implemented once per platform) | /// Allocate and initialise view internals (implemented once per platform) | ||||
| PUGL_MALLOC_FUNC | |||||
| PuglInternals* | |||||
| PUGL_MALLOC_FUNC PuglInternals* | |||||
| puglInitViewInternals(PuglWorld* world); | puglInitViewInternals(PuglWorld* world); | ||||
| /// Destroy and free view internals (implemented once per platform) | /// Destroy and free view internals (implemented once per platform) | ||||
| void | void | ||||
| puglFreeViewInternals(PuglView* view); | 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 | PUGL_END_DECLS | ||||
| #endif // PUGL_PLATFORM_H | #endif // PUGL_PLATFORM_H | ||||
| @@ -4,7 +4,8 @@ | |||||
| #ifndef PUGL_SRC_STUB_H | #ifndef PUGL_SRC_STUB_H | ||||
| #define PUGL_SRC_STUB_H | #define PUGL_SRC_STUB_H | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| #include <stddef.h> | #include <stddef.h> | ||||
| @@ -6,7 +6,7 @@ | |||||
| #include "attributes.h" | #include "attributes.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stddef.h> | #include <stddef.h> | ||||
| @@ -21,24 +21,20 @@ typedef struct PuglInternalsImpl PuglInternals; | |||||
| /// View hints | /// View hints | ||||
| typedef int PuglHints[PUGL_NUM_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 | /// Blob of arbitrary data | ||||
| typedef struct { | typedef struct { | ||||
| void* data; ///< Dynamically allocated data | void* data; ///< Dynamically allocated data | ||||
| size_t len; ///< Length of data in bytes | size_t len; ///< Length of data in bytes | ||||
| } PuglBlob; | } 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 | /// Stage of a view along its lifespan | ||||
| typedef enum { | typedef enum { | ||||
| PUGL_VIEW_STAGE_ALLOCATED, | PUGL_VIEW_STAGE_ALLOCATED, | ||||
| @@ -57,10 +53,9 @@ struct PuglViewImpl { | |||||
| uintptr_t transientParent; | uintptr_t transientParent; | ||||
| PuglConfigureEvent lastConfigure; | PuglConfigureEvent lastConfigure; | ||||
| PuglHints hints; | 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]; | char* strings[PUGL_NUM_STRING_HINTS]; | ||||
| int defaultX; | |||||
| int defaultY; | |||||
| PuglViewStage stage; | PuglViewStage stage; | ||||
| bool resizing; | bool resizing; | ||||
| }; | }; | ||||
| @@ -74,6 +69,7 @@ struct PuglWorldImpl { | |||||
| PuglView** views; | PuglView** views; | ||||
| char* strings[PUGL_NUM_STRING_HINTS]; | char* strings[PUGL_NUM_STRING_HINTS]; | ||||
| PuglWorldType type; | PuglWorldType type; | ||||
| PuglWorldState state; | |||||
| }; | }; | ||||
| /// Opaque surface used by graphics backend | /// Opaque surface used by graphics backend | ||||
| @@ -82,23 +78,21 @@ typedef void PuglSurface; | |||||
| /// Graphics backend interface | /// Graphics backend interface | ||||
| struct PuglBackendImpl { | struct PuglBackendImpl { | ||||
| /// Get visual information from display and setup view as necessary | /// 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 | /// Create surface and drawing context | ||||
| PUGL_WARN_UNUSED_RESULT | |||||
| PuglStatus (*create)(PuglView*); | |||||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*create)(PuglView*); | |||||
| /// Destroy surface and drawing context | /// Destroy surface and drawing context | ||||
| void (*destroy)(PuglView*); | void (*destroy)(PuglView*); | ||||
| /// Enter drawing context, for drawing if expose is non-null | /// 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 | /// 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 | /// Return the puglGetContext() handle for the application, if any | ||||
| void* (*getContext)(PuglView*); | void* (*getContext)(PuglView*); | ||||
| @@ -4,9 +4,10 @@ | |||||
| #include "win.h" | #include "win.h" | ||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "macros.h" | |||||
| #include "platform.h" | #include "platform.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <dwmapi.h> | #include <dwmapi.h> | ||||
| #include <windows.h> | #include <windows.h> | ||||
| @@ -42,13 +43,11 @@ | |||||
| #define PUGL_USER_TIMER_MIN 9470 | #define PUGL_USER_TIMER_MIN 9470 | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| # define PUGL_INIT_STRUCT \ | |||||
| {} | |||||
| #else | |||||
| # define PUGL_INIT_STRUCT \ | # define PUGL_INIT_STRUCT \ | ||||
| { \ | { \ | ||||
| 0 \ | |||||
| } | } | ||||
| #else | |||||
| # define PUGL_INIT_STRUCT {0} | |||||
| #endif | #endif | ||||
| typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void); | typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void); | ||||
| @@ -58,8 +57,12 @@ typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||||
| LRESULT CALLBACK | LRESULT CALLBACK | ||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | 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); | const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); | ||||
| if (len > 0) { | if (len > 0) { | ||||
| @@ -71,6 +74,30 @@ puglUtf8ToWideChar(const char* const utf8) | |||||
| return NULL; | 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* | static char* | ||||
| puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) | puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) | ||||
| { | { | ||||
| @@ -94,11 +121,7 @@ puglWinStatus(const BOOL success) | |||||
| static bool | static bool | ||||
| puglRegisterWindowClass(const char* name) | 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; | HMODULE module = NULL; | ||||
| if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | ||||
| @@ -109,7 +132,7 @@ puglRegisterWindowClass(const char* name) | |||||
| } | } | ||||
| WNDCLASSEX wc = PUGL_INIT_STRUCT; | WNDCLASSEX wc = PUGL_INIT_STRUCT; | ||||
| if (GetClassInfoEx(module, wname, &wc)) { | |||||
| if (GetClassInfoEx(module, nameArg, &wc)) { | |||||
| return true; // Already registered | return true; // Already registered | ||||
| } | } | ||||
| @@ -120,12 +143,10 @@ puglRegisterWindowClass(const char* name) | |||||
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | ||||
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); | wc.hCursor = LoadCursor(NULL, IDC_ARROW); | ||||
| wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | ||||
| wc.lpszClassName = wname; | |||||
| wc.lpszClassName = nameArg; | |||||
| const bool success = !!RegisterClassEx(&wc); | const bool success = !!RegisterClassEx(&wc); | ||||
| #ifdef UNICODE | |||||
| free(wname); | |||||
| #endif | |||||
| puglArgStringFree(nameArg); | |||||
| return success; | return success; | ||||
| } | } | ||||
| @@ -184,7 +205,7 @@ static double | |||||
| puglWinGetViewScaleFactor(const PuglView* const view) | puglWinGetViewScaleFactor(const PuglView* const view) | ||||
| { | { | ||||
| const HMODULE shcore = | const HMODULE shcore = | ||||
| LoadLibraryExA("Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||||
| LoadLibraryEx(TEXT("Shcore.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||||
| if (!shcore) { | if (!shcore) { | ||||
| return 1.0; | return 1.0; | ||||
| } | } | ||||
| @@ -219,7 +240,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) | |||||
| if (type == PUGL_PROGRAM) { | if (type == PUGL_PROGRAM) { | ||||
| HMODULE user32 = | HMODULE user32 = | ||||
| LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||||
| LoadLibraryEx(TEXT("user32.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||||
| if (user32) { | if (user32) { | ||||
| PFN_SetProcessDPIAware SetProcessDPIAware = | PFN_SetProcessDPIAware SetProcessDPIAware = | ||||
| (PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | (PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | ||||
| @@ -250,20 +271,6 @@ puglInitViewInternals(PuglWorld* PUGL_UNUSED(world)) | |||||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | 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 | PuglStatus | ||||
| puglRealize(PuglView* view) | puglRealize(PuglView* view) | ||||
| { | { | ||||
| @@ -287,9 +294,9 @@ puglRealize(PuglView* view) | |||||
| puglEnsureHint(view, PUGL_ALPHA_BITS, 8); | puglEnsureHint(view, PUGL_ALPHA_BITS, 8); | ||||
| // Get refresh rate for resize draw timer | // Get refresh rate for resize draw timer | ||||
| DEVMODEA devMode; | |||||
| DEVMODE devMode; | |||||
| memset(&devMode, 0, sizeof(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; | view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; | ||||
| // Register window class if necessary | // Register window class if necessary | ||||
| @@ -308,16 +315,16 @@ puglRealize(PuglView* view) | |||||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | ||||
| puglSetTransientParent(view, view->transientParent); | 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]) { | if (view->hints[PUGL_DARK_FRAME]) { | ||||
| const BOOL useDarkMode = TRUE; | const BOOL useDarkMode = TRUE; | ||||
| if ((DwmSetWindowAttribute(view->impl->hwnd, | |||||
| if ((DwmSetWindowAttribute(impl->hwnd, | |||||
| DWMWA_USE_IMMERSIVE_DARK_MODE, | DWMWA_USE_IMMERSIVE_DARK_MODE, | ||||
| &useDarkMode, | &useDarkMode, | ||||
| sizeof(useDarkMode)) != S_OK)) { | sizeof(useDarkMode)) != S_OK)) { | ||||
| DwmSetWindowAttribute(view->impl->hwnd, | |||||
| DwmSetWindowAttribute(impl->hwnd, | |||||
| PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE, | PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE, | ||||
| &useDarkMode, | &useDarkMode, | ||||
| sizeof(useDarkMode)); | sizeof(useDarkMode)); | ||||
| @@ -343,51 +350,48 @@ puglUnrealize(PuglView* const view) | |||||
| view->backend->destroy(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)); | 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 | PuglStatus | ||||
| puglShow(PuglView* view, const PuglShowCommand command) | puglShow(PuglView* view, const PuglShowCommand command) | ||||
| { | { | ||||
| PuglInternals* impl = view->impl; | 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 | PuglStatus | ||||
| puglHide(PuglView* view) | 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; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| @@ -408,14 +412,11 @@ puglFreeViewInternals(PuglView* view) | |||||
| void | void | ||||
| puglFreeWorldInternals(PuglWorld* world) | 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); | free(world->impl); | ||||
| } | } | ||||
| @@ -426,14 +427,14 @@ keyInRange(const WPARAM winSym, | |||||
| const PuglKey puglMin) | const PuglKey puglMin) | ||||
| { | { | ||||
| return (winSym >= winMin && winSym <= winMax) | return (winSym >= winMin && winSym <= winMax) | ||||
| ? (PuglKey)(puglMin + (winSym - winMin)) | |||||
| : (PuglKey)0; | |||||
| ? (PuglKey)((WPARAM)puglMin + (winSym - winMin)) | |||||
| : PUGL_KEY_NONE; | |||||
| } | } | ||||
| static PuglKey | static PuglKey | ||||
| keySymToSpecial(const WPARAM sym, const bool ext) | 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)) || | if ((key = keyInRange(sym, VK_F1, VK_F12, PUGL_KEY_F1)) || | ||||
| (key = keyInRange(sym, | (key = keyInRange(sym, | ||||
| VK_PRIOR, | VK_PRIOR, | ||||
| @@ -482,23 +483,26 @@ keySymToSpecial(const WPARAM sym, const bool ext) | |||||
| // clang-format on | // clang-format on | ||||
| } | } | ||||
| return (PuglKey)0; | |||||
| return PUGL_KEY_NONE; | |||||
| } | |||||
| static bool | |||||
| is_toggled(int vkey) | |||||
| { | |||||
| return (unsigned)GetKeyState(vkey) & 1U; | |||||
| } | } | ||||
| static uint32_t | static uint32_t | ||||
| getModifiers(void) | 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 | static void | ||||
| @@ -673,7 +677,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||||
| const PuglCrossingEvent ev = { | const PuglCrossingEvent ev = { | ||||
| type, | type, | ||||
| 0, | |||||
| 0U, | |||||
| GetMessageTime() / 1e3, | GetMessageTime() / 1e3, | ||||
| (double)pos.x, | (double)pos.x, | ||||
| (double)pos.y, | (double)pos.y, | ||||
| @@ -683,7 +687,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||||
| PUGL_CROSSING_NORMAL, | PUGL_CROSSING_NORMAL, | ||||
| }; | }; | ||||
| PuglEvent crossingEvent = {{type, 0}}; | |||||
| PuglEvent crossingEvent = {{type, 0U}}; | |||||
| crossingEvent.crossing = ev; | crossingEvent.crossing = ev; | ||||
| puglDispatchEvent(view, &crossingEvent); | puglDispatchEvent(view, &crossingEvent); | ||||
| } | } | ||||
| @@ -693,8 +697,8 @@ constrainAspect(const PuglView* const view, | |||||
| RECT* const size, | RECT* const size, | ||||
| const WPARAM wParam) | 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 minA = (float)minAspect.width / (float)minAspect.height; | ||||
| const float maxA = (float)maxAspect.width / (float)maxAspect.height; | const float maxA = (float)maxAspect.width / (float)maxAspect.height; | ||||
| @@ -733,7 +737,7 @@ constrainAspect(const PuglView* const view, | |||||
| static LRESULT | static LRESULT | ||||
| handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | 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}; | RECT rect = {0, 0, 0, 0}; | ||||
| POINT pt = {0, 0}; | POINT pt = {0, 0}; | ||||
| MINMAXINFO* mmi = NULL; | MINMAXINFO* mmi = NULL; | ||||
| @@ -779,8 +783,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| handleConfigure(view, &event); | handleConfigure(view, &event); | ||||
| break; | break; | ||||
| case WM_SIZING: | 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); | constrainAspect(view, (RECT*)lParam, wParam); | ||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| @@ -795,7 +799,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| break; | break; | ||||
| case WM_TIMER: | case WM_TIMER: | ||||
| if (wParam >= PUGL_USER_TIMER_MIN) { | if (wParam >= PUGL_USER_TIMER_MIN) { | ||||
| PuglEvent ev = {{PUGL_TIMER, 0}}; | |||||
| PuglEvent ev = {{PUGL_TIMER, 0U}}; | |||||
| ev.timer.id = wParam - PUGL_USER_TIMER_MIN; | ev.timer.id = wParam - PUGL_USER_TIMER_MIN; | ||||
| puglDispatchEvent(view, &ev); | puglDispatchEvent(view, &ev); | ||||
| } | } | ||||
| @@ -812,7 +816,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| mmi = (MINMAXINFO*)lParam; | mmi = (MINMAXINFO*)lParam; | ||||
| mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; | mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; | ||||
| mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height; | 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.x = view->sizeHints[PUGL_MAX_SIZE].width; | ||||
| mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; | mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; | ||||
| } | } | ||||
| @@ -948,8 +952,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| PuglStatus | PuglStatus | ||||
| puglGrabFocus(PuglView* view) | puglGrabFocus(PuglView* view) | ||||
| { | { | ||||
| SetFocus(view->impl->hwnd); | |||||
| return PUGL_SUCCESS; | |||||
| return puglWinStatus(!!SetFocus(view->impl->hwnd)); | |||||
| } | } | ||||
| bool | bool | ||||
| @@ -1023,20 +1026,32 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags) | |||||
| const bool newMaximized = styleIsMaximized(flags); | const bool newMaximized = styleIsMaximized(flags); | ||||
| if (oldMaximized != newMaximized) { | if (oldMaximized != newMaximized) { | ||||
| ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); | ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); | ||||
| puglPostRedisplay(view); | |||||
| puglObscureView(view); | |||||
| } | } | ||||
| return PUGL_SUCCESS; | 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 | PuglStatus | ||||
| puglStartTimer(PuglView* view, uintptr_t id, double timeout) | puglStartTimer(PuglView* view, uintptr_t id, double timeout) | ||||
| { | { | ||||
| const UINT msec = (UINT)floor(timeout * 1000.0); | 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 | PuglStatus | ||||
| @@ -1048,32 +1063,24 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||||
| PuglStatus | PuglStatus | ||||
| puglSendEvent(PuglView* view, const PuglEvent* event) | puglSendEvent(PuglView* view, const PuglEvent* event) | ||||
| { | { | ||||
| if (!view->impl->hwnd || view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| return PUGL_FAILURE; | |||||
| } | |||||
| if (event->type == PUGL_CLOSE) { | 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) { | 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; | return PUGL_UNSUPPORTED; | ||||
| } | } | ||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglWaitForEvent(PuglView* PUGL_UNUSED(view)) | |||||
| { | |||||
| WaitMessage(); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| #endif | |||||
| static PuglStatus | static PuglStatus | ||||
| puglDispatchViewEvents(PuglView* view) | puglDispatchViewEvents(PuglView* view) | ||||
| { | { | ||||
| @@ -1118,21 +1125,31 @@ puglDispatchWinEvents(PuglWorld* world) | |||||
| PuglStatus | PuglStatus | ||||
| puglUpdate(PuglWorld* world, double timeout) | 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) { | 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); | st = puglDispatchWinEvents(world); | ||||
| } else { | } 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); | UpdateWindow(world->views[i]->impl->hwnd); | ||||
| } | } | ||||
| world->state = startState; | |||||
| return st; | return st; | ||||
| } | } | ||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* view) | |||||
| { | |||||
| return puglUpdate(view->world, 0.0); | |||||
| } | |||||
| #endif | |||||
| LRESULT CALLBACK | LRESULT CALLBACK | ||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| @@ -1188,23 +1198,35 @@ puglGetTime(const PuglWorld* world) | |||||
| } | } | ||||
| PuglStatus | 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 | 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 | PuglNativeView | ||||
| @@ -1218,19 +1240,18 @@ puglViewStringChanged(PuglView* const view, | |||||
| const PuglStringHint key, | const PuglStringHint key, | ||||
| const char* const value) | const char* const value) | ||||
| { | { | ||||
| PuglStatus st = PUGL_SUCCESS; | |||||
| if (!view->impl->hwnd) { | if (!view->impl->hwnd) { | ||||
| return PUGL_SUCCESS; | |||||
| return st; | |||||
| } | } | ||||
| if (key == PUGL_WINDOW_TITLE) { | 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 | static RECT | ||||
| @@ -1256,44 +1277,8 @@ puglGetScaleFactor(const PuglView* const view) | |||||
| } | } | ||||
| PuglStatus | 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( | const RECT rect = adjustedWindowRect( | ||||
| view, x, y, view->lastConfigure.width, view->lastConfigure.height); | view, x, y, view->lastConfigure.width, view->lastConfigure.height); | ||||
| @@ -1305,19 +1290,10 @@ puglSetPosition(PuglView* const view, const int x, const int y) | |||||
| } | } | ||||
| PuglStatus | 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, | const RECT rect = adjustedWindowRect(view, | ||||
| view->lastConfigure.x, | view->lastConfigure.x, | ||||
| view->lastConfigure.y, | view->lastConfigure.y, | ||||
| @@ -1336,21 +1312,6 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||||
| flags)); | 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 | PuglStatus | ||||
| puglSetTransientParent(PuglView* view, PuglNativeView parent) | puglSetTransientParent(PuglView* view, PuglNativeView parent) | ||||
| { | { | ||||
| @@ -1394,15 +1355,14 @@ puglAcceptOffer(PuglView* const view, | |||||
| const PuglDataEvent data = { | const PuglDataEvent data = { | ||||
| PUGL_DATA, | PUGL_DATA, | ||||
| 0, | |||||
| 0U, | |||||
| GetMessageTime() / 1e3, | GetMessageTime() / 1e3, | ||||
| 0, | 0, | ||||
| }; | }; | ||||
| PuglEvent dataEvent; | PuglEvent dataEvent; | ||||
| dataEvent.data = data; | dataEvent.data = data; | ||||
| puglDispatchEvent(view, &dataEvent); | |||||
| return PUGL_SUCCESS; | |||||
| return puglDispatchEvent(view, &dataEvent); | |||||
| } | } | ||||
| const void* | const void* | ||||
| @@ -1412,8 +1372,21 @@ puglGetClipboard(PuglView* const view, | |||||
| { | { | ||||
| PuglInternals* const impl = view->impl; | 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; | return NULL; | ||||
| } | } | ||||
| @@ -1424,15 +1397,14 @@ puglGetClipboard(PuglView* const view, | |||||
| return NULL; | 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); | GlobalUnlock(mem); | ||||
| CloseClipboard(); | CloseClipboard(); | ||||
| *len = view->impl->clipboard.len; | |||||
| return view->impl->clipboard.data; | |||||
| *len = impl->clipboard.len; | |||||
| return impl->clipboard.data; | |||||
| } | } | ||||
| PuglStatus | PuglStatus | ||||
| @@ -1443,7 +1415,7 @@ puglSetClipboard(PuglView* const view, | |||||
| { | { | ||||
| PuglInternals* const impl = view->impl; | PuglInternals* const impl = view->impl; | ||||
| PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len); | |||||
| PuglStatus st = puglSetBlob(&impl->clipboard, data, len); | |||||
| if (st) { | if (st) { | ||||
| return st; | return st; | ||||
| } | } | ||||
| @@ -1488,14 +1460,13 @@ puglPaste(PuglView* const view) | |||||
| { | { | ||||
| const PuglDataOfferEvent offer = { | const PuglDataOfferEvent offer = { | ||||
| PUGL_DATA_OFFER, | PUGL_DATA_OFFER, | ||||
| 0, | |||||
| 0U, | |||||
| GetMessageTime() / 1e3, | GetMessageTime() / 1e3, | ||||
| }; | }; | ||||
| PuglEvent offerEvent; | PuglEvent offerEvent; | ||||
| offerEvent.offer = offer; | offerEvent.offer = offer; | ||||
| puglDispatchEvent(view, &offerEvent); | |||||
| return PUGL_SUCCESS; | |||||
| return puglDispatchEvent(view, &offerEvent); | |||||
| } | } | ||||
| static const TCHAR* const cursor_ids[] = { | static const TCHAR* const cursor_ids[] = { | ||||
| @@ -1563,42 +1534,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) | |||||
| return pfd; | 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 | PuglStatus | ||||
| @@ -1613,28 +1560,35 @@ puglWinCreateWindow(PuglView* const view, | |||||
| PuglNativeView parent = view->parent ? view->parent : view->transientParent; | PuglNativeView parent = view->parent ? view->parent : view->transientParent; | ||||
| // Calculate initial window rectangle | // 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); | AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); | ||||
| ArgStringChar* const classNameArg = puglArgStringNew(className); | |||||
| ArgStringChar* const titleArg = puglArgStringNew(title); | |||||
| // Create window and get drawing context | // 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; | return PUGL_REALIZE_FAILED; | ||||
| } | } | ||||
| @@ -1660,7 +1614,6 @@ puglWinConfigure(PuglView* view) | |||||
| { | { | ||||
| PuglInternals* const impl = view->impl; | PuglInternals* const impl = view->impl; | ||||
| PuglStatus st = PUGL_SUCCESS; | PuglStatus st = PUGL_SUCCESS; | ||||
| if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { | if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { | ||||
| return st; | return st; | ||||
| } | } | ||||
| @@ -1673,20 +1626,18 @@ puglWinConfigure(PuglView* view) | |||||
| DestroyWindow(impl->hwnd); | DestroyWindow(impl->hwnd); | ||||
| impl->hwnd = NULL; | impl->hwnd = NULL; | ||||
| impl->hdc = NULL; | impl->hdc = NULL; | ||||
| return PUGL_SET_FORMAT_FAILED; | |||||
| st = PUGL_SET_FORMAT_FAILED; | |||||
| } | } | ||||
| return PUGL_SUCCESS; | |||||
| return st; | |||||
| } | } | ||||
| PuglStatus | PuglStatus | ||||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose) | 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 | PuglStatus | ||||
| @@ -6,7 +6,7 @@ | |||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <windows.h> | #include <windows.h> | ||||
| @@ -37,28 +37,19 @@ struct PuglInternalsImpl { | |||||
| bool fullscreen; | bool fullscreen; | ||||
| }; | }; | ||||
| PUGL_API | |||||
| PuglWinPFD | |||||
| PUGL_API PuglWinPFD | |||||
| puglWinGetPixelFormatDescriptor(const PuglHints hints); | 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); | 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); | puglWinConfigure(PuglView* view); | ||||
| PUGL_WARN_UNUSED_RESULT | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose); | 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); | puglWinLeave(PuglView* view, const PuglExposeEvent* expose); | ||||
| #endif // PUGL_SRC_WIN_H | #endif // PUGL_SRC_WIN_H | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "win.h" | #include "win.h" | ||||
| #include "pugl/cairo.h" | |||||
| #include <pugl/cairo.h> | |||||
| #include <cairo-win32.h> | #include <cairo-win32.h> | ||||
| #include <cairo.h> | #include <cairo.h> | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "win.h" | #include "win.h" | ||||
| #include "pugl/gl.h" | |||||
| #include <pugl/gl.h> | |||||
| #include <windows.h> | #include <windows.h> | ||||
| @@ -14,30 +14,42 @@ | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #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*); | typedef HGLRC(WINAPI* WglCreateContextAttribs)(HDC, HGLRC, const int*); | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "win.h" | #include "win.h" | ||||
| #include "pugl/stub.h" | |||||
| #include <pugl/stub.h> | |||||
| static PuglStatus | static PuglStatus | ||||
| puglWinStubConfigure(PuglView* view) | puglWinStubConfigure(PuglView* view) | ||||
| @@ -7,7 +7,7 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "win.h" | #include "win.h" | ||||
| #include "pugl/vulkan.h" | |||||
| #include <pugl/vulkan.h> | |||||
| #include <vulkan/vulkan.h> | #include <vulkan/vulkan.h> | ||||
| #include <vulkan/vulkan_win32.h> | #include <vulkan/vulkan_win32.h> | ||||
| @@ -11,7 +11,7 @@ | |||||
| #include "platform.h" | #include "platform.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <X11/X.h> | #include <X11/X.h> | ||||
| #include <X11/Xatom.h> | #include <X11/Xatom.h> | ||||
| @@ -69,13 +69,11 @@ | |||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| # define PUGL_INIT_STRUCT \ | |||||
| {} | |||||
| #else | |||||
| # define PUGL_INIT_STRUCT \ | # define PUGL_INIT_STRUCT \ | ||||
| { \ | { \ | ||||
| 0 \ | |||||
| } | } | ||||
| #else | |||||
| # define PUGL_INIT_STRUCT {0} | |||||
| #endif | #endif | ||||
| enum WmClientStateMessageAction { | enum WmClientStateMessageAction { | ||||
| @@ -396,8 +394,15 @@ findView(PuglWorld* const world, const Window window) | |||||
| return NULL; | 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) { | if (!view->impl->win) { | ||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| @@ -407,40 +412,44 @@ updateSizeHints(const PuglView* const view) | |||||
| XSizeHints sizeHints = PUGL_INIT_STRUCT; | XSizeHints sizeHints = PUGL_INIT_STRUCT; | ||||
| if (!view->hints[PUGL_RESIZABLE]) { | 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.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 { | } else { | ||||
| // Avoid setting PBaseSize for top level views to avoid window manager bugs | // 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.flags |= PBaseSize; | ||||
| sizeHints.base_width = defaultSize.width; | sizeHints.base_width = defaultSize.width; | ||||
| sizeHints.base_height = defaultSize.height; | 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.flags |= PMinSize; | ||||
| sizeHints.min_width = minSize.width; | sizeHints.min_width = minSize.width; | ||||
| sizeHints.min_height = minSize.height; | 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.flags |= PMaxSize; | ||||
| sizeHints.max_width = maxSize.width; | sizeHints.max_width = maxSize.width; | ||||
| sizeHints.max_height = maxSize.height; | 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.flags |= PAspect; | ||||
| sizeHints.min_aspect.x = minAspect.width; | sizeHints.min_aspect.x = minAspect.width; | ||||
| sizeHints.min_aspect.y = minAspect.height; | sizeHints.min_aspect.y = minAspect.height; | ||||
| @@ -448,8 +457,8 @@ updateSizeHints(const PuglView* const view) | |||||
| sizeHints.max_aspect.y = maxAspect.height; | 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.flags |= PAspect; | ||||
| sizeHints.min_aspect.x = fixedAspect.width; | sizeHints.min_aspect.x = fixedAspect.width; | ||||
| sizeHints.min_aspect.y = fixedAspect.height; | sizeHints.min_aspect.y = fixedAspect.height; | ||||
| @@ -513,48 +522,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) | |||||
| board->data.len = 0; | 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 | PuglStatus | ||||
| @@ -609,16 +591,17 @@ puglRealize(PuglView* const view) | |||||
| attr.event_mask |= StructureNotifyMask; | attr.event_mask |= StructureNotifyMask; | ||||
| attr.event_mask |= VisibilityChangeMask; | 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 | // Create the window | ||||
| impl->win = XCreateWindow(display, | impl->win = XCreateWindow(display, | ||||
| parent, | parent, | ||||
| initialFrame.x, | |||||
| initialFrame.y, | |||||
| initialFrame.width, | |||||
| initialFrame.height, | |||||
| initialPos.x, | |||||
| initialPos.y, | |||||
| initialSize.width, | |||||
| initialSize.height, | |||||
| 0, | 0, | ||||
| impl->vi->depth, | impl->vi->depth, | ||||
| InputOutput, | InputOutput, | ||||
| @@ -650,7 +633,7 @@ puglRealize(PuglView* const view) | |||||
| if (XRRQueryExtension(display, &ignored, &ignored)) { | if (XRRQueryExtension(display, &ignored, &ignored)) { | ||||
| // Set refresh rate hint to the real refresh rate | // Set refresh rate hint to the real refresh rate | ||||
| XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent); | XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent); | ||||
| short current_rate = XRRConfigCurrentRate(conf); | |||||
| const short current_rate = XRRConfigCurrentRate(conf); | |||||
| view->hints[PUGL_REFRESH_RATE] = current_rate; | view->hints[PUGL_REFRESH_RATE] = current_rate; | ||||
| XRRFreeScreenConfigInfo(conf); | XRRFreeScreenConfigInfo(conf); | ||||
| @@ -663,7 +646,7 @@ puglRealize(PuglView* const view) | |||||
| XSetClassHint(display, impl->win, &classHint); | XSetClassHint(display, impl->win, &classHint); | ||||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | ||||
| puglSetTransientParent(view, view->transientParent); | puglSetTransientParent(view, view->transientParent); | ||||
| updateSizeHints(view); | |||||
| puglUpdateSizeHints(view); | |||||
| // Set PID and hostname so the window manager can access our process | // Set PID and hostname so the window manager can access our process | ||||
| char hostname[256] = PUGL_INIT_STRUCT; | char hostname[256] = PUGL_INIT_STRUCT; | ||||
| @@ -759,21 +742,22 @@ puglUnrealize(PuglView* const view) | |||||
| PuglStatus | PuglStatus | ||||
| puglShow(PuglView* const view, const PuglShowCommand command) | 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) { | if (!st) { | ||||
| switch (command) { | switch (command) { | ||||
| case PUGL_SHOW_PASSIVE: | case PUGL_SHOW_PASSIVE: | ||||
| XMapWindow(view->world->impl->display, view->impl->win); | |||||
| XMapWindow(view->world->impl->display, impl->win); | |||||
| break; | break; | ||||
| case PUGL_SHOW_RAISE: | case PUGL_SHOW_RAISE: | ||||
| case PUGL_SHOW_FORCE_RAISE: | case PUGL_SHOW_FORCE_RAISE: | ||||
| XMapRaised(view->world->impl->display, view->impl->win); | |||||
| XMapRaised(view->world->impl->display, impl->win); | |||||
| break; | break; | ||||
| } | } | ||||
| if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) { | 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 | PuglStatus | ||||
| puglHide(PuglView* const view) | puglHide(PuglView* const view) | ||||
| { | { | ||||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| return PUGL_BAD_CALL; | |||||
| } | |||||
| XUnmapWindow(view->world->impl->display, view->impl->win); | XUnmapWindow(view->world->impl->display, view->impl->win); | ||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| @@ -817,13 +805,13 @@ keyInRange(const KeySym xSym, | |||||
| const PuglKey puglMin) | const PuglKey puglMin) | ||||
| { | { | ||||
| return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin)) | return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin)) | ||||
| : (PuglKey)0; | |||||
| : PUGL_KEY_NONE; | |||||
| } | } | ||||
| static PuglKey | static PuglKey | ||||
| keySymToSpecial(const KeySym sym) | keySymToSpecial(const KeySym sym) | ||||
| { | { | ||||
| PuglKey key = (PuglKey)0; | |||||
| PuglKey key = PUGL_KEY_NONE; | |||||
| if ((key = keyInRange(sym, XK_F1, XK_F12, PUGL_KEY_F1)) || | 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_Page_Up, XK_End, PUGL_KEY_PAGE_UP)) || | ||||
| (key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) || | (key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) || | ||||
| @@ -856,7 +844,7 @@ keySymToSpecial(const KeySym sym) | |||||
| } | } | ||||
| // clang-format on | // clang-format on | ||||
| return (PuglKey)0; | |||||
| return PUGL_KEY_NONE; | |||||
| } | } | ||||
| static int | 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); | 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 | // Lookup shifted key for possible text event | ||||
| xevent->xkey.state = state; | xevent->xkey.state = state; | ||||
| @@ -1028,7 +1017,7 @@ translateClientMessage(PuglView* const view, XClientMessageEvent message) | |||||
| { | { | ||||
| Display* const display = view->world->impl->display; | Display* const display = view->world->impl->display; | ||||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | 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) { | if (message.message_type == atoms->WM_PROTOCOLS) { | ||||
| const Atom protocol = (Atom)message.data.l[0]; | const Atom protocol = (Atom)message.data.l[0]; | ||||
| @@ -1101,17 +1090,19 @@ getCurrentConfiguration(PuglView* const view) | |||||
| XWindowAttributes attrs; | XWindowAttributes attrs; | ||||
| XGetWindowAttributes(display, view->impl->win, &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; | 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 | // 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.width = (PuglSpan)attrs.width; | ||||
| configureEvent.configure.height = (PuglSpan)attrs.height; | configureEvent.configure.height = (PuglSpan)attrs.height; | ||||
| configureEvent.configure.style = getCurrentViewStyleFlags(view); | configureEvent.configure.style = getCurrentViewStyleFlags(view); | ||||
| @@ -1140,7 +1131,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||||
| { | { | ||||
| const PuglInternals* const impl = view->impl; | const PuglInternals* const impl = view->impl; | ||||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | 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) { | if (message.atom == atoms->NET_WM_STATE) { | ||||
| // Get all the current states set in the window hints | // Get all the current states set in the window hints | ||||
| @@ -1190,7 +1181,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||||
| static PuglEvent | static PuglEvent | ||||
| translateEvent(PuglView* const view, XEvent xevent) | 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; | event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0; | ||||
| switch (xevent.type) { | switch (xevent.type) { | ||||
| @@ -1211,6 +1202,9 @@ translateEvent(PuglView* const view, XEvent xevent) | |||||
| view->impl->mapped = false; | view->impl->mapped = false; | ||||
| event = makeConfigureEvent(view); | event = makeConfigureEvent(view); | ||||
| break; | break; | ||||
| case DestroyNotify: | |||||
| view->impl->win = None; | |||||
| break; | |||||
| case ConfigureNotify: | case ConfigureNotify: | ||||
| event = makeConfigureEvent(view); | event = makeConfigureEvent(view); | ||||
| event.configure.width = (PuglSpan)xevent.xconfigure.width; | event.configure.width = (PuglSpan)xevent.xconfigure.width; | ||||
| @@ -1503,7 +1497,7 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||||
| PuglInternals* const impl = view->impl; | PuglInternals* const impl = view->impl; | ||||
| Display* const display = view->world->impl->display; | Display* const display = view->world->impl->display; | ||||
| XEvent xev = PUGL_INIT_STRUCT; | XEvent xev = PUGL_INIT_STRUCT; | ||||
| if (!impl->win) { | |||||
| if (!impl->win || view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| return PUGL_FAILURE; | return PUGL_FAILURE; | ||||
| } | } | ||||
| @@ -1534,21 +1528,11 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||||
| return PUGL_UNSUPPORTED; | 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 | static void | ||||
| mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) | mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) | ||||
| { | { | ||||
| if (!dst->type) { | if (!dst->type) { | ||||
| if (src->width > 0.0 && src->height > 0.0) { | |||||
| if (src->width && src->height) { | |||||
| *dst = *src; | *dst = *src; | ||||
| } | } | ||||
| } else { | } else { | ||||
| @@ -1612,7 +1596,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||||
| Display* const display = view->world->impl->display; | Display* const display = view->world->impl->display; | ||||
| const Atom selection = event->selection; | const Atom selection = event->selection; | ||||
| PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection); | PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection); | ||||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0}}; | |||||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0U}}; | |||||
| if (event->target == atoms->TARGETS) { | if (event->target == atoms->TARGETS) { | ||||
| // Notification of available datatypes | // Notification of available datatypes | ||||
| @@ -1622,7 +1606,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||||
| view, event->requestor, event->property, &numFormats, &formats) && | view, event->requestor, event->property, &numFormats, &formats) && | ||||
| !setClipboardFormats(view, board, numFormats, formats)) { | !setClipboardFormats(view, board, numFormats, formats)) { | ||||
| const PuglDataOfferEvent offer = { | const PuglDataOfferEvent offer = { | ||||
| PUGL_DATA_OFFER, 0, (double)event->time / 1e3}; | |||||
| PUGL_DATA_OFFER, 0U, (double)event->time / 1e3}; | |||||
| puglEvent.offer = offer; | puglEvent.offer = offer; | ||||
| board->acceptedFormatIndex = UINT32_MAX; | board->acceptedFormatIndex = UINT32_MAX; | ||||
| @@ -1699,21 +1683,24 @@ handleSelectionRequest(const PuglWorld* const world, | |||||
| } | } | ||||
| /// Flush pending configure and expose events for all views | /// 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) | flushExposures(PuglWorld* const world) | ||||
| { | { | ||||
| PuglStatus st0 = PUGL_SUCCESS; | PuglStatus st0 = PUGL_SUCCESS; | ||||
| PuglStatus st1 = PUGL_SUCCESS; | PuglStatus st1 = PUGL_SUCCESS; | ||||
| PuglStatus st2 = PUGL_SUCCESS; | PuglStatus st2 = PUGL_SUCCESS; | ||||
| // Send update events so the application can trigger redraws | |||||
| for (size_t i = 0; i < world->numViews; ++i) { | 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) | // Copy and reset pending events (in case their handlers write new ones) | ||||
| const PuglEvent configure = view->impl->pendingConfigure; | 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) { | for (size_t i = 0; i < world->impl->numTimers; ++i) { | ||||
| if (world->impl->timers[i].alarm == notify->alarm) { | 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; | event.timer.id = world->impl->timers[i].id; | ||||
| puglDispatchEvent(world->impl->timers[i].view, &event); | puglDispatchEvent(world->impl->timers[i].view, &event); | ||||
| } | } | ||||
| @@ -1851,26 +1838,24 @@ dispatchX11Events(PuglWorld* const world) | |||||
| return st; | return st; | ||||
| } | } | ||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglProcessEvents(PuglView* const view) | |||||
| { | |||||
| return puglUpdate(view->world, 0.0); | |||||
| } | |||||
| #endif | |||||
| PuglStatus | PuglStatus | ||||
| puglUpdate(PuglWorld* const world, const double timeout) | 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) { | 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) { | } else if (timeout <= 0.001) { | ||||
| st0 = dispatchX11Events(world); | st0 = dispatchX11Events(world); | ||||
| } else { | } 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; | return st0 ? st0 : st1; | ||||
| } | } | ||||
| @@ -1896,38 +1879,52 @@ double | |||||
| puglGetTime(const PuglWorld* const world) | puglGetTime(const PuglWorld* const world) | ||||
| { | { | ||||
| struct timespec ts; | 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) - | return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - | ||||
| world->startTime; | world->startTime; | ||||
| } | } | ||||
| PuglStatus | 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 | 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 | // Currently dispatching events, add/expand expose for the loop end | ||||
| mergeExposeEvents(&view->impl->pendingExpose.expose, &event); | mergeExposeEvents(&view->impl->pendingExpose.expose, &event); | ||||
| } else if (view->world->state == PUGL_WORLD_EXPOSING) { | |||||
| st = PUGL_BAD_CALL; | |||||
| } else if (view->impl->win) { | } else if (view->impl->win) { | ||||
| // Not dispatching events, send an X expose so we wake up next time | // 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; | exposeEvent.expose = event; | ||||
| return puglSendEvent(view, &exposeEvent); | |||||
| st = puglSendEvent(view, &exposeEvent); | |||||
| } | } | ||||
| return PUGL_SUCCESS; | |||||
| return st; | |||||
| } | } | ||||
| PuglNativeView | PuglNativeView | ||||
| @@ -1977,79 +1974,21 @@ puglGetScaleFactor(const PuglView* const view) | |||||
| } | } | ||||
| PuglStatus | 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, | view->impl->win, | ||||
| (int)(x - view->impl->frameExtentLeft), | (int)(x - view->impl->frameExtentLeft), | ||||
| (int)(y - view->impl->frameExtentTop))); | (int)(y - view->impl->frameExtentTop))); | ||||
| } | } | ||||
| PuglStatus | 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 | PuglStatus | ||||
| @@ -7,8 +7,8 @@ | |||||
| #include "attributes.h" | #include "attributes.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "pugl/attributes.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/attributes.h> | |||||
| #include <pugl/pugl.h> | |||||
| #include <X11/X.h> | #include <X11/X.h> | ||||
| #include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||
| @@ -75,7 +75,6 @@ struct PuglWorldInternalsImpl { | |||||
| XID serverTimeCounter; | XID serverTimeCounter; | ||||
| int syncEventBase; | int syncEventBase; | ||||
| bool syncSupported; | bool syncSupported; | ||||
| bool dispatchingEvents; | |||||
| }; | }; | ||||
| struct PuglInternalsImpl { | struct PuglInternalsImpl { | ||||
| @@ -93,9 +92,7 @@ struct PuglInternalsImpl { | |||||
| bool mapped; | bool mapped; | ||||
| }; | }; | ||||
| PUGL_WARN_UNUSED_RESULT | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||||
| puglX11Configure(PuglView* view); | puglX11Configure(PuglView* view); | ||||
| #endif // PUGL_SRC_X11_H | #endif // PUGL_SRC_X11_H | ||||
| @@ -5,8 +5,8 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "x11.h" | #include "x11.h" | ||||
| #include "pugl/cairo.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/cairo.h> | |||||
| #include <pugl/pugl.h> | |||||
| #include <cairo-xlib.h> | #include <cairo-xlib.h> | ||||
| #include <cairo.h> | #include <cairo.h> | ||||
| @@ -19,10 +19,10 @@ typedef struct { | |||||
| cairo_t* cr; | cairo_t* cr; | ||||
| } PuglX11CairoSurface; | } PuglX11CairoSurface; | ||||
| static PuglViewSize | |||||
| static PuglArea | |||||
| puglX11CairoGetViewSize(const PuglView* const view) | puglX11CairoGetViewSize(const PuglView* const view) | ||||
| { | { | ||||
| PuglViewSize size = {0U, 0U}; | |||||
| PuglArea size = {0U, 0U}; | |||||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | if (view->lastConfigure.type == PUGL_CONFIGURE) { | ||||
| // Use the size of the last configured frame | // Use the size of the last configured frame | ||||
| @@ -97,11 +97,11 @@ puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| PuglStatus st = PUGL_SUCCESS; | PuglStatus st = PUGL_SUCCESS; | ||||
| if (expose) { | 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))) { | if (!(st = puglX11CairoOpen(view, surfaceWidth, surfaceHeight))) { | ||||
| surface->cr = cairo_create(surface->front); | surface->cr = cairo_create(surface->front); | ||||
| if (cairo_status(surface->cr)) { | if (cairo_status(surface->cr)) { | ||||
| @@ -6,8 +6,8 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "x11.h" | #include "x11.h" | ||||
| #include "pugl/gl.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/gl.h> | |||||
| #include <pugl/pugl.h> | |||||
| #include <GL/glx.h> | #include <GL/glx.h> | ||||
| #include <X11/X.h> | #include <X11/X.h> | ||||
| @@ -101,8 +101,7 @@ puglX11GlConfigure(PuglView* view) | |||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| PUGL_WARN_UNUSED_RESULT | |||||
| static PuglStatus | |||||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||||
| puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | ||||
| { | { | ||||
| PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | ||||
| @@ -115,8 +114,7 @@ puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||||
| : PUGL_FAILURE; | : PUGL_FAILURE; | ||||
| } | } | ||||
| PUGL_WARN_UNUSED_RESULT | |||||
| static PuglStatus | |||||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||||
| puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) | puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) | ||||
| { | { | ||||
| Display* const display = view->world->impl->display; | Display* const display = view->world->impl->display; | ||||
| @@ -1,13 +1,13 @@ | |||||
| // Copyright 2012-2021 David Robillard <d@drobilla.net> | // Copyright 2012-2021 David Robillard <d@drobilla.net> | ||||
| // SPDX-License-Identifier: ISC | // SPDX-License-Identifier: ISC | ||||
| #include "pugl/stub.h" | |||||
| #include <pugl/stub.h> | |||||
| #include "stub.h" | #include "stub.h" | ||||
| #include "types.h" | #include "types.h" | ||||
| #include "x11.h" | #include "x11.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include <pugl/pugl.h> | |||||
| const PuglBackend* | const PuglBackend* | ||||
| puglStubBackend(void) | puglStubBackend(void) | ||||
| @@ -8,8 +8,8 @@ | |||||
| #include "types.h" | #include "types.h" | ||||
| #include "x11.h" | #include "x11.h" | ||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/vulkan.h" | |||||
| #include <pugl/pugl.h> | |||||
| #include <pugl/vulkan.h> | |||||
| #include <vulkan/vulkan_core.h> | #include <vulkan/vulkan_core.h> | ||||
| #include <vulkan/vulkan_xlib.h> | #include <vulkan/vulkan_xlib.h> | ||||
| @@ -250,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||||
| if (view->backend != nullptr) | if (view->backend != nullptr) | ||||
| { | { | ||||
| #ifdef DGL_OPENGL | |||||
| #if defined(DGL_USE_GLES2) | #if defined(DGL_USE_GLES2) | ||||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | 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) | #elif defined(DGL_USE_OPENGL3) | ||||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | ||||
| #else | |||||
| #elif defined(DGL_OPENGL) | |||||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE); | puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE); | ||||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -277,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||||
| void puglRaiseWindow(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 | // nothing | ||||
| #elif defined(DISTRHO_OS_WINDOWS) | |||||
| #elif defined(DISTRHO_OS_WINDOWS) | |||||
| SetForegroundWindow(view->impl->hwnd); | SetForegroundWindow(view->impl->hwnd); | ||||
| SetActiveWindow(view->impl->hwnd); | SetActiveWindow(view->impl->hwnd); | ||||
| #elif defined(HAVE_X11) | |||||
| #elif defined(HAVE_X11) | |||||
| XRaiseWindow(view->world->impl->display, view->impl->win); | 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<PuglSpan>(height); | view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast<PuglSpan>(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 (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; | 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 | // nothing | ||||
| #elif defined(DISTRHO_OS_WINDOWS) | |||||
| // nothing | |||||
| #elif defined(HAVE_X11) | |||||
| #elif defined(HAVE_X11) | |||||
| if (view->impl->win) | if (view->impl->win) | ||||
| { | { | ||||
| if (const PuglStatus status = updateSizeHints(view)) | |||||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||||
| return status; | return status; | ||||
| XFlush(view->world->impl->display); | XFlush(view->world->impl->display); | ||||
| } | } | ||||
| #endif | |||||
| #endif | |||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| @@ -340,99 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable) | |||||
| { | { | ||||
| puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | 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) | if (PuglWindow* const window = view->impl->window) | ||||
| { | { | ||||
| const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) | const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) | ||||
| | (resizable ? NSResizableWindowMask : 0x0); | |||||
| | (resizable ? NSResizableWindowMask : 0); | |||||
| [window setStyleMask:style]; | [window setStyleMask:style]; | ||||
| } | } | ||||
| // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? | // 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) | if (const HWND hwnd = view->impl->hwnd) | ||||
| { | { | ||||
| const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) | const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) | ||||
| : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); | : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); | ||||
| SetWindowLong(hwnd, GWL_STYLE, winFlags); | 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 | // 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 | // set default size first | ||||
| { | |||||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast<PuglSpan>(width); | |||||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast<PuglSpan>(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 | // matches upstream pugl | ||||
| if (view->impl->wrapperView) | if (view->impl->wrapperView) | ||||
| { | { | ||||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||||
| return status; | |||||
| // nothing to do for PUGL_DEFAULT_SIZE hint | // 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); | 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 | // matches upstream pugl, except we re-enter context after resize | ||||
| if (view->impl->hwnd) | if (view->impl->hwnd) | ||||
| { | { | ||||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||||
| return status; | |||||
| // nothing to do for PUGL_DEFAULT_SIZE hint | // 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 | // make sure to return context back to ourselves | ||||
| puglBackendEnter(view); | puglBackendEnter(view); | ||||
| } | } | ||||
| #elif defined(HAVE_X11) | |||||
| #elif defined(HAVE_X11) | |||||
| // matches upstream pugl, adds flush at the end | // matches upstream pugl, adds flush at the end | ||||
| if (view->impl->win) | if (view->impl->win) | ||||
| { | { | ||||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||||
| return status; | 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<PuglSpan>(width); | |||||
| view->lastConfigure.height = static_cast<PuglSpan>(height); | |||||
| updateSizeHints(view); | |||||
| view->lastConfigure.width = lastWidth; | |||||
| view->lastConfigure.height = lastHeight; | |||||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||||
| return status; | |||||
| // flush size changes | // flush size changes | ||||
| XFlush(view->world->impl->display); | XFlush(view->world->impl->display); | ||||
| } | } | ||||
| #endif | |||||
| #endif | |||||
| return PUGL_SUCCESS; | return PUGL_SUCCESS; | ||||
| } | } | ||||
| @@ -456,11 +441,12 @@ void puglOnDisplayPrepare(PuglView*) | |||||
| void puglFallbackOnResize(PuglView* const view, const uint width, const uint height) | void puglFallbackOnResize(PuglView* const view, const uint width, const uint height) | ||||
| { | { | ||||
| #ifdef DGL_OPENGL | #ifdef DGL_OPENGL | ||||
| #if defined(DGL_USE_OPENGL3) | |||||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||||
| #else | |||||
| glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
| #ifdef DGL_USE_OPENGL3 | |||||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | ||||
| #else | |||||
| glMatrixMode(GL_PROJECTION); | glMatrixMode(GL_PROJECTION); | ||||
| glLoadIdentity(); | glLoadIdentity(); | ||||
| glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); | glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); | ||||
| @@ -469,9 +455,10 @@ void puglFallbackOnResize(PuglView* const view, const uint width, const uint hei | |||||
| glLoadIdentity(); | glLoadIdentity(); | ||||
| #endif | #endif | ||||
| #else | #else | ||||
| return; | |||||
| // unused | // unused | ||||
| (void)view; | (void)view; | ||||
| (void)width; | |||||
| (void)height; | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -618,55 +605,64 @@ void puglWin32ShowCentered(PuglView* const view) | |||||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) | 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; | PuglStatus st = PUGL_SUCCESS; | ||||
| const double startTime = puglGetTime(world); | 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)) | 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; | 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; | const PuglInternals* const impl = view->impl; | ||||
| Display* const display = view->world->impl->display; | 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 defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE) | ||||
| if (isStandalone) | 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<const uchar*>(DGL_X11_WINDOW_ICON_NAME), | |||||
| DGL_X11_WINDOW_ICON_SIZE); | |||||
| } | } | ||||
| #endif | #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) | 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<const uchar*>(&windowTypes), | |||||
| numWindowTypes); | |||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -121,8 +121,8 @@ void puglWin32ShowCentered(PuglView* view); | |||||
| // X11 specific, update world without triggering exposure events | // X11 specific, update world without triggering exposure events | ||||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); | 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 | #endif | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -29,15 +29,14 @@ | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | ||||
| # include "src/DistrhoPluginLV2.cpp" | # include "src/DistrhoPluginLV2.cpp" | ||||
| # include "src/DistrhoPluginLV2export.cpp" | # include "src/DistrhoPluginLV2export.cpp" | ||||
| #elif defined(DISTRHO_PLUGIN_TARGET_MAPI) | |||||
| # include "src/DistrhoPluginMAPI.cpp" | |||||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST2) | #elif defined(DISTRHO_PLUGIN_TARGET_VST2) | ||||
| # include "src/DistrhoPluginVST2.cpp" | # include "src/DistrhoPluginVST2.cpp" | ||||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST3) | #elif defined(DISTRHO_PLUGIN_TARGET_VST3) | ||||
| # include "src/DistrhoPluginVST3.cpp" | # include "src/DistrhoPluginVST3.cpp" | ||||
| #elif defined(DISTRHO_PLUGIN_TARGET_EXPORT) | #elif defined(DISTRHO_PLUGIN_TARGET_EXPORT) | ||||
| # include "src/DistrhoPluginExport.cpp" | # 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) | #elif defined(DISTRHO_PLUGIN_TARGET_STATIC) | ||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| Plugin* createStaticPlugin() { return createPlugin(); } | Plugin* createStaticPlugin() { return createPlugin(); } | ||||
| @@ -106,10 +106,11 @@ public: | |||||
| The following example code can be use to extract individual colors: | 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; | uint getBackgroundColor() const noexcept; | ||||
| @@ -119,10 +120,11 @@ public: | |||||
| The following example code can be use to extract individual colors: | 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; | uint getForegroundColor() const noexcept; | ||||
| @@ -172,6 +172,11 @@ public: | |||||
| /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ | /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ | ||||
| ObjectType* operator->() const noexcept { return object; } | 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. | /** Removes the current object from this ScopedPointer without deleting it. | ||||
| This will return the current object, and set the ScopedPointer to a null pointer. | This will return the current object, and set the ScopedPointer to a null pointer. | ||||
| @@ -269,7 +269,7 @@ public: | |||||
| /* | /* | ||||
| * Get length of the string. | * Get length of the string. | ||||
| */ | */ | ||||
| std::size_t length() const noexcept | |||||
| size_t length() const noexcept | |||||
| { | { | ||||
| return fBufferLen; | return fBufferLen; | ||||
| } | } | ||||
| @@ -295,7 +295,7 @@ public: | |||||
| */ | */ | ||||
| bool contains(const char c) const noexcept | bool contains(const char c) const noexcept | ||||
| { | { | ||||
| for (std::size_t i=0; i<fBufferLen; ++i) | |||||
| for (size_t i=0; i<fBufferLen; ++i) | |||||
| { | { | ||||
| if (fBuffer[i] == c) | if (fBuffer[i] == c) | ||||
| return true; | return true; | ||||
| @@ -334,7 +334,7 @@ public: | |||||
| /* | /* | ||||
| * Check if character at 'pos' is a digit. | * Check if character at 'pos' is a digit. | ||||
| */ | */ | ||||
| bool isDigit(const std::size_t pos) const noexcept | |||||
| bool isDigit(const size_t pos) const noexcept | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); | DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); | ||||
| @@ -358,7 +358,7 @@ public: | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); | DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); | ||||
| const std::size_t prefixLen(std::strlen(prefix)); | |||||
| const size_t prefixLen(std::strlen(prefix)); | |||||
| if (fBufferLen < prefixLen) | if (fBufferLen < prefixLen) | ||||
| return false; | return false; | ||||
| @@ -383,7 +383,7 @@ public: | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); | DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); | ||||
| const std::size_t suffixLen(std::strlen(suffix)); | |||||
| const size_t suffixLen(std::strlen(suffix)); | |||||
| if (fBufferLen < suffixLen) | if (fBufferLen < suffixLen) | ||||
| return false; | return false; | ||||
| @@ -395,7 +395,7 @@ public: | |||||
| * Find the first occurrence of character 'c' in the string. | * Find the first occurrence of character 'c' in the string. | ||||
| * Returns "length()" if the character is not found. | * Returns "length()" if the character is not found. | ||||
| */ | */ | ||||
| std::size_t find(const char c, bool* const found = nullptr) const noexcept | |||||
| size_t find(const char c, bool* const found = nullptr) const noexcept | |||||
| { | { | ||||
| if (fBufferLen == 0 || c == '\0') | if (fBufferLen == 0 || c == '\0') | ||||
| { | { | ||||
| @@ -404,7 +404,7 @@ public: | |||||
| return fBufferLen; | return fBufferLen; | ||||
| } | } | ||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| for (size_t i=0; i < fBufferLen; ++i) | |||||
| { | { | ||||
| if (fBuffer[i] == c) | if (fBuffer[i] == c) | ||||
| { | { | ||||
| @@ -423,7 +423,7 @@ public: | |||||
| * Find the first occurrence of string 'strBuf' in the string. | * Find the first occurrence of string 'strBuf' in the string. | ||||
| * Returns "length()" if the string is not found. | * Returns "length()" if the string is not found. | ||||
| */ | */ | ||||
| std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |||||
| size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |||||
| { | { | ||||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | ||||
| { | { | ||||
| @@ -448,7 +448,7 @@ public: | |||||
| if (found != nullptr) | if (found != nullptr) | ||||
| *found = true; | *found = true; | ||||
| return static_cast<std::size_t>(ret); | |||||
| return static_cast<size_t>(ret); | |||||
| } | } | ||||
| if (found != nullptr) | if (found != nullptr) | ||||
| @@ -460,7 +460,7 @@ public: | |||||
| * Find the last occurrence of character 'c' in the string. | * Find the last occurrence of character 'c' in the string. | ||||
| * Returns "length()" if the character is not found. | * 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') | if (fBufferLen == 0 || c == '\0') | ||||
| { | { | ||||
| @@ -469,7 +469,7 @@ public: | |||||
| return fBufferLen; | return fBufferLen; | ||||
| } | } | ||||
| for (std::size_t i=fBufferLen; i > 0; --i) | |||||
| for (size_t i=fBufferLen; i > 0; --i) | |||||
| { | { | ||||
| if (fBuffer[i-1] == c) | if (fBuffer[i-1] == c) | ||||
| { | { | ||||
| @@ -488,7 +488,7 @@ public: | |||||
| * Find the last occurrence of string 'strBuf' in the string. | * Find the last occurrence of string 'strBuf' in the string. | ||||
| * Returns "length()" if the string is not found. | * 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) | if (found != nullptr) | ||||
| *found = false; | *found = false; | ||||
| @@ -496,12 +496,12 @@ public: | |||||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | ||||
| return fBufferLen; | 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; | 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) | 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); | 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) | if (fBuffer[i] == before) | ||||
| fBuffer[i] = after; | fBuffer[i] = after; | ||||
| @@ -551,7 +551,7 @@ public: | |||||
| if (fBufferLen == 0) | if (fBufferLen == 0) | ||||
| return *this; | return *this; | ||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| for (size_t i=0; i < fBufferLen; ++i) | |||||
| { | { | ||||
| if (fBuffer[i] == c) | if (fBuffer[i] == c) | ||||
| { | { | ||||
| @@ -567,7 +567,7 @@ public: | |||||
| /* | /* | ||||
| * Truncate the string to size 'n'. | * Truncate the string to size 'n'. | ||||
| */ | */ | ||||
| String& truncate(const std::size_t n) noexcept | |||||
| String& truncate(const size_t n) noexcept | |||||
| { | { | ||||
| if (n >= fBufferLen) | if (n >= fBufferLen) | ||||
| return *this; | return *this; | ||||
| @@ -583,7 +583,7 @@ public: | |||||
| */ | */ | ||||
| String& toBasic() noexcept | 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') | if (fBuffer[i] >= '0' && fBuffer[i] <= '9') | ||||
| continue; | continue; | ||||
| @@ -607,7 +607,7 @@ public: | |||||
| { | { | ||||
| static constexpr const char kCharDiff = 'a' - 'A'; | 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') | if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | ||||
| fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | ||||
| @@ -623,7 +623,7 @@ public: | |||||
| { | { | ||||
| static constexpr const char kCharDiff = 'a' - 'A'; | 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') | if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | ||||
| fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | ||||
| @@ -688,31 +688,28 @@ public: | |||||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | ||||
| // Copyright (C) 2004-2008 René Nyffenegger | // 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 = | static constexpr const char* const kBase64Chars = | ||||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
| "abcdefghijklmnopqrstuvwxyz" | "abcdefghijklmnopqrstuvwxyz" | ||||
| "0123456789+/"; | "0123456789+/"; | ||||
| #ifndef _MSC_VER | |||||
| const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |||||
| #else | |||||
| constexpr std::size_t kTmpBufSize = 65536U; | |||||
| #endif | |||||
| const size_t strBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |||||
| char* strBuf = static_cast<char*>(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<const uchar*>(data); | |||||
| uint i=0, j=0; | uint i=0, j=0; | ||||
| uint charArray3[3], charArray4[4]; | uint charArray3[3], charArray4[4]; | ||||
| char strBuf[kTmpBufSize + 1]; | |||||
| strBuf[kTmpBufSize] = '\0'; | |||||
| std::size_t strBufIndex = 0; | |||||
| String ret; | String ret; | ||||
| for (std::size_t s=0; s<dataSize; ++s) | |||||
| for (size_t s = 0; s < dataSize; ++s) | |||||
| { | { | ||||
| charArray3[i++] = *(bytesToEncode++); | charArray3[i++] = *(bytesToEncode++); | ||||
| @@ -723,10 +720,10 @@ public: | |||||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | ||||
| charArray4[3] = charArray3[2] & 0x3f; | charArray4[3] = charArray3[2] & 0x3f; | ||||
| for (i=0; i<4; ++i) | |||||
| for (i = 0; i < 4; ++i) | |||||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | ||||
| if (strBufIndex >= kTmpBufSize-7) | |||||
| if (strBufIndex >= strBufSize - 7) | |||||
| { | { | ||||
| strBuf[strBufIndex] = '\0'; | strBuf[strBufIndex] = '\0'; | ||||
| strBufIndex = 0; | strBufIndex = 0; | ||||
| @@ -739,7 +736,7 @@ public: | |||||
| if (i != 0) | if (i != 0) | ||||
| { | { | ||||
| for (j=i; j<3; ++j) | |||||
| for (j = i; j < 3; ++j) | |||||
| charArray3[j] = '\0'; | charArray3[j] = '\0'; | ||||
| charArray4[0] = (charArray3[0] & 0xfc) >> 2; | charArray4[0] = (charArray3[0] & 0xfc) >> 2; | ||||
| @@ -747,7 +744,7 @@ public: | |||||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | ||||
| charArray4[3] = charArray3[2] & 0x3f; | charArray4[3] = charArray3[2] & 0x3f; | ||||
| for (j=0; j<4 && i<3 && j<i+1; ++j) | |||||
| for (j = 0; j < 4 && i < 3 && j < i + 1; ++j) | |||||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | ||||
| for (; i++ < 3;) | for (; i++ < 3;) | ||||
| @@ -760,6 +757,7 @@ public: | |||||
| ret += strBuf; | ret += strBuf; | ||||
| } | } | ||||
| std::free(strBuf); | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| @@ -778,7 +776,7 @@ public: | |||||
| char* newbufptr = newbuf; | char* newbufptr = newbuf; | ||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| for (size_t i=0; i < fBufferLen; ++i) | |||||
| { | { | ||||
| const char c = fBuffer[i]; | const char c = fBuffer[i]; | ||||
| @@ -901,7 +899,7 @@ public: | |||||
| char* newbufptr = newbuf; | char* newbufptr = newbuf; | ||||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||||
| for (size_t i=0; i < fBufferLen; ++i) | |||||
| { | { | ||||
| const char c = fBuffer[i]; | const char c = fBuffer[i]; | ||||
| @@ -957,19 +955,16 @@ public: | |||||
| return fBuffer; | return fBuffer; | ||||
| } | } | ||||
| char operator[](const std::size_t pos) const noexcept | |||||
| char operator[](const size_t pos) const noexcept | |||||
| { | { | ||||
| if (pos < fBufferLen) | if (pos < fBufferLen) | ||||
| return fBuffer[pos]; | return fBuffer[pos]; | ||||
| d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | ||||
| static char fallback; | |||||
| fallback = '\0'; | |||||
| return fallback; | |||||
| return '\0'; | |||||
| } | } | ||||
| char& operator[](const std::size_t pos) noexcept | |||||
| char& operator[](const size_t pos) noexcept | |||||
| { | { | ||||
| if (pos < fBufferLen) | if (pos < fBufferLen) | ||||
| return fBuffer[pos]; | return fBuffer[pos]; | ||||
| @@ -1020,7 +1015,7 @@ public: | |||||
| if (strBuf == nullptr || strBuf[0] == '\0') | if (strBuf == nullptr || strBuf[0] == '\0') | ||||
| return *this; | return *this; | ||||
| const std::size_t strBufLen = std::strlen(strBuf); | |||||
| const size_t strBufLen = std::strlen(strBuf); | |||||
| // for empty strings, we can just take the appended string as our entire data | // for empty strings, we can just take the appended string as our entire data | ||||
| if (isEmpty()) | if (isEmpty()) | ||||
| @@ -1054,8 +1049,8 @@ public: | |||||
| if (isEmpty()) | if (isEmpty()) | ||||
| return String(strBuf); | return String(strBuf); | ||||
| const std::size_t strBufLen = std::strlen(strBuf); | |||||
| const std::size_t newBufSize = fBufferLen + strBufLen; | |||||
| const size_t strBufLen = std::strlen(strBuf); | |||||
| const size_t newBufSize = fBufferLen + strBufLen; | |||||
| char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1)); | char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1)); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | ||||
| @@ -1080,7 +1075,7 @@ public: | |||||
| private: | private: | ||||
| char* fBuffer; // the actual string buffer | 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() | 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 | * - Allocates string only if 'strBuf' is not null and new string contents are different | ||||
| * - If 'strBuf' is null, 'size' must be 0 | * - 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) | if (strBuf != nullptr) | ||||
| { | { | ||||
| @@ -1158,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep | |||||
| if (strBefore.isEmpty()) | if (strBefore.isEmpty()) | ||||
| return String(strBufAfter); | 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<char*>(malloc(newBufSize + 1)); | char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | 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') | if (strBufBefore == nullptr || strBufBefore[0] == '\0') | ||||
| return strAfter; | 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<char*>(malloc(newBufSize + 1)); | char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| * or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -21,18 +21,18 @@ | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // UI exporter class | // UI exporter class | ||||
| class UIExporter | class UIExporter | ||||
| { | { | ||||
| // ------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // UI Widget and its private data | // UI Widget and its private data | ||||
| UI* ui; | UI* ui; | ||||
| UI::PrivateData* uiData; | UI::PrivateData* uiData; | ||||
| // ------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| public: | public: | ||||
| UIExporter(void* const callbacksPtr, | UIExporter(void* const callbacksPtr, | ||||
| @@ -47,11 +47,12 @@ public: | |||||
| const char* const bundlePath = nullptr, | const char* const bundlePath = nullptr, | ||||
| void* const dspPtr = nullptr, | void* const dspPtr = nullptr, | ||||
| const double scaleFactor = 0.0, | const double scaleFactor = 0.0, | ||||
| const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto, | |||||
| const uint32_t bgColor = 0, | const uint32_t bgColor = 0, | ||||
| const uint32_t fgColor = 0xffffffff, | const uint32_t fgColor = 0xffffffff, | ||||
| const char* const appClassName = nullptr) | const char* const appClassName = nullptr) | ||||
| : ui(nullptr), | : ui(nullptr), | ||||
| uiData(new UI::PrivateData(appClassName)) | |||||
| uiData(new UI::PrivateData(appClassName, appType)) | |||||
| { | { | ||||
| uiData->sampleRate = sampleRate; | uiData->sampleRate = sampleRate; | ||||
| uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | ||||
| @@ -67,14 +67,14 @@ START_NAMESPACE_DISTRHO | |||||
| int dpf_webview_start(int argc, char* argv[]); | int dpf_webview_start(int argc, char* argv[]); | ||||
| #endif | #endif | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Plugin Application, will set class name based on plugin details | // Plugin Application, will set class name based on plugin details | ||||
| class PluginApplication : public DGL_NAMESPACE::Application | class PluginApplication : public DGL_NAMESPACE::Application | ||||
| { | { | ||||
| public: | 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 defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__) | ||||
| if (className == nullptr) | if (className == nullptr) | ||||
| @@ -108,7 +108,7 @@ public: | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | ||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Plugin Window, will pass some Window events to UI | // Plugin Window, will pass some Window events to UI | ||||
| class PluginWindow : public DGL_NAMESPACE::Window | class PluginWindow : public DGL_NAMESPACE::Window | ||||
| @@ -138,7 +138,10 @@ public: | |||||
| // this is called just before creating UI, ensuring proper context to it | // this is called just before creating UI, ensuring proper context to it | ||||
| if (pData->initPost()) | if (pData->initPost()) | ||||
| { | |||||
| puglBackendEnter(pData->view); | puglBackendEnter(pData->view); | ||||
| pData->createContextIfNeeded(); | |||||
| } | |||||
| } | } | ||||
| ~PluginWindow() override | ~PluginWindow() override | ||||
| @@ -240,7 +243,7 @@ protected: | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | ||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // UI callbacks | // UI callbacks | ||||
| typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | 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 void (*setSizeFunc) (void* ptr, uint width, uint height); | ||||
| typedef bool (*fileRequestFunc) (void* ptr, const char* key); | typedef bool (*fileRequestFunc) (void* ptr, const char* key); | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // UI private data | // UI private data | ||||
| struct UI::PrivateData { | struct UI::PrivateData { | ||||
| @@ -289,8 +292,8 @@ struct UI::PrivateData { | |||||
| setSizeFunc setSizeCallbackFunc; | setSizeFunc setSizeCallbackFunc; | ||||
| fileRequestFunc fileRequestCallbackFunc; | 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), | window(nullptr), | ||||
| #if DISTRHO_UI_USE_WEB_VIEW | #if DISTRHO_UI_USE_WEB_VIEW | ||||
| webview(nullptr), | webview(nullptr), | ||||
| @@ -389,7 +392,7 @@ struct UI::PrivateData { | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // UI private data fileRequestCallback, which requires PluginWindow definitions | // UI private data fileRequestCallback, which requires PluginWindow definitions | ||||
| inline bool UI::PrivateData::fileRequestCallback(const char* const key) | inline bool UI::PrivateData::fileRequestCallback(const char* const key) | ||||
| @@ -416,7 +419,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||||
| return false; | return false; | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // PluginWindow onFileSelected that require UI::PrivateData definitions | // PluginWindow onFileSelected that require UI::PrivateData definitions | ||||
| #if DISTRHO_UI_FILE_BROWSER | #if DISTRHO_UI_FILE_BROWSER | ||||
| @@ -454,7 +457,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) | |||||
| } | } | ||||
| #endif | #endif | ||||
| // ----------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
| @@ -119,8 +119,10 @@ const char* getPluginFormatName() noexcept | |||||
| static inline | static inline | ||||
| void _createDirIfNeeded(const char* const dir) | 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 | #endif | ||||
| @@ -255,7 +257,10 @@ static const char* _getDocumentsDir() | |||||
| // ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data | // ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data | ||||
| if (dir.isEmpty()) | if (dir.isEmpty()) | ||||
| dir = _getDocumentsDir(); | |||||
| { | |||||
| dir = _getHomeDir(); | |||||
| dir += "Documents/"; | |||||
| } | |||||
| _createDirIfNeeded(dir); | _createDirIfNeeded(dir); | ||||
| #endif | #endif | ||||