| @@ -53,7 +53,7 @@ rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/src/.clang-tidy | |||
| rm ${CARLA_DIR}/source/modules/distrho/DistrhoInfo.hpp | |||
| rm ${CARLA_DIR}/source/modules/distrho/DistrhoUI_win32.cpp | |||
| rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPlugin{AU,CLAP,Export,JACK,LADSPA+DSSI,LV2,LV2export,Stub,VST2,VST3}.cpp | |||
| rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPlugin{AU,CLAP,Export,JACK,LADSPA+DSSI,LV2,LV2export,MAPI,Stub,VST2,VST3}.cpp | |||
| rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPluginVST.hpp | |||
| rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoUI{DSSI,LV2,Stub,VST3}.cpp | |||
| @@ -83,10 +83,21 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_off) | |||
| class DISTRHO_API Application | |||
| { | |||
| public: | |||
| /** | |||
| Type of application to setup, either "classic" or "modern". | |||
| What this means depends on the OS. | |||
| */ | |||
| enum Type { | |||
| kTypeAuto, | |||
| kTypeClassic, | |||
| kTypeModern, | |||
| }; | |||
| /** | |||
| Constructor for standalone or plugin application. | |||
| */ | |||
| Application(bool isStandalone = true); | |||
| Application(bool isStandalone = true, Type type = kTypeAuto); | |||
| /** | |||
| Constructor for a standalone application. | |||
| @@ -141,6 +152,12 @@ public: | |||
| */ | |||
| double getTime() const; | |||
| /** | |||
| Return the application type, either kTypeClassic or kTypeModern. | |||
| This function never return kTypeAuto. | |||
| */ | |||
| Type getType() const noexcept; | |||
| /** | |||
| Add a callback function to be triggered on every idle cycle. | |||
| You can add more than one, and remove them at anytime with removeIdleCallback(). | |||
| @@ -49,6 +49,10 @@ | |||
| # error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out) | |||
| #endif | |||
| #ifndef DGL_ALLOW_DEPRECATED_METHODS | |||
| # define DGL_ALLOW_DEPRECATED_METHODS 1 | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Define namespace | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -115,6 +115,17 @@ struct Color { | |||
| */ | |||
| static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; | |||
| /** | |||
| Create a color from a RGB unsigned integer. | |||
| Basically doing: | |||
| ``` | |||
| uint8_t red = (color >> 24) & 0xff; | |||
| uint8_t green = (color >> 16) & 0xff; | |||
| uint8_t blue = (color >> 8) & 0xff; | |||
| ``` | |||
| */ | |||
| static Color fromRGB(uint color, float alpha = 1.0f) noexcept; | |||
| /** | |||
| Linearly interpolate this color against another. | |||
| */ | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -360,25 +360,23 @@ public: | |||
| */ | |||
| bool isNotNull() const noexcept; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| /** | |||
| Draw this line using the provided graphics context, optionally specifying line width. | |||
| */ | |||
| void draw(const GraphicsContext& context, T width = 1); | |||
| #endif | |||
| Line<T>& operator=(const Line<T>& line) 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 | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||
| void draw(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| 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; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this circle using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -503,7 +501,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| Point<T> fPos; | |||
| @@ -582,7 +580,7 @@ public: | |||
| 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 | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -596,7 +594,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| 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; | |||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this rectangle using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -826,6 +825,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| private: | |||
| Point<T> pos; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -17,17 +17,21 @@ | |||
| #ifndef DGL_IMAGE_HPP_INCLUDED | |||
| #define DGL_IMAGE_HPP_INCLUDED | |||
| #ifdef DGL_CAIRO | |||
| #include "Cairo.hpp" | |||
| #if defined(DGL_CAIRO) | |||
| # include "Cairo.hpp" | |||
| #elif defined(DGL_OPENGL) | |||
| # include "OpenGL.hpp" | |||
| #elif defined(DGL_VULKAN) | |||
| # include "Vulkan.hpp" | |||
| #else | |||
| #include "OpenGL.hpp" | |||
| # include "Base.hpp" | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| #ifdef DGL_CAIRO | |||
| #if defined(DGL_CAIRO) | |||
| typedef CairoImage Image; | |||
| #else | |||
| #elif defined(DGL_OPENGL) | |||
| typedef OpenGLImage Image; | |||
| #endif | |||
| @@ -32,6 +32,7 @@ OBJS = \ | |||
| $(OBJDIR)/ImageBaseWidgets.cpp.o \ | |||
| $(OBJDIR)/NanoVG.cpp.o \ | |||
| $(OBJDIR)/OpenGL.cpp.o \ | |||
| $(OBJDIR)/OpenGL2.cpp.o \ | |||
| $(OBJDIR)/SubWidget.cpp.o \ | |||
| $(OBJDIR)/SubWidgetPrivateData.cpp.o \ | |||
| $(OBJDIR)/TopLevelWidget.cpp.o \ | |||
| @@ -51,6 +52,7 @@ OBJS_wine = \ | |||
| $(OBJDIR)/ImageBase.cpp-wine.o \ | |||
| $(OBJDIR)/ImageBaseWidgets.cpp-wine.o \ | |||
| $(OBJDIR)/OpenGL.cpp-wine.o \ | |||
| $(OBJDIR)/OpenGL2.cpp-wine.o \ | |||
| $(OBJDIR)/SubWidget.cpp-wine.o \ | |||
| $(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \ | |||
| $(OBJDIR)/TopLevelWidget.cpp-wine.o \ | |||
| @@ -53,6 +53,12 @@ | |||
| #ifdef DISTRHO_OS_MAC | |||
| # ifdef DGL_USE_OPENGL3 | |||
| // NOTE GLES with macOS is not supported | |||
| # ifdef DGL_USE_GLES | |||
| # undef DGL_USE_GLES | |||
| # undef DGL_USE_GLES2 | |||
| # undef DGL_USE_GLES3 | |||
| # endif | |||
| # include <OpenGL/gl3.h> | |||
| # include <OpenGL/gl3ext.h> | |||
| # else | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -24,34 +24,131 @@ | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef DGL_USE_OPENGL3 | |||
| /** | |||
| OpenGL Graphics context. | |||
| OpenGL3 Graphics context. | |||
| This provides access to the program, shaders and uniforms used by the underlying DPF implementation. | |||
| */ | |||
| struct OpenGLGraphicsContext : GraphicsContext | |||
| struct OpenGL3GraphicsContext : GraphicsContext | |||
| { | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #endif | |||
| /** | |||
| The OpenGL3 program used for this context. | |||
| It is activated automatically before any widget onDisplay() is called. | |||
| If changing the current OpenGL program make sure to revert back to this one at the end of your pipeline. | |||
| @code | |||
| // use custom program | |||
| glUseProgram(context.program); | |||
| // custom stuff here | |||
| // revert back | |||
| glUseProgram(context.program); | |||
| @endcode | |||
| */ | |||
| GLuint program; | |||
| /** | |||
| A vec4 uniform used to set the next drawing color. | |||
| @code | |||
| const GLfloat color[4] = { red, green, blue, alpha }; | |||
| glUniform4fv(context.color, 1, color); | |||
| @endcode | |||
| */ | |||
| GLuint color; | |||
| /** | |||
| A vertex shader attribute directly linked to gl_Position. | |||
| Use this to set the bounds for drawing, normalized as -1.0 to +1.0. | |||
| The @a width and @a height provide the total window size for convenience. | |||
| @code | |||
| const GLfloat triangle[] = { x1, y1, x2, y2, x3, y3 }; | |||
| glEnableVertexAttribArray(context.bounds); | |||
| glVertexAttribPointer(context.bounds, 2, GL_FLOAT, GL_FALSE, 0, triangle); | |||
| @endcode | |||
| */ | |||
| GLuint bounds; | |||
| /** | |||
| A vertex shader attribute directly linked to GL_TEXTURE0 map. | |||
| // TODO find the correct wording, map??. | |||
| @code | |||
| const GLfloat map[] = { 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f }; | |||
| glEnableVertexAttribArray(context.textureMap); | |||
| glVertexAttribPointer(context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, map); | |||
| @endcode | |||
| */ | |||
| GLuint textureMap; | |||
| /** | |||
| A boolean uniform used to indicate if next drawing should @a texture or @a color. | |||
| Set to 0 for color mode, 1 for texture. | |||
| Default mode is color, if changed make sure to revert to color mode at the end of your pipeline. | |||
| @code | |||
| // setup for drawing based on texture | |||
| glUniform1i(context.usingTexture, 1); | |||
| // bind texture | |||
| glBindTexture(GL_TEXTURE_2D, myTextureId); | |||
| // etc.. | |||
| // glDrawElements or similar | |||
| // unbind texture | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| // etc.. | |||
| // revert to color mode | |||
| glUniform1i(context.usingTexture, 0); | |||
| @endcode | |||
| */ | |||
| GLuint usingTexture; | |||
| /** | |||
| Set of buffers created with glGenBuffers. | |||
| Used internally in DPF to draw generic shapes, can be reused in custom code. | |||
| Unbound by default, make sure to leave them unbound at the end of your pipeline. | |||
| */ | |||
| GLuint buffers[2]; | |||
| /** | |||
| Total width of the window used for this context. | |||
| */ | |||
| uint width; | |||
| /** | |||
| Total height of the window used for this context. | |||
| */ | |||
| uint height; | |||
| }; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static inline | |||
| ImageFormat asDISTRHOImageFormat(const GLenum format) | |||
| { | |||
| switch (format) | |||
| { | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||
| case GL_RED: | |||
| #else | |||
| #else | |||
| case GL_LUMINANCE: | |||
| #endif | |||
| #endif | |||
| return kImageFormatGrayscale; | |||
| #ifndef DGL_USE_GLES | |||
| case GL_BGR: | |||
| return kImageFormatBGR; | |||
| case GL_BGRA: | |||
| return kImageFormatBGRA; | |||
| #endif | |||
| case GL_RGB: | |||
| return kImageFormatRGB; | |||
| case GL_RGBA: | |||
| @@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format) | |||
| case kImageFormatNull: | |||
| break; | |||
| case kImageFormatGrayscale: | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||
| return GL_RED; | |||
| #else | |||
| #else | |||
| return GL_LUMINANCE; | |||
| #endif | |||
| #endif | |||
| case kImageFormatBGR: | |||
| #ifndef DGL_USE_GLES | |||
| return GL_BGR; | |||
| #else | |||
| return 0; | |||
| #endif | |||
| case kImageFormatBGRA: | |||
| #ifndef DGL_USE_GLES | |||
| return GL_BGRA; | |||
| #else | |||
| return 0; | |||
| #endif | |||
| case kImageFormatRGB: | |||
| return GL_RGB; | |||
| case kImageFormatRGBA: | |||
| return GL_RGBA; | |||
| } | |||
| return 0x0; | |||
| return 0; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| OpenGL Image class. | |||
| @@ -144,6 +249,18 @@ public: | |||
| */ | |||
| void drawAt(const GraphicsContext& context, const Point<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. | |||
| */ | |||
| @@ -157,6 +274,7 @@ public: | |||
| inline void drawAt(const GraphicsContext& context, int x, int y) | |||
| { drawAt(context, Point<int>(x, y)); } | |||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Constructor using raw image data, specifying an OpenGL image format. | |||
| @note @a rawData must remain valid for the lifetime of this Image. | |||
| @@ -200,14 +318,19 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED | |||
| GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | |||
| #endif // DGL_ALLOW_DEPRECATED_METHODS | |||
| private: | |||
| bool setupCalled; | |||
| bool textureInit; | |||
| GLuint textureId; | |||
| #ifdef DGL_USE_GLES | |||
| mutable char* convertedData; | |||
| mutable const char* rawDataLast; | |||
| #endif | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | |||
| typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | |||
| @@ -215,7 +338,7 @@ typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||
| typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | |||
| typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -98,8 +98,8 @@ static void app_idle(void* const app) | |||
| } | |||
| #endif | |||
| Application::Application(const bool isStandalone) | |||
| : pData(new PrivateData(isStandalone)) | |||
| Application::Application(const bool isStandalone, const Type type) | |||
| : pData(new PrivateData(isStandalone, type)) | |||
| { | |||
| // build config sentinels | |||
| #ifdef DPF_DEBUG | |||
| @@ -126,7 +126,7 @@ Application::Application(const bool isStandalone) | |||
| } | |||
| Application::Application(int argc, char* argv[]) | |||
| : pData(new PrivateData(true)) | |||
| : pData(new PrivateData(true, kTypeAuto)) | |||
| { | |||
| #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) | |||
| if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | |||
| @@ -213,6 +213,11 @@ double Application::getTime() const | |||
| return pData->getTime(); | |||
| } | |||
| Application::Type Application::getType() const noexcept | |||
| { | |||
| return pData->isModern ? kTypeModern : kTypeClassic; | |||
| } | |||
| void Application::addIdleCallback(IdleCallback* const callback) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -53,13 +53,14 @@ const char* Application::getClassName() const noexcept | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| Application::PrivateData::PrivateData(const bool standalone) | |||
| Application::PrivateData::PrivateData(const bool standalone, const Type type) | |||
| : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||
| standalone ? PUGL_WORLD_THREADS : 0x0)), | |||
| (standalone ? PUGL_WORLD_THREADS : 0))), | |||
| isModern(false), | |||
| isStandalone(standalone), | |||
| isStarting(true), | |||
| isQuitting(false), | |||
| isQuittingInNextCycle(false), | |||
| isStarting(true), | |||
| needsRepaint(false), | |||
| visibleWindows(0), | |||
| mainThreadHandle(getCurrentThreadHandle()), | |||
| @@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||
| #ifdef DGL_USING_SDL | |||
| SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO); | |||
| #else | |||
| puglSetWorldHandle(world, this); | |||
| #ifdef __EMSCRIPTEN__ | |||
| puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | |||
| #else | |||
| puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | |||
| #endif | |||
| #endif | |||
| } | |||
| Application::PrivateData::~PrivateData() | |||
| @@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData() | |||
| windows.clear(); | |||
| idleCallbacks.clear(); | |||
| #ifdef DGL_USING_SDL | |||
| SDL_Quit(); | |||
| #else | |||
| if (world != nullptr) | |||
| puglFreeWorld(world); | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -51,18 +51,21 @@ struct Application::PrivateData { | |||
| /** Pugl world instance. */ | |||
| PuglWorld* const world; | |||
| /** Whether the applicating uses modern backend, otherwise classic. */ | |||
| const bool isModern; | |||
| /** Whether the application is running as standalone, otherwise it is part of a plugin. */ | |||
| const bool isStandalone; | |||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||
| bool isStarting; | |||
| /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | |||
| bool isQuitting; | |||
| /** Helper for safely close everything from main thread. */ | |||
| bool isQuittingInNextCycle; | |||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||
| bool isStarting; | |||
| /** When true force all windows to be repainted on next idle. */ | |||
| bool needsRepaint; | |||
| @@ -80,7 +83,7 @@ struct Application::PrivateData { | |||
| std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | |||
| /** Constructor and destructor */ | |||
| explicit PrivateData(bool standalone); | |||
| explicit PrivateData(bool standalone, Type type); | |||
| ~PrivateData(); | |||
| /** Flag one window as shown, which increments @a visibleWindows. | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -242,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept | |||
| return Color(r, g, b, alpha); | |||
| } | |||
| Color Color::fromRGB(const uint color, const float alpha) noexcept | |||
| { | |||
| return Color(static_cast<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 | |||
| { | |||
| fixRange(u); | |||
| @@ -108,13 +108,6 @@ struct ButtonEventHandler::PrivateData { | |||
| if (! enabledInput) | |||
| return false; | |||
| // keep pressed | |||
| if (button != -1) | |||
| { | |||
| lastMotionPos = ev.pos; | |||
| return true; | |||
| } | |||
| bool ret = false; | |||
| if (widget->contains(ev.pos)) | |||
| @@ -143,7 +136,7 @@ struct ButtonEventHandler::PrivateData { | |||
| } | |||
| lastMotionPos = ev.pos; | |||
| return ret; | |||
| return ret || button != -1; | |||
| } | |||
| void setActive(const bool active2, const bool sendCallback) noexcept | |||
| @@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
| //#define STB_IMAGE_STATIC | |||
| #if defined(DGL_USE_GLES2) | |||
| # define NANOVG_GLES2_IMPLEMENTATION | |||
| #elif defined(DGL_USE_GLES3) | |||
| # define NANOVG_GLES3_IMPLEMENTATION | |||
| #elif defined(DGL_USE_OPENGL3) | |||
| # define NANOVG_GL3_IMPLEMENTATION | |||
| #else | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -20,7 +20,6 @@ | |||
| #endif | |||
| #include "../OpenGL.hpp" | |||
| #include "../Color.hpp" | |||
| #include "../ImageWidgets.hpp" | |||
| #include "SubWidgetPrivateData.hpp" | |||
| @@ -28,416 +27,20 @@ | |||
| #include "WidgetPrivateData.hpp" | |||
| #include "WindowPrivateData.hpp" | |||
| // templated classes | |||
| #include "ImageBaseWidgets.cpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| #ifdef DGL_USE_OPENGL3 | |||
| static void notImplemented(const char* const name) | |||
| { | |||
| d_stderr2("OpenGL3 function not implemented: %s", name); | |||
| } | |||
| #else | |||
| # define DGL_USE_COMPAT_OPENGL | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Color | |||
| void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| if (includeAlpha) | |||
| glColor4f(red, green, blue, alpha); | |||
| else | |||
| glColor3f(red, green, blue); | |||
| #else | |||
| notImplemented("Color::setFor"); | |||
| // unused | |||
| (void)includeAlpha; | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Line | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| template<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 | |||
| 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() | |||
| : ImageBase(), | |||
| setupCalled(false), | |||
| textureInit(false), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| } | |||
| @@ -446,6 +49,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, co | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const Ima | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image) | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage() | |||
| { | |||
| if (textureId != 0) | |||
| glDeleteTextures(1, &textureId); | |||
| #ifdef DGL_USE_GLES | |||
| std::free(convertedData); | |||
| #endif | |||
| } | |||
| void OpenGLImage::loadFromMemory(const char* const rdata, const Size<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); | |||
| } | |||
| void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||
| { | |||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
| } | |||
| OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||
| { | |||
| rawData = image.rawData; | |||
| @@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||
| return *this; | |||
| } | |||
| // deprecated calls | |||
| #ifdef DGL_ALLOW_DEPRECATED_METHODS | |||
| OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) | |||
| : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLe | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| } | |||
| void OpenGLImage::draw() | |||
| { | |||
| drawOpenGLImage(*this, Point<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 | |||
| 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) | |||
| { | |||
| @@ -736,7 +216,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const | |||
| selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void TopLevelWidget::PrivateData::display() | |||
| { | |||
| @@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display() | |||
| selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void Window::PrivateData::renderToPicture(const char* const filename, | |||
| const GraphicsContext&, | |||
| @@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename, | |||
| fclose(f); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| return (const GraphicsContext&)graphicsContext; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -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), | |||
| ppData(nullptr), | |||
| active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)), | |||
| reenter(false) {} | |||
| reenter(false) | |||
| { | |||
| if (active) | |||
| window.pData->createContextIfNeeded(); | |||
| } | |||
| Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin) | |||
| : window(win), | |||
| @@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi | |||
| { | |||
| puglBackendLeave(ppData->view); | |||
| active = puglBackendEnter(window.pData->view); | |||
| if (active) | |||
| window.pData->createContextIfNeeded(); | |||
| } | |||
| } | |||
| @@ -180,22 +186,22 @@ int Window::getOffsetX() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| return puglGetFrame(pData->view).x; | |||
| return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).x; | |||
| } | |||
| int Window::getOffsetY() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| return puglGetFrame(pData->view).y; | |||
| return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y; | |||
| } | |||
| Point<int> Window::getOffset() const noexcept | |||
| { | |||
| 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) | |||
| @@ -214,7 +220,7 @@ void Window::setOffset(const int x, const int y) | |||
| DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,); | |||
| if (pData->view != nullptr) | |||
| puglSetPosition(pData->view, x, y); | |||
| puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y); | |||
| } | |||
| void Window::setOffset(const Point<int>& offset) | |||
| @@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| const double width = puglGetFrame(pData->view).width; | |||
| DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0); | |||
| return static_cast<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 | |||
| { | |||
| 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 | |||
| { | |||
| 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) | |||
| @@ -443,7 +448,7 @@ void Window::repaint() noexcept | |||
| if (pData->usesScheduledRepaints) | |||
| pData->appData->needsRepaint = true; | |||
| puglPostRedisplay(pData->view); | |||
| puglObscureView(pData->view); | |||
| } | |||
| void Window::repaint(const Rectangle<uint>& rect) noexcept | |||
| @@ -454,22 +459,22 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||
| if (pData->usesScheduledRepaints) | |||
| 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) | |||
| { | |||
| 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) | |||
| @@ -523,8 +528,8 @@ void Window::setGeometryConstraints(uint minimumWidth, | |||
| { | |||
| 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)) | |||
| { | |||
| puglSetParentWindow(view, parentWindowHandle); | |||
| puglSetParent(view, parentWindowHandle); | |||
| if (parentWindowHandle != 0) | |||
| puglSetPosition(view, 0, 0); | |||
| puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0); | |||
| return view; | |||
| } | |||
| @@ -268,6 +268,9 @@ Window::PrivateData::~PrivateData() | |||
| isVisible = false; | |||
| } | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| destroyContext(); | |||
| #endif | |||
| puglFreeView(view); | |||
| } | |||
| @@ -300,8 +303,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||
| // PUGL_SAMPLES ?? | |||
| puglSetEventFunc(view, puglEventCallback); | |||
| // setting default size triggers system-level calls, do it last | |||
| puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height)); | |||
| // setting size triggers system-level calls, do it last | |||
| puglSetSizeAndDefault(view, width, height); | |||
| } | |||
| bool Window::PrivateData::initPost() | |||
| @@ -366,10 +369,6 @@ void Window::PrivateData::show() | |||
| isClosed = false; | |||
| appData->oneWindowShown(); | |||
| // FIXME | |||
| // PuglRect rect = puglGetFrame(view); | |||
| // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| puglWin32ShowCentered(view); | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| @@ -453,6 +452,13 @@ void Window::PrivateData::setResizable(const bool resizable) | |||
| puglSetResizable(view, resizable); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| return reinterpret_cast<const GraphicsContext&>(graphicsContext); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| void Window::PrivateData::idleCallback() | |||
| @@ -538,9 +544,9 @@ bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPA | |||
| if (webViewHandle != nullptr) | |||
| webViewDestroy(webViewHandle); | |||
| const PuglRect rect = puglGetFrame(view); | |||
| uint initialWidth = static_cast<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); | |||
| @@ -640,6 +646,10 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||
| DGL_DBGp("PUGL: onReshape : %d %d\n", width, height); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| createContextIfNeeded(); | |||
| #endif | |||
| if (autoScaling) | |||
| { | |||
| const double scaleHorizontal = width / static_cast<double>(minWidth); | |||
| @@ -682,7 +692,7 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||
| #endif | |||
| // always repaint after a resize | |||
| puglPostRedisplay(view); | |||
| puglObscureView(view); | |||
| } | |||
| void Window::PrivateData::onPuglExpose() | |||
| @@ -692,6 +702,8 @@ void Window::PrivateData::onPuglExpose() | |||
| puglOnDisplayPrepare(view); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| startContext(); | |||
| FOR_EACH_TOP_LEVEL_WIDGET(it) | |||
| { | |||
| TopLevelWidget* const widget(*it); | |||
| @@ -702,11 +714,13 @@ void Window::PrivateData::onPuglExpose() | |||
| if (char* const filename = filenameToRenderInto) | |||
| { | |||
| const PuglRect rect = puglGetFrame(view); | |||
| const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| filenameToRenderInto = nullptr; | |||
| renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||
| renderToPicture(filename, getGraphicsContext(), size.width, size.height); | |||
| std::free(filename); | |||
| } | |||
| endContext(); | |||
| #endif | |||
| } | |||
| @@ -978,7 +992,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
| SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); | |||
| #endif | |||
| #ifdef DGL_USING_X11 | |||
| puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |||
| puglX11SetWindowType(view, pData->appData->isStandalone); | |||
| #endif | |||
| } | |||
| break; | |||
| @@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback { | |||
| PuglView* view; | |||
| /** Reserved space for graphics context. */ | |||
| mutable uint8_t graphicsContext[sizeof(void*)]; | |||
| mutable uint8_t graphicsContext[sizeof(int) * 9]; | |||
| void createContextIfNeeded(); | |||
| void destroyContext(); | |||
| void startContext(); | |||
| void endContext(); | |||
| /** The top-level widgets associated with this Window. */ | |||
| std::list<TopLevelWidget*> topLevelWidgets; | |||
| @@ -173,7 +173,7 @@ struct GLNVGtexture { | |||
| int width, height; | |||
| int type; | |||
| int flags; | |||
| #if defined NANOVG_GLES2 | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| unsigned char* data; | |||
| #endif | |||
| }; | |||
| @@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
| switch (type) | |||
| { | |||
| case NVG_TEXTURE_BGR: | |||
| #if NANOVG_GLES2 | |||
| // GLES2 cannot handle GL_BGR, do local conversion to GL_RGB | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| // GLES cannot handle GL_BGR, do local conversion to GL_RGB | |||
| tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h); | |||
| for (uint32_t i=0; 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+1] = data[i*3+1]; | |||
| @@ -829,34 +829,34 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
| #endif | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| #if NANOVG_GLES2 | |||
| // GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| // GLES cannot handle GL_BGRA, do local conversion to GL_RGBA | |||
| tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h); | |||
| for (uint32_t i=0; 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; | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| #endif | |||
| break; | |||
| case NVG_TEXTURE_RGB: | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_RGBA: | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| default: | |||
| #if defined(NANOVG_GLES2) || defined (NANOVG_GL2) | |||
| #if defined(NANOVG_GL2) || defined(NANOVG_GLES2) | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
| #elif defined(NANOVG_GLES3) | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| #endif | |||
| break; | |||
| @@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||
| switch (tex->type) | |||
| { | |||
| case NVG_TEXTURE_BGR: | |||
| #if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| #endif | |||
| case NVG_TEXTURE_RGB: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| #if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| #endif | |||
| case NVG_TEXTURE_RGBA: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| default: | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GL2) | |||
| #if defined(NANOVG_GL2) || defined(NANOVG_GLES2) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| @@ -26,17 +26,6 @@ | |||
| # endif | |||
| #endif | |||
| // Deprecated API | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| # if defined(__clang__) | |||
| # define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("", rep))) | |||
| # elif defined(__GNUC__) | |||
| # define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("Use " rep))) | |||
| # else | |||
| # define PUGL_DEPRECATED_BY(rep) | |||
| # endif | |||
| #endif | |||
| // GCC function attributes | |||
| #if defined(__GNUC__) | |||
| # define PUGL_CONST_FUNC __attribute__((const)) | |||
| @@ -47,13 +36,9 @@ | |||
| #endif | |||
| /// A const function in the public API that only reads parameters | |||
| #define PUGL_CONST_API \ | |||
| PUGL_API \ | |||
| PUGL_CONST_FUNC | |||
| #define PUGL_CONST_API PUGL_API PUGL_CONST_FUNC | |||
| /// A malloc function in the public API that returns allocated memory | |||
| #define PUGL_MALLOC_API \ | |||
| PUGL_API \ | |||
| PUGL_MALLOC_FUNC | |||
| #define PUGL_MALLOC_API PUGL_API PUGL_MALLOC_FUNC | |||
| #endif // PUGL_ATTRIBUTES_H | |||
| @@ -4,8 +4,8 @@ | |||
| #ifndef 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 | |||
| @@ -42,8 +42,7 @@ typedef void (*PuglGlFunc)(void); | |||
| /** | |||
| Return the address of an OpenGL extension function. | |||
| */ | |||
| PUGL_API | |||
| PuglGlFunc | |||
| PUGL_API PuglGlFunc | |||
| puglGetProcAddress(const char* name); | |||
| /** | |||
| @@ -53,8 +52,7 @@ puglGetProcAddress(const char* name); | |||
| doing things like loading textures. Note that this must not be used for | |||
| drawing, which may only be done while processing an expose event. | |||
| */ | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_API PuglStatus | |||
| puglEnterContext(PuglView* view); | |||
| /** | |||
| @@ -62,8 +60,7 @@ puglEnterContext(PuglView* view); | |||
| This must only be called after puglEnterContext(). | |||
| */ | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_API PuglStatus | |||
| puglLeaveContext(PuglView* view); | |||
| /** | |||
| @@ -71,8 +68,7 @@ puglLeaveContext(PuglView* view); | |||
| Pass the returned value to puglSetBackend() to draw to a view with OpenGL. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglGlBackend(void); | |||
| PUGL_END_DECLS | |||
| @@ -4,8 +4,8 @@ | |||
| #ifndef 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 | |||
| @@ -22,8 +22,7 @@ PUGL_BEGIN_DECLS | |||
| This backend just creates a simple native window without setting up any | |||
| portable graphics API. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglStubBackend(void); | |||
| /** | |||
| @@ -4,13 +4,11 @@ | |||
| // Common implementations of public API functions in the core library | |||
| #include "internal.h" | |||
| #include "platform.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <limits.h> | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #include <stdlib.h> | |||
| @@ -27,6 +25,7 @@ puglStrerror(const PuglStatus status) | |||
| case PUGL_BAD_BACKEND: return "Invalid or missing backend"; | |||
| case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; | |||
| case PUGL_BAD_PARAMETER: return "Invalid parameter"; | |||
| case PUGL_BAD_CALL: return "Invalid call"; | |||
| case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; | |||
| case PUGL_REGISTRATION_FAILED: return "Class registration failed"; | |||
| case PUGL_REALIZE_FAILED: return "View creation failed"; | |||
| @@ -106,27 +105,37 @@ puglGetWorldString(const PuglWorld* const world, const PuglStringHint key) | |||
| } | |||
| static void | |||
| puglSetDefaultHints(PuglHints hints) | |||
| puglSetDefaultHints(PuglView* const view) | |||
| { | |||
| hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; | |||
| hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; | |||
| hints[PUGL_CONTEXT_VERSION_MINOR] = 0; | |||
| hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; | |||
| hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; | |||
| hints[PUGL_RED_BITS] = 8; | |||
| hints[PUGL_GREEN_BITS] = 8; | |||
| hints[PUGL_BLUE_BITS] = 8; | |||
| hints[PUGL_ALPHA_BITS] = 8; | |||
| hints[PUGL_DEPTH_BITS] = 0; | |||
| hints[PUGL_STENCIL_BITS] = 0; | |||
| hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; | |||
| hints[PUGL_SAMPLES] = 0; | |||
| hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; | |||
| hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; | |||
| hints[PUGL_RESIZABLE] = PUGL_FALSE; | |||
| hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; | |||
| hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; | |||
| hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; | |||
| view->hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; | |||
| view->hints[PUGL_CONTEXT_VERSION_MINOR] = 0; | |||
| view->hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; | |||
| view->hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; | |||
| view->hints[PUGL_RED_BITS] = 8; | |||
| view->hints[PUGL_GREEN_BITS] = 8; | |||
| view->hints[PUGL_BLUE_BITS] = 8; | |||
| view->hints[PUGL_ALPHA_BITS] = 8; | |||
| view->hints[PUGL_DEPTH_BITS] = 0; | |||
| view->hints[PUGL_STENCIL_BITS] = 0; | |||
| view->hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_SAMPLES] = 0; | |||
| view->hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; | |||
| view->hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_RESIZABLE] = PUGL_FALSE; | |||
| view->hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; | |||
| view->hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; | |||
| for (unsigned i = 0U; i < PUGL_NUM_POSITION_HINTS; ++i) { | |||
| view->positionHints[i].x = INT16_MIN; | |||
| view->positionHints[i].y = INT16_MIN; | |||
| } | |||
| for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { | |||
| view->sizeHints[i].width = 0U; | |||
| view->sizeHints[i].height = 0U; | |||
| } | |||
| } | |||
| PuglView* | |||
| @@ -138,13 +147,8 @@ puglNewView(PuglWorld* const world) | |||
| return NULL; | |||
| } | |||
| view->world = world; | |||
| view->sizeHints[PUGL_MIN_SIZE].width = 1; | |||
| view->sizeHints[PUGL_MIN_SIZE].height = 1; | |||
| view->defaultX = INT_MIN; | |||
| view->defaultY = INT_MIN; | |||
| puglSetDefaultHints(view->hints); | |||
| view->world = world; | |||
| puglSetDefaultHints(view); | |||
| // Enlarge world view list | |||
| const size_t newNumViews = world->numViews + 1U; | |||
| @@ -287,43 +291,84 @@ puglGetViewString(const PuglView* const view, const PuglStringHint key) | |||
| return view->strings[key]; | |||
| } | |||
| PuglRect | |||
| puglGetFrame(const PuglView* view) | |||
| PuglPoint | |||
| puglGetPositionHint(const PuglView* const view, const PuglPositionHint hint) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Return the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| if (hint == PUGL_CURRENT_POSITION) { | |||
| PuglPoint pos = {0, 0}; | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| pos.x = view->lastConfigure.x; | |||
| pos.y = view->lastConfigure.y; | |||
| } else { | |||
| const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; | |||
| if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { | |||
| pos.x = defaultPos.x; | |||
| pos.y = defaultPos.y; | |||
| } | |||
| } | |||
| return pos; | |||
| } | |||
| // Get the default position if set, or fallback to (0, 0) | |||
| int x = view->defaultX; | |||
| int y = view->defaultY; | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| x = 0; | |||
| y = 0; | |||
| return view->positionHints[hint]; | |||
| } | |||
| PuglStatus | |||
| puglSetPositionHint(PuglView* const view, | |||
| const PuglPositionHint hint, | |||
| const int x, | |||
| const int y) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| // Return the default frame, sanitized if necessary | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| if (x <= INT16_MIN || x > INT16_MAX || y <= INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->positionHints[hint].x = (PuglCoord)x; | |||
| view->positionHints[hint].y = (PuglCoord)y; | |||
| return (hint != PUGL_CURRENT_POSITION) ? PUGL_SUCCESS | |||
| : puglGetNativeView(view) ? puglSetWindowPosition(view, x, y) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PuglArea | |||
| puglGetSizeHint(const PuglView* const view, const PuglSizeHint hint) | |||
| { | |||
| if (hint == PUGL_CURRENT_SIZE && view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| const PuglArea area = {view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return area; | |||
| } | |||
| return view->sizeHints[hint]; | |||
| } | |||
| PuglStatus | |||
| puglSetParentWindow(PuglView* view, PuglNativeView parent) | |||
| puglSetParent(PuglView* view, PuglNativeView parent) | |||
| { | |||
| view->parent = parent; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const PuglStatus st = puglStoreSizeHint(view, hint, width, height); | |||
| return st ? st | |||
| : (hint != PUGL_CURRENT_SIZE) ? puglApplySizeHint(view, hint) | |||
| : puglGetNativeView(view) ? puglSetWindowSize(view, width, height) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PuglNativeView | |||
| puglGetParentWindow(const PuglView* const view) | |||
| puglGetParent(const PuglView* const view) | |||
| { | |||
| return view->parent; | |||
| } | |||
| @@ -5,19 +5,80 @@ | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <assert.h> | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #include <stdlib.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 | |||
| puglIsValidSize(const PuglViewSize size) | |||
| puglIsValidSize(const unsigned width, const unsigned height) | |||
| { | |||
| return width && height && width <= INT16_MAX && height <= INT16_MAX; | |||
| } | |||
| bool | |||
| puglIsValidArea(const PuglArea size) | |||
| { | |||
| return size.width && size.height; | |||
| } | |||
| PuglArea | |||
| puglGetInitialSize(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured size | |||
| const PuglConfigureEvent config = view->lastConfigure; | |||
| const PuglArea size = {config.width, config.height}; | |||
| return size; | |||
| } | |||
| // Use the default size hint set by the application | |||
| return view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| } | |||
| PuglPoint | |||
| puglGetInitialPosition(const PuglView* const view, const PuglArea size) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| return make_point(view->lastConfigure.x, view->lastConfigure.y); | |||
| } | |||
| const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; | |||
| if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { | |||
| // Use the default position hint set by the application | |||
| return make_point(defaultPos.x, defaultPos.y); | |||
| } | |||
| if (view->parent) { | |||
| // Default to the top/left origin of the parent | |||
| return make_point(0, 0); | |||
| } | |||
| // Center frame on a transient ancestor, or failing that, the screen | |||
| const PuglPoint center = puglGetAncestorCenter(view); | |||
| const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)), | |||
| (PuglCoord)(center.y - (size.height / 2))}; | |||
| return pos; | |||
| } | |||
| void | |||
| puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) | |||
| { | |||
| @@ -68,6 +129,25 @@ puglSetString(char** dest, const char* string) | |||
| } | |||
| } | |||
| PuglStatus | |||
| puglStoreSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (!puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = (PuglSpan)width; | |||
| view->sizeHints[hint].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| uint32_t | |||
| puglDecodeUTF8(const uint8_t* buf) | |||
| { | |||
| @@ -159,11 +239,12 @@ puglPreRealize(PuglView* const view) | |||
| } | |||
| // Ensure that the default size is set to a valid size | |||
| if (!puglIsValidSize(view->sizeHints[PUGL_DEFAULT_SIZE])) { | |||
| if (!puglIsValidArea(view->sizeHints[PUGL_DEFAULT_SIZE])) { | |||
| return PUGL_BAD_CONFIGURATION; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return (view->world->state == PUGL_WORLD_EXPOSING) ? PUGL_BAD_CALL | |||
| : PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| @@ -173,7 +254,7 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) | |||
| type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER || | |||
| type == PUGL_LOOP_LEAVE); | |||
| const PuglEvent event = {{type, 0}}; | |||
| const PuglEvent event = {{type, 0U}}; | |||
| return puglDispatchEvent(view, &event); | |||
| } | |||
| @@ -240,8 +321,12 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
| case PUGL_EXPOSE: | |||
| assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED); | |||
| if (!(st0 = view->backend->enter(view, &event->expose))) { | |||
| st0 = view->eventFunc(view, event); | |||
| st1 = view->backend->leave(view, &event->expose); | |||
| const PuglWorldState old_state = view->world->state; | |||
| view->world->state = PUGL_WORLD_EXPOSING; | |||
| st0 = view->eventFunc(view, event); | |||
| view->world->state = old_state; | |||
| st1 = view->backend->leave(view, &event->expose); | |||
| } | |||
| break; | |||
| @@ -9,8 +9,8 @@ | |||
| #include "attributes.h" | |||
| #include "types.h" | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <stdbool.h> | |||
| #include <stddef.h> | |||
| @@ -18,9 +18,29 @@ | |||
| PUGL_BEGIN_DECLS | |||
| /// Return true if `x`,`y` is a valid position | |||
| bool | |||
| puglIsValidPosition(int x, int y); | |||
| /// Return true if `width`,`height` is a valid position | |||
| bool | |||
| puglIsValidSize(unsigned width, unsigned height); | |||
| /// Return true if `size` is a valid view size | |||
| bool | |||
| puglIsValidSize(PuglViewSize size); | |||
| puglIsValidArea(PuglArea size); | |||
| /// Return the center point of some "soft" ancestor (parent window or screen) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* view); | |||
| /// Return the initial size of a view | |||
| PuglArea | |||
| puglGetInitialSize(const PuglView* view); | |||
| /// Return the initial position of a view if known, or an invalid position | |||
| PuglPoint | |||
| puglGetInitialPosition(const PuglView* view, PuglArea size); | |||
| /// Set hint to a default value if it is unset (PUGL_DONT_CARE) | |||
| void | |||
| @@ -34,8 +54,14 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len); | |||
| void | |||
| puglSetString(char** dest, const char* string); | |||
| /// Store `width` and `height` as the current value of a size `hint` | |||
| PuglStatus | |||
| puglStoreSizeHint(PuglView* view, | |||
| PuglSizeHint hint, | |||
| unsigned width, | |||
| unsigned height); | |||
| /// Handle a changed string property | |||
| PUGL_API | |||
| PuglStatus | |||
| puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); | |||
| @@ -56,8 +82,7 @@ PuglStatus | |||
| puglDispatchSimpleEvent(PuglView* view, PuglEventType type); | |||
| /// Process configure event while already in the graphics context | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus | |||
| puglConfigure(PuglView* view, const PuglEvent* event); | |||
| /// Dispatch `event` to `view`, entering graphics context if necessary | |||
| @@ -5,7 +5,7 @@ | |||
| #ifndef PUGL_SRC_MAC_H | |||
| #define PUGL_SRC_MAC_H | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -7,9 +7,10 @@ | |||
| #include "mac.h" | |||
| #include "internal.h" | |||
| #include "macros.h" | |||
| #include "platform.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -121,7 +122,7 @@ viewScreen(const PuglView* view) | |||
| } | |||
| static NSRect | |||
| nsRectToPoints(PuglView* view, const NSRect rect) | |||
| nsRectToPoints(const PuglView* view, const NSRect rect) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) | |||
| } | |||
| static NSRect | |||
| nsRectFromPoints(PuglView* view, const NSRect rect) | |||
| nsRectFromPoints(const PuglView* view, const NSRect rect) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -143,21 +144,15 @@ nsRectFromPoints(PuglView* view, const NSRect rect) | |||
| } | |||
| static NSPoint | |||
| nsPointFromPoints(PuglView* view, const NSPoint point) | |||
| nsPointFromPoints(const PuglView* view, const NSPoint point) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor); | |||
| } | |||
| static NSRect | |||
| rectToNsRect(const PuglRect rect) | |||
| { | |||
| return NSMakeRect(rect.x, rect.y, rect.width, rect.height); | |||
| } | |||
| static NSSize | |||
| sizePoints(PuglView* view, const double width, const double height) | |||
| sizePoints(PuglView* view, const PuglSpan width, const PuglSpan height) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -188,7 +183,7 @@ getCurrentViewStyleFlags(PuglView* const view) | |||
| } | |||
| static PuglStatus | |||
| dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| dispatchCurrentChildViewConfiguration(PuglView* const view) | |||
| { | |||
| const NSRect framePt = [view->impl->wrapperView frame]; | |||
| const NSRect framePx = nsRectFromPoints(view, framePt); | |||
| @@ -197,14 +192,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (drawViewResize) { | |||
| const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size]; | |||
| [view->impl->drawView setFrameSize:sizePt]; | |||
| } | |||
| const PuglConfigureEvent ev = { | |||
| PUGL_CONFIGURE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)framePx.origin.x, | |||
| (PuglCoord)framePx.origin.y, | |||
| (PuglSpan)framePx.size.width, | |||
| @@ -254,7 +244,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| const PuglConfigureEvent ev = { | |||
| PUGL_CONFIGURE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)contentPx.origin.x, | |||
| (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), | |||
| (PuglSpan)contentPx.size.width, | |||
| @@ -322,7 +312,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| if (puglview->impl->window) { | |||
| [puglview->impl->window dispatchCurrentConfiguration]; | |||
| } else { | |||
| dispatchCurrentChildViewConfiguration(puglview, true); | |||
| dispatchCurrentChildViewConfiguration(puglview); | |||
| } | |||
| reshaped = false; | |||
| } | |||
| @@ -336,7 +326,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| const PuglExposeEvent ev = { | |||
| PUGL_EXPOSE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)(rect.origin.x * scaleFactor), | |||
| (PuglCoord)viewY, | |||
| (PuglSpan)(rect.size.width * scaleFactor), | |||
| @@ -350,9 +340,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| - (NSSize)intrinsicContentSize | |||
| { | |||
| const PuglViewSize defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| const PuglArea defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| return puglIsValidSize(defaultSize) | |||
| return puglIsValidArea(defaultSize) | |||
| ? sizePoints(puglview, defaultSize.width, defaultSize.height) | |||
| : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); | |||
| } | |||
| @@ -468,7 +458,7 @@ keySymToSpecial(const NSEvent* const ev) | |||
| return PUGL_KEY_PAD_9; | |||
| } | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| - (void)updateTrackingAreas | |||
| @@ -501,7 +491,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglCrossingEvent ev = { | |||
| type, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -542,7 +532,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglMotionEvent ev = { | |||
| PUGL_MOTION, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -577,7 +567,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglButtonEvent ev = { | |||
| PUGL_BUTTON_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -598,7 +588,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglButtonEvent ev = { | |||
| PUGL_BUTTON_RELEASE, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -638,11 +628,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint wloc = [self eventLocation:event]; | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| double dx = -[event scrollingDeltaX] / 2.0; | |||
| double dy = [event scrollingDeltaY] / 2.0; | |||
| double dx = -[event scrollingDeltaX]; | |||
| double dy = [event scrollingDeltaY]; | |||
| if ([event hasPreciseScrollingDeltas]) { | |||
| dx /= 10.0; | |||
| dy /= 10.0; | |||
| dx /= 20.0; | |||
| dy /= 20.0; | |||
| } | |||
| const PuglScrollDirection dir = | |||
| @@ -657,7 +647,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglScrollEvent ev = { | |||
| PUGL_SCROLL, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -689,7 +679,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglKeyEvent ev = { | |||
| PUGL_KEY_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -720,7 +710,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglKeyEvent ev = { | |||
| PUGL_KEY_RELEASE, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -832,7 +822,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| PuglTextEvent ev = { | |||
| PUGL_TEXT, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -861,7 +851,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| - (void)flagsChanged:(NSEvent*)event | |||
| { | |||
| const uint32_t mods = getModifiers(event); | |||
| PuglKey special = (PuglKey)0; | |||
| PuglKey special = PUGL_KEY_NONE; | |||
| const uint16_t keyCode = [event keyCode]; | |||
| if (flagDiffers(mods, puglview->impl->mods, PUGL_MOD_SHIFT)) { | |||
| @@ -882,7 +872,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| const bool release = [event type] == NSEventTypeKeyUp; | |||
| const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -924,7 +914,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| - (void)timerTick:(NSTimer*)userTimer | |||
| { | |||
| const NSNumber* userInfo = userTimer.userInfo; | |||
| const PuglTimerEvent ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue}; | |||
| const PuglTimerEvent ev = {PUGL_TIMER, 0U, userInfo.unsignedLongValue}; | |||
| PuglEvent timerEvent; | |||
| timerEvent.timer = ev; | |||
| @@ -989,7 +979,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| { | |||
| (void)notification; | |||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0}}; | |||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0U}}; | |||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | |||
| puglDispatchEvent(window->puglview, &ev); | |||
| } | |||
| @@ -998,7 +988,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| { | |||
| (void)notification; | |||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0}}; | |||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0U}}; | |||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | |||
| puglDispatchEvent(window->puglview, &ev); | |||
| } | |||
| @@ -1117,17 +1107,18 @@ puglConstraint(const id item, | |||
| constant:(CGFloat)constant]; | |||
| } | |||
| static PuglStatus | |||
| updateSizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| { | |||
| const PuglSpan width = view->sizeHints[hint].width; | |||
| const PuglSpan height = view->sizeHints[hint].height; | |||
| if (!puglIsValidSize(view->sizeHints[hint])) { | |||
| if (!puglIsValidArea(view->sizeHints[hint])) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| switch (hint) { | |||
| case PUGL_DEFAULT_SIZE: | |||
| case PUGL_CURRENT_SIZE: | |||
| break; | |||
| case PUGL_MIN_SIZE: | |||
| @@ -1150,38 +1141,18 @@ updateSizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| static void | |||
| updateSizeHints(PuglView* const view) | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const view) | |||
| { | |||
| for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { | |||
| updateSizeHint(view, (PuglSizeHint)i); | |||
| puglApplySizeHint(view, (PuglSizeHint)i); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| } | |||
| // Get a bounding rect from the transient parent or the screen | |||
| const NSScreen* const screen = viewScreen(view); | |||
| const NSRect boundsPt = | |||
| rectFromScreen(screen, | |||
| @@ -1189,17 +1160,11 @@ getInitialFrame(PuglView* const view) | |||
| ? [[(const NSView*)view->transientParent window] frame] | |||
| : [screen frame]); | |||
| // Center the frame around the center of the bounding rectangle | |||
| const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| const NSRect boundsPx = nsRectFromPoints(view, boundsPt); | |||
| const double centerX = boundsPx.origin.x + boundsPx.size.width / 2; | |||
| const double centerY = boundsPx.origin.y + boundsPx.size.height / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)), | |||
| (PuglCoord)(centerY - (defaultSize.height / 2U)), | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| const NSRect boundsPx = nsRectFromPoints(view, boundsPt); | |||
| const PuglPoint center = { | |||
| (PuglCoord)(boundsPx.origin.x + (boundsPx.size.width / 2.0)), | |||
| (PuglCoord)(boundsPx.origin.y + (boundsPx.size.height / 2.0))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -1247,11 +1212,12 @@ puglRealize(PuglView* view) | |||
| CVDisplayLinkRelease(link); | |||
| } | |||
| // Get the initial frame to use from the defaults or last configuration | |||
| const PuglRect initialFrame = getInitialFrame(view); | |||
| // Get the initial size and position from the defaults or last configuration | |||
| const PuglArea size = puglGetInitialSize(view); | |||
| const PuglPoint pos = puglGetInitialPosition(view, size); | |||
| // Convert frame to points | |||
| const NSRect framePx = rectToNsRect(initialFrame); | |||
| const NSRect framePx = NSMakeRect(pos.x, pos.y, size.width, size.height); | |||
| const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, | |||
| framePx.origin.y / scaleFactor, | |||
| framePx.size.width / scaleFactor, | |||
| @@ -1277,7 +1243,7 @@ puglRealize(PuglView* view) | |||
| NSLayoutRelationGreaterThanOrEqual, | |||
| view->sizeHints[PUGL_MIN_SIZE].height)]; | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| [impl->wrapperView | |||
| addConstraint:puglConstraint(impl->wrapperView, | |||
| NSLayoutAttributeWidth, | |||
| @@ -1337,10 +1303,19 @@ puglRealize(PuglView* view) | |||
| ((NSWindow*)window).delegate = | |||
| [[PuglWindowDelegate alloc] initWithPuglWindow:window]; | |||
| // Set basic window hints and attributes | |||
| puglSetFrame(view, initialFrame); | |||
| // Set window frame | |||
| const NSRect screenPt = rectToScreen(screen, framePt); | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| // Resize views and move them to (0, 0) | |||
| const NSRect sizePx = {{0, 0}, {framePx.size.width, framePx.size.height}}; | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:sizePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| puglSetTransientParent(view, view->transientParent); | |||
| updateSizeHints(view); | |||
| puglUpdateSizeHints(view); | |||
| [window setContentView:impl->wrapperView]; | |||
| [view->world->impl->app activateIgnoringOtherApps:YES]; | |||
| @@ -1393,24 +1368,23 @@ puglUnrealize(PuglView* const view) | |||
| PuglStatus | |||
| puglShow(PuglView* view, const PuglShowCommand command) | |||
| { | |||
| if (!view->impl->wrapperView) { | |||
| const PuglStatus st = puglRealize(view); | |||
| if (st) { | |||
| return st; | |||
| } | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->wrapperView ? PUGL_SUCCESS : puglRealize(view); | |||
| if (st || !impl->wrapperView) { | |||
| return st; | |||
| } | |||
| NSWindow* const window = [view->impl->wrapperView window]; | |||
| NSWindow* const window = [impl->wrapperView window]; | |||
| if (![window isVisible]) { | |||
| [window setIsVisible:YES]; | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| [impl->drawView setNeedsDisplay:YES]; | |||
| } | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| [window orderFront:view->impl->wrapperView]; | |||
| [window orderFront:impl->wrapperView]; | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| [window orderFrontRegardless]; | |||
| @@ -1583,6 +1557,10 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||
| PuglStatus | |||
| puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| if (!view->impl->window || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| if (event->type == PUGL_CLIENT) { | |||
| PuglEvent copiedEvent = *event; | |||
| @@ -1610,18 +1588,17 @@ puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* view) | |||
| { | |||
| return puglPollEvents(view->world, -1.0); | |||
| } | |||
| #endif | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* world, const double timeout) | |||
| { | |||
| @autoreleasepool { | |||
| const PuglWorldState startState = world->state; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (world->type == PUGL_PROGRAM) { | |||
| NSDate* date = | |||
| ((timeout < 0) ? [NSDate distantFuture] | |||
| @@ -1650,19 +1627,13 @@ puglUpdate(PuglWorld* world, const double timeout) | |||
| [view->impl->drawView displayIfNeeded]; | |||
| } | |||
| world->state = startState; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| return puglDispatchEvents(view->world); | |||
| } | |||
| #endif | |||
| double | |||
| puglGetTime(const PuglWorld* world) | |||
| { | |||
| @@ -1674,22 +1645,45 @@ puglGetTime(const PuglWorld* world) | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* view) | |||
| puglObscureView(PuglView* view) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const NSRect rectPx = { | |||
| {(double)rect.x, | |||
| (double)view->lastConfigure.height - (rect.y + rect.height)}, | |||
| {(double)rect.width, (double)rect.height}, | |||
| }; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| const PuglSpan viewHeight = view->lastConfigure.height; | |||
| const int cx = MAX(0, x); | |||
| const int cy = MAX(0, viewHeight - y - (int)height); | |||
| const unsigned cw = MIN(view->lastConfigure.width, width); | |||
| const unsigned ch = MIN(view->lastConfigure.height, height); | |||
| if (cw == view->lastConfigure.width && ch == view->lastConfigure.height) { | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| } else { | |||
| const NSRect rectPx = NSMakeRect(cx, cy, cw, ch); | |||
| [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; | |||
| [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -1735,107 +1729,35 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* view, const PuglRect frame) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| const NSRect framePx = rectToNsRect(frame); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (impl->window) { | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| // Move and resize window to fit new content rect | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| // Resize views | |||
| const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:sizePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| [impl->window dispatchCurrentConfiguration]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| // Resize view | |||
| const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:framePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| PuglInternals* const impl = view->impl; | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| const NSRect framePx = | |||
| NSMakeRect(x, y, view->lastConfigure.width, view->lastConfigure.height); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| if (impl->window) { | |||
| // Adjust top-level window frame | |||
| return puglSetFrame(view, frame); | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| [impl->window setFrameOrigin:screenPt.origin]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| // Set wrapper view origin | |||
| const NSRect framePx = rectToNsRect(frame); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| [impl->wrapperView setFrameOrigin:framePt.origin]; | |||
| // Set draw view origin | |||
| const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; | |||
| [impl->drawView setFrameOrigin:drawPt.origin]; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| return dispatchCurrentChildViewConfiguration(view); | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| PuglInternals* const impl = view->impl; | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (impl->window) { | |||
| // Adjust top-level window frame | |||
| PuglRect frame = puglGetFrame(view); | |||
| frame.width = (PuglSpan)width; | |||
| frame.height = (PuglSpan)height; | |||
| return puglSetFrame(view, frame); | |||
| } | |||
| // Set wrapper view size | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -1847,24 +1769,20 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; | |||
| [impl->drawView setFrameSize:drawPt.size]; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| } | |||
| if (impl->window) { | |||
| const NSRect framePx = | |||
| NSMakeRect(view->lastConfigure.x, view->lastConfigure.y, width, height); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| // Resize window to fit new content rect | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| [impl->window dispatchCurrentConfiguration]; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return view->impl->window ? updateSizeHint(view, hint) : PUGL_SUCCESS; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view); | |||
| } | |||
| PuglStatus | |||
| @@ -1892,7 +1810,7 @@ puglPaste(PuglView* const view) | |||
| { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, | |||
| 0, | |||
| 0U, | |||
| puglGetTime(view->world), | |||
| }; | |||
| @@ -1939,7 +1857,7 @@ puglAcceptOffer(PuglView* const view, | |||
| PuglWrapperView* const wrapper = view->impl->wrapperView; | |||
| NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; | |||
| if (!pasteboard) { | |||
| return PUGL_BAD_PARAMETER; | |||
| return PUGL_UNKNOWN_ERROR; | |||
| } | |||
| const NSArray<NSPasteboardType>* const types = [pasteboard types]; | |||
| @@ -1950,13 +1868,12 @@ puglAcceptOffer(PuglView* const view, | |||
| wrapper->dragOperation = NSDragOperationCopy; | |||
| wrapper->dragTypeIndex = typeIndex; | |||
| const PuglDataEvent data = { | |||
| PUGL_DATA, 0U, puglGetTime(view->world), (uint32_t)typeIndex}; | |||
| const double now = puglGetTime(view->world); | |||
| const PuglDataEvent data = {PUGL_DATA, 0U, now, typeIndex}; | |||
| PuglEvent dataEvent; | |||
| dataEvent.data = data; | |||
| puglDispatchEvent(view, &dataEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &dataEvent); | |||
| } | |||
| const void* | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/cairo.h" | |||
| #include <pugl/cairo.h> | |||
| #include <cairo-quartz.h> | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/gl.h" | |||
| #include <pugl/gl.h> | |||
| #ifndef __MAC_10_10 | |||
| # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -8,9 +8,9 @@ | |||
| #include "stub.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_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 | |||
| // The API that a platform implementation must define | |||
| // The internal API that a platform implementation must define | |||
| #ifndef PUGL_PLATFORM_H | |||
| #define PUGL_PLATFORM_H | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| PUGL_BEGIN_DECLS | |||
| /// Allocate and initialise world internals (implemented once per platform) | |||
| PUGL_MALLOC_FUNC | |||
| PuglWorldInternals* | |||
| PUGL_MALLOC_FUNC PuglWorldInternals* | |||
| puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); | |||
| /// Destroy and free world internals (implemented once per platform) | |||
| @@ -22,14 +22,29 @@ void | |||
| puglFreeWorldInternals(PuglWorld* world); | |||
| /// Allocate and initialise view internals (implemented once per platform) | |||
| PUGL_MALLOC_FUNC | |||
| PuglInternals* | |||
| PUGL_MALLOC_FUNC PuglInternals* | |||
| puglInitViewInternals(PuglWorld* world); | |||
| /// Destroy and free view internals (implemented once per platform) | |||
| void | |||
| puglFreeViewInternals(PuglView* view); | |||
| /// Adapt to the change of a size hint if necessary | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* view, PuglSizeHint hint); | |||
| /// Adapt to all configured size hints | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* view); | |||
| /// Set the current position of a view window | |||
| PuglStatus | |||
| puglSetWindowPosition(PuglView* view, int x, int y); | |||
| /// Set the current size of a view window | |||
| PuglStatus | |||
| puglSetWindowSize(PuglView* view, unsigned width, unsigned height); | |||
| PUGL_END_DECLS | |||
| #endif // PUGL_PLATFORM_H | |||
| @@ -4,7 +4,8 @@ | |||
| #ifndef PUGL_SRC_STUB_H | |||
| #define PUGL_SRC_STUB_H | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <stddef.h> | |||
| @@ -6,7 +6,7 @@ | |||
| #include "attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <stdbool.h> | |||
| #include <stddef.h> | |||
| @@ -21,24 +21,20 @@ typedef struct PuglInternalsImpl PuglInternals; | |||
| /// View hints | |||
| typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; | |||
| /// View position (both X and Y coordinates) or general point | |||
| typedef struct { | |||
| PuglCoord x; | |||
| PuglCoord y; | |||
| } PuglPoint; | |||
| /// View size (both X and Y coordinates) | |||
| typedef struct { | |||
| PuglSpan width; | |||
| PuglSpan height; | |||
| } PuglViewSize; | |||
| /// Blob of arbitrary data | |||
| typedef struct { | |||
| void* data; ///< Dynamically allocated data | |||
| size_t len; ///< Length of data in bytes | |||
| } PuglBlob; | |||
| /// State of the world in the process of an update | |||
| typedef enum { | |||
| PUGL_WORLD_IDLE, ///< Idle, not in puglUpdate() | |||
| PUGL_WORLD_UPDATING, ///< Event processing stage of puglUpdate() | |||
| PUGL_WORLD_EXPOSING, ///< Exposing stage of puglUpdate() | |||
| PUGL_WORLD_RECURSING, ///< Currently in recursive loop (Windows) | |||
| } PuglWorldState; | |||
| /// Stage of a view along its lifespan | |||
| typedef enum { | |||
| PUGL_VIEW_STAGE_ALLOCATED, | |||
| @@ -57,10 +53,9 @@ struct PuglViewImpl { | |||
| uintptr_t transientParent; | |||
| PuglConfigureEvent lastConfigure; | |||
| PuglHints hints; | |||
| PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS]; | |||
| PuglPoint positionHints[PUGL_NUM_POSITION_HINTS]; | |||
| PuglArea sizeHints[PUGL_NUM_SIZE_HINTS]; | |||
| char* strings[PUGL_NUM_STRING_HINTS]; | |||
| int defaultX; | |||
| int defaultY; | |||
| PuglViewStage stage; | |||
| bool resizing; | |||
| }; | |||
| @@ -74,6 +69,7 @@ struct PuglWorldImpl { | |||
| PuglView** views; | |||
| char* strings[PUGL_NUM_STRING_HINTS]; | |||
| PuglWorldType type; | |||
| PuglWorldState state; | |||
| }; | |||
| /// Opaque surface used by graphics backend | |||
| @@ -82,23 +78,21 @@ typedef void PuglSurface; | |||
| /// Graphics backend interface | |||
| struct PuglBackendImpl { | |||
| /// Get visual information from display and setup view as necessary | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*configure)(PuglView*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*configure)(PuglView*); | |||
| /// Create surface and drawing context | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*create)(PuglView*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*create)(PuglView*); | |||
| /// Destroy surface and drawing context | |||
| void (*destroy)(PuglView*); | |||
| /// Enter drawing context, for drawing if expose is non-null | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*enter)(PuglView*, const PuglExposeEvent*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*enter)(PuglView*, | |||
| const PuglExposeEvent*); | |||
| /// Leave drawing context, after drawing if expose is non-null | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*leave)(PuglView*, const PuglExposeEvent*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*leave)(PuglView*, | |||
| const PuglExposeEvent*); | |||
| /// Return the puglGetContext() handle for the application, if any | |||
| void* (*getContext)(PuglView*); | |||
| @@ -4,9 +4,10 @@ | |||
| #include "win.h" | |||
| #include "internal.h" | |||
| #include "macros.h" | |||
| #include "platform.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <dwmapi.h> | |||
| #include <windows.h> | |||
| @@ -42,13 +43,11 @@ | |||
| #define PUGL_USER_TIMER_MIN 9470 | |||
| #ifdef __cplusplus | |||
| # define PUGL_INIT_STRUCT \ | |||
| {} | |||
| #else | |||
| # define PUGL_INIT_STRUCT \ | |||
| { \ | |||
| 0 \ | |||
| } | |||
| #else | |||
| # define PUGL_INIT_STRUCT {0} | |||
| #endif | |||
| typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void); | |||
| @@ -58,8 +57,12 @@ typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| static wchar_t* | |||
| puglUtf8ToWideChar(const char* const utf8) | |||
| #ifdef UNICODE | |||
| typedef wchar_t ArgStringChar; | |||
| static ArgStringChar* | |||
| puglArgStringNew(const char* const utf8) | |||
| { | |||
| const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); | |||
| if (len > 0) { | |||
| @@ -71,6 +74,30 @@ puglUtf8ToWideChar(const char* const utf8) | |||
| return NULL; | |||
| } | |||
| static void | |||
| puglArgStringFree(ArgStringChar* const utf8) | |||
| { | |||
| free(utf8); | |||
| } | |||
| #else // !defined(UNICODE) | |||
| typedef const char ArgStringChar; | |||
| static ArgStringChar* | |||
| puglArgStringNew(const char* const utf8) | |||
| { | |||
| return utf8; | |||
| } | |||
| static void | |||
| puglArgStringFree(ArgStringChar* const utf8) | |||
| { | |||
| (void)utf8; | |||
| } | |||
| #endif | |||
| static char* | |||
| puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) | |||
| { | |||
| @@ -94,11 +121,7 @@ puglWinStatus(const BOOL success) | |||
| static bool | |||
| puglRegisterWindowClass(const char* name) | |||
| { | |||
| #ifdef UNICODE | |||
| wchar_t* const wname = puglUtf8ToWideChar(name); | |||
| #else | |||
| const char* const wname = name; | |||
| #endif | |||
| ArgStringChar* const nameArg = puglArgStringNew(name); | |||
| HMODULE module = NULL; | |||
| if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | |||
| @@ -109,7 +132,7 @@ puglRegisterWindowClass(const char* name) | |||
| } | |||
| WNDCLASSEX wc = PUGL_INIT_STRUCT; | |||
| if (GetClassInfoEx(module, wname, &wc)) { | |||
| if (GetClassInfoEx(module, nameArg, &wc)) { | |||
| return true; // Already registered | |||
| } | |||
| @@ -120,12 +143,10 @@ puglRegisterWindowClass(const char* name) | |||
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |||
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |||
| wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||
| wc.lpszClassName = wname; | |||
| wc.lpszClassName = nameArg; | |||
| const bool success = !!RegisterClassEx(&wc); | |||
| #ifdef UNICODE | |||
| free(wname); | |||
| #endif | |||
| puglArgStringFree(nameArg); | |||
| return success; | |||
| } | |||
| @@ -184,7 +205,7 @@ static double | |||
| puglWinGetViewScaleFactor(const PuglView* const view) | |||
| { | |||
| const HMODULE shcore = | |||
| LoadLibraryExA("Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| LoadLibraryEx(TEXT("Shcore.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| if (!shcore) { | |||
| return 1.0; | |||
| } | |||
| @@ -219,7 +240,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) | |||
| if (type == PUGL_PROGRAM) { | |||
| HMODULE user32 = | |||
| LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| LoadLibraryEx(TEXT("user32.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| if (user32) { | |||
| PFN_SetProcessDPIAware SetProcessDPIAware = | |||
| (PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | |||
| @@ -250,20 +271,6 @@ puglInitViewInternals(PuglWorld* PUGL_UNUSED(world)) | |||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| } | |||
| static PuglStatus | |||
| puglPollWinEvents(PuglWorld* world, const double timeout) | |||
| { | |||
| (void)world; | |||
| if (timeout < 0) { | |||
| WaitMessage(); | |||
| } else { | |||
| MsgWaitForMultipleObjects( | |||
| 0, NULL, FALSE, (DWORD)(timeout * 1e3), QS_ALLEVENTS); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglRealize(PuglView* view) | |||
| { | |||
| @@ -287,9 +294,9 @@ puglRealize(PuglView* view) | |||
| puglEnsureHint(view, PUGL_ALPHA_BITS, 8); | |||
| // Get refresh rate for resize draw timer | |||
| DEVMODEA devMode; | |||
| DEVMODE devMode; | |||
| memset(&devMode, 0, sizeof(devMode)); | |||
| EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode); | |||
| EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode); | |||
| view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; | |||
| // Register window class if necessary | |||
| @@ -308,16 +315,16 @@ puglRealize(PuglView* view) | |||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | |||
| puglSetTransientParent(view, view->transientParent); | |||
| view->impl->scaleFactor = puglWinGetViewScaleFactor(view); | |||
| view->impl->cursor = LoadCursor(NULL, IDC_ARROW); | |||
| impl->scaleFactor = puglWinGetViewScaleFactor(view); | |||
| impl->cursor = LoadCursor(NULL, IDC_ARROW); | |||
| if (view->hints[PUGL_DARK_FRAME]) { | |||
| const BOOL useDarkMode = TRUE; | |||
| if ((DwmSetWindowAttribute(view->impl->hwnd, | |||
| if ((DwmSetWindowAttribute(impl->hwnd, | |||
| DWMWA_USE_IMMERSIVE_DARK_MODE, | |||
| &useDarkMode, | |||
| sizeof(useDarkMode)) != S_OK)) { | |||
| DwmSetWindowAttribute(view->impl->hwnd, | |||
| DwmSetWindowAttribute(impl->hwnd, | |||
| PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE, | |||
| &useDarkMode, | |||
| sizeof(useDarkMode)); | |||
| @@ -343,51 +350,48 @@ puglUnrealize(PuglView* const view) | |||
| view->backend->destroy(view); | |||
| } | |||
| ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||
| view->impl->hdc = NULL; | |||
| DestroyWindow(view->impl->hwnd); | |||
| view->impl->hwnd = NULL; | |||
| memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent)); | |||
| return PUGL_SUCCESS; | |||
| ReleaseDC(impl->hwnd, impl->hdc); | |||
| impl->hdc = NULL; | |||
| const PuglStatus st = puglWinStatus(DestroyWindow(impl->hwnd)); | |||
| impl->hwnd = NULL; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglShow(PuglView* view, const PuglShowCommand command) | |||
| { | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->hwnd ? PUGL_SUCCESS : puglRealize(view); | |||
| if (!impl->hwnd) { | |||
| const PuglStatus st = puglRealize(view); | |||
| if (st) { | |||
| return st; | |||
| if (!st) { | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| st = SetActiveWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| st = SetForegroundWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
| break; | |||
| } | |||
| } | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| SetActiveWindow(impl->hwnd); | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| SetForegroundWindow(impl->hwnd); | |||
| break; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglHide(PuglView* view) | |||
| { | |||
| PuglInternals* impl = view->impl; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| ShowWindow(impl->hwnd, SW_HIDE); | |||
| ShowWindow(view->impl->hwnd, SW_HIDE); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -408,14 +412,11 @@ puglFreeViewInternals(PuglView* view) | |||
| void | |||
| puglFreeWorldInternals(PuglWorld* world) | |||
| { | |||
| #ifdef UNICODE | |||
| wchar_t* const wname = puglUtf8ToWideChar(world->strings[PUGL_CLASS_NAME]); | |||
| UnregisterClass(wname, NULL); | |||
| free(wname); | |||
| #else | |||
| UnregisterClass(world->strings[PUGL_CLASS_NAME], NULL); | |||
| #endif | |||
| const char* const className = world->strings[PUGL_CLASS_NAME]; | |||
| ArgStringChar* const classNameArg = puglArgStringNew(className); | |||
| UnregisterClass(classNameArg, NULL); | |||
| puglArgStringFree(classNameArg); | |||
| free(world->impl); | |||
| } | |||
| @@ -426,14 +427,14 @@ keyInRange(const WPARAM winSym, | |||
| const PuglKey puglMin) | |||
| { | |||
| return (winSym >= winMin && winSym <= winMax) | |||
| ? (PuglKey)(puglMin + (winSym - winMin)) | |||
| : (PuglKey)0; | |||
| ? (PuglKey)((WPARAM)puglMin + (winSym - winMin)) | |||
| : PUGL_KEY_NONE; | |||
| } | |||
| static PuglKey | |||
| keySymToSpecial(const WPARAM sym, const bool ext) | |||
| { | |||
| PuglKey key = (PuglKey)0; | |||
| PuglKey key = PUGL_KEY_NONE; | |||
| if ((key = keyInRange(sym, VK_F1, VK_F12, PUGL_KEY_F1)) || | |||
| (key = keyInRange(sym, | |||
| VK_PRIOR, | |||
| @@ -482,23 +483,26 @@ keySymToSpecial(const WPARAM sym, const bool ext) | |||
| // clang-format on | |||
| } | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| static bool | |||
| is_toggled(int vkey) | |||
| { | |||
| return (unsigned)GetKeyState(vkey) & 1U; | |||
| } | |||
| static uint32_t | |||
| getModifiers(void) | |||
| { | |||
| // clang-format off | |||
| return ( | |||
| ((GetKeyState(VK_SHIFT) < 0) ? (uint32_t)PUGL_MOD_SHIFT : 0U) | | |||
| ((GetKeyState(VK_CONTROL) < 0) ? (uint32_t)PUGL_MOD_CTRL : 0U) | | |||
| ((GetKeyState(VK_MENU) < 0) ? (uint32_t)PUGL_MOD_ALT : 0U) | | |||
| ((GetKeyState(VK_LWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | | |||
| ((GetKeyState(VK_RWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | | |||
| ((GetKeyState(VK_NUMLOCK) & 1U) ? (uint32_t)PUGL_MOD_NUM_LOCK : 0U) | | |||
| ((GetKeyState(VK_SCROLL) & 1U) ? (uint32_t)PUGL_MOD_SCROLL_LOCK : 0U) | | |||
| ((GetKeyState(VK_CAPITAL) & 1U) ? (uint32_t)PUGL_MOD_CAPS_LOCK : 0U)); | |||
| // clang-format on | |||
| return ((uint32_t)(((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0) | | |||
| ((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0) | | |||
| ((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0) | | |||
| ((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0) | | |||
| ((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0) | | |||
| (is_toggled(VK_NUMLOCK) ? PUGL_MOD_NUM_LOCK : 0) | | |||
| (is_toggled(VK_SCROLL) ? PUGL_MOD_SCROLL_LOCK : 0) | | |||
| (is_toggled(VK_CAPITAL) ? PUGL_MOD_CAPS_LOCK : 0))); | |||
| } | |||
| static void | |||
| @@ -673,7 +677,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||
| const PuglCrossingEvent ev = { | |||
| type, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| (double)pos.x, | |||
| (double)pos.y, | |||
| @@ -683,7 +687,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||
| PUGL_CROSSING_NORMAL, | |||
| }; | |||
| PuglEvent crossingEvent = {{type, 0}}; | |||
| PuglEvent crossingEvent = {{type, 0U}}; | |||
| crossingEvent.crossing = ev; | |||
| puglDispatchEvent(view, &crossingEvent); | |||
| } | |||
| @@ -693,8 +697,8 @@ constrainAspect(const PuglView* const view, | |||
| RECT* const size, | |||
| const WPARAM wParam) | |||
| { | |||
| const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| const float minA = (float)minAspect.width / (float)minAspect.height; | |||
| const float maxA = (float)maxAspect.width / (float)maxAspect.height; | |||
| @@ -733,7 +737,7 @@ constrainAspect(const PuglView* const view, | |||
| static LRESULT | |||
| handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| RECT rect = {0, 0, 0, 0}; | |||
| POINT pt = {0, 0}; | |||
| MINMAXINFO* mmi = NULL; | |||
| @@ -779,8 +783,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| handleConfigure(view, &event); | |||
| break; | |||
| case WM_SIZING: | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MIN_ASPECT]) && | |||
| puglIsValidSize(view->sizeHints[PUGL_MAX_ASPECT])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MIN_ASPECT]) && | |||
| puglIsValidArea(view->sizeHints[PUGL_MAX_ASPECT])) { | |||
| constrainAspect(view, (RECT*)lParam, wParam); | |||
| return TRUE; | |||
| } | |||
| @@ -795,7 +799,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| break; | |||
| case WM_TIMER: | |||
| if (wParam >= PUGL_USER_TIMER_MIN) { | |||
| PuglEvent ev = {{PUGL_TIMER, 0}}; | |||
| PuglEvent ev = {{PUGL_TIMER, 0U}}; | |||
| ev.timer.id = wParam - PUGL_USER_TIMER_MIN; | |||
| puglDispatchEvent(view, &ev); | |||
| } | |||
| @@ -812,7 +816,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| mmi = (MINMAXINFO*)lParam; | |||
| mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; | |||
| mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height; | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| mmi->ptMaxTrackSize.x = view->sizeHints[PUGL_MAX_SIZE].width; | |||
| mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; | |||
| } | |||
| @@ -948,8 +952,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| PuglStatus | |||
| puglGrabFocus(PuglView* view) | |||
| { | |||
| SetFocus(view->impl->hwnd); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(!!SetFocus(view->impl->hwnd)); | |||
| } | |||
| bool | |||
| @@ -1023,20 +1026,32 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags) | |||
| const bool newMaximized = styleIsMaximized(flags); | |||
| if (oldMaximized != newMaximized) { | |||
| ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); | |||
| puglPostRedisplay(view); | |||
| puglObscureView(view); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const PUGL_UNUSED(view), | |||
| const PuglSizeHint PUGL_UNUSED(hint)) | |||
| { | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const PUGL_UNUSED(view)) | |||
| { | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglStartTimer(PuglView* view, uintptr_t id, double timeout) | |||
| { | |||
| const UINT msec = (UINT)floor(timeout * 1000.0); | |||
| return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL) | |||
| ? PUGL_SUCCESS | |||
| : PUGL_UNKNOWN_ERROR); | |||
| SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| @@ -1048,32 +1063,24 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||
| PuglStatus | |||
| puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| if (!view->impl->hwnd || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| if (event->type == PUGL_CLOSE) { | |||
| PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0)); | |||
| } | |||
| if (event->type == PUGL_CLIENT) { | |||
| PostMessage(view->impl->hwnd, | |||
| PUGL_LOCAL_CLIENT_MSG, | |||
| (WPARAM)event->client.data1, | |||
| (LPARAM)event->client.data2); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(PostMessage(view->impl->hwnd, | |||
| PUGL_LOCAL_CLIENT_MSG, | |||
| (WPARAM)event->client.data1, | |||
| (LPARAM)event->client.data2)); | |||
| } | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* PUGL_UNUSED(view)) | |||
| { | |||
| WaitMessage(); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #endif | |||
| static PuglStatus | |||
| puglDispatchViewEvents(PuglView* view) | |||
| { | |||
| @@ -1118,21 +1125,31 @@ puglDispatchWinEvents(PuglWorld* world) | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* world, double timeout) | |||
| { | |||
| const double startTime = puglGetTime(world); | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| static const double minWaitSeconds = 0.002; | |||
| const double startTime = puglGetTime(world); | |||
| const PuglWorldState startState = world->state; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (timeout < 0.0) { | |||
| st = puglPollWinEvents(world, timeout); | |||
| st = st ? st : puglDispatchWinEvents(world); | |||
| } else if (timeout <= 0.001) { | |||
| WaitMessage(); | |||
| st = puglDispatchWinEvents(world); | |||
| } else if (timeout < minWaitSeconds) { | |||
| st = puglDispatchWinEvents(world); | |||
| } else { | |||
| const double endTime = startTime + timeout - 0.001; | |||
| for (double t = startTime; t < endTime; t = puglGetTime(world)) { | |||
| if ((st = puglPollWinEvents(world, endTime - t)) || | |||
| (st = puglDispatchWinEvents(world))) { | |||
| break; | |||
| } | |||
| const double endTime = startTime + timeout - minWaitSeconds; | |||
| double t = startTime; | |||
| while (!st && t < endTime) { | |||
| const DWORD timeoutMs = (DWORD)((endTime - t) * 1e3); | |||
| MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutMs, QS_ALLEVENTS); | |||
| st = puglDispatchWinEvents(world); | |||
| t = puglGetTime(world); | |||
| } | |||
| } | |||
| @@ -1144,17 +1161,10 @@ puglUpdate(PuglWorld* world, double timeout) | |||
| UpdateWindow(world->views[i]->impl->hwnd); | |||
| } | |||
| world->state = startState; | |||
| return st; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| return puglUpdate(view->world, 0.0); | |||
| } | |||
| #endif | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| @@ -1188,23 +1198,35 @@ puglGetTime(const PuglWorld* world) | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* view) | |||
| puglObscureView(PuglView* view) | |||
| { | |||
| InvalidateRect(view->impl->hwnd, NULL, false); | |||
| return PUGL_SUCCESS; | |||
| return view->world->state == PUGL_WORLD_EXPOSING | |||
| ? PUGL_BAD_CALL | |||
| : puglWinStatus(InvalidateRect(view->impl->hwnd, NULL, false)); | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* const view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const RECT r = {(long)floor(rect.x), | |||
| (long)floor(rect.y), | |||
| (long)ceil(rect.x + rect.width), | |||
| (long)ceil(rect.y + rect.height)}; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| InvalidateRect(view->impl->hwnd, &r, false); | |||
| const int cx = MAX(0, x); | |||
| const int cy = MAX(0, y); | |||
| const unsigned cw = MIN(view->lastConfigure.width, width); | |||
| const unsigned ch = MIN(view->lastConfigure.height, height); | |||
| return PUGL_SUCCESS; | |||
| const RECT r = {cx, cy, cx + (long)cw, cy + (long)ch}; | |||
| return puglWinStatus(InvalidateRect(view->impl->hwnd, &r, false)); | |||
| } | |||
| PuglNativeView | |||
| @@ -1218,19 +1240,18 @@ puglViewStringChanged(PuglView* const view, | |||
| const PuglStringHint key, | |||
| const char* const value) | |||
| { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (!view->impl->hwnd) { | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| if (key == PUGL_WINDOW_TITLE) { | |||
| wchar_t* const wtitle = puglUtf8ToWideChar(value); | |||
| if (wtitle) { | |||
| SetWindowTextW(view->impl->hwnd, wtitle); | |||
| free(wtitle); | |||
| } | |||
| ArgStringChar* const titleArg = puglArgStringNew(value); | |||
| st = puglWinStatus(SetWindowText(view->impl->hwnd, titleArg)); | |||
| puglArgStringFree(titleArg); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| static RECT | |||
| @@ -1256,44 +1277,8 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* view, const PuglRect frame) | |||
| { | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = | |||
| adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height); | |||
| return puglWinStatus( | |||
| SetWindowPos(view->impl->hwnd, | |||
| HWND_TOP, | |||
| rect.left, | |||
| rect.top, | |||
| rect.right - rect.left, | |||
| rect.bottom - rect.top, | |||
| SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = adjustedWindowRect( | |||
| view, x, y, view->lastConfigure.width, view->lastConfigure.height); | |||
| @@ -1305,19 +1290,10 @@ puglSetPosition(PuglView* const view, const int x, const int y) | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = adjustedWindowRect(view, | |||
| view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| @@ -1336,21 +1312,6 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| flags)); | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglSetTransientParent(PuglView* view, PuglNativeView parent) | |||
| { | |||
| @@ -1394,15 +1355,14 @@ puglAcceptOffer(PuglView* const view, | |||
| const PuglDataEvent data = { | |||
| PUGL_DATA, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| 0, | |||
| }; | |||
| PuglEvent dataEvent; | |||
| dataEvent.data = data; | |||
| puglDispatchEvent(view, &dataEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &dataEvent); | |||
| } | |||
| const void* | |||
| @@ -1412,8 +1372,21 @@ puglGetClipboard(PuglView* const view, | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT) || | |||
| !OpenClipboard(impl->hwnd)) { | |||
| if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT)) { | |||
| return NULL; | |||
| } | |||
| // Try to open the clipboard several times since others may have locked it | |||
| BOOL opened = FALSE; | |||
| static const unsigned max_tries = 16U; | |||
| for (unsigned i = 0U; !opened && i < max_tries; ++i) { | |||
| opened = OpenClipboard(impl->hwnd); | |||
| if (!opened) { | |||
| Sleep(0); | |||
| } | |||
| } | |||
| if (!opened) { | |||
| return NULL; | |||
| } | |||
| @@ -1424,15 +1397,14 @@ puglGetClipboard(PuglView* const view, | |||
| return NULL; | |||
| } | |||
| free(view->impl->clipboard.data); | |||
| view->impl->clipboard.data = | |||
| puglWideCharToUtf8(wstr, &view->impl->clipboard.len); | |||
| free(impl->clipboard.data); | |||
| impl->clipboard.data = puglWideCharToUtf8(wstr, &impl->clipboard.len); | |||
| GlobalUnlock(mem); | |||
| CloseClipboard(); | |||
| *len = view->impl->clipboard.len; | |||
| return view->impl->clipboard.data; | |||
| *len = impl->clipboard.len; | |||
| return impl->clipboard.data; | |||
| } | |||
| PuglStatus | |||
| @@ -1443,7 +1415,7 @@ puglSetClipboard(PuglView* const view, | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len); | |||
| PuglStatus st = puglSetBlob(&impl->clipboard, data, len); | |||
| if (st) { | |||
| return st; | |||
| } | |||
| @@ -1488,14 +1460,13 @@ puglPaste(PuglView* const view) | |||
| { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| }; | |||
| PuglEvent offerEvent; | |||
| offerEvent.offer = offer; | |||
| puglDispatchEvent(view, &offerEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &offerEvent); | |||
| } | |||
| static const TCHAR* const cursor_ids[] = { | |||
| @@ -1563,42 +1534,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) | |||
| return pfd; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| RECT rect = {0, 0, 0, 0}; | |||
| GetWindowRect(view->transientParent ? (HWND)view->transientParent | |||
| : GetDesktopWindow(), | |||
| &rect); | |||
| const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; | |||
| const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = { | |||
| (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; | |||
| return frame; | |||
| } | |||
| // Get a bounding rect from the "nearest" parent or parent-like window | |||
| const HWND hwnd = puglWinGetWindow(view); | |||
| RECT rect = {0, 0, 0, 0}; | |||
| GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect); | |||
| // Center the frame around the center of the bounding rectangle | |||
| const LONG centerX = rect.left + (rect.right - rect.left) / 2; | |||
| const LONG centerY = rect.top + (rect.bottom - rect.top) / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), | |||
| (PuglCoord)(centerY - (defaultHeight / 2)), | |||
| defaultWidth, | |||
| defaultHeight}; | |||
| return frame; | |||
| const PuglPoint center = { | |||
| (PuglCoord)(rect.left + ((rect.right - rect.left) / 2)), | |||
| (PuglCoord)(rect.top + ((rect.bottom - rect.top) / 2))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -1613,28 +1560,35 @@ puglWinCreateWindow(PuglView* const view, | |||
| PuglNativeView parent = view->parent ? view->parent : view->transientParent; | |||
| // Calculate initial window rectangle | |||
| const unsigned winFlags = puglWinGetWindowFlags(view); | |||
| const unsigned winExFlags = puglWinGetWindowExFlags(view); | |||
| const PuglRect frame = getInitialFrame(view); | |||
| RECT wr = {(long)frame.x, | |||
| (long)frame.y, | |||
| (long)frame.x + frame.width, | |||
| (long)frame.y + frame.height}; | |||
| const unsigned winFlags = puglWinGetWindowFlags(view); | |||
| const unsigned winExFlags = puglWinGetWindowExFlags(view); | |||
| const PuglArea size = puglGetInitialSize(view); | |||
| const PuglPoint pos = puglGetInitialPosition(view, size); | |||
| RECT wr = {(long)pos.x, | |||
| (long)pos.y, | |||
| (long)pos.x + size.width, | |||
| (long)pos.y + size.height}; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); | |||
| ArgStringChar* const classNameArg = puglArgStringNew(className); | |||
| ArgStringChar* const titleArg = puglArgStringNew(title); | |||
| // Create window and get drawing context | |||
| if (!(*hwnd = CreateWindowExA(winExFlags, | |||
| className, | |||
| title, | |||
| winFlags, | |||
| wr.left, | |||
| wr.right, | |||
| wr.right - wr.left, | |||
| wr.bottom - wr.top, | |||
| (HWND)parent, | |||
| NULL, | |||
| NULL, | |||
| NULL))) { | |||
| *hwnd = CreateWindowEx(winExFlags, | |||
| classNameArg, | |||
| titleArg, | |||
| winFlags, | |||
| wr.left, | |||
| wr.right, | |||
| wr.right - wr.left, | |||
| wr.bottom - wr.top, | |||
| (HWND)parent, | |||
| NULL, | |||
| NULL, | |||
| NULL); | |||
| puglArgStringFree(titleArg); | |||
| puglArgStringFree(classNameArg); | |||
| if (!*hwnd) { | |||
| return PUGL_REALIZE_FAILED; | |||
| } | |||
| @@ -1660,7 +1614,6 @@ puglWinConfigure(PuglView* view) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { | |||
| return st; | |||
| } | |||
| @@ -1673,20 +1626,18 @@ puglWinConfigure(PuglView* view) | |||
| DestroyWindow(impl->hwnd); | |||
| impl->hwnd = NULL; | |||
| impl->hdc = NULL; | |||
| return PUGL_SET_FORMAT_FAILED; | |||
| st = PUGL_SET_FORMAT_FAILED; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose) | |||
| { | |||
| if (expose) { | |||
| BeginPaint(view->impl->hwnd, &view->impl->paint); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return expose | |||
| ? puglWinStatus(!!BeginPaint(view->impl->hwnd, &view->impl->paint)) | |||
| : PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| @@ -6,7 +6,7 @@ | |||
| #include "internal.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <windows.h> | |||
| @@ -37,28 +37,19 @@ struct PuglInternalsImpl { | |||
| bool fullscreen; | |||
| }; | |||
| PUGL_API | |||
| PuglWinPFD | |||
| PUGL_API PuglWinPFD | |||
| puglWinGetPixelFormatDescriptor(const PuglHints hints); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinCreateWindow(PuglView* view, const char* title, HWND* hwnd, HDC* hdc); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinConfigure(PuglView* view); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinLeave(PuglView* view, const PuglExposeEvent* expose); | |||
| #endif // PUGL_SRC_WIN_H | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/cairo.h" | |||
| #include <pugl/cairo.h> | |||
| #include <cairo-win32.h> | |||
| #include <cairo.h> | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/gl.h" | |||
| #include <pugl/gl.h> | |||
| #include <windows.h> | |||
| @@ -14,30 +14,42 @@ | |||
| #include <stdbool.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*); | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| static PuglStatus | |||
| puglWinStubConfigure(PuglView* view) | |||
| @@ -7,7 +7,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/vulkan.h" | |||
| #include <pugl/vulkan.h> | |||
| #include <vulkan/vulkan.h> | |||
| #include <vulkan/vulkan_win32.h> | |||
| @@ -11,7 +11,7 @@ | |||
| #include "platform.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <X11/X.h> | |||
| #include <X11/Xatom.h> | |||
| @@ -69,13 +69,11 @@ | |||
| #include <unistd.h> | |||
| #ifdef __cplusplus | |||
| # define PUGL_INIT_STRUCT \ | |||
| {} | |||
| #else | |||
| # define PUGL_INIT_STRUCT \ | |||
| { \ | |||
| 0 \ | |||
| } | |||
| #else | |||
| # define PUGL_INIT_STRUCT {0} | |||
| #endif | |||
| enum WmClientStateMessageAction { | |||
| @@ -396,8 +394,15 @@ findView(PuglWorld* const world, const Window window) | |||
| return NULL; | |||
| } | |||
| static PuglStatus | |||
| updateSizeHints(const PuglView* const view) | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint)) | |||
| { | |||
| // No fine-grained updates, hints are always recalculated together | |||
| return puglUpdateSizeHints(view); | |||
| } | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const view) | |||
| { | |||
| if (!view->impl->win) { | |||
| return PUGL_SUCCESS; | |||
| @@ -407,40 +412,44 @@ updateSizeHints(const PuglView* const view) | |||
| XSizeHints sizeHints = PUGL_INIT_STRUCT; | |||
| if (!view->hints[PUGL_RESIZABLE]) { | |||
| const PuglRect frame = puglGetFrame(view); | |||
| PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| if (!puglIsValidSize(size.width, size.height)) { | |||
| size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE); | |||
| } | |||
| sizeHints.flags = PBaseSize | PMinSize | PMaxSize; | |||
| sizeHints.base_width = (int)frame.width; | |||
| sizeHints.base_height = (int)frame.height; | |||
| sizeHints.min_width = (int)frame.width; | |||
| sizeHints.min_height = (int)frame.height; | |||
| sizeHints.max_width = (int)frame.width; | |||
| sizeHints.max_height = (int)frame.height; | |||
| sizeHints.base_width = (int)size.width; | |||
| sizeHints.base_height = (int)size.height; | |||
| sizeHints.min_width = (int)size.width; | |||
| sizeHints.min_height = (int)size.height; | |||
| sizeHints.max_width = (int)size.width; | |||
| sizeHints.max_height = (int)size.height; | |||
| } else { | |||
| // Avoid setting PBaseSize for top level views to avoid window manager bugs | |||
| const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| if (puglIsValidSize(defaultSize) && view->parent) { | |||
| const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| if (puglIsValidArea(defaultSize) && view->parent) { | |||
| sizeHints.flags |= PBaseSize; | |||
| sizeHints.base_width = defaultSize.width; | |||
| sizeHints.base_height = defaultSize.height; | |||
| } | |||
| const PuglViewSize minSize = view->sizeHints[PUGL_MIN_SIZE]; | |||
| if (puglIsValidSize(minSize)) { | |||
| const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE]; | |||
| if (puglIsValidArea(minSize)) { | |||
| sizeHints.flags |= PMinSize; | |||
| sizeHints.min_width = minSize.width; | |||
| sizeHints.min_height = minSize.height; | |||
| } | |||
| const PuglViewSize maxSize = view->sizeHints[PUGL_MAX_SIZE]; | |||
| if (puglIsValidSize(maxSize)) { | |||
| const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE]; | |||
| if (puglIsValidArea(maxSize)) { | |||
| sizeHints.flags |= PMaxSize; | |||
| sizeHints.max_width = maxSize.width; | |||
| sizeHints.max_height = maxSize.height; | |||
| } | |||
| const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| if (puglIsValidSize(minAspect) && puglIsValidSize(maxAspect)) { | |||
| const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| if (puglIsValidArea(minAspect) && puglIsValidArea(maxAspect)) { | |||
| sizeHints.flags |= PAspect; | |||
| sizeHints.min_aspect.x = minAspect.width; | |||
| sizeHints.min_aspect.y = minAspect.height; | |||
| @@ -448,8 +457,8 @@ updateSizeHints(const PuglView* const view) | |||
| sizeHints.max_aspect.y = maxAspect.height; | |||
| } | |||
| const PuglViewSize fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; | |||
| if (puglIsValidSize(fixedAspect)) { | |||
| const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; | |||
| if (puglIsValidArea(fixedAspect)) { | |||
| sizeHints.flags |= PAspect; | |||
| sizeHints.min_aspect.x = fixedAspect.width; | |||
| sizeHints.min_aspect.y = fixedAspect.height; | |||
| @@ -513,48 +522,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) | |||
| board->data.len = 0; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; | |||
| const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = { | |||
| (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; | |||
| return frame; | |||
| } | |||
| // Get the best "parentish" window to position the window in | |||
| Display* const display = view->world->impl->display; | |||
| const Window parent = | |||
| (view->parent ? (Window)view->parent | |||
| : view->transientParent ? (Window)view->transientParent | |||
| : RootWindow(display, view->impl->screen)); | |||
| // Get the position/size of the parent as bounds for the new window | |||
| XWindowAttributes parentAttrs = PUGL_INIT_STRUCT; | |||
| XGetWindowAttributes(display, parent, &parentAttrs); | |||
| Display* const display = view->world->impl->display; | |||
| const int screen = view->impl->screen; | |||
| XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT; | |||
| XGetWindowAttributes(display, | |||
| view->transientParent ? (Window)view->transientParent | |||
| : RootWindow(display, screen), | |||
| &ancestorAttrs); | |||
| // Center the frame within the parent bounds | |||
| const int centerX = parentAttrs.x + parentAttrs.width / 2; | |||
| const int centerY = parentAttrs.y + parentAttrs.height / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), | |||
| (PuglCoord)(centerY - (defaultHeight / 2)), | |||
| defaultWidth, | |||
| defaultHeight}; | |||
| return frame; | |||
| const PuglPoint center = { | |||
| (PuglCoord)(ancestorAttrs.x + (ancestorAttrs.width / 2)), | |||
| (PuglCoord)(ancestorAttrs.y + (ancestorAttrs.height / 2))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -609,16 +591,17 @@ puglRealize(PuglView* const view) | |||
| attr.event_mask |= StructureNotifyMask; | |||
| attr.event_mask |= VisibilityChangeMask; | |||
| // Calculate the initial window rectangle | |||
| const PuglRect initialFrame = getInitialFrame(view); | |||
| // Calculate the initial window frame | |||
| const PuglArea initialSize = puglGetInitialSize(view); | |||
| const PuglPoint initialPos = puglGetInitialPosition(view, initialSize); | |||
| // Create the window | |||
| impl->win = XCreateWindow(display, | |||
| parent, | |||
| initialFrame.x, | |||
| initialFrame.y, | |||
| initialFrame.width, | |||
| initialFrame.height, | |||
| initialPos.x, | |||
| initialPos.y, | |||
| initialSize.width, | |||
| initialSize.height, | |||
| 0, | |||
| impl->vi->depth, | |||
| InputOutput, | |||
| @@ -650,7 +633,7 @@ puglRealize(PuglView* const view) | |||
| if (XRRQueryExtension(display, &ignored, &ignored)) { | |||
| // Set refresh rate hint to the real refresh rate | |||
| XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent); | |||
| short current_rate = XRRConfigCurrentRate(conf); | |||
| const short current_rate = XRRConfigCurrentRate(conf); | |||
| view->hints[PUGL_REFRESH_RATE] = current_rate; | |||
| XRRFreeScreenConfigInfo(conf); | |||
| @@ -663,7 +646,7 @@ puglRealize(PuglView* const view) | |||
| XSetClassHint(display, impl->win, &classHint); | |||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | |||
| puglSetTransientParent(view, view->transientParent); | |||
| updateSizeHints(view); | |||
| puglUpdateSizeHints(view); | |||
| // Set PID and hostname so the window manager can access our process | |||
| char hostname[256] = PUGL_INIT_STRUCT; | |||
| @@ -759,21 +742,22 @@ puglUnrealize(PuglView* const view) | |||
| PuglStatus | |||
| puglShow(PuglView* const view, const PuglShowCommand command) | |||
| { | |||
| PuglStatus st = view->impl->win ? PUGL_SUCCESS : puglRealize(view); | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->win ? PUGL_SUCCESS : puglRealize(view); | |||
| if (!st) { | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| XMapWindow(view->world->impl->display, view->impl->win); | |||
| XMapWindow(view->world->impl->display, impl->win); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| XMapRaised(view->world->impl->display, view->impl->win); | |||
| XMapRaised(view->world->impl->display, impl->win); | |||
| break; | |||
| } | |||
| if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) { | |||
| st = puglPostRedisplay(view); | |||
| st = puglObscureView(view); | |||
| } | |||
| } | |||
| @@ -783,6 +767,10 @@ puglShow(PuglView* const view, const PuglShowCommand command) | |||
| PuglStatus | |||
| puglHide(PuglView* const view) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| XUnmapWindow(view->world->impl->display, view->impl->win); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -817,13 +805,13 @@ keyInRange(const KeySym xSym, | |||
| const PuglKey puglMin) | |||
| { | |||
| return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin)) | |||
| : (PuglKey)0; | |||
| : PUGL_KEY_NONE; | |||
| } | |||
| static PuglKey | |||
| keySymToSpecial(const KeySym sym) | |||
| { | |||
| PuglKey key = (PuglKey)0; | |||
| PuglKey key = PUGL_KEY_NONE; | |||
| if ((key = keyInRange(sym, XK_F1, XK_F12, PUGL_KEY_F1)) || | |||
| (key = keyInRange(sym, XK_Page_Up, XK_End, PUGL_KEY_PAGE_UP)) || | |||
| (key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) || | |||
| @@ -856,7 +844,7 @@ keySymToSpecial(const KeySym sym) | |||
| } | |||
| // clang-format on | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| static int | |||
| @@ -897,7 +885,8 @@ translateKey(PuglView* const view, XEvent* const xevent, PuglEvent* const event) | |||
| event->key.key = (PuglKey)puglDecodeUTF8((const uint8_t*)ustr); | |||
| } | |||
| if (xevent->type == KeyPress && !filter && !special && view->impl->xic) { | |||
| if (xevent->type == KeyPress && !filter && (!special || ufound > 0) && | |||
| view->impl->xic) { | |||
| // Lookup shifted key for possible text event | |||
| xevent->xkey.state = state; | |||
| @@ -1028,7 +1017,7 @@ translateClientMessage(PuglView* const view, XClientMessageEvent message) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| if (message.message_type == atoms->WM_PROTOCOLS) { | |||
| const Atom protocol = (Atom)message.data.l[0]; | |||
| @@ -1101,17 +1090,19 @@ getCurrentConfiguration(PuglView* const view) | |||
| XWindowAttributes attrs; | |||
| XGetWindowAttributes(display, view->impl->win, &attrs); | |||
| // Get window position relative to the root window | |||
| // Get window position (relative to the root window if not a child) | |||
| Window ignoredChild = 0; | |||
| int rootX = 0; | |||
| int rootY = 0; | |||
| XTranslateCoordinates( | |||
| display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild); | |||
| int x = attrs.x; | |||
| int y = attrs.y; | |||
| if (!view->parent) { | |||
| XTranslateCoordinates( | |||
| display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild); | |||
| } | |||
| // Build a configure event based on the current window configuration | |||
| PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}}; | |||
| configureEvent.configure.x = (PuglCoord)rootX; | |||
| configureEvent.configure.y = (PuglCoord)rootY; | |||
| PuglEvent configureEvent = {{PUGL_CONFIGURE, 0U}}; | |||
| configureEvent.configure.x = (PuglCoord)x; | |||
| configureEvent.configure.y = (PuglCoord)y; | |||
| configureEvent.configure.width = (PuglSpan)attrs.width; | |||
| configureEvent.configure.height = (PuglSpan)attrs.height; | |||
| configureEvent.configure.style = getCurrentViewStyleFlags(view); | |||
| @@ -1140,7 +1131,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||
| { | |||
| const PuglInternals* const impl = view->impl; | |||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| if (message.atom == atoms->NET_WM_STATE) { | |||
| // Get all the current states set in the window hints | |||
| @@ -1190,7 +1181,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||
| static PuglEvent | |||
| translateEvent(PuglView* const view, XEvent xevent) | |||
| { | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0; | |||
| switch (xevent.type) { | |||
| @@ -1211,6 +1202,9 @@ translateEvent(PuglView* const view, XEvent xevent) | |||
| view->impl->mapped = false; | |||
| event = makeConfigureEvent(view); | |||
| break; | |||
| case DestroyNotify: | |||
| view->impl->win = None; | |||
| break; | |||
| case ConfigureNotify: | |||
| event = makeConfigureEvent(view); | |||
| event.configure.width = (PuglSpan)xevent.xconfigure.width; | |||
| @@ -1503,7 +1497,7 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||
| PuglInternals* const impl = view->impl; | |||
| Display* const display = view->world->impl->display; | |||
| XEvent xev = PUGL_INIT_STRUCT; | |||
| if (!impl->win) { | |||
| if (!impl->win || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| @@ -1534,21 +1528,11 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* const view) | |||
| { | |||
| XEvent xevent; | |||
| XPeekEvent(view->world->impl->display, &xevent); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #endif | |||
| static void | |||
| mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) | |||
| { | |||
| if (!dst->type) { | |||
| if (src->width > 0.0 && src->height > 0.0) { | |||
| if (src->width && src->height) { | |||
| *dst = *src; | |||
| } | |||
| } else { | |||
| @@ -1612,7 +1596,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||
| Display* const display = view->world->impl->display; | |||
| const Atom selection = event->selection; | |||
| PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection); | |||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0U}}; | |||
| if (event->target == atoms->TARGETS) { | |||
| // Notification of available datatypes | |||
| @@ -1622,7 +1606,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||
| view, event->requestor, event->property, &numFormats, &formats) && | |||
| !setClipboardFormats(view, board, numFormats, formats)) { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, 0, (double)event->time / 1e3}; | |||
| PUGL_DATA_OFFER, 0U, (double)event->time / 1e3}; | |||
| puglEvent.offer = offer; | |||
| board->acceptedFormatIndex = UINT32_MAX; | |||
| @@ -1699,21 +1683,24 @@ handleSelectionRequest(const PuglWorld* const world, | |||
| } | |||
| /// Flush pending configure and expose events for all views | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| flushExposures(PuglWorld* const world) | |||
| { | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| PuglStatus st2 = PUGL_SUCCESS; | |||
| // Send update events so the application can trigger redraws | |||
| for (size_t i = 0; i < world->numViews; ++i) { | |||
| PuglView* const view = world->views[i]; | |||
| // Send update event so the application can trigger redraws | |||
| if (puglGetVisible(view)) { | |||
| puglDispatchSimpleEvent(view, PUGL_UPDATE); | |||
| if (puglGetVisible(world->views[i])) { | |||
| puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE); | |||
| } | |||
| } | |||
| // Expose any dirty views | |||
| world->state = PUGL_WORLD_EXPOSING; | |||
| for (size_t i = 0; i < world->numViews; ++i) { | |||
| PuglView* const view = world->views[i]; | |||
| // Copy and reset pending events (in case their handlers write new ones) | |||
| const PuglEvent configure = view->impl->pendingConfigure; | |||
| @@ -1753,7 +1740,7 @@ handleTimerEvent(PuglWorld* const world, const XEvent xevent) | |||
| for (size_t i = 0; i < world->impl->numTimers; ++i) { | |||
| if (world->impl->timers[i].alarm == notify->alarm) { | |||
| PuglEvent event = {{PUGL_TIMER, 0}}; | |||
| PuglEvent event = {{PUGL_TIMER, 0U}}; | |||
| event.timer.id = world->impl->timers[i].id; | |||
| puglDispatchEvent(world->impl->timers[i].view, &event); | |||
| } | |||
| @@ -1851,26 +1838,24 @@ dispatchX11Events(PuglWorld* const world) | |||
| return st; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* const view) | |||
| { | |||
| return puglUpdate(view->world, 0.0); | |||
| } | |||
| #endif | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* const world, const double timeout) | |||
| { | |||
| const double startTime = puglGetTime(world); | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| const double startTime = puglGetTime(world); | |||
| const PuglWorldState startState = world->state; | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| world->impl->dispatchingEvents = true; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (timeout < 0.0) { | |||
| st0 = pollX11Socket(world, timeout); | |||
| st0 = st0 ? st0 : dispatchX11Events(world); | |||
| if (!(st0 = pollX11Socket(world, timeout))) { | |||
| st0 = dispatchX11Events(world); | |||
| } | |||
| } else if (timeout <= 0.001) { | |||
| st0 = dispatchX11Events(world); | |||
| } else { | |||
| @@ -1885,10 +1870,8 @@ puglUpdate(PuglWorld* const world, const double timeout) | |||
| } | |||
| } | |||
| st1 = flushExposures(world); | |||
| world->impl->dispatchingEvents = false; | |||
| st1 = flushExposures(world); | |||
| world->state = startState; | |||
| return st0 ? st0 : st1; | |||
| } | |||
| @@ -1896,38 +1879,52 @@ double | |||
| puglGetTime(const PuglWorld* const world) | |||
| { | |||
| struct timespec ts; | |||
| clock_gettime(CLOCK_MONOTONIC, &ts); | |||
| if (clock_gettime(CLOCK_MONOTONIC, &ts)) { | |||
| return 0.0; | |||
| } | |||
| return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - | |||
| world->startTime; | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* const view) | |||
| puglObscureView(PuglView* const view) | |||
| { | |||
| PuglRect rect = puglGetFrame(view); | |||
| rect.x = 0; | |||
| rect.y = 0; | |||
| return puglPostRedisplayRect(view, rect); | |||
| return puglObscureRegion( | |||
| view, 0, 0, view->lastConfigure.width, view->lastConfigure.height); | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* const view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* const view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const PuglExposeEvent event = { | |||
| PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height}; | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| const PuglCoord cx = MAX((PuglCoord)0, (PuglCoord)x); | |||
| const PuglCoord cy = MAX((PuglCoord)0, (PuglCoord)y); | |||
| const PuglSpan cw = MIN(view->lastConfigure.width, (PuglSpan)width); | |||
| const PuglSpan ch = MIN(view->lastConfigure.height, (PuglSpan)height); | |||
| const PuglExposeEvent event = {PUGL_EXPOSE, 0U, cx, cy, cw, ch}; | |||
| if (view->world->impl->dispatchingEvents) { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (view->world->state == PUGL_WORLD_UPDATING) { | |||
| // Currently dispatching events, add/expand expose for the loop end | |||
| mergeExposeEvents(&view->impl->pendingExpose.expose, &event); | |||
| } else if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| st = PUGL_BAD_CALL; | |||
| } else if (view->impl->win) { | |||
| // Not dispatching events, send an X expose so we wake up next time | |||
| PuglEvent exposeEvent = {{PUGL_EXPOSE, 0}}; | |||
| PuglEvent exposeEvent = {{PUGL_EXPOSE, 0U}}; | |||
| exposeEvent.expose = event; | |||
| return puglSendEvent(view, &exposeEvent); | |||
| st = puglSendEvent(view, &exposeEvent); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglNativeView | |||
| @@ -1977,79 +1974,21 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* const view, const PuglRect frame) | |||
| { | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XMoveResizeWindow(view->world->impl->display, | |||
| view->impl->win, | |||
| frame.x, | |||
| frame.y, | |||
| frame.width, | |||
| frame.height)); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XMoveWindow(display, | |||
| return puglX11Status(XMoveWindow(view->world->impl->display, | |||
| view->impl->win, | |||
| (int)(x - view->impl->frameExtentLeft), | |||
| (int)(y - view->impl->frameExtentTop))); | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XResizeWindow(display, view->impl->win, width, height)); | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return updateSizeHints(view); | |||
| return puglX11Status( | |||
| XResizeWindow(view->world->impl->display, view->impl->win, width, height)); | |||
| } | |||
| PuglStatus | |||
| @@ -7,8 +7,8 @@ | |||
| #include "attributes.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/Xlib.h> | |||
| @@ -75,7 +75,6 @@ struct PuglWorldInternalsImpl { | |||
| XID serverTimeCounter; | |||
| int syncEventBase; | |||
| bool syncSupported; | |||
| bool dispatchingEvents; | |||
| }; | |||
| struct PuglInternalsImpl { | |||
| @@ -93,9 +92,7 @@ struct PuglInternalsImpl { | |||
| bool mapped; | |||
| }; | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglX11Configure(PuglView* view); | |||
| #endif // PUGL_SRC_X11_H | |||
| @@ -5,8 +5,8 @@ | |||
| #include "types.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.h> | |||
| @@ -19,10 +19,10 @@ typedef struct { | |||
| cairo_t* cr; | |||
| } PuglX11CairoSurface; | |||
| static PuglViewSize | |||
| static PuglArea | |||
| puglX11CairoGetViewSize(const PuglView* const view) | |||
| { | |||
| PuglViewSize size = {0U, 0U}; | |||
| PuglArea size = {0U, 0U}; | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the size of the last configured frame | |||
| @@ -97,11 +97,11 @@ puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (expose) { | |||
| const PuglViewSize viewSize = puglX11CairoGetViewSize(view); | |||
| const PuglSpan right = (PuglSpan)(expose->x + expose->width); | |||
| const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); | |||
| const PuglSpan surfaceWidth = MAX(right, viewSize.width); | |||
| const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); | |||
| const PuglArea viewSize = puglX11CairoGetViewSize(view); | |||
| const PuglSpan right = (PuglSpan)(expose->x + expose->width); | |||
| const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); | |||
| const PuglSpan surfaceWidth = MAX(right, viewSize.width); | |||
| const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); | |||
| if (!(st = puglX11CairoOpen(view, surfaceWidth, surfaceHeight))) { | |||
| surface->cr = cairo_create(surface->front); | |||
| if (cairo_status(surface->cr)) { | |||
| @@ -6,8 +6,8 @@ | |||
| #include "types.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 <X11/X.h> | |||
| @@ -101,8 +101,7 @@ puglX11GlConfigure(PuglView* view) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||
| { | |||
| PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | |||
| @@ -115,8 +114,7 @@ puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| @@ -1,13 +1,13 @@ | |||
| // Copyright 2012-2021 David Robillard <d@drobilla.net> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| #include "stub.h" | |||
| #include "types.h" | |||
| #include "x11.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| const PuglBackend* | |||
| puglStubBackend(void) | |||
| @@ -8,8 +8,8 @@ | |||
| #include "types.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_xlib.h> | |||
| @@ -250,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||
| if (view->backend != nullptr) | |||
| { | |||
| #ifdef DGL_OPENGL | |||
| #if defined(DGL_USE_GLES2) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
| #elif defined(DGL_USE_GLES3) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||
| #elif defined(DGL_USE_OPENGL3) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||
| #else | |||
| #elif defined(DGL_OPENGL) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
| #endif | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| @@ -277,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||
| void puglRaiseWindow(PuglView* const view) | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (NSWindow* const window = view->impl->window ? view->impl->window | |||
| : [view->impl->wrapperView window]) | |||
| [window orderFrontRegardless]; | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // this does the same as puglShow(view, PUGL_SHOW_FORCE_RAISE) + puglShow(view, PUGL_SHOW_RAISE) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSWindow* const window = [view->impl->wrapperView window]; | |||
| [window orderFrontRegardless]; | |||
| [window orderFront:view->impl->wrapperView]; | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| SetForegroundWindow(view->impl->hwnd); | |||
| SetActiveWindow(view->impl->hwnd); | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| XRaiseWindow(view->world->impl->display, view->impl->win); | |||
| #endif | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -306,29 +309,31 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co | |||
| view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast<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 (const PuglStatus status = updateSizeHint(view, PUGL_MIN_SIZE)) | |||
| return status; | |||
| if (const PuglStatus status = updateSizeHint(view, PUGL_FIXED_ASPECT)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| } | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| const char* const className = view->world->strings[PUGL_CLASS_NAME]; | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px'); | |||
| canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px'); | |||
| }, className, width, height); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| // nothing | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| if (view->impl->win) | |||
| { | |||
| if (const PuglStatus status = updateSizeHints(view)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| XFlush(view->world->impl->display); | |||
| } | |||
| #endif | |||
| #endif | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -340,99 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable) | |||
| { | |||
| puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (PuglWindow* const window = view->impl->window) | |||
| { | |||
| const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) | |||
| | (resizable ? NSResizableWindowMask : 0x0); | |||
| | (resizable ? NSResizableWindowMask : 0); | |||
| [window setStyleMask:style]; | |||
| } | |||
| // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| puglUpdateSizeHints(view); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (const HWND hwnd = view->impl->hwnd) | |||
| { | |||
| const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) | |||
| : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); | |||
| SetWindowLong(hwnd, GWL_STYLE, winFlags); | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| updateSizeHints(view); | |||
| #endif | |||
| #elif defined(HAVE_X11) | |||
| puglUpdateSizeHints(view); | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // set window size while also changing default | |||
| PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) | |||
| PuglStatus puglSetSizeAndDefault(PuglView* const view, const uint width, const uint height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) | |||
| return PUGL_BAD_PARAMETER; | |||
| #ifdef DGL_USING_X11 | |||
| // workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118 | |||
| // NOTE troublesome if used under KDE | |||
| if (view->impl->win && !view->parent && !view->transientParent && std::getenv("KDE_SESSION_VERSION") == nullptr) | |||
| { | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0; | |||
| } | |||
| else | |||
| #endif | |||
| // set default size first | |||
| { | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast<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 | |||
| if (view->impl->wrapperView) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| return status; | |||
| // nothing to do for PUGL_DEFAULT_SIZE hint | |||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||
| return status; | |||
| } | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| d_stdout("className is %s", view->world->strings[PUGL_CLASS_NAME]); | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| emscripten_set_canvas_element_size(view->world->strings[PUGL_CLASS_NAME], width, height); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| // matches upstream pugl, except we re-enter context after resize | |||
| if (view->impl->hwnd) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| return status; | |||
| // nothing to do for PUGL_DEFAULT_SIZE hint | |||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||
| return status; | |||
| // make sure to return context back to ourselves | |||
| puglBackendEnter(view); | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| // matches upstream pugl, adds flush at the end | |||
| if (view->impl->win) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| // updateSizeHints will use last known size, which is not yet updated | |||
| const PuglSpan lastWidth = view->lastConfigure.width; | |||
| const PuglSpan lastHeight = view->lastConfigure.height; | |||
| view->lastConfigure.width = static_cast<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 | |||
| XFlush(view->world->impl->display); | |||
| } | |||
| #endif | |||
| #endif | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -456,11 +441,12 @@ void puglOnDisplayPrepare(PuglView*) | |||
| void puglFallbackOnResize(PuglView* const view, const uint width, const uint height) | |||
| { | |||
| #ifdef DGL_OPENGL | |||
| #if defined(DGL_USE_OPENGL3) | |||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
| #else | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| #ifdef DGL_USE_OPENGL3 | |||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
| #else | |||
| glMatrixMode(GL_PROJECTION); | |||
| glLoadIdentity(); | |||
| 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(); | |||
| #endif | |||
| #else | |||
| return; | |||
| // unused | |||
| (void)view; | |||
| (void)width; | |||
| (void)height; | |||
| #endif | |||
| } | |||
| @@ -618,55 +605,64 @@ void puglWin32ShowCentered(PuglView* const view) | |||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) | |||
| { | |||
| const bool wasDispatchingEvents = world->impl->dispatchingEvents; | |||
| world->impl->dispatchingEvents = true; | |||
| const PuglWorldState startState = world->state; | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| const double startTime = puglGetTime(world); | |||
| const double endTime = startTime + 0.03; | |||
| const double endTime = startTime + 0.03; | |||
| for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) | |||
| { | |||
| pollX11Socket(world, endTime - t); | |||
| st = dispatchX11Events(world); | |||
| if (!(st = pollX11Socket(world, endTime - t))) | |||
| st = dispatchX11Events(world); | |||
| } | |||
| world->impl->dispatchingEvents = wasDispatchingEvents; | |||
| world->state = startState; | |||
| return st; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // X11 specific, set dialog window type and pid hints | |||
| // X11 specific, set dialog window type | |||
| void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) | |||
| void puglX11SetWindowType(const PuglView* const view, const bool isStandalone) | |||
| { | |||
| const PuglInternals* const impl = view->impl; | |||
| Display* const display = view->world->impl->display; | |||
| const pid_t pid = getpid(); | |||
| const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False); | |||
| XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); | |||
| #if defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE) | |||
| if (isStandalone) | |||
| { | |||
| const Atom _nwi = XInternAtom(display, "_NET_WM_ICON", False); | |||
| XChangeProperty(display, impl->win, _nwi, XA_CARDINAL, 32, PropModeReplace, | |||
| (const uchar*)DGL_X11_WINDOW_ICON_NAME, DGL_X11_WINDOW_ICON_SIZE); | |||
| const Atom NET_WM_ICON = XInternAtom(display, "_NET_WM_ICON", False); | |||
| XChangeProperty(display, | |||
| impl->win, | |||
| NET_WM_ICON, | |||
| XA_CARDINAL, | |||
| 32, | |||
| PropModeReplace, | |||
| reinterpret_cast<const uchar*>(DGL_X11_WINDOW_ICON_NAME), | |||
| DGL_X11_WINDOW_ICON_SIZE); | |||
| } | |||
| #endif | |||
| const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | |||
| const Atom NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | |||
| Atom _wts[2]; | |||
| int numAtoms = 0; | |||
| Atom windowTypes[2]; | |||
| int numWindowTypes = 0; | |||
| if (! isStandalone) | |||
| _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | |||
| _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); | |||
| XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); | |||
| windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | |||
| windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); | |||
| XChangeProperty(display, | |||
| impl->win, | |||
| NET_WM_WINDOW_TYPE, | |||
| XA_ATOM, | |||
| 32, | |||
| PropModeReplace, | |||
| reinterpret_cast<const uchar*>(&windowTypes), | |||
| numWindowTypes); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -121,8 +121,8 @@ void puglWin32ShowCentered(PuglView* view); | |||
| // X11 specific, update world without triggering exposure events | |||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); | |||
| // X11 specific, set dialog window type and pid hints | |||
| void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); | |||
| // X11 specific, set dialog window type | |||
| void puglX11SetWindowType(const PuglView* view, bool isStandalone); | |||
| #endif | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -29,15 +29,14 @@ | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
| # include "src/DistrhoPluginLV2.cpp" | |||
| # include "src/DistrhoPluginLV2export.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_MAPI) | |||
| # include "src/DistrhoPluginMAPI.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
| # include "src/DistrhoPluginVST2.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
| # include "src/DistrhoPluginVST3.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_EXPORT) | |||
| # include "src/DistrhoPluginExport.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_SHARED) | |||
| DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); | |||
| DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_STATIC) | |||
| START_NAMESPACE_DISTRHO | |||
| Plugin* createStaticPlugin() { return createPlugin(); } | |||
| @@ -106,10 +106,11 @@ public: | |||
| The following example code can be use to extract individual colors: | |||
| ``` | |||
| const int red = (bgColor >> 24) & 0xff; | |||
| const int green = (bgColor >> 16) & 0xff; | |||
| const int blue = (bgColor >> 8) & 0xff; | |||
| int red = (bgColor >> 24) & 0xff; | |||
| int green = (bgColor >> 16) & 0xff; | |||
| int blue = (bgColor >> 8) & 0xff; | |||
| ``` | |||
| @see Color::fromRGB | |||
| */ | |||
| uint getBackgroundColor() const noexcept; | |||
| @@ -119,10 +120,11 @@ public: | |||
| The following example code can be use to extract individual colors: | |||
| ``` | |||
| const int red = (fgColor >> 24) & 0xff; | |||
| const int green = (fgColor >> 16) & 0xff; | |||
| const int blue = (fgColor >> 8) & 0xff; | |||
| int red = (fgColor >> 24) & 0xff; | |||
| int green = (fgColor >> 16) & 0xff; | |||
| int blue = (fgColor >> 8) & 0xff; | |||
| ``` | |||
| @see Color::fromRGB | |||
| */ | |||
| uint getForegroundColor() const noexcept; | |||
| @@ -172,6 +172,11 @@ public: | |||
| /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ | |||
| ObjectType* operator->() const noexcept { return object; } | |||
| //============================================================================== | |||
| /** Removes the current object from this ScopedPointer and deletes it. | |||
| */ | |||
| void reset() noexcept { ObjectType* const o = object; object = nullptr; delete o; } | |||
| //============================================================================== | |||
| /** Removes the current object from this ScopedPointer without deleting it. | |||
| This will return the current object, and set the ScopedPointer to a null pointer. | |||
| @@ -269,7 +269,7 @@ public: | |||
| /* | |||
| * Get length of the string. | |||
| */ | |||
| std::size_t length() const noexcept | |||
| size_t length() const noexcept | |||
| { | |||
| return fBufferLen; | |||
| } | |||
| @@ -295,7 +295,7 @@ public: | |||
| */ | |||
| bool contains(const char c) const noexcept | |||
| { | |||
| for (std::size_t i=0; i<fBufferLen; ++i) | |||
| for (size_t i=0; i<fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| return true; | |||
| @@ -334,7 +334,7 @@ public: | |||
| /* | |||
| * 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); | |||
| @@ -358,7 +358,7 @@ public: | |||
| { | |||
| 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) | |||
| return false; | |||
| @@ -383,7 +383,7 @@ public: | |||
| { | |||
| 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) | |||
| return false; | |||
| @@ -395,7 +395,7 @@ public: | |||
| * Find the first occurrence of character 'c' in the string. | |||
| * 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') | |||
| { | |||
| @@ -404,7 +404,7 @@ public: | |||
| return fBufferLen; | |||
| } | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| { | |||
| @@ -423,7 +423,7 @@ public: | |||
| * Find the first occurrence of string 'strBuf' in the string. | |||
| * 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') | |||
| { | |||
| @@ -448,7 +448,7 @@ public: | |||
| if (found != nullptr) | |||
| *found = true; | |||
| return static_cast<std::size_t>(ret); | |||
| return static_cast<size_t>(ret); | |||
| } | |||
| if (found != nullptr) | |||
| @@ -460,7 +460,7 @@ public: | |||
| * Find the last occurrence of character 'c' in the string. | |||
| * Returns "length()" if the character is not found. | |||
| */ | |||
| std::size_t rfind(const char c, bool* const found = nullptr) const noexcept | |||
| size_t rfind(const char c, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (fBufferLen == 0 || c == '\0') | |||
| { | |||
| @@ -469,7 +469,7 @@ public: | |||
| return fBufferLen; | |||
| } | |||
| for (std::size_t i=fBufferLen; i > 0; --i) | |||
| for (size_t i=fBufferLen; i > 0; --i) | |||
| { | |||
| if (fBuffer[i-1] == c) | |||
| { | |||
| @@ -488,7 +488,7 @@ public: | |||
| * Find the last occurrence of string 'strBuf' in the string. | |||
| * Returns "length()" if the string is not found. | |||
| */ | |||
| std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (found != nullptr) | |||
| *found = false; | |||
| @@ -496,12 +496,12 @@ public: | |||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |||
| return fBufferLen; | |||
| const std::size_t strBufLen(std::strlen(strBuf)); | |||
| const size_t strBufLen(std::strlen(strBuf)); | |||
| std::size_t ret = fBufferLen; | |||
| size_t ret = fBufferLen; | |||
| const char* tmpBuf = fBuffer; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) | |||
| { | |||
| @@ -532,7 +532,7 @@ public: | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == before) | |||
| fBuffer[i] = after; | |||
| @@ -551,7 +551,7 @@ public: | |||
| if (fBufferLen == 0) | |||
| return *this; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| { | |||
| @@ -567,7 +567,7 @@ public: | |||
| /* | |||
| * Truncate the string to size 'n'. | |||
| */ | |||
| String& truncate(const std::size_t n) noexcept | |||
| String& truncate(const size_t n) noexcept | |||
| { | |||
| if (n >= fBufferLen) | |||
| return *this; | |||
| @@ -583,7 +583,7 @@ public: | |||
| */ | |||
| String& toBasic() noexcept | |||
| { | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= '0' && fBuffer[i] <= '9') | |||
| continue; | |||
| @@ -607,7 +607,7 @@ public: | |||
| { | |||
| static constexpr const char kCharDiff = 'a' - 'A'; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |||
| fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | |||
| @@ -623,7 +623,7 @@ public: | |||
| { | |||
| static constexpr const char kCharDiff = 'a' - 'A'; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |||
| fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | |||
| @@ -688,31 +688,28 @@ public: | |||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
| // Copyright (C) 2004-2008 René Nyffenegger | |||
| static String asBase64(const void* const data, const std::size_t dataSize) | |||
| static String asBase64(const void* const data, const size_t dataSize) | |||
| { | |||
| static constexpr const char* const kBase64Chars = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| "abcdefghijklmnopqrstuvwxyz" | |||
| "0123456789+/"; | |||
| #ifndef _MSC_VER | |||
| const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<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 charArray3[3], charArray4[4]; | |||
| char strBuf[kTmpBufSize + 1]; | |||
| strBuf[kTmpBufSize] = '\0'; | |||
| std::size_t strBufIndex = 0; | |||
| String ret; | |||
| for (std::size_t s=0; s<dataSize; ++s) | |||
| for (size_t s = 0; s < dataSize; ++s) | |||
| { | |||
| charArray3[i++] = *(bytesToEncode++); | |||
| @@ -723,10 +720,10 @@ public: | |||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||
| charArray4[3] = charArray3[2] & 0x3f; | |||
| for (i=0; i<4; ++i) | |||
| for (i = 0; i < 4; ++i) | |||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | |||
| if (strBufIndex >= kTmpBufSize-7) | |||
| if (strBufIndex >= strBufSize - 7) | |||
| { | |||
| strBuf[strBufIndex] = '\0'; | |||
| strBufIndex = 0; | |||
| @@ -739,7 +736,7 @@ public: | |||
| if (i != 0) | |||
| { | |||
| for (j=i; j<3; ++j) | |||
| for (j = i; j < 3; ++j) | |||
| charArray3[j] = '\0'; | |||
| charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |||
| @@ -747,7 +744,7 @@ public: | |||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||
| charArray4[3] = charArray3[2] & 0x3f; | |||
| for (j=0; j<4 && i<3 && j<i+1; ++j) | |||
| for (j = 0; j < 4 && i < 3 && j < i + 1; ++j) | |||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | |||
| for (; i++ < 3;) | |||
| @@ -760,6 +757,7 @@ public: | |||
| ret += strBuf; | |||
| } | |||
| std::free(strBuf); | |||
| return ret; | |||
| } | |||
| @@ -778,7 +776,7 @@ public: | |||
| 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]; | |||
| @@ -901,7 +899,7 @@ public: | |||
| 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]; | |||
| @@ -957,19 +955,16 @@ public: | |||
| return fBuffer; | |||
| } | |||
| char operator[](const std::size_t pos) const noexcept | |||
| char operator[](const size_t pos) const noexcept | |||
| { | |||
| if (pos < fBufferLen) | |||
| return fBuffer[pos]; | |||
| 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) | |||
| return fBuffer[pos]; | |||
| @@ -1020,7 +1015,7 @@ public: | |||
| if (strBuf == nullptr || strBuf[0] == '\0') | |||
| 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 | |||
| if (isEmpty()) | |||
| @@ -1054,8 +1049,8 @@ public: | |||
| if (isEmpty()) | |||
| 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)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1080,7 +1075,7 @@ public: | |||
| private: | |||
| char* fBuffer; // the actual string buffer | |||
| std::size_t fBufferLen; // string length | |||
| size_t fBufferLen; // string length | |||
| bool fBufferAlloc; // wherever the buffer is allocated, not using _null() | |||
| /* | |||
| @@ -1101,7 +1096,7 @@ private: | |||
| * - Allocates string only if 'strBuf' is not null and new string contents are different | |||
| * - If 'strBuf' is null, 'size' must be 0 | |||
| */ | |||
| void _dup(const char* const strBuf, const std::size_t size = 0) noexcept | |||
| void _dup(const char* const strBuf, const size_t size = 0) noexcept | |||
| { | |||
| if (strBuf != nullptr) | |||
| { | |||
| @@ -1158,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep | |||
| if (strBefore.isEmpty()) | |||
| return String(strBufAfter); | |||
| const std::size_t strBeforeLen = strBefore.length(); | |||
| const std::size_t strBufAfterLen = std::strlen(strBufAfter); | |||
| const std::size_t newBufSize = strBeforeLen + strBufAfterLen; | |||
| const size_t strBeforeLen = strBefore.length(); | |||
| const size_t strBufAfterLen = std::strlen(strBufAfter); | |||
| const size_t newBufSize = strBeforeLen + strBufAfterLen; | |||
| char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1178,9 +1173,9 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep | |||
| if (strBufBefore == nullptr || strBufBefore[0] == '\0') | |||
| return strAfter; | |||
| const std::size_t strBufBeforeLen = std::strlen(strBufBefore); | |||
| const std::size_t strAfterLen = strAfter.length(); | |||
| const std::size_t newBufSize = strBufBeforeLen + strAfterLen; | |||
| const size_t strBufBeforeLen = std::strlen(strBufBefore); | |||
| const size_t strAfterLen = strAfter.length(); | |||
| const size_t newBufSize = strBufBeforeLen + strAfterLen; | |||
| char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * 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 | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -21,18 +21,18 @@ | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // UI exporter class | |||
| class UIExporter | |||
| { | |||
| // ------------------------------------------------------------------- | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| // UI Widget and its private data | |||
| UI* ui; | |||
| UI::PrivateData* uiData; | |||
| // ------------------------------------------------------------------- | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| public: | |||
| UIExporter(void* const callbacksPtr, | |||
| @@ -47,11 +47,12 @@ public: | |||
| const char* const bundlePath = nullptr, | |||
| void* const dspPtr = nullptr, | |||
| const double scaleFactor = 0.0, | |||
| const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto, | |||
| const uint32_t bgColor = 0, | |||
| const uint32_t fgColor = 0xffffffff, | |||
| const char* const appClassName = nullptr) | |||
| : ui(nullptr), | |||
| uiData(new UI::PrivateData(appClassName)) | |||
| uiData(new UI::PrivateData(appClassName, appType)) | |||
| { | |||
| uiData->sampleRate = sampleRate; | |||
| uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | |||
| @@ -67,14 +67,14 @@ START_NAMESPACE_DISTRHO | |||
| int dpf_webview_start(int argc, char* argv[]); | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Plugin Application, will set class name based on plugin details | |||
| class PluginApplication : public DGL_NAMESPACE::Application | |||
| { | |||
| public: | |||
| explicit PluginApplication(const char* className) | |||
| : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) | |||
| explicit PluginApplication(const char* className, const Application::Type type) | |||
| : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE, type) | |||
| { | |||
| #if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__) | |||
| if (className == nullptr) | |||
| @@ -108,7 +108,7 @@ public: | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Plugin Window, will pass some Window events to UI | |||
| class PluginWindow : public DGL_NAMESPACE::Window | |||
| @@ -138,7 +138,10 @@ public: | |||
| // this is called just before creating UI, ensuring proper context to it | |||
| if (pData->initPost()) | |||
| { | |||
| puglBackendEnter(pData->view); | |||
| pData->createContextIfNeeded(); | |||
| } | |||
| } | |||
| ~PluginWindow() override | |||
| @@ -240,7 +243,7 @@ protected: | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // UI callbacks | |||
| typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||
| @@ -250,7 +253,7 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8 | |||
| typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||
| typedef bool (*fileRequestFunc) (void* ptr, const char* key); | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // UI private data | |||
| struct UI::PrivateData { | |||
| @@ -289,8 +292,8 @@ struct UI::PrivateData { | |||
| setSizeFunc setSizeCallbackFunc; | |||
| fileRequestFunc fileRequestCallbackFunc; | |||
| PrivateData(const char* const appClassName) noexcept | |||
| : app(appClassName), | |||
| PrivateData(const char* const appClassName, const DGL_NAMESPACE::Application::Type appType) noexcept | |||
| : app(appClassName, appType), | |||
| window(nullptr), | |||
| #if DISTRHO_UI_USE_WEB_VIEW | |||
| webview(nullptr), | |||
| @@ -389,7 +392,7 @@ struct UI::PrivateData { | |||
| #endif | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // UI private data fileRequestCallback, which requires PluginWindow definitions | |||
| inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
| @@ -416,7 +419,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
| return false; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // PluginWindow onFileSelected that require UI::PrivateData definitions | |||
| #if DISTRHO_UI_FILE_BROWSER | |||
| @@ -454,7 +457,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -119,8 +119,10 @@ const char* getPluginFormatName() noexcept | |||
| static inline | |||
| void _createDirIfNeeded(const char* const dir) | |||
| { | |||
| if (access(dir, F_OK) != 0) | |||
| mkdir(dir, 0755); | |||
| try { | |||
| if (access(dir, F_OK) != 0) | |||
| mkdir(dir, 0755); | |||
| } DISTRHO_SAFE_EXCEPTION("createDirIfNeeded"); | |||
| } | |||
| #endif | |||
| @@ -255,7 +257,10 @@ static const char* _getDocumentsDir() | |||
| // ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data | |||
| if (dir.isEmpty()) | |||
| dir = _getDocumentsDir(); | |||
| { | |||
| dir = _getHomeDir(); | |||
| dir += "Documents/"; | |||
| } | |||
| _createDirIfNeeded(dir); | |||
| #endif | |||