| @@ -1 +0,0 @@ | |||||
| Subproject commit b1d9703ecbdb0a033fe0b9acdf58b90f7d81a8e5 | |||||
| @@ -0,0 +1,13 @@ | |||||
| Pugl is primarily written and maintained by David Robillard <d@drobilla.net> | |||||
| with contributions from (in increasing chronological order): | |||||
| Ben Loftis <ben@harrisonconsoles.com> | |||||
| Robin Gareus <robin@gareus.org> | |||||
| Erik Åldstedt Sund <erikalds@gmail.com> | |||||
| Hanspeter Portner <dev@open-music-kontrollers.ch> | |||||
| Stefan Westerfeld <stefan@space.twc.de> | |||||
| Jordan Halase <jordan@halase.me> | |||||
| Oliver Schmidt <oliver@luced.de> | |||||
| Zoë Sparks <zoe@milky.flowers> | |||||
| Jean Pierre Cimalando <jp-dev@inbox.ru> | |||||
| Thomas Brand <tom@trellis.ch> | |||||
| @@ -0,0 +1,13 @@ | |||||
| Copyright 2011-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| @@ -0,0 +1,10 @@ | |||||
| Checks: > | |||||
| *, | |||||
| -*-magic-numbers, | |||||
| -*-uppercase-literal-suffix, | |||||
| -altera-struct-pack-align, | |||||
| -clang-diagnostic-unused-function, | |||||
| -clang-diagnostic-unused-macros, | |||||
| -llvmlibc-*, | |||||
| FormatStyle: file | |||||
| HeaderFilterRegex: 'pugl/.*' | |||||
| @@ -0,0 +1,12 @@ | |||||
| c_headers = [ | |||||
| 'pugl/pugl.h', | |||||
| 'pugl/cairo.h', | |||||
| 'pugl/gl.h', | |||||
| 'pugl/stub.h', | |||||
| 'pugl/vulkan.h', | |||||
| ] | |||||
| c_header_files = files(c_headers) | |||||
| install_headers(c_headers, subdir: versioned_name / 'pugl') | |||||
| @@ -0,0 +1,46 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_CAIRO_H | |||||
| #define PUGL_CAIRO_H | |||||
| #include "pugl/pugl.h" | |||||
| PUGL_BEGIN_DECLS | |||||
| /** | |||||
| @defgroup cairo Cairo | |||||
| Cairo graphics support. | |||||
| @ingroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Cairo graphics backend accessor. | |||||
| Pass the returned value to puglSetBackend() to draw to a view with Cairo. | |||||
| */ | |||||
| PUGL_CONST_API | |||||
| const PuglBackend* | |||||
| puglCairoBackend(void); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| PUGL_END_DECLS | |||||
| #endif // PUGL_CAIRO_H | |||||
| @@ -0,0 +1,107 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_GL_H | |||||
| #define PUGL_GL_H | |||||
| #include "pugl/pugl.h" | |||||
| // IWYU pragma: begin_exports | |||||
| /* Unfortunately, GL includes vary across platforms, so include them here to | |||||
| enable pure portable programs. */ | |||||
| #ifndef PUGL_NO_INCLUDE_GL_H | |||||
| # ifdef __APPLE__ | |||||
| # include <OpenGL/gl.h> | |||||
| # else | |||||
| # ifdef _WIN32 | |||||
| # include <windows.h> | |||||
| # endif | |||||
| # include <GL/gl.h> | |||||
| # endif | |||||
| #endif | |||||
| #ifndef PUGL_NO_INCLUDE_GLU_H | |||||
| # ifdef __APPLE__ | |||||
| # include <OpenGL/glu.h> | |||||
| # else | |||||
| # ifdef _WIN32 | |||||
| # include <windows.h> | |||||
| # endif | |||||
| # include <GL/glu.h> | |||||
| # endif | |||||
| #endif | |||||
| // IWYU pragma: end_exports | |||||
| PUGL_BEGIN_DECLS | |||||
| /** | |||||
| @defgroup gl OpenGL | |||||
| OpenGL graphics support. | |||||
| @ingroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| OpenGL extension function. | |||||
| */ | |||||
| typedef void (*PuglGlFunc)(void); | |||||
| /** | |||||
| Return the address of an OpenGL extension function. | |||||
| */ | |||||
| PUGL_API | |||||
| PuglGlFunc | |||||
| puglGetProcAddress(const char* name); | |||||
| /** | |||||
| Enter the OpenGL context. | |||||
| This can be used to enter the graphics context in unusual situations, for | |||||
| doing things like loading textures. Note that this must not be used for | |||||
| drawing, which may only be done while processing an expose event. | |||||
| */ | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglEnterContext(PuglView* view); | |||||
| /** | |||||
| Leave the OpenGL context. | |||||
| This must only be called after puglEnterContext(). | |||||
| */ | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglLeaveContext(PuglView* view); | |||||
| /** | |||||
| OpenGL graphics backend. | |||||
| Pass the returned value to puglSetBackend() to draw to a view with OpenGL. | |||||
| */ | |||||
| PUGL_CONST_API | |||||
| const PuglBackend* | |||||
| puglGlBackend(void); | |||||
| PUGL_END_DECLS | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #endif // PUGL_GL_H | |||||
| @@ -0,0 +1,47 @@ | |||||
| /* | |||||
| Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_STUB_H | |||||
| #define PUGL_STUB_H | |||||
| #include "pugl/pugl.h" | |||||
| PUGL_BEGIN_DECLS | |||||
| /** | |||||
| @defgroup stub Stub | |||||
| Native graphics support. | |||||
| @ingroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Stub graphics backend accessor. | |||||
| This backend just creates a simple native window without setting up any | |||||
| portable graphics API. | |||||
| */ | |||||
| PUGL_CONST_API | |||||
| const PuglBackend* | |||||
| puglStubBackend(void); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| PUGL_END_DECLS | |||||
| #endif // PUGL_STUB_H | |||||
| @@ -0,0 +1,156 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /* | |||||
| Note that this header includes Vulkan headers, so if you are writing a | |||||
| program or plugin that dynamically loads vulkan, you should first define | |||||
| `VK_NO_PROTOTYPES` before including it. | |||||
| */ | |||||
| #ifndef PUGL_VULKAN_H | |||||
| #define PUGL_VULKAN_H | |||||
| #include "pugl/pugl.h" | |||||
| #include <vulkan/vulkan_core.h> | |||||
| #include <stdint.h> | |||||
| PUGL_BEGIN_DECLS | |||||
| /** | |||||
| @defgroup vulkan Vulkan | |||||
| Vulkan graphics support. | |||||
| Vulkan support differs from OpenGL because almost all most configuration is | |||||
| done using the Vulkan API itself, rather than by setting view hints to | |||||
| configure the context. Pugl only provides a minimal loader for loading the | |||||
| Vulkan library, and a portable function to create a Vulkan surface for a | |||||
| view, which hides the platform-specific implementation details. | |||||
| @ingroup pugl | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Dynamic Vulkan loader. | |||||
| This can be used to dynamically load the Vulkan library. Applications or | |||||
| plugins should not link against the Vulkan library, but instead use this at | |||||
| runtime. This ensures that things will work on as many systems as possible, | |||||
| and allows errors to be handled gracefully. | |||||
| This is not a "loader" in the sense of loading all the required Vulkan | |||||
| functions (which is the application's responsibility), but just a minimal | |||||
| implementation to portably load the Vulkan library and get the two functions | |||||
| that are used to load everything else. | |||||
| Note that this owns the loaded Vulkan library, so it must outlive all use of | |||||
| the Vulkan API. | |||||
| @see https://www.khronos.org/registry/vulkan/specs/1.0/html/chap4.html | |||||
| */ | |||||
| typedef struct PuglVulkanLoaderImpl PuglVulkanLoader; | |||||
| /** | |||||
| Create a new dynamic loader for Vulkan functions. | |||||
| This dynamically loads the Vulkan library and gets the load functions from | |||||
| it. | |||||
| @return A new Vulkan loader, or null on failure. | |||||
| */ | |||||
| PUGL_API | |||||
| PuglVulkanLoader* | |||||
| puglNewVulkanLoader(PuglWorld* world); | |||||
| /** | |||||
| Free a loader created with puglNewVulkanLoader(). | |||||
| Note that this closes the Vulkan library, so no Vulkan objects or API may be | |||||
| used after this is called. | |||||
| */ | |||||
| PUGL_API | |||||
| void | |||||
| puglFreeVulkanLoader(PuglVulkanLoader* loader); | |||||
| /** | |||||
| Return the `vkGetInstanceProcAddr` function. | |||||
| @return Null if the Vulkan library does not contain this function (which is | |||||
| unlikely and indicates a broken system). | |||||
| */ | |||||
| PUGL_API | |||||
| PFN_vkGetInstanceProcAddr | |||||
| puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader); | |||||
| /** | |||||
| Return the `vkGetDeviceProcAddr` function. | |||||
| @return Null if the Vulkan library does not contain this function (which is | |||||
| unlikely and indicates a broken system). | |||||
| */ | |||||
| PUGL_API | |||||
| PFN_vkGetDeviceProcAddr | |||||
| puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader); | |||||
| /** | |||||
| Return the Vulkan instance extensions required to draw to a PuglView. | |||||
| This simply returns static strings, it does not access Vulkan or the window | |||||
| system. The returned array always contains at least "VK_KHR_surface". | |||||
| @param[out] count The number of extensions in the returned array. | |||||
| @return An array of extension name strings. | |||||
| */ | |||||
| PUGL_API | |||||
| const char* const* | |||||
| puglGetInstanceExtensions(uint32_t* count); | |||||
| /** | |||||
| Create a Vulkan surface for a Pugl view. | |||||
| @param vkGetInstanceProcAddr Accessor for Vulkan functions. | |||||
| @param view The view the surface is to be displayed on. | |||||
| @param instance The Vulkan instance. | |||||
| @param allocator Vulkan allocation callbacks, may be NULL. | |||||
| @param[out] surface Pointed to a newly created Vulkan surface. | |||||
| @return `VK_SUCCESS` on success, or a Vulkan error code. | |||||
| */ | |||||
| PUGL_API | |||||
| VkResult | |||||
| puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||||
| PuglView* view, | |||||
| VkInstance instance, | |||||
| const VkAllocationCallbacks* allocator, | |||||
| VkSurfaceKHR* surface); | |||||
| /** | |||||
| Vulkan graphics backend. | |||||
| Pass the returned value to puglSetBackend() to draw to a view with Vulkan. | |||||
| */ | |||||
| PUGL_CONST_API | |||||
| const PuglBackend* | |||||
| puglVulkanBackend(void); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| PUGL_END_DECLS | |||||
| #endif // PUGL_VULKAN_H | |||||
| @@ -0,0 +1,16 @@ | |||||
| Checks: > | |||||
| *, | |||||
| -*-uppercase-literal-suffix, | |||||
| -*magic-numbers, | |||||
| -altera-struct-pack-align, | |||||
| -bugprone-reserved-identifier, | |||||
| -cert-dcl37-c, | |||||
| -cert-dcl51-cpp, | |||||
| -cert-flp30-c, | |||||
| -hicpp-multiway-paths-covered, | |||||
| -hicpp-signed-bitwise, | |||||
| -llvm-header-guard, | |||||
| -llvmlibc-*, | |||||
| -readability-function-cognitive-complexity, | |||||
| FormatStyle: file | |||||
| HeaderFilterRegex: 'pugl/.*' | |||||
| @@ -0,0 +1,468 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "implementation.h" | |||||
| #include "types.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <assert.h> | |||||
| #include <stdbool.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| const char* | |||||
| puglStrerror(const PuglStatus status) | |||||
| { | |||||
| // clang-format off | |||||
| switch (status) { | |||||
| case PUGL_SUCCESS: return "Success"; | |||||
| case PUGL_FAILURE: return "Non-fatal failure"; | |||||
| case PUGL_UNKNOWN_ERROR: return "Unknown system error"; | |||||
| 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_BACKEND_FAILED: return "Backend initialisation failed"; | |||||
| case PUGL_REGISTRATION_FAILED: return "Class registration failed"; | |||||
| case PUGL_REALIZE_FAILED: return "View creation failed"; | |||||
| case PUGL_SET_FORMAT_FAILED: return "Failed to set pixel format"; | |||||
| case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context"; | |||||
| case PUGL_UNSUPPORTED_TYPE: return "Unsupported data type"; | |||||
| } | |||||
| // clang-format on | |||||
| return "Unknown error"; | |||||
| } | |||||
| void | |||||
| puglSetString(char** dest, const char* string) | |||||
| { | |||||
| if (*dest != string) { | |||||
| const size_t len = strlen(string); | |||||
| *dest = (char*)realloc(*dest, len + 1); | |||||
| strncpy(*dest, string, len + 1); | |||||
| } | |||||
| } | |||||
| void | |||||
| puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len) | |||||
| { | |||||
| if (data) { | |||||
| dest->len = len; | |||||
| dest->data = realloc(dest->data, len + 1); | |||||
| memcpy(dest->data, data, len); | |||||
| ((char*)dest->data)[len] = 0; | |||||
| } else { | |||||
| dest->len = 0; | |||||
| dest->data = NULL; | |||||
| } | |||||
| } | |||||
| static void | |||||
| puglSetDefaultHints(PuglHints hints) | |||||
| { | |||||
| hints[PUGL_USE_COMPAT_PROFILE] = PUGL_TRUE; | |||||
| hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; | |||||
| hints[PUGL_CONTEXT_VERSION_MINOR] = 0; | |||||
| 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_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; | |||||
| } | |||||
| PuglWorld* | |||||
| puglNewWorld(PuglWorldType type, PuglWorldFlags flags) | |||||
| { | |||||
| PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld)); | |||||
| if (!world || !(world->impl = puglInitWorldInternals(type, flags))) { | |||||
| free(world); | |||||
| return NULL; | |||||
| } | |||||
| world->startTime = puglGetTime(world); | |||||
| puglSetString(&world->className, "Pugl"); | |||||
| return world; | |||||
| } | |||||
| void | |||||
| puglFreeWorld(PuglWorld* const world) | |||||
| { | |||||
| puglFreeWorldInternals(world); | |||||
| free(world->className); | |||||
| free(world->views); | |||||
| free(world); | |||||
| } | |||||
| void | |||||
| puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle) | |||||
| { | |||||
| world->handle = handle; | |||||
| } | |||||
| PuglWorldHandle | |||||
| puglGetWorldHandle(PuglWorld* world) | |||||
| { | |||||
| return world->handle; | |||||
| } | |||||
| PuglStatus | |||||
| puglSetClassName(PuglWorld* const world, const char* const name) | |||||
| { | |||||
| puglSetString(&world->className, name); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| PuglView* | |||||
| puglNewView(PuglWorld* const world) | |||||
| { | |||||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
| if (!view || !(view->impl = puglInitViewInternals())) { | |||||
| free(view); | |||||
| return NULL; | |||||
| } | |||||
| view->world = world; | |||||
| view->minWidth = 1; | |||||
| view->minHeight = 1; | |||||
| puglSetDefaultHints(view->hints); | |||||
| // Add to world view list | |||||
| ++world->numViews; | |||||
| world->views = | |||||
| (PuglView**)realloc(world->views, world->numViews * sizeof(PuglView*)); | |||||
| world->views[world->numViews - 1] = view; | |||||
| return view; | |||||
| } | |||||
| void | |||||
| puglFreeView(PuglView* view) | |||||
| { | |||||
| if (view->eventFunc && view->backend) { | |||||
| puglDispatchSimpleEvent(view, PUGL_DESTROY); | |||||
| } | |||||
| // Remove from world view list | |||||
| PuglWorld* world = view->world; | |||||
| for (size_t i = 0; i < world->numViews; ++i) { | |||||
| if (world->views[i] == view) { | |||||
| if (i == world->numViews - 1) { | |||||
| world->views[i] = NULL; | |||||
| } else { | |||||
| memmove(world->views + i, | |||||
| world->views + i + 1, | |||||
| sizeof(PuglView*) * (world->numViews - i - 1)); | |||||
| world->views[world->numViews - 1] = NULL; | |||||
| } | |||||
| --world->numViews; | |||||
| } | |||||
| } | |||||
| free(view->title); | |||||
| free(view->clipboard.data); | |||||
| puglFreeViewInternals(view); | |||||
| free(view); | |||||
| } | |||||
| PuglWorld* | |||||
| puglGetWorld(PuglView* view) | |||||
| { | |||||
| return view->world; | |||||
| } | |||||
| PuglStatus | |||||
| puglSetViewHint(PuglView* view, PuglViewHint hint, int value) | |||||
| { | |||||
| if (value == PUGL_DONT_CARE) { | |||||
| switch (hint) { | |||||
| case PUGL_USE_COMPAT_PROFILE: | |||||
| case PUGL_USE_DEBUG_CONTEXT: | |||||
| case PUGL_CONTEXT_VERSION_MAJOR: | |||||
| case PUGL_CONTEXT_VERSION_MINOR: | |||||
| case PUGL_SWAP_INTERVAL: | |||||
| return PUGL_BAD_PARAMETER; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (hint < PUGL_NUM_VIEW_HINTS) { | |||||
| view->hints[hint] = value; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| return PUGL_BAD_PARAMETER; | |||||
| } | |||||
| int | |||||
| puglGetViewHint(const PuglView* view, PuglViewHint hint) | |||||
| { | |||||
| return (hint < PUGL_NUM_VIEW_HINTS) ? view->hints[hint] : PUGL_DONT_CARE; | |||||
| } | |||||
| PuglStatus | |||||
| puglSetParentWindow(PuglView* view, PuglNativeView parent) | |||||
| { | |||||
| view->parent = parent; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| PuglStatus | |||||
| puglSetBackend(PuglView* view, const PuglBackend* backend) | |||||
| { | |||||
| view->backend = backend; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| void | |||||
| puglSetHandle(PuglView* view, PuglHandle handle) | |||||
| { | |||||
| view->handle = handle; | |||||
| } | |||||
| PuglHandle | |||||
| puglGetHandle(PuglView* view) | |||||
| { | |||||
| return view->handle; | |||||
| } | |||||
| bool | |||||
| puglGetVisible(const PuglView* view) | |||||
| { | |||||
| return view->visible; | |||||
| } | |||||
| PuglRect | |||||
| puglGetFrame(const PuglView* view) | |||||
| { | |||||
| return view->frame; | |||||
| } | |||||
| void* | |||||
| puglGetContext(PuglView* view) | |||||
| { | |||||
| return view->backend->getContext(view); | |||||
| } | |||||
| #ifndef PUGL_DISABLE_DEPRECATED | |||||
| PuglStatus | |||||
| puglPollEvents(PuglWorld* world, double timeout) | |||||
| { | |||||
| return puglUpdate(world, timeout); | |||||
| } | |||||
| PuglStatus | |||||
| puglDispatchEvents(PuglWorld* world) | |||||
| { | |||||
| return puglUpdate(world, 0.0); | |||||
| } | |||||
| PuglStatus | |||||
| puglShowWindow(PuglView* view) | |||||
| { | |||||
| return puglShow(view); | |||||
| } | |||||
| PuglStatus | |||||
| puglHideWindow(PuglView* view) | |||||
| { | |||||
| return puglHide(view); | |||||
| } | |||||
| #endif | |||||
| PuglStatus | |||||
| puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc) | |||||
| { | |||||
| view->eventFunc = eventFunc; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| /// Return the code point for buf, or the replacement character on error | |||||
| uint32_t | |||||
| puglDecodeUTF8(const uint8_t* buf) | |||||
| { | |||||
| #define FAIL_IF(cond) \ | |||||
| do { \ | |||||
| if (cond) \ | |||||
| return 0xFFFD; \ | |||||
| } while (0) | |||||
| // http://en.wikipedia.org/wiki/UTF-8 | |||||
| if (buf[0] < 0x80) { | |||||
| return buf[0]; | |||||
| } | |||||
| if (buf[0] < 0xC2) { | |||||
| return 0xFFFD; | |||||
| } | |||||
| if (buf[0] < 0xE0) { | |||||
| FAIL_IF((buf[1] & 0xC0u) != 0x80); | |||||
| return ((uint32_t)buf[0] << 6u) + buf[1] - 0x3080u; | |||||
| } | |||||
| if (buf[0] < 0xF0) { | |||||
| FAIL_IF((buf[1] & 0xC0u) != 0x80); | |||||
| FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0); | |||||
| FAIL_IF((buf[2] & 0xC0u) != 0x80); | |||||
| return ((uint32_t)buf[0] << 12u) + // | |||||
| ((uint32_t)buf[1] << 6u) + // | |||||
| ((uint32_t)buf[2] - 0xE2080u); | |||||
| } | |||||
| if (buf[0] < 0xF5) { | |||||
| FAIL_IF((buf[1] & 0xC0u) != 0x80); | |||||
| FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90); | |||||
| FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90); | |||||
| FAIL_IF((buf[2] & 0xC0u) != 0x80u); | |||||
| FAIL_IF((buf[3] & 0xC0u) != 0x80u); | |||||
| return (((uint32_t)buf[0] << 18u) + // | |||||
| ((uint32_t)buf[1] << 12u) + // | |||||
| ((uint32_t)buf[2] << 6u) + // | |||||
| ((uint32_t)buf[3] - 0x3C82080u)); | |||||
| } | |||||
| return 0xFFFD; | |||||
| } | |||||
| static inline bool | |||||
| puglMustConfigure(PuglView* view, const PuglConfigureEvent* configure) | |||||
| { | |||||
| return memcmp(configure, &view->lastConfigure, sizeof(PuglConfigureEvent)); | |||||
| } | |||||
| void | |||||
| puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) | |||||
| { | |||||
| assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP || | |||||
| type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE || | |||||
| type == PUGL_LOOP_ENTER || type == PUGL_LOOP_LEAVE); | |||||
| const PuglEvent event = {{type, 0}}; | |||||
| puglDispatchEvent(view, &event); | |||||
| } | |||||
| void | |||||
| puglConfigure(PuglView* view, const PuglEvent* event) | |||||
| { | |||||
| assert(event->type == PUGL_CONFIGURE); | |||||
| view->frame.x = event->configure.x; | |||||
| view->frame.y = event->configure.y; | |||||
| view->frame.width = event->configure.width; | |||||
| view->frame.height = event->configure.height; | |||||
| if (puglMustConfigure(view, &event->configure)) { | |||||
| view->eventFunc(view, event); | |||||
| view->lastConfigure = event->configure; | |||||
| } | |||||
| } | |||||
| void | |||||
| puglExpose(PuglView* view, const PuglEvent* event) | |||||
| { | |||||
| if (event->expose.width > 0.0 && event->expose.height > 0.0) { | |||||
| view->eventFunc(view, event); | |||||
| } | |||||
| } | |||||
| void | |||||
| puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||||
| { | |||||
| switch (event->type) { | |||||
| case PUGL_NOTHING: | |||||
| break; | |||||
| case PUGL_CREATE: | |||||
| case PUGL_DESTROY: | |||||
| view->backend->enter(view, NULL); | |||||
| view->eventFunc(view, event); | |||||
| view->backend->leave(view, NULL); | |||||
| break; | |||||
| case PUGL_CONFIGURE: | |||||
| if (puglMustConfigure(view, &event->configure)) { | |||||
| view->backend->enter(view, NULL); | |||||
| puglConfigure(view, event); | |||||
| view->backend->leave(view, NULL); | |||||
| } | |||||
| break; | |||||
| case PUGL_MAP: | |||||
| if (!view->visible) { | |||||
| view->visible = true; | |||||
| view->eventFunc(view, event); | |||||
| } | |||||
| break; | |||||
| case PUGL_UNMAP: | |||||
| if (view->visible) { | |||||
| view->visible = false; | |||||
| view->eventFunc(view, event); | |||||
| } | |||||
| break; | |||||
| case PUGL_EXPOSE: | |||||
| view->backend->enter(view, &event->expose); | |||||
| puglExpose(view, event); | |||||
| view->backend->leave(view, &event->expose); | |||||
| break; | |||||
| default: | |||||
| view->eventFunc(view, event); | |||||
| } | |||||
| } | |||||
| const void* | |||||
| puglGetInternalClipboard(const PuglView* const view, | |||||
| const char** const type, | |||||
| size_t* const len) | |||||
| { | |||||
| if (len) { | |||||
| *len = view->clipboard.len; | |||||
| } | |||||
| if (type) { | |||||
| *type = "text/plain"; | |||||
| } | |||||
| return view->clipboard.data; | |||||
| } | |||||
| PuglStatus | |||||
| puglSetInternalClipboard(PuglView* const view, | |||||
| const char* const type, | |||||
| const void* const data, | |||||
| const size_t len) | |||||
| { | |||||
| if (type && !!strcmp(type, "text/plain")) { | |||||
| return PUGL_UNSUPPORTED_TYPE; | |||||
| } | |||||
| puglSetBlob(&view->clipboard, data, len); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| @@ -0,0 +1,86 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_IMPLEMENTATION_H | |||||
| #define PUGL_IMPLEMENTATION_H | |||||
| #include "types.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| PUGL_BEGIN_DECLS | |||||
| /// Set `blob` to `data` with length `len`, reallocating if necessary | |||||
| void | |||||
| puglSetBlob(PuglBlob* dest, const void* data, size_t len); | |||||
| /// Reallocate and set `*dest` to `string` | |||||
| void | |||||
| puglSetString(char** dest, const char* string); | |||||
| /// Allocate and initialise world internals (implemented once per platform) | |||||
| PuglWorldInternals* | |||||
| puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); | |||||
| /// Destroy and free world internals (implemented once per platform) | |||||
| void | |||||
| puglFreeWorldInternals(PuglWorld* world); | |||||
| /// Allocate and initialise view internals (implemented once per platform) | |||||
| PuglInternals* | |||||
| puglInitViewInternals(void); | |||||
| /// Destroy and free view internals (implemented once per platform) | |||||
| void | |||||
| puglFreeViewInternals(PuglView* view); | |||||
| /// Return the Unicode code point for `buf` or the replacement character | |||||
| uint32_t | |||||
| puglDecodeUTF8(const uint8_t* buf); | |||||
| /// Dispatch an event with a simple `type` to `view` | |||||
| void | |||||
| puglDispatchSimpleEvent(PuglView* view, PuglEventType type); | |||||
| /// Process configure event while already in the graphics context | |||||
| void | |||||
| puglConfigure(PuglView* view, const PuglEvent* event); | |||||
| /// Process expose event while already in the graphics context | |||||
| void | |||||
| puglExpose(PuglView* view, const PuglEvent* event); | |||||
| /// Dispatch `event` to `view`, entering graphics context if necessary | |||||
| void | |||||
| puglDispatchEvent(PuglView* view, const PuglEvent* event); | |||||
| /// Set internal (stored in view) clipboard contents | |||||
| const void* | |||||
| puglGetInternalClipboard(const PuglView* view, const char** type, size_t* len); | |||||
| /// Set internal (stored in view) clipboard contents | |||||
| PuglStatus | |||||
| puglSetInternalClipboard(PuglView* view, | |||||
| const char* type, | |||||
| const void* data, | |||||
| size_t len); | |||||
| PUGL_END_DECLS | |||||
| #endif // PUGL_IMPLEMENTATION_H | |||||
| @@ -0,0 +1,55 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_DETAIL_MAC_H | |||||
| #define PUGL_DETAIL_MAC_H | |||||
| #include "pugl/pugl.h" | |||||
| #import <Cocoa/Cocoa.h> | |||||
| #include <stdint.h> | |||||
| @interface PuglWrapperView : NSView<NSTextInputClient> | |||||
| - (void)dispatchExpose:(NSRect)rect; | |||||
| - (void)setReshaped; | |||||
| @end | |||||
| @interface PuglWindow : NSWindow | |||||
| - (void)setPuglview:(PuglView*)view; | |||||
| @end | |||||
| struct PuglWorldInternalsImpl { | |||||
| NSApplication* app; | |||||
| NSAutoreleasePool* autoreleasePool; | |||||
| }; | |||||
| struct PuglInternalsImpl { | |||||
| NSApplication* app; | |||||
| PuglWrapperView* wrapperView; | |||||
| NSView* drawView; | |||||
| NSCursor* cursor; | |||||
| PuglWindow* window; | |||||
| uint32_t mods; | |||||
| bool mouseTracked; | |||||
| }; | |||||
| #endif // PUGL_DETAIL_MAC_H | |||||
| @@ -0,0 +1,160 @@ | |||||
| /* | |||||
| Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "implementation.h" | |||||
| #include "mac.h" | |||||
| #include "stub.h" | |||||
| #include "pugl/cairo.h" | |||||
| #include <cairo-quartz.h> | |||||
| #import <Cocoa/Cocoa.h> | |||||
| #include <assert.h> | |||||
| @interface PuglCairoView : NSView | |||||
| @end | |||||
| @implementation PuglCairoView { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| cairo_surface_t* surface; | |||||
| cairo_t* cr; | |||||
| } | |||||
| - (id)initWithFrame:(NSRect)frame | |||||
| { | |||||
| self = [super initWithFrame:frame]; | |||||
| return self; | |||||
| } | |||||
| - (void)resizeWithOldSuperviewSize:(NSSize)oldSize | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [super resizeWithOldSuperviewSize:oldSize]; | |||||
| [wrapper setReshaped]; | |||||
| } | |||||
| - (void)drawRect:(NSRect)rect | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [wrapper dispatchExpose:rect]; | |||||
| } | |||||
| @end | |||||
| static PuglStatus | |||||
| puglMacCairoCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| PuglCairoView* drawView = [PuglCairoView alloc]; | |||||
| drawView->puglview = view; | |||||
| [drawView initWithFrame:[impl->wrapperView bounds]]; | |||||
| if (view->hints[PUGL_RESIZABLE]) { | |||||
| [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | |||||
| } else { | |||||
| [drawView setAutoresizingMask:NSViewNotSizable]; | |||||
| } | |||||
| impl->drawView = drawView; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacCairoDestroy(PuglView* view) | |||||
| { | |||||
| PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; | |||||
| [drawView removeFromSuperview]; | |||||
| [drawView release]; | |||||
| view->impl->drawView = nil; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacCairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; | |||||
| if (!expose) { | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| assert(!drawView->surface); | |||||
| assert(!drawView->cr); | |||||
| const double scale = 1.0 / [[NSScreen mainScreen] backingScaleFactor]; | |||||
| CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; | |||||
| const CGSize sizePx = {view->frame.width, view->frame.height}; | |||||
| const CGSize sizePt = CGContextConvertSizeToUserSpace(context, sizePx); | |||||
| // Convert coordinates to standard Cairo space | |||||
| CGContextTranslateCTM(context, 0.0, -sizePt.height); | |||||
| CGContextScaleCTM(context, scale, -scale); | |||||
| drawView->surface = cairo_quartz_surface_create_for_cg_context( | |||||
| context, (unsigned)sizePx.width, (unsigned)sizePx.height); | |||||
| drawView->cr = cairo_create(drawView->surface); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacCairoLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; | |||||
| if (!expose) { | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| assert(drawView->surface); | |||||
| assert(drawView->cr); | |||||
| CGContextRef context = cairo_quartz_surface_get_cg_context(drawView->surface); | |||||
| cairo_destroy(drawView->cr); | |||||
| cairo_surface_destroy(drawView->surface); | |||||
| drawView->cr = NULL; | |||||
| drawView->surface = NULL; | |||||
| CGContextFlush(context); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static void* | |||||
| puglMacCairoGetContext(PuglView* view) | |||||
| { | |||||
| return ((PuglCairoView*)view->impl->drawView)->cr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglCairoBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglStubConfigure, | |||||
| puglMacCairoCreate, | |||||
| puglMacCairoDestroy, | |||||
| puglMacCairoEnter, | |||||
| puglMacCairoLeave, | |||||
| puglMacCairoGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,211 @@ | |||||
| /* | |||||
| Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "implementation.h" | |||||
| #include "mac.h" | |||||
| #include "stub.h" | |||||
| #include "pugl/gl.h" | |||||
| #ifndef __MAC_10_10 | |||||
| # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core | |||||
| #endif | |||||
| @interface PuglOpenGLView : NSOpenGLView | |||||
| @end | |||||
| @implementation PuglOpenGLView { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| } | |||||
| - (id)initWithFrame:(NSRect)frame | |||||
| { | |||||
| const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE]; | |||||
| const unsigned samples = (unsigned)puglview->hints[PUGL_SAMPLES]; | |||||
| const int major = puglview->hints[PUGL_CONTEXT_VERSION_MAJOR]; | |||||
| const unsigned profile = | |||||
| ((compat || major < 3) ? NSOpenGLProfileVersionLegacy | |||||
| : (major >= 4 ? NSOpenGLProfileVersion4_1Core | |||||
| : NSOpenGLProfileVersion3_2Core)); | |||||
| // Set attributes to default if they are unset | |||||
| // (There is no GLX_DONT_CARE equivalent on MacOS) | |||||
| if (puglview->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { | |||||
| puglview->hints[PUGL_DEPTH_BITS] = 0; | |||||
| } | |||||
| if (puglview->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { | |||||
| puglview->hints[PUGL_STENCIL_BITS] = 0; | |||||
| } | |||||
| if (puglview->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { | |||||
| puglview->hints[PUGL_SAMPLES] = 1; | |||||
| } | |||||
| if (puglview->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { | |||||
| puglview->hints[PUGL_DOUBLE_BUFFER] = 1; | |||||
| } | |||||
| if (puglview->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { | |||||
| puglview->hints[PUGL_SWAP_INTERVAL] = 1; | |||||
| } | |||||
| const unsigned colorSize = (unsigned)(puglview->hints[PUGL_RED_BITS] + | |||||
| puglview->hints[PUGL_BLUE_BITS] + | |||||
| puglview->hints[PUGL_GREEN_BITS] + | |||||
| puglview->hints[PUGL_ALPHA_BITS]); | |||||
| // clang-format off | |||||
| NSOpenGLPixelFormatAttribute pixelAttribs[17] = { | |||||
| NSOpenGLPFADoubleBuffer, | |||||
| NSOpenGLPFAAccelerated, | |||||
| NSOpenGLPFAOpenGLProfile, profile, | |||||
| NSOpenGLPFAColorSize, colorSize, | |||||
| NSOpenGLPFADepthSize, (unsigned)puglview->hints[PUGL_DEPTH_BITS], | |||||
| NSOpenGLPFAStencilSize, (unsigned)puglview->hints[PUGL_STENCIL_BITS], | |||||
| NSOpenGLPFAMultisample, samples ? 1u : 0u, | |||||
| NSOpenGLPFASampleBuffers, samples ? 1u : 0u, | |||||
| NSOpenGLPFASamples, samples, | |||||
| 0}; | |||||
| // clang-format on | |||||
| NSOpenGLPixelFormat* pixelFormat = | |||||
| [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs]; | |||||
| if (pixelFormat) { | |||||
| self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||||
| [pixelFormat release]; | |||||
| } else { | |||||
| self = [super initWithFrame:frame]; | |||||
| } | |||||
| [self setWantsBestResolutionOpenGLSurface:YES]; | |||||
| if (self) { | |||||
| [[self openGLContext] makeCurrentContext]; | |||||
| [self reshape]; | |||||
| [NSOpenGLContext clearCurrentContext]; | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (void)reshape | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [super reshape]; | |||||
| [wrapper setReshaped]; | |||||
| } | |||||
| - (void)drawRect:(NSRect)rect | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [wrapper dispatchExpose:rect]; | |||||
| } | |||||
| @end | |||||
| static PuglStatus | |||||
| puglMacGlCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| PuglOpenGLView* drawView = [PuglOpenGLView alloc]; | |||||
| drawView->puglview = view; | |||||
| [drawView initWithFrame:[impl->wrapperView bounds]]; | |||||
| if (view->hints[PUGL_RESIZABLE]) { | |||||
| [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | |||||
| } else { | |||||
| [drawView setAutoresizingMask:NSViewNotSizable]; | |||||
| } | |||||
| impl->drawView = drawView; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacGlDestroy(PuglView* view) | |||||
| { | |||||
| PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; | |||||
| [drawView removeFromSuperview]; | |||||
| [drawView release]; | |||||
| view->impl->drawView = nil; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacGlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||||
| { | |||||
| PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; | |||||
| [[drawView openGLContext] makeCurrentContext]; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacGlLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; | |||||
| if (expose) { | |||||
| [[drawView openGLContext] flushBuffer]; | |||||
| } | |||||
| [NSOpenGLContext clearCurrentContext]; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| PuglGlFunc | |||||
| puglGetProcAddress(const char* name) | |||||
| { | |||||
| CFBundleRef framework = | |||||
| CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); | |||||
| CFStringRef symbol = CFStringCreateWithCString( | |||||
| kCFAllocatorDefault, name, kCFStringEncodingASCII); | |||||
| PuglGlFunc func = | |||||
| (PuglGlFunc)CFBundleGetFunctionPointerForName(framework, symbol); | |||||
| CFRelease(symbol); | |||||
| return func; | |||||
| } | |||||
| PuglStatus | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| return view->backend->enter(view, NULL); | |||||
| } | |||||
| PuglStatus | |||||
| puglLeaveContext(PuglView* view) | |||||
| { | |||||
| return view->backend->leave(view, NULL); | |||||
| } | |||||
| const PuglBackend* | |||||
| puglGlBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglStubConfigure, | |||||
| puglMacGlCreate, | |||||
| puglMacGlDestroy, | |||||
| puglMacGlEnter, | |||||
| puglMacGlLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,92 @@ | |||||
| /* | |||||
| Copyright 2019-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "implementation.h" | |||||
| #include "mac.h" | |||||
| #include "stub.h" | |||||
| #include "pugl/stub.h" | |||||
| #import <Cocoa/Cocoa.h> | |||||
| @interface PuglStubView : NSView | |||||
| @end | |||||
| @implementation PuglStubView { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| } | |||||
| - (void)resizeWithOldSuperviewSize:(NSSize)oldSize | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [super resizeWithOldSuperviewSize:oldSize]; | |||||
| [wrapper setReshaped]; | |||||
| } | |||||
| - (void)drawRect:(NSRect)rect | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [wrapper dispatchExpose:rect]; | |||||
| } | |||||
| @end | |||||
| static PuglStatus | |||||
| puglMacStubCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| PuglStubView* drawView = [PuglStubView alloc]; | |||||
| drawView->puglview = view; | |||||
| [drawView | |||||
| initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)]; | |||||
| if (view->hints[PUGL_RESIZABLE]) { | |||||
| [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | |||||
| } else { | |||||
| [drawView setAutoresizingMask:NSViewNotSizable]; | |||||
| } | |||||
| impl->drawView = drawView; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacStubDestroy(PuglView* view) | |||||
| { | |||||
| PuglStubView* const drawView = (PuglStubView*)view->impl->drawView; | |||||
| [drawView removeFromSuperview]; | |||||
| [drawView release]; | |||||
| view->impl->drawView = nil; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglStubBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglStubConfigure, | |||||
| puglMacStubCreate, | |||||
| puglMacStubDestroy, | |||||
| puglStubEnter, | |||||
| puglStubLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,211 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #define VK_NO_PROTOTYPES 1 | |||||
| #include "implementation.h" | |||||
| #include "mac.h" | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/stub.h" | |||||
| #include "pugl/vulkan.h" | |||||
| #include <vulkan/vulkan_core.h> | |||||
| #include <vulkan/vulkan_macos.h> | |||||
| #import <Cocoa/Cocoa.h> | |||||
| #import <QuartzCore/CAMetalLayer.h> | |||||
| #include <dlfcn.h> | |||||
| #include <stdint.h> | |||||
| #include <stdlib.h> | |||||
| @interface PuglVulkanView : NSView<CALayerDelegate> | |||||
| @end | |||||
| @implementation PuglVulkanView { | |||||
| @public | |||||
| PuglView* puglview; | |||||
| } | |||||
| - (id)initWithFrame:(NSRect)frame | |||||
| { | |||||
| self = [super initWithFrame:frame]; | |||||
| if (self) { | |||||
| self.wantsLayer = YES; | |||||
| self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (CALayer*)makeBackingLayer | |||||
| { | |||||
| CAMetalLayer* layer = [CAMetalLayer layer]; | |||||
| [layer setDelegate:self]; | |||||
| return layer; | |||||
| } | |||||
| - (void)setFrameSize:(NSSize)newSize | |||||
| { | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [super setFrameSize:newSize]; | |||||
| [wrapper setReshaped]; | |||||
| self.layer.frame = self.bounds; | |||||
| } | |||||
| - (void)displayLayer:(CALayer*)layer | |||||
| { | |||||
| (void)layer; | |||||
| PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; | |||||
| [wrapper dispatchExpose:[self bounds]]; | |||||
| } | |||||
| @end | |||||
| static PuglStatus | |||||
| puglMacVulkanCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| PuglVulkanView* drawView = [PuglVulkanView alloc]; | |||||
| const NSRect rect = NSMakeRect(0, 0, view->frame.width, view->frame.height); | |||||
| drawView->puglview = view; | |||||
| [drawView initWithFrame:rect]; | |||||
| if (view->hints[PUGL_RESIZABLE]) { | |||||
| [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | |||||
| } else { | |||||
| [drawView setAutoresizingMask:NSViewNotSizable]; | |||||
| } | |||||
| impl->drawView = drawView; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglMacVulkanDestroy(PuglView* view) | |||||
| { | |||||
| PuglVulkanView* const drawView = (PuglVulkanView*)view->impl->drawView; | |||||
| [drawView removeFromSuperview]; | |||||
| [drawView release]; | |||||
| view->impl->drawView = nil; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| struct PuglVulkanLoaderImpl { | |||||
| void* libvulkan; | |||||
| PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | |||||
| PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; | |||||
| }; | |||||
| PuglVulkanLoader* | |||||
| puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) | |||||
| { | |||||
| PuglVulkanLoader* loader = | |||||
| (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); | |||||
| if (!loader) { | |||||
| return NULL; | |||||
| } | |||||
| if (!(loader->libvulkan = dlopen("libvulkan.dylib", RTLD_LAZY))) { | |||||
| free(loader); | |||||
| return NULL; | |||||
| } | |||||
| loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym( | |||||
| loader->libvulkan, "vkGetInstanceProcAddr"); | |||||
| loader->vkGetDeviceProcAddr = | |||||
| (PFN_vkGetDeviceProcAddr)dlsym(loader->libvulkan, "vkGetDeviceProcAddr"); | |||||
| return loader; | |||||
| } | |||||
| void | |||||
| puglFreeVulkanLoader(PuglVulkanLoader* loader) | |||||
| { | |||||
| if (loader) { | |||||
| dlclose(loader->libvulkan); | |||||
| free(loader); | |||||
| } | |||||
| } | |||||
| PFN_vkGetInstanceProcAddr | |||||
| puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetInstanceProcAddr; | |||||
| } | |||||
| PFN_vkGetDeviceProcAddr | |||||
| puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetDeviceProcAddr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglVulkanBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglStubConfigure, | |||||
| puglMacVulkanCreate, | |||||
| puglMacVulkanDestroy, | |||||
| puglStubEnter, | |||||
| puglStubLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| const char* const* | |||||
| puglGetInstanceExtensions(uint32_t* const count) | |||||
| { | |||||
| static const char* const extensions[] = {"VK_KHR_surface", | |||||
| "VK_MVK_macos_surface"}; | |||||
| *count = 2; | |||||
| return extensions; | |||||
| } | |||||
| VkResult | |||||
| puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||||
| PuglView* const view, | |||||
| VkInstance instance, | |||||
| const VkAllocationCallbacks* const allocator, | |||||
| VkSurfaceKHR* const surface) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = | |||||
| (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( | |||||
| instance, "vkCreateMacOSSurfaceMVK"); | |||||
| const VkMacOSSurfaceCreateInfoMVK info = { | |||||
| VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, | |||||
| NULL, | |||||
| 0, | |||||
| impl->drawView, | |||||
| }; | |||||
| return vkCreateMacOSSurfaceMVK(instance, &info, allocator, surface); | |||||
| } | |||||
| @@ -0,0 +1,72 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_DETAIL_STUB_H | |||||
| #define PUGL_DETAIL_STUB_H | |||||
| #include "pugl/pugl.h" | |||||
| #include <stddef.h> | |||||
| PUGL_BEGIN_DECLS | |||||
| static inline PuglStatus | |||||
| puglStubConfigure(PuglView* const view) | |||||
| { | |||||
| (void)view; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static inline PuglStatus | |||||
| puglStubCreate(PuglView* const view) | |||||
| { | |||||
| (void)view; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static inline PuglStatus | |||||
| puglStubDestroy(PuglView* const view) | |||||
| { | |||||
| (void)view; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static inline PuglStatus | |||||
| puglStubEnter(PuglView* const view, const PuglExposeEvent* const expose) | |||||
| { | |||||
| (void)view; | |||||
| (void)expose; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static inline PuglStatus | |||||
| puglStubLeave(PuglView* const view, const PuglExposeEvent* const expose) | |||||
| { | |||||
| (void)view; | |||||
| (void)expose; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static inline void* | |||||
| puglStubGetContext(PuglView* const view) | |||||
| { | |||||
| (void)view; | |||||
| return NULL; | |||||
| } | |||||
| PUGL_END_DECLS | |||||
| #endif // PUGL_DETAIL_STUB_H | |||||
| @@ -0,0 +1,111 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_DETAIL_TYPES_H | |||||
| #define PUGL_DETAIL_TYPES_H | |||||
| #include "pugl/pugl.h" | |||||
| #include <stdbool.h> | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| // Unused parameter macro to suppresses warnings and make it impossible to use | |||||
| #if defined(__cplusplus) | |||||
| # define PUGL_UNUSED(name) | |||||
| #elif defined(__GNUC__) || defined(__clang__) | |||||
| # define PUGL_UNUSED(name) name##_unused __attribute__((__unused__)) | |||||
| #else | |||||
| # define PUGL_UNUSED(name) name | |||||
| #endif | |||||
| /// Platform-specific world internals | |||||
| typedef struct PuglWorldInternalsImpl PuglWorldInternals; | |||||
| /// Platform-specific view internals | |||||
| typedef struct PuglInternalsImpl PuglInternals; | |||||
| /// View hints | |||||
| typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; | |||||
| /// Blob of arbitrary data | |||||
| typedef struct { | |||||
| void* data; ///< Dynamically allocated data | |||||
| size_t len; ///< Length of data in bytes | |||||
| } PuglBlob; | |||||
| /// Cross-platform view definition | |||||
| struct PuglViewImpl { | |||||
| PuglWorld* world; | |||||
| const PuglBackend* backend; | |||||
| PuglInternals* impl; | |||||
| PuglHandle handle; | |||||
| PuglEventFunc eventFunc; | |||||
| char* title; | |||||
| PuglBlob clipboard; | |||||
| PuglNativeView parent; | |||||
| uintptr_t transientParent; | |||||
| PuglRect frame; | |||||
| PuglConfigureEvent lastConfigure; | |||||
| PuglHints hints; | |||||
| int defaultWidth; | |||||
| int defaultHeight; | |||||
| int minWidth; | |||||
| int minHeight; | |||||
| int maxWidth; | |||||
| int maxHeight; | |||||
| int minAspectX; | |||||
| int minAspectY; | |||||
| int maxAspectX; | |||||
| int maxAspectY; | |||||
| bool visible; | |||||
| }; | |||||
| /// Cross-platform world definition | |||||
| struct PuglWorldImpl { | |||||
| PuglWorldInternals* impl; | |||||
| PuglWorldHandle handle; | |||||
| char* className; | |||||
| double startTime; | |||||
| size_t numViews; | |||||
| PuglView** views; | |||||
| }; | |||||
| /// Opaque surface used by graphics backend | |||||
| typedef void PuglSurface; | |||||
| /// Graphics backend interface | |||||
| struct PuglBackendImpl { | |||||
| /// Get visual information from display and setup view as necessary | |||||
| PuglStatus (*configure)(PuglView*); | |||||
| /// Create surface and drawing context | |||||
| PuglStatus (*create)(PuglView*); | |||||
| /// Destroy surface and drawing context | |||||
| PuglStatus (*destroy)(PuglView*); | |||||
| /// Enter drawing context, for drawing if expose is non-null | |||||
| PuglStatus (*enter)(PuglView*, const PuglExposeEvent*); | |||||
| /// Leave drawing context, after drawing if expose is non-null | |||||
| PuglStatus (*leave)(PuglView*, const PuglExposeEvent*); | |||||
| /// Return the puglGetContext() handle for the application, if any | |||||
| void* (*getContext)(PuglView*); | |||||
| }; | |||||
| #endif // PUGL_DETAIL_TYPES_H | |||||
| @@ -0,0 +1,68 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_DETAIL_WIN_H | |||||
| #define PUGL_DETAIL_WIN_H | |||||
| #include "implementation.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <windows.h> | |||||
| #include <stdbool.h> | |||||
| typedef PIXELFORMATDESCRIPTOR PuglWinPFD; | |||||
| struct PuglWorldInternalsImpl { | |||||
| double timerFrequency; | |||||
| }; | |||||
| struct PuglInternalsImpl { | |||||
| PuglWinPFD pfd; | |||||
| int pfId; | |||||
| HWND hwnd; | |||||
| HCURSOR cursor; | |||||
| HDC hdc; | |||||
| PuglSurface* surface; | |||||
| bool flashing; | |||||
| bool mouseTracked; | |||||
| }; | |||||
| PUGL_API | |||||
| PuglWinPFD | |||||
| puglWinGetPixelFormatDescriptor(const PuglHints hints); | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglWinCreateWindow(PuglView* const view, | |||||
| const char* const title, | |||||
| HWND* const hwnd, | |||||
| HDC* const hdc); | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglWinConfigure(PuglView* view); | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose); | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglWinLeave(PuglView* view, const PuglExposeEvent* expose); | |||||
| #endif // PUGL_DETAIL_WIN_H | |||||
| @@ -0,0 +1,178 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "win.h" | |||||
| #include "pugl/cairo.h" | |||||
| #include <cairo-win32.h> | |||||
| #include <cairo.h> | |||||
| #include <stdlib.h> | |||||
| typedef struct { | |||||
| cairo_surface_t* surface; | |||||
| cairo_t* cr; | |||||
| HDC drawDc; | |||||
| HBITMAP drawBitmap; | |||||
| } PuglWinCairoSurface; | |||||
| static PuglStatus | |||||
| puglWinCairoCreateDrawContext(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| surface->drawDc = CreateCompatibleDC(impl->hdc); | |||||
| surface->drawBitmap = CreateCompatibleBitmap( | |||||
| impl->hdc, (int)view->frame.width, (int)view->frame.height); | |||||
| DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap)); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoDestroyDrawContext(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| DeleteDC(surface->drawDc); | |||||
| DeleteObject(surface->drawBitmap); | |||||
| surface->drawDc = NULL; | |||||
| surface->drawBitmap = NULL; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoConfigure(PuglView* view) | |||||
| { | |||||
| const PuglStatus st = puglWinConfigure(view); | |||||
| if (!st) { | |||||
| view->impl->surface = | |||||
| (PuglWinCairoSurface*)calloc(1, sizeof(PuglWinCairoSurface)); | |||||
| } | |||||
| return st; | |||||
| } | |||||
| static void | |||||
| puglWinCairoClose(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| cairo_surface_destroy(surface->surface); | |||||
| surface->surface = NULL; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoOpen(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| if (!(surface->surface = cairo_win32_surface_create(surface->drawDc)) || | |||||
| cairo_surface_status(surface->surface) || | |||||
| !(surface->cr = cairo_create(surface->surface)) || | |||||
| cairo_status(surface->cr)) { | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoDestroy(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| puglWinCairoClose(view); | |||||
| puglWinCairoDestroyDrawContext(view); | |||||
| free(surface); | |||||
| impl->surface = NULL; | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglStatus st = PUGL_SUCCESS; | |||||
| if (expose && !(st = puglWinCairoCreateDrawContext(view)) && | |||||
| !(st = puglWinCairoOpen(view))) { | |||||
| PAINTSTRUCT ps; | |||||
| BeginPaint(view->impl->hwnd, &ps); | |||||
| } | |||||
| return st; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinCairoLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; | |||||
| if (expose) { | |||||
| cairo_surface_flush(surface->surface); | |||||
| BitBlt(impl->hdc, | |||||
| 0, | |||||
| 0, | |||||
| (int)view->frame.width, | |||||
| (int)view->frame.height, | |||||
| surface->drawDc, | |||||
| 0, | |||||
| 0, | |||||
| SRCCOPY); | |||||
| puglWinCairoClose(view); | |||||
| puglWinCairoDestroyDrawContext(view); | |||||
| PAINTSTRUCT ps; | |||||
| EndPaint(view->impl->hwnd, &ps); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static void* | |||||
| puglWinCairoGetContext(PuglView* view) | |||||
| { | |||||
| return ((PuglWinCairoSurface*)view->impl->surface)->cr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglCairoBackend() | |||||
| { | |||||
| static const PuglBackend backend = {puglWinCairoConfigure, | |||||
| puglStubCreate, | |||||
| puglWinCairoDestroy, | |||||
| puglWinCairoEnter, | |||||
| puglWinCairoLeave, | |||||
| puglWinCairoGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,333 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "win.h" | |||||
| #include "pugl/gl.h" | |||||
| #include <windows.h> | |||||
| #include <GL/gl.h> | |||||
| #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 HGLRC(WINAPI* WglCreateContextAttribs)(HDC, HGLRC, const int*); | |||||
| typedef BOOL(WINAPI* WglSwapInterval)(int); | |||||
| typedef BOOL(WINAPI* WglChoosePixelFormat)(HDC, | |||||
| const int*, | |||||
| const FLOAT*, | |||||
| UINT, | |||||
| int*, | |||||
| UINT*); | |||||
| typedef struct { | |||||
| WglChoosePixelFormat wglChoosePixelFormat; | |||||
| WglCreateContextAttribs wglCreateContextAttribs; | |||||
| WglSwapInterval wglSwapInterval; | |||||
| } PuglWinGlProcs; | |||||
| typedef struct { | |||||
| PuglWinGlProcs procs; | |||||
| HGLRC hglrc; | |||||
| } PuglWinGlSurface; | |||||
| // Struct to manage the fake window used during configuration | |||||
| typedef struct { | |||||
| HWND hwnd; | |||||
| HDC hdc; | |||||
| } PuglFakeWindow; | |||||
| static PuglStatus | |||||
| puglWinError(PuglFakeWindow* fakeWin, const PuglStatus status) | |||||
| { | |||||
| if (fakeWin->hwnd) { | |||||
| ReleaseDC(fakeWin->hwnd, fakeWin->hdc); | |||||
| DestroyWindow(fakeWin->hwnd); | |||||
| } | |||||
| return status; | |||||
| } | |||||
| static PuglWinGlProcs | |||||
| puglWinGlGetProcs(void) | |||||
| { | |||||
| const PuglWinGlProcs procs = { | |||||
| (WglChoosePixelFormat)(wglGetProcAddress("wglChoosePixelFormatARB")), | |||||
| (WglCreateContextAttribs)(wglGetProcAddress("wglCreateContextAttribsARB")), | |||||
| (WglSwapInterval)(wglGetProcAddress("wglSwapIntervalEXT"))}; | |||||
| return procs; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinGlConfigure(PuglView* view) | |||||
| { | |||||
| PuglInternals* impl = view->impl; | |||||
| // Set attributes to default if they are unset | |||||
| // (There is no GLX_DONT_CARE equivalent on Windows) | |||||
| if (view->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { | |||||
| view->hints[PUGL_DEPTH_BITS] = 0; | |||||
| } | |||||
| if (view->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { | |||||
| view->hints[PUGL_STENCIL_BITS] = 0; | |||||
| } | |||||
| if (view->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { | |||||
| view->hints[PUGL_SAMPLES] = 1; | |||||
| } | |||||
| if (view->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { | |||||
| view->hints[PUGL_DOUBLE_BUFFER] = 1; | |||||
| } | |||||
| if (view->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { | |||||
| view->hints[PUGL_SWAP_INTERVAL] = 1; | |||||
| } | |||||
| // clang-format off | |||||
| const int pixelAttrs[] = { | |||||
| WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, | |||||
| WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, | |||||
| WGL_SUPPORT_OPENGL_ARB, GL_TRUE, | |||||
| WGL_DOUBLE_BUFFER_ARB, view->hints[PUGL_DOUBLE_BUFFER], | |||||
| WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, | |||||
| WGL_SAMPLE_BUFFERS_ARB, view->hints[PUGL_SAMPLES] ? 1 : 0, | |||||
| WGL_SAMPLES_ARB, view->hints[PUGL_SAMPLES], | |||||
| WGL_RED_BITS_ARB, view->hints[PUGL_RED_BITS], | |||||
| WGL_GREEN_BITS_ARB, view->hints[PUGL_GREEN_BITS], | |||||
| WGL_BLUE_BITS_ARB, view->hints[PUGL_BLUE_BITS], | |||||
| WGL_ALPHA_BITS_ARB, view->hints[PUGL_ALPHA_BITS], | |||||
| WGL_DEPTH_BITS_ARB, view->hints[PUGL_DEPTH_BITS], | |||||
| WGL_STENCIL_BITS_ARB, view->hints[PUGL_STENCIL_BITS], | |||||
| 0, | |||||
| }; | |||||
| // clang-format on | |||||
| PuglWinGlSurface* const surface = | |||||
| (PuglWinGlSurface*)calloc(1, sizeof(PuglWinGlSurface)); | |||||
| impl->surface = surface; | |||||
| // Create fake window for getting at GL context | |||||
| PuglStatus st = PUGL_SUCCESS; | |||||
| PuglFakeWindow fakeWin = {0, 0}; | |||||
| static const char* title = "Pugl Configuration"; | |||||
| if ((st = puglWinCreateWindow(view, title, &fakeWin.hwnd, &fakeWin.hdc))) { | |||||
| return puglWinError(&fakeWin, st); | |||||
| } | |||||
| // Set pixel format for fake window | |||||
| const PuglWinPFD fakePfd = puglWinGetPixelFormatDescriptor(view->hints); | |||||
| const int fakePfId = ChoosePixelFormat(fakeWin.hdc, &fakePfd); | |||||
| if (!fakePfId || !SetPixelFormat(fakeWin.hdc, fakePfId, &fakePfd)) { | |||||
| return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED); | |||||
| } | |||||
| // Create fake GL context to get at the functions we need | |||||
| HGLRC fakeRc = wglCreateContext(fakeWin.hdc); | |||||
| if (!fakeRc) { | |||||
| return puglWinError(&fakeWin, PUGL_CREATE_CONTEXT_FAILED); | |||||
| } | |||||
| // Enter fake context and get extension functions | |||||
| wglMakeCurrent(fakeWin.hdc, fakeRc); | |||||
| surface->procs = puglWinGlGetProcs(); | |||||
| if (surface->procs.wglChoosePixelFormat) { | |||||
| // Choose pixel format based on attributes | |||||
| UINT numFormats = 0; | |||||
| if (!surface->procs.wglChoosePixelFormat( | |||||
| fakeWin.hdc, pixelAttrs, NULL, 1u, &impl->pfId, &numFormats)) { | |||||
| return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED); | |||||
| } | |||||
| DescribePixelFormat(impl->hdc, impl->pfId, sizeof(impl->pfd), &impl->pfd); | |||||
| } else { | |||||
| // Modern extensions not available, use basic pixel format | |||||
| impl->pfd = fakePfd; | |||||
| impl->pfId = fakePfId; | |||||
| } | |||||
| // Dispose of fake window and context | |||||
| wglMakeCurrent(NULL, NULL); | |||||
| wglDeleteContext(fakeRc); | |||||
| ReleaseDC(fakeWin.hwnd, fakeWin.hdc); | |||||
| DestroyWindow(fakeWin.hwnd); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinGlCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWinGlSurface* const surface = (PuglWinGlSurface*)impl->surface; | |||||
| PuglStatus st = PUGL_SUCCESS; | |||||
| const int contextAttribs[] = { | |||||
| WGL_CONTEXT_MAJOR_VERSION_ARB, | |||||
| view->hints[PUGL_CONTEXT_VERSION_MAJOR], | |||||
| WGL_CONTEXT_MINOR_VERSION_ARB, | |||||
| view->hints[PUGL_CONTEXT_VERSION_MINOR], | |||||
| WGL_CONTEXT_FLAGS_ARB, | |||||
| (view->hints[PUGL_USE_DEBUG_CONTEXT] ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), | |||||
| WGL_CONTEXT_PROFILE_MASK_ARB, | |||||
| (view->hints[PUGL_USE_COMPAT_PROFILE] | |||||
| ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB | |||||
| : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB), | |||||
| 0}; | |||||
| // Create real window with desired pixel format | |||||
| if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { | |||||
| return st; | |||||
| } else if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) { | |||||
| ReleaseDC(impl->hwnd, impl->hdc); | |||||
| DestroyWindow(impl->hwnd); | |||||
| impl->hwnd = NULL; | |||||
| impl->hdc = NULL; | |||||
| return PUGL_SET_FORMAT_FAILED; | |||||
| } | |||||
| // Create GL context | |||||
| if (surface->procs.wglCreateContextAttribs && | |||||
| !(surface->hglrc = surface->procs.wglCreateContextAttribs( | |||||
| impl->hdc, 0, contextAttribs))) { | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } else if (!(surface->hglrc = wglCreateContext(impl->hdc))) { | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } | |||||
| // Enter context and set swap interval | |||||
| wglMakeCurrent(impl->hdc, surface->hglrc); | |||||
| const int swapInterval = view->hints[PUGL_SWAP_INTERVAL]; | |||||
| if (surface->procs.wglSwapInterval && swapInterval != PUGL_DONT_CARE) { | |||||
| surface->procs.wglSwapInterval(swapInterval); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinGlDestroy(PuglView* view) | |||||
| { | |||||
| PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface; | |||||
| if (surface) { | |||||
| wglMakeCurrent(NULL, NULL); | |||||
| wglDeleteContext(surface->hglrc); | |||||
| free(surface); | |||||
| view->impl->surface = NULL; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinGlEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface; | |||||
| wglMakeCurrent(view->impl->hdc, surface->hglrc); | |||||
| if (expose) { | |||||
| PAINTSTRUCT ps; | |||||
| BeginPaint(view->impl->hwnd, &ps); | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinGlLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| if (expose) { | |||||
| PAINTSTRUCT ps; | |||||
| EndPaint(view->impl->hwnd, &ps); | |||||
| SwapBuffers(view->impl->hdc); | |||||
| } | |||||
| wglMakeCurrent(NULL, NULL); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| PuglGlFunc | |||||
| puglGetProcAddress(const char* name) | |||||
| { | |||||
| const PuglGlFunc func = (PuglGlFunc)wglGetProcAddress(name); | |||||
| /* Windows has the annoying property that wglGetProcAddress returns NULL | |||||
| for functions from OpenGL 1.1, so we fall back to pulling them directly | |||||
| from opengl32.dll */ | |||||
| return func | |||||
| ? func | |||||
| : (PuglGlFunc)GetProcAddress(GetModuleHandle("opengl32.dll"), name); | |||||
| } | |||||
| PuglStatus | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| return view->backend->enter(view, NULL); | |||||
| } | |||||
| PuglStatus | |||||
| puglLeaveContext(PuglView* view) | |||||
| { | |||||
| return view->backend->leave(view, NULL); | |||||
| } | |||||
| const PuglBackend* | |||||
| puglGlBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglWinGlConfigure, | |||||
| puglWinGlCreate, | |||||
| puglWinGlDestroy, | |||||
| puglWinGlEnter, | |||||
| puglWinGlLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,52 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "win.h" | |||||
| #include "pugl/stub.h" | |||||
| static PuglStatus | |||||
| puglWinStubConfigure(PuglView* view) | |||||
| { | |||||
| return puglWinConfigure(view); | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinStubEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| return puglWinEnter(view, expose); | |||||
| } | |||||
| static PuglStatus | |||||
| puglWinStubLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| return puglWinLeave(view, expose); | |||||
| } | |||||
| const PuglBackend* | |||||
| puglStubBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglWinStubConfigure, | |||||
| puglStubCreate, | |||||
| puglStubDestroy, | |||||
| puglWinStubEnter, | |||||
| puglWinStubLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,125 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #define VK_NO_PROTOTYPES 1 | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "win.h" | |||||
| #include "pugl/vulkan.h" | |||||
| #include <vulkan/vulkan.h> | |||||
| #include <vulkan/vulkan_win32.h> | |||||
| #include <stdlib.h> | |||||
| struct PuglVulkanLoaderImpl { | |||||
| HMODULE libvulkan; | |||||
| PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | |||||
| PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; | |||||
| }; | |||||
| PuglVulkanLoader* | |||||
| puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) | |||||
| { | |||||
| PuglVulkanLoader* loader = | |||||
| (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); | |||||
| if (!loader) { | |||||
| return NULL; | |||||
| } | |||||
| if (!(loader->libvulkan = LoadLibrary("vulkan-1.dll"))) { | |||||
| free(loader); | |||||
| return NULL; | |||||
| } | |||||
| loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress( | |||||
| loader->libvulkan, "vkGetInstanceProcAddr"); | |||||
| loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)GetProcAddress( | |||||
| loader->libvulkan, "vkGetDeviceProcAddr"); | |||||
| return loader; | |||||
| } | |||||
| void | |||||
| puglFreeVulkanLoader(PuglVulkanLoader* loader) | |||||
| { | |||||
| if (loader) { | |||||
| FreeLibrary(loader->libvulkan); | |||||
| free(loader); | |||||
| } | |||||
| } | |||||
| PFN_vkGetInstanceProcAddr | |||||
| puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetInstanceProcAddr; | |||||
| } | |||||
| PFN_vkGetDeviceProcAddr | |||||
| puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetDeviceProcAddr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglVulkanBackend() | |||||
| { | |||||
| static const PuglBackend backend = {puglWinConfigure, | |||||
| puglStubCreate, | |||||
| puglStubDestroy, | |||||
| puglWinEnter, | |||||
| puglWinLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| const char* const* | |||||
| puglGetInstanceExtensions(uint32_t* const count) | |||||
| { | |||||
| static const char* const extensions[] = {"VK_KHR_surface", | |||||
| "VK_KHR_win32_surface"}; | |||||
| *count = 2; | |||||
| return extensions; | |||||
| } | |||||
| VkResult | |||||
| puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||||
| PuglView* const view, | |||||
| VkInstance instance, | |||||
| const VkAllocationCallbacks* const pAllocator, | |||||
| VkSurfaceKHR* const pSurface) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = | |||||
| (PFN_vkCreateWin32SurfaceKHR)vkGetInstanceProcAddr( | |||||
| instance, "vkCreateWin32SurfaceKHR"); | |||||
| const VkWin32SurfaceCreateInfoKHR createInfo = { | |||||
| VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, | |||||
| NULL, | |||||
| 0, | |||||
| GetModuleHandle(NULL), | |||||
| impl->hwnd, | |||||
| }; | |||||
| return vkCreateWin32SurfaceKHR(instance, &createInfo, pAllocator, pSurface); | |||||
| } | |||||
| @@ -0,0 +1,80 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef PUGL_DETAIL_X11_H | |||||
| #define PUGL_DETAIL_X11_H | |||||
| #include "types.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <X11/X.h> | |||||
| #include <X11/Xlib.h> | |||||
| #include <X11/Xutil.h> | |||||
| #include <stdbool.h> | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| typedef struct { | |||||
| Atom CLIPBOARD; | |||||
| Atom UTF8_STRING; | |||||
| Atom WM_PROTOCOLS; | |||||
| Atom WM_DELETE_WINDOW; | |||||
| Atom PUGL_CLIENT_MSG; | |||||
| Atom NET_WM_NAME; | |||||
| Atom NET_WM_STATE; | |||||
| Atom NET_WM_STATE_DEMANDS_ATTENTION; | |||||
| Atom NET_WM_STATE_HIDDEN; | |||||
| } PuglX11Atoms; | |||||
| typedef struct { | |||||
| XID alarm; | |||||
| PuglView* view; | |||||
| uintptr_t id; | |||||
| } PuglTimer; | |||||
| struct PuglWorldInternalsImpl { | |||||
| Display* display; | |||||
| PuglX11Atoms atoms; | |||||
| XIM xim; | |||||
| PuglTimer* timers; | |||||
| size_t numTimers; | |||||
| XID serverTimeCounter; | |||||
| int syncEventBase; | |||||
| bool syncSupported; | |||||
| bool dispatchingEvents; | |||||
| }; | |||||
| struct PuglInternalsImpl { | |||||
| Display* display; | |||||
| XVisualInfo* vi; | |||||
| Window win; | |||||
| XIC xic; | |||||
| PuglSurface* surface; | |||||
| PuglEvent pendingConfigure; | |||||
| PuglEvent pendingExpose; | |||||
| int screen; | |||||
| #ifdef HAVE_XCURSOR | |||||
| const char* cursorName; | |||||
| #endif | |||||
| }; | |||||
| PUGL_API | |||||
| PuglStatus | |||||
| puglX11Configure(PuglView* view); | |||||
| #endif // PUGL_DETAIL_X11_H | |||||
| @@ -0,0 +1,160 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "types.h" | |||||
| #include "x11.h" | |||||
| #include "pugl/cairo.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <X11/Xutil.h> | |||||
| #include <cairo-xlib.h> | |||||
| #include <cairo.h> | |||||
| #include <stdlib.h> | |||||
| typedef struct { | |||||
| cairo_surface_t* back; | |||||
| cairo_surface_t* front; | |||||
| cairo_t* cr; | |||||
| } PuglX11CairoSurface; | |||||
| static void | |||||
| puglX11CairoClose(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| cairo_surface_destroy(surface->front); | |||||
| cairo_surface_destroy(surface->back); | |||||
| surface->front = surface->back = NULL; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11CairoOpen(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| surface->back = cairo_xlib_surface_create(impl->display, | |||||
| impl->win, | |||||
| impl->vi->visual, | |||||
| (int)view->frame.width, | |||||
| (int)view->frame.height); | |||||
| surface->front = | |||||
| cairo_surface_create_similar(surface->back, | |||||
| cairo_surface_get_content(surface->back), | |||||
| (int)view->frame.width, | |||||
| (int)view->frame.height); | |||||
| if (cairo_surface_status(surface->back) || | |||||
| cairo_surface_status(surface->front)) { | |||||
| puglX11CairoClose(view); | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11CairoCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface)); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11CairoDestroy(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| puglX11CairoClose(view); | |||||
| free(surface); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| PuglStatus st = PUGL_SUCCESS; | |||||
| if (expose && !(st = puglX11CairoOpen(view))) { | |||||
| surface->cr = cairo_create(surface->front); | |||||
| st = cairo_status(surface->cr) ? PUGL_CREATE_CONTEXT_FAILED : PUGL_SUCCESS; | |||||
| } | |||||
| return st; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11CairoLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| if (expose) { | |||||
| // Destroy front context and create a new one for drawing to the back | |||||
| cairo_destroy(surface->cr); | |||||
| surface->cr = cairo_create(surface->back); | |||||
| // Clip to expose region | |||||
| cairo_rectangle( | |||||
| surface->cr, expose->x, expose->y, expose->width, expose->height); | |||||
| cairo_clip(surface->cr); | |||||
| // Paint front onto back | |||||
| cairo_set_source_surface(surface->cr, surface->front, 0, 0); | |||||
| cairo_paint(surface->cr); | |||||
| // Flush to X and close everything | |||||
| cairo_destroy(surface->cr); | |||||
| cairo_surface_flush(surface->back); | |||||
| puglX11CairoClose(view); | |||||
| surface->cr = NULL; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static void* | |||||
| puglX11CairoGetContext(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; | |||||
| return surface->cr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglCairoBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglX11Configure, | |||||
| puglX11CairoCreate, | |||||
| puglX11CairoDestroy, | |||||
| puglX11CairoEnter, | |||||
| puglX11CairoLeave, | |||||
| puglX11CairoGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,238 @@ | |||||
| /* | |||||
| Copyright 2012-2020 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "x11.h" | |||||
| #include "pugl/gl.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include <GL/glx.h> | |||||
| #include <X11/X.h> | |||||
| #include <X11/Xlib.h> | |||||
| #include <stdint.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| typedef struct { | |||||
| GLXFBConfig fb_config; | |||||
| GLXContext ctx; | |||||
| } PuglX11GlSurface; | |||||
| static int | |||||
| puglX11GlHintValue(const int value) | |||||
| { | |||||
| return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value; | |||||
| } | |||||
| static int | |||||
| puglX11GlGetAttrib(Display* const display, | |||||
| GLXFBConfig fb_config, | |||||
| const int attrib) | |||||
| { | |||||
| int value = 0; | |||||
| glXGetFBConfigAttrib(display, fb_config, attrib, &value); | |||||
| return value; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11GlConfigure(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| const int screen = impl->screen; | |||||
| Display* const display = impl->display; | |||||
| PuglX11GlSurface* const surface = | |||||
| (PuglX11GlSurface*)calloc(1, sizeof(PuglX11GlSurface)); | |||||
| impl->surface = surface; | |||||
| // clang-format off | |||||
| const int attrs[] = { | |||||
| #ifdef DGL_USE_RGBA | |||||
| GLX_RGBA, | |||||
| #endif | |||||
| GLX_X_RENDERABLE, True, | |||||
| GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, | |||||
| GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, | |||||
| GLX_RENDER_TYPE, GLX_RGBA_BIT, | |||||
| GLX_SAMPLES, puglX11GlHintValue(view->hints[PUGL_SAMPLES]), | |||||
| GLX_RED_SIZE, puglX11GlHintValue(view->hints[PUGL_RED_BITS]), | |||||
| GLX_GREEN_SIZE, puglX11GlHintValue(view->hints[PUGL_GREEN_BITS]), | |||||
| GLX_BLUE_SIZE, puglX11GlHintValue(view->hints[PUGL_BLUE_BITS]), | |||||
| GLX_ALPHA_SIZE, puglX11GlHintValue(view->hints[PUGL_ALPHA_BITS]), | |||||
| GLX_DEPTH_SIZE, puglX11GlHintValue(view->hints[PUGL_DEPTH_BITS]), | |||||
| GLX_STENCIL_SIZE, puglX11GlHintValue(view->hints[PUGL_STENCIL_BITS]), | |||||
| GLX_DOUBLEBUFFER, puglX11GlHintValue(view->hints[PUGL_DOUBLE_BUFFER]), | |||||
| None | |||||
| }; | |||||
| // clang-format on | |||||
| int n_fbc = 0; | |||||
| GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc); | |||||
| if (n_fbc <= 0) { | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } | |||||
| surface->fb_config = fbc[0]; | |||||
| impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]); | |||||
| view->hints[PUGL_RED_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE); | |||||
| view->hints[PUGL_GREEN_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE); | |||||
| view->hints[PUGL_BLUE_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE); | |||||
| view->hints[PUGL_ALPHA_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE); | |||||
| view->hints[PUGL_DEPTH_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE); | |||||
| view->hints[PUGL_STENCIL_BITS] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_STENCIL_SIZE); | |||||
| view->hints[PUGL_SAMPLES] = puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES); | |||||
| view->hints[PUGL_DOUBLE_BUFFER] = | |||||
| puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER); | |||||
| XFree(fbc); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||||
| { | |||||
| PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | |||||
| glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) | |||||
| { | |||||
| if (expose && view->hints[PUGL_DOUBLE_BUFFER]) { | |||||
| glXSwapBuffers(view->impl->display, view->impl->win); | |||||
| } | |||||
| glXMakeCurrent(view->impl->display, None, NULL); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11GlCreate(PuglView* view) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglX11GlSurface* const surface = (PuglX11GlSurface*)impl->surface; | |||||
| Display* const display = impl->display; | |||||
| GLXFBConfig fb_config = surface->fb_config; | |||||
| const int ctx_attrs[] = { | |||||
| GLX_CONTEXT_MAJOR_VERSION_ARB, | |||||
| view->hints[PUGL_CONTEXT_VERSION_MAJOR], | |||||
| GLX_CONTEXT_MINOR_VERSION_ARB, | |||||
| view->hints[PUGL_CONTEXT_VERSION_MINOR], | |||||
| GLX_CONTEXT_FLAGS_ARB, | |||||
| (view->hints[PUGL_USE_DEBUG_CONTEXT] ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), | |||||
| GLX_CONTEXT_PROFILE_MASK_ARB, | |||||
| (view->hints[PUGL_USE_COMPAT_PROFILE] | |||||
| ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB | |||||
| : GLX_CONTEXT_CORE_PROFILE_BIT_ARB), | |||||
| 0}; | |||||
| PFNGLXCREATECONTEXTATTRIBSARBPROC create_context = | |||||
| (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress( | |||||
| (const uint8_t*)"glXCreateContextAttribsARB"); | |||||
| PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = | |||||
| (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress( | |||||
| (const uint8_t*)"glXSwapIntervalEXT"); | |||||
| surface->ctx = create_context(display, fb_config, 0, True, ctx_attrs); | |||||
| if (!surface->ctx) { | |||||
| surface->ctx = | |||||
| glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True); | |||||
| } | |||||
| if (!surface->ctx) { | |||||
| return PUGL_CREATE_CONTEXT_FAILED; | |||||
| } | |||||
| const int swapInterval = view->hints[PUGL_SWAP_INTERVAL]; | |||||
| if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) { | |||||
| puglX11GlEnter(view, NULL); | |||||
| glXSwapIntervalEXT(display, impl->win, swapInterval); | |||||
| puglX11GlLeave(view, NULL); | |||||
| } | |||||
| glXGetConfig(impl->display, | |||||
| impl->vi, | |||||
| GLX_DOUBLEBUFFER, | |||||
| &view->hints[PUGL_DOUBLE_BUFFER]); | |||||
| glXQueryDrawable(display, | |||||
| impl->win, | |||||
| GLX_SWAP_INTERVAL_EXT, | |||||
| (unsigned int*)&view->hints[PUGL_SWAP_INTERVAL]); | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| static PuglStatus | |||||
| puglX11GlDestroy(PuglView* view) | |||||
| { | |||||
| PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | |||||
| if (surface) { | |||||
| glXDestroyContext(view->impl->display, surface->ctx); | |||||
| free(surface); | |||||
| view->impl->surface = NULL; | |||||
| } | |||||
| return PUGL_SUCCESS; | |||||
| } | |||||
| PuglGlFunc | |||||
| puglGetProcAddress(const char* name) | |||||
| { | |||||
| return glXGetProcAddress((const uint8_t*)name); | |||||
| } | |||||
| PuglStatus | |||||
| puglEnterContext(PuglView* view) | |||||
| { | |||||
| return view->backend->enter(view, NULL); | |||||
| } | |||||
| PuglStatus | |||||
| puglLeaveContext(PuglView* view) | |||||
| { | |||||
| return view->backend->leave(view, NULL); | |||||
| } | |||||
| const PuglBackend* | |||||
| puglGlBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglX11GlConfigure, | |||||
| puglX11GlCreate, | |||||
| puglX11GlDestroy, | |||||
| puglX11GlEnter, | |||||
| puglX11GlLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,38 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "pugl/stub.h" | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "x11.h" | |||||
| #include "pugl/pugl.h" | |||||
| const PuglBackend* | |||||
| puglStubBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = { | |||||
| puglX11Configure, | |||||
| puglStubCreate, | |||||
| puglStubDestroy, | |||||
| puglStubEnter, | |||||
| puglStubLeave, | |||||
| puglStubGetContext, | |||||
| }; | |||||
| return &backend; | |||||
| } | |||||
| @@ -0,0 +1,127 @@ | |||||
| /* | |||||
| Copyright 2012-2021 David Robillard <d@drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #define VK_NO_PROTOTYPES 1 | |||||
| #include "stub.h" | |||||
| #include "types.h" | |||||
| #include "x11.h" | |||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/vulkan.h" | |||||
| #include <vulkan/vulkan_core.h> | |||||
| #include <vulkan/vulkan_xlib.h> | |||||
| #include <dlfcn.h> | |||||
| #include <stdint.h> | |||||
| #include <stdlib.h> | |||||
| struct PuglVulkanLoaderImpl { | |||||
| void* libvulkan; | |||||
| PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | |||||
| PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; | |||||
| }; | |||||
| PuglVulkanLoader* | |||||
| puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) | |||||
| { | |||||
| PuglVulkanLoader* const loader = | |||||
| (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); | |||||
| if (!loader || !(loader->libvulkan = dlopen("libvulkan.so", RTLD_LAZY))) { | |||||
| free(loader); | |||||
| return NULL; | |||||
| } | |||||
| loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym( | |||||
| loader->libvulkan, "vkGetInstanceProcAddr"); | |||||
| loader->vkGetDeviceProcAddr = | |||||
| (PFN_vkGetDeviceProcAddr)dlsym(loader->libvulkan, "vkGetDeviceProcAddr"); | |||||
| return loader; | |||||
| } | |||||
| void | |||||
| puglFreeVulkanLoader(PuglVulkanLoader* loader) | |||||
| { | |||||
| if (loader) { | |||||
| dlclose(loader->libvulkan); | |||||
| free(loader); | |||||
| } | |||||
| } | |||||
| PFN_vkGetInstanceProcAddr | |||||
| puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetInstanceProcAddr; | |||||
| } | |||||
| PFN_vkGetDeviceProcAddr | |||||
| puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) | |||||
| { | |||||
| return loader->vkGetDeviceProcAddr; | |||||
| } | |||||
| const PuglBackend* | |||||
| puglVulkanBackend(void) | |||||
| { | |||||
| static const PuglBackend backend = {puglX11Configure, | |||||
| puglStubCreate, | |||||
| puglStubDestroy, | |||||
| puglStubEnter, | |||||
| puglStubLeave, | |||||
| puglStubGetContext}; | |||||
| return &backend; | |||||
| } | |||||
| const char* const* | |||||
| puglGetInstanceExtensions(uint32_t* const count) | |||||
| { | |||||
| static const char* const extensions[] = {"VK_KHR_surface", | |||||
| "VK_KHR_xlib_surface"}; | |||||
| *count = 2; | |||||
| return extensions; | |||||
| } | |||||
| VkResult | |||||
| puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||||
| PuglView* const view, | |||||
| VkInstance instance, | |||||
| const VkAllocationCallbacks* const allocator, | |||||
| VkSurfaceKHR* const surface) | |||||
| { | |||||
| PuglInternals* const impl = view->impl; | |||||
| PuglWorldInternals* world_impl = view->world->impl; | |||||
| PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = | |||||
| (PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance, | |||||
| "vkCreateXlibSurfaceKHR"); | |||||
| const VkXlibSurfaceCreateInfoKHR info = { | |||||
| VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, | |||||
| NULL, | |||||
| 0, | |||||
| world_impl->display, | |||||
| impl->win, | |||||
| }; | |||||
| return vkCreateXlibSurfaceKHR(instance, &info, allocator, surface); | |||||
| } | |||||