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