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