Page Menu
Home
Search
Configure Global Search
Log In
Paste
P3032
D15250: dynamic loading with varargs wrapper using `__builtin_apply`
Archived
Public
Actions
Authored by
Campbell Barton (campbellbarton)
on Jun 28 2022, 9:03 AM.
Edit Paste
Activate Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Tags
None
Subscribers
None
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c3127e0f64..b7ca9eb1803 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -229,6 +229,9 @@ if(UNIX AND NOT (APPLE OR HAIKU))
option(WITH_GHOST_WAYLAND_DBUS "Optionally build with DBUS support (used for Cursor themes). May hang on startup systems where DBUS is not used." OFF)
mark_as_advanced(WITH_GHOST_WAYLAND_DBUS)
+
+ option(WITH_GHOST_WAYLAND_DYNLOAD "Enable runtime dynamic WAYLAND libraries loading" ON)
+ mark_as_advanced(WITH_GHOST_WAYLAND_DYNLOAD)
endif()
endif()
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 875305b0564..933e3ef8b85 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -625,12 +625,17 @@ if(WITH_GHOST_WAYLAND)
set(WITH_GL_EGL ON)
list(APPEND PLATFORM_LINKLIBS
- ${wayland-client_LINK_LIBRARIES}
- ${wayland-egl_LINK_LIBRARIES}
${xkbcommon_LINK_LIBRARIES}
- ${wayland-cursor_LINK_LIBRARIES}
)
+ if(NOT WITH_GHOST_WAYLAND_DYNLOAD)
+ list(APPEND PLATFORM_LINKLIBS
+ ${wayland-egl_LINK_LIBRARIES}
+ ${wayland-cursor_LINK_LIBRARIES}
+ ${wayland-client_LINK_LIBRARIES}
+ )
+ endif()
+
if(WITH_GHOST_WAYLAND_DBUS)
list(APPEND PLATFORM_LINKLIBS
${dbus_LINK_LIBRARIES}
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 6a11d00cbc4..53a24e8b5c3 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -298,6 +298,15 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
intern/GHOST_WindowWayland.h
)
+ if(WITH_GHOST_WAYLAND_DYNLOAD)
+ list(APPEND SRC
+ intern/GHOST_WaylandDynload.cpp
+
+ intern/GHOST_WaylandDynload.h
+ )
+ add_definitions(-DWITH_GHOST_WAYLAND_DYNLOAD)
+ endif()
+
pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner)
pkg_check_modules(wayland-protocols wayland-protocols>=1.15)
diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp
index 745d5faeed4..1876e284971 100644
--- a/intern/ghost/intern/GHOST_ISystem.cpp
+++ b/intern/ghost/intern/GHOST_ISystem.cpp
@@ -21,6 +21,9 @@
# include "GHOST_SystemX11.h"
#elif defined(WITH_GHOST_WAYLAND)
# include "GHOST_SystemWayland.h"
+# if defined(WITH_GHOST_WAYLAND_DYNLOAD)
+# include "GHOST_WaylandDynload.h"
+# endif
#elif defined(WITH_GHOST_SDL)
# include "GHOST_SystemSDL.h"
#elif defined(WIN32)
@@ -41,21 +44,32 @@ GHOST_TSuccess GHOST_ISystem::createSystem()
m_system = new GHOST_SystemNULL();
#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
/* Special case, try Wayland, fall back to X11. */
- try {
- m_system = new GHOST_SystemWayland();
- }
- catch (const std::runtime_error &) {
- /* fallback to X11. */
- delete m_system;
- m_system = nullptr;
- }
- if (!m_system) {
- m_system = new GHOST_SystemX11();
+
+# if defined(WITH_GHOST_WAYLAND_DYNLOAD)
+ if (GHOST_WaylandDynload_init() == GHOST_WAYLAND_DYNLOAD_SUCCESS)
+# endif
+ {
+ try {
+ m_system = new GHOST_SystemWayland();
+ }
+ catch (const std::runtime_error &) {
+ /* fallback to X11. */
+ delete m_system;
+ m_system = nullptr;
+ }
+ if (!m_system) {
+ m_system = new GHOST_SystemX11();
+ }
}
#elif defined(WITH_GHOST_X11)
m_system = new GHOST_SystemX11();
#elif defined(WITH_GHOST_WAYLAND)
- m_system = new GHOST_SystemWayland();
+# if defined(WITH_GHOST_WAYLAND_DYNLOAD)
+ if (GHOST_WaylandDynload_init() == GHOST_WAYLAND_DYNLOAD_SUCCESS)
+# endif
+ {
+ m_system = new GHOST_SystemWayland();
+ }
#elif defined(WITH_GHOST_SDL)
m_system = new GHOST_SystemSDL();
#elif defined(WIN32)
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 96340fc9090..eb0e3a2d82c 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -5,6 +5,7 @@
*/
#include "GHOST_SystemWayland.h"
+
#include "GHOST_Event.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventCursor.h"
diff --git a/intern/ghost/intern/GHOST_WaylandDynload.cpp b/intern/ghost/intern/GHOST_WaylandDynload.cpp
new file mode 100644
index 00000000000..ce3c65f12a0
--- /dev/null
+++ b/intern/ghost/intern/GHOST_WaylandDynload.cpp
@@ -0,0 +1,420 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+/* As this is wrapping a C-API, use C. */
+extern "C" {
+
+#include "GHOST_WaylandDynload.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <wayland-client-core.h>
+#include <wayland-cursor.h>
+#include <wayland-egl.h>
+
+#include <dlfcn.h> /* Dynamic loading. */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Types
+ * \{ */
+
+typedef void *DynamicLibrary;
+
+#define dynamic_library_open(path) dlopen(path, RTLD_NOW)
+#define dynamic_library_close(lib) dlclose(lib)
+#define dynamic_library_find(lib, symbol) dlsym(lib, symbol)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Macros for Assigning Members
+ * \{ */
+
+static void report_error(const char *ty, const char *name)
+{
+ fprintf(stderr, "WAYLAND: could not load %s in %s\n", name, ty);
+}
+#define GHOST_WL_LIB_FIND(ty, name) \
+ if (!(ty.name = (decltype(ty.name))(void *)dynamic_library_find(ty.lib, #name))) { \
+ report_error("" #ty, "" #name); \
+ } \
+ ((void)0)
+
+#define GHOST_WL_LIB_FIND_COPY(ty, name) \
+ { \
+ void *_var = (void *)dynamic_library_find(ty.lib, #name); \
+ if (!_var) { \
+ report_error("" #ty, "" #name); \
+ } \
+ else { \
+ memcpy(&(name##_), _var, sizeof(name)); \
+ } \
+ } \
+ ((void)0)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Impelment `wayland-client-core.h`
+ * \{ */
+
+static struct {
+ DynamicLibrary lib;
+
+ struct wl_display *(*wl_display_connect)(const char *name);
+ void (*wl_display_disconnect)(struct wl_display *display);
+ int (*wl_display_dispatch)(struct wl_display *display);
+ int (*wl_display_roundtrip)(struct wl_display *display);
+ void (*wl_log_set_handler_client)(wl_log_func_t);
+ int (*wl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data);
+ void (*wl_proxy_destroy)(struct wl_proxy *proxy);
+ struct wl_proxy *(*wl_proxy_marshal_flags)(struct wl_proxy *proxy,
+ uint32_t opcode,
+ const struct wl_interface *interface,
+ uint32_t version,
+ uint32_t flags,
+ ...);
+ struct wl_proxy *(*wl_proxy_marshal_array_flags)(struct wl_proxy *proxy,
+ uint32_t opcode,
+ const struct wl_interface *interface,
+ uint32_t version,
+ uint32_t flags,
+ union wl_argument *args);
+ void (*wl_proxy_set_user_data)(struct wl_proxy *proxy, void *user_data);
+ void *(*wl_proxy_get_user_data)(struct wl_proxy *proxy);
+ uint32_t (*wl_proxy_get_version)(struct wl_proxy *proxy);
+} g_wayland_client_core = {NULL};
+
+void wl_log_set_handler_client(wl_log_func_t handler)
+{
+ g_wayland_client_core.wl_log_set_handler_client(handler);
+}
+uint32_t wl_proxy_get_version(struct wl_proxy *proxy)
+{
+ return g_wayland_client_core.wl_proxy_get_version(proxy);
+}
+
+int wl_proxy_add_listener(struct wl_proxy *proxy, void (**implementation)(void), void *data)
+{
+ return g_wayland_client_core.wl_proxy_add_listener(proxy, implementation, data);
+}
+
+void wl_proxy_destroy(struct wl_proxy *proxy)
+{
+ g_wayland_client_core.wl_proxy_destroy(proxy);
+}
+
+void *wl_proxy_get_user_data(struct wl_proxy *proxy)
+{
+ return g_wayland_client_core.wl_proxy_get_user_data(proxy);
+}
+
+void wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data)
+{
+
+ return g_wayland_client_core.wl_proxy_set_user_data(proxy, user_data);
+}
+
+struct wl_display *wl_display_connect(const char *name)
+{
+ return g_wayland_client_core.wl_display_connect(name);
+}
+
+void wl_display_disconnect(struct wl_display *display)
+{
+ g_wayland_client_core.wl_display_disconnect(display);
+}
+
+int wl_display_dispatch(struct wl_display *display)
+{
+ return g_wayland_client_core.wl_display_dispatch(display);
+}
+
+int wl_display_roundtrip(struct wl_display *display)
+{
+ return g_wayland_client_core.wl_display_roundtrip(display);
+}
+
+struct wl_proxy *wl_proxy_marshal_flags(struct wl_proxy *proxy,
+ uint32_t opcode,
+ const struct wl_interface *interface,
+ uint32_t version,
+ uint32_t flags,
+ ...)
+{
+ /* These are used, via `args`. */
+ (void)proxy;
+ (void)opcode;
+ (void)interface;
+ (void)version;
+ (void)flags;
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+
+ void *args = __builtin_apply_args();
+ void *ret = __builtin_apply(
+ reinterpret_cast<void (*)(...)>(g_wayland_client_core.wl_proxy_marshal_flags), args, 1000);
+ __builtin_return(ret);
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+}
+
+/* These are needed as these values can't be declared `const` as they are in the header. */
+#pragma redefine_extname wl_buffer_interface_ wl_buffer_interface
+#pragma redefine_extname wl_buffer_interface_ wl_buffer_interface
+#pragma redefine_extname wl_compositor_interface_ wl_compositor_interface
+#pragma redefine_extname wl_data_device_interface_ wl_data_device_interface
+#pragma redefine_extname wl_data_device_manager_interface_ wl_data_device_manager_interface
+#pragma redefine_extname wl_data_source_interface_ wl_data_source_interface
+#pragma redefine_extname wl_keyboard_interface_ wl_keyboard_interface
+#pragma redefine_extname wl_output_interface_ wl_output_interface
+#pragma redefine_extname wl_pointer_interface_ wl_pointer_interface
+#pragma redefine_extname wl_region_interface_ wl_region_interface
+#pragma redefine_extname wl_registry_interface_ wl_registry_interface
+#pragma redefine_extname wl_seat_interface_ wl_seat_interface
+#pragma redefine_extname wl_shm_interface_ wl_shm_interface
+#pragma redefine_extname wl_shm_pool_interface_ wl_shm_pool_interface
+#pragma redefine_extname wl_surface_interface_ wl_surface_interface
+
+struct wl_interface wl_buffer_interface_;
+struct wl_interface wl_compositor_interface_;
+struct wl_interface wl_data_device_interface_;
+struct wl_interface wl_data_device_manager_interface_;
+struct wl_interface wl_data_source_interface_;
+struct wl_interface wl_keyboard_interface_;
+struct wl_interface wl_output_interface_;
+struct wl_interface wl_pointer_interface_;
+struct wl_interface wl_region_interface_;
+struct wl_interface wl_registry_interface_;
+struct wl_interface wl_seat_interface_;
+struct wl_interface wl_shm_interface_;
+struct wl_interface wl_shm_pool_interface_;
+struct wl_interface wl_surface_interface_;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Impelment `wayland-cursor.h`
+ * \{ */
+
+static struct {
+ DynamicLibrary lib;
+
+ struct wl_cursor_theme *(*wl_cursor_theme_load)(const char *name, int size, struct wl_shm *shm);
+ void (*wl_cursor_theme_destroy)(struct wl_cursor_theme *theme);
+ struct wl_cursor *(*wl_cursor_theme_get_cursor)(struct wl_cursor_theme *theme, const char *name);
+ struct wl_buffer *(*wl_cursor_image_get_buffer)(struct wl_cursor_image *image);
+ int (*wl_cursor_frame)(struct wl_cursor *cursor, uint32_t time);
+ int (*wl_cursor_frame_and_duration)(struct wl_cursor *cursor, uint32_t time, uint32_t *duration);
+
+} g_wayland_cursor = {NULL};
+
+struct wl_cursor_theme *wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
+{
+ return g_wayland_cursor.wl_cursor_theme_load(name, size, shm);
+}
+
+void wl_cursor_theme_destroy(struct wl_cursor_theme *theme)
+{
+ return g_wayland_cursor.wl_cursor_theme_destroy(theme);
+}
+
+struct wl_cursor *wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme, const char *name)
+{
+ return g_wayland_cursor.wl_cursor_theme_get_cursor(theme, name);
+}
+
+struct wl_buffer *wl_cursor_image_get_buffer(struct wl_cursor_image *image)
+{
+
+ return g_wayland_cursor.wl_cursor_image_get_buffer(image);
+}
+
+int wl_cursor_frame(struct wl_cursor *cursor, uint32_t time)
+{
+ return g_wayland_cursor.wl_cursor_frame(cursor, time);
+}
+
+int wl_cursor_frame_and_duration(struct wl_cursor *cursor, uint32_t time, uint32_t *duration)
+{
+ return g_wayland_cursor.wl_cursor_frame_and_duration(cursor, time, duration);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Impelment `wayland-egl.h`
+ * \{ */
+
+struct {
+ DynamicLibrary lib;
+
+ struct wl_egl_window *(*wl_egl_window_create)(struct wl_surface *surface, int width, int height);
+ void (*wl_egl_window_destroy)(struct wl_egl_window *egl_window);
+ void (*wl_egl_window_resize)(
+ struct wl_egl_window *egl_window, int width, int height, int dx, int dy);
+ void (*wl_egl_window_get_attached_size)(struct wl_egl_window *egl_window,
+ int *width,
+ int *height);
+} g_wayland_egl = {NULL};
+
+struct wl_egl_window *wl_egl_window_create(struct wl_surface *surface, int width, int height)
+{
+ return g_wayland_egl.wl_egl_window_create(surface, width, height);
+}
+
+void wl_egl_window_destroy(struct wl_egl_window *egl_window)
+{
+ return g_wayland_egl.wl_egl_window_destroy(egl_window);
+}
+
+void wl_egl_window_resize(struct wl_egl_window *egl_window, int width, int height, int dx, int dy)
+{
+ return g_wayland_egl.wl_egl_window_resize(egl_window, width, height, dx, dy);
+}
+
+void wl_egl_window_get_attached_size(struct wl_egl_window *egl_window, int *width, int *height)
+{
+ return g_wayland_egl.wl_egl_window_get_attached_size(egl_window, width, height);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main Initialization & Exit Functions
+ * \{ */
+
+/** Runs `atexit`. */
+static void GHOST_WaylandDynload_exit(void)
+{
+ void **lib_array[] = {
+ &g_wayland_client_core.lib,
+ &g_wayland_cursor.lib,
+ &g_wayland_egl.lib,
+ NULL,
+ };
+ for (int i = 0; lib_array[i]; i++) {
+ void **lib_p = lib_array[i];
+ if (*lib_p != NULL) {
+ /* Ignore errors. */
+ dynamic_library_close(*lib_p);
+ *lib_p = NULL;
+ }
+ }
+}
+
+int GHOST_WaylandDynload_init(void)
+{
+ /* Library paths. */
+ const char *client_paths[] = {
+ "libwayland-client.so.0",
+ "libwayland-client.so",
+ NULL,
+ };
+ const char *egl_paths[] = {
+ "libwayland-egl.so.1",
+ "libwayland-egl.so",
+ NULL,
+ };
+ const char *cursor_paths[] = {
+ "libwayland-cursor.so.1",
+ "libwayland-cursor.so",
+ NULL,
+ };
+ static int8_t initialized = 0;
+ static int8_t result = 0;
+
+ if (initialized) {
+ return result;
+ }
+
+ initialized = 1;
+
+ if (atexit(GHOST_WaylandDynload_exit)) {
+ result = GHOST_WAYLAND_DYNLOAD_ERROR_ATEXIT_FAILED;
+ return result;
+ }
+
+ struct {
+ const char **paths;
+ void **lib_p;
+ } lib_array[] = {
+ {client_paths, &g_wayland_client_core.lib},
+ {egl_paths, &g_wayland_egl.lib},
+ {cursor_paths, &g_wayland_cursor.lib},
+ {NULL, NULL},
+ };
+
+ for (int lib_index = 0; lib_array[lib_index].lib_p; lib_index++) {
+ const char **paths = lib_array[lib_index].paths;
+ void **lib_p = lib_array[lib_index].lib_p;
+ for (int a = 0; client_paths[a] != NULL && *lib_p == NULL; a++) {
+ *lib_p = dynamic_library_open(paths[a]);
+ }
+ if (*lib_p == NULL) {
+ fprintf(stderr, "Unable to find '%s' or other libs.\n", paths[0]);
+ result = GHOST_WAYLAND_DYNLOAD_ERROR_OPEN_FAILED;
+ return result;
+ }
+ }
+
+ /* `wayland-client-core.h` */
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_display_connect);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_display_disconnect);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_display_dispatch);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_display_roundtrip);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_log_set_handler_client);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_add_listener);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_destroy);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_marshal_array_flags);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_marshal_flags);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_set_user_data);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_get_user_data);
+ GHOST_WL_LIB_FIND(g_wayland_client_core, wl_proxy_get_version);
+
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_buffer_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_compositor_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_data_device_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_data_device_manager_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_data_source_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_keyboard_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_output_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_pointer_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_region_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_registry_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_seat_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_shm_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_shm_pool_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_surface_interface);
+ GHOST_WL_LIB_FIND_COPY(g_wayland_client_core, wl_surface_interface);
+
+ /* `wayland-egl.h` */
+ GHOST_WL_LIB_FIND(g_wayland_egl, wl_egl_window_create);
+ GHOST_WL_LIB_FIND(g_wayland_egl, wl_egl_window_destroy);
+ GHOST_WL_LIB_FIND(g_wayland_egl, wl_egl_window_get_attached_size);
+ GHOST_WL_LIB_FIND(g_wayland_egl, wl_egl_window_resize);
+
+ /* `wayland-cursor.h` */
+ GHOST_WL_LIB_FIND(g_wayland_cursor, wl_cursor_image_get_buffer);
+ GHOST_WL_LIB_FIND(g_wayland_cursor, wl_cursor_theme_destroy);
+ GHOST_WL_LIB_FIND(g_wayland_cursor, wl_cursor_theme_get_cursor);
+ GHOST_WL_LIB_FIND(g_wayland_cursor, wl_cursor_theme_load);
+
+ return result;
+}
+
+/** \} */
+
+}; /* `extern "C"` */
diff --git a/intern/ghost/intern/GHOST_WaylandDynload.h b/intern/ghost/intern/GHOST_WaylandDynload.h
new file mode 100644
index 00000000000..52268edc9d2
--- /dev/null
+++ b/intern/ghost/intern/GHOST_WaylandDynload.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+extern "C" {
+
+enum {
+ GHOST_WAYLAND_DYNLOAD_SUCCESS = 0,
+ GHOST_WAYLAND_DYNLOAD_ERROR_OPEN_FAILED = -1,
+ GHOST_WAYLAND_DYNLOAD_ERROR_ATEXIT_FAILED = -2,
+};
+
+int GHOST_WaylandDynload_init(void);
+};
Event Timeline
Campbell Barton (campbellbarton)
created this paste.
Jun 28 2022, 9:03 AM
Campbell Barton (campbellbarton)
archived this paste.
Campbell Barton (campbellbarton)
mentioned this in
D15250: GHOST/Wayland: dynamic loading support for libwayland-* (WITH_GHOST_WAYLAND_DYNLOAD)
.
Log In to Comment