| @@ -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); | |||
| } | |||