|
@@ -0,0 +1,226 @@
|
|
|
+/*
|
|
|
+ Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
|
|
+
|
|
|
+ This software is provided 'as-is', without any express or implied
|
|
|
+ warranty. In no event will the authors be held liable for any damages
|
|
|
+ arising from the use of this software.
|
|
|
+
|
|
|
+ Permission is granted to anyone to use this software for any purpose,
|
|
|
+ including commercial applications, and to alter it and redistribute it
|
|
|
+ freely.
|
|
|
+*/
|
|
|
+
|
|
|
+#include "testnative.h"
|
|
|
+
|
|
|
+#ifdef TEST_NATIVE_WAYLAND
|
|
|
+
|
|
|
+#include <SDL3/SDL.h>
|
|
|
+#include <wayland-client.h>
|
|
|
+#include <xdg-shell-client-protocol.h>
|
|
|
+
|
|
|
+static void *native_userdata_ptr = (void *)0xBAADF00D;
|
|
|
+static const char *native_surface_tag = "SDL_NativeSurfaceTag";
|
|
|
+
|
|
|
+static void *CreateWindowWayland(int w, int h);
|
|
|
+static void DestroyWindowWayland(void *window);
|
|
|
+
|
|
|
+NativeWindowFactory WaylandWindowFactory = {
|
|
|
+ "wayland",
|
|
|
+ CreateWindowWayland,
|
|
|
+ DestroyWindowWayland
|
|
|
+};
|
|
|
+
|
|
|
+/* Encapsulated in a struct to silence shadow variable warnings */
|
|
|
+static struct _state
|
|
|
+{
|
|
|
+ struct wl_display *wl_display;
|
|
|
+ struct wl_registry *wl_registry;
|
|
|
+ struct wl_compositor *wl_compositor;
|
|
|
+ struct xdg_wm_base *xdg_wm_base;
|
|
|
+ struct wl_surface *wl_surface;
|
|
|
+ struct xdg_surface *xdg_surface;
|
|
|
+ struct xdg_toplevel *xdg_toplevel;
|
|
|
+} state;
|
|
|
+
|
|
|
+static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
|
|
|
+{
|
|
|
+ xdg_surface_ack_configure(state.xdg_surface, serial);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct xdg_surface_listener xdg_surface_listener = {
|
|
|
+ .configure = xdg_surface_configure,
|
|
|
+};
|
|
|
+
|
|
|
+static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states)
|
|
|
+{
|
|
|
+ /* NOP */
|
|
|
+}
|
|
|
+
|
|
|
+static void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
|
|
+{
|
|
|
+ SDL_Event event;
|
|
|
+ SDL_zero(event);
|
|
|
+
|
|
|
+ event.type = SDL_EVENT_QUIT;
|
|
|
+ SDL_PushEvent(&event);
|
|
|
+}
|
|
|
+
|
|
|
+static void xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height)
|
|
|
+{
|
|
|
+ /* NOP */
|
|
|
+}
|
|
|
+
|
|
|
+static void xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities)
|
|
|
+{
|
|
|
+ /* NOP */
|
|
|
+}
|
|
|
+
|
|
|
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
|
+ .configure = xdg_toplevel_configure,
|
|
|
+ .close = xdg_toplevel_close,
|
|
|
+ .configure_bounds = xdg_toplevel_configure_bounds,
|
|
|
+ .wm_capabilities = xdg_toplevel_wm_capabilities
|
|
|
+};
|
|
|
+
|
|
|
+static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
|
|
|
+{
|
|
|
+ xdg_wm_base_pong(state.xdg_wm_base, serial);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|
|
+ .ping = xdg_wm_base_ping,
|
|
|
+};
|
|
|
+
|
|
|
+static void registry_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version)
|
|
|
+{
|
|
|
+ if (SDL_strcmp(interface, wl_compositor_interface.name) == 0) {
|
|
|
+ state.wl_compositor = wl_registry_bind(state.wl_registry, name, &wl_compositor_interface, SDL_min(version, 4));
|
|
|
+ } else if (SDL_strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
|
+ state.xdg_wm_base = wl_registry_bind(state.wl_registry, name, &xdg_wm_base_interface, 1);
|
|
|
+ xdg_wm_base_add_listener(state.xdg_wm_base, &xdg_wm_base_listener, NULL);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
|
|
|
+{
|
|
|
+ /* NOP */
|
|
|
+}
|
|
|
+
|
|
|
+static const struct wl_registry_listener wl_registry_listener = {
|
|
|
+ .global = registry_global,
|
|
|
+ .global_remove = registry_global_remove,
|
|
|
+};
|
|
|
+
|
|
|
+static void *CreateWindowWayland(int w, int h)
|
|
|
+{
|
|
|
+ /* Export the display object from SDL and use it to create a registry object,
|
|
|
+ * which will enumerate the wl_compositor and xdg_wm_base protocols.
|
|
|
+ */
|
|
|
+ state.wl_display = SDL_GetProperty(SDL_GetGlobalProperties(), SDL_PROPERTY_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, NULL);
|
|
|
+
|
|
|
+ if (!state.wl_display) {
|
|
|
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid 'wl_display' object!");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ state.wl_registry = wl_display_get_registry(state.wl_display);
|
|
|
+ wl_registry_add_listener(state.wl_registry, &wl_registry_listener, NULL);
|
|
|
+
|
|
|
+ /* Roundtrip to enumerate registry objects. */
|
|
|
+ wl_display_roundtrip(state.wl_display);
|
|
|
+
|
|
|
+ /* Protocol sanity check */
|
|
|
+ if (!state.wl_compositor) {
|
|
|
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "'wl_compositor' protocol not found!");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if (!state.xdg_wm_base) {
|
|
|
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "'xdg_wm_base' protocol not found!");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Crate the backing wl_surface for the window. */
|
|
|
+ state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
|
|
|
+
|
|
|
+ /* Set the native tag and userdata values, which should be the same at exit. */
|
|
|
+ wl_proxy_set_tag((struct wl_proxy *)state.wl_surface, &native_surface_tag);
|
|
|
+ wl_surface_set_user_data(state.wl_surface, native_userdata_ptr);
|
|
|
+
|
|
|
+ /* Create the xdg_surface from the wl_surface. */
|
|
|
+ state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.wl_surface);
|
|
|
+ xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, NULL);
|
|
|
+
|
|
|
+ /* Create the xdg_toplevel from the xdg_surface. */
|
|
|
+ state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
|
|
|
+ xdg_toplevel_add_listener(state.xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
|
+ xdg_toplevel_set_title(state.xdg_toplevel, "Native Wayland Window");
|
|
|
+
|
|
|
+ /* Return the wl_surface to be wrapped in an SDL_Window. */
|
|
|
+ return state.wl_surface;
|
|
|
+
|
|
|
+error:
|
|
|
+ if (state.xdg_toplevel) {
|
|
|
+ xdg_toplevel_destroy(state.xdg_toplevel);
|
|
|
+ state.xdg_toplevel = NULL;
|
|
|
+ }
|
|
|
+ if (state.xdg_surface) {
|
|
|
+ xdg_surface_destroy(state.xdg_surface);
|
|
|
+ state.xdg_surface = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_surface) {
|
|
|
+ wl_surface_destroy(state.wl_surface);
|
|
|
+ state.wl_surface = NULL;
|
|
|
+ }
|
|
|
+ if (state.xdg_wm_base) {
|
|
|
+ xdg_wm_base_destroy(state.xdg_wm_base);
|
|
|
+ state.xdg_wm_base = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_compositor) {
|
|
|
+ wl_compositor_destroy(state.wl_compositor);
|
|
|
+ state.wl_compositor = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_registry) {
|
|
|
+ wl_registry_destroy(state.wl_registry);
|
|
|
+ state.wl_registry = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void DestroyWindowWayland(void *window)
|
|
|
+{
|
|
|
+ if (state.xdg_toplevel) {
|
|
|
+ xdg_toplevel_destroy(state.xdg_toplevel);
|
|
|
+ state.xdg_toplevel = NULL;
|
|
|
+ }
|
|
|
+ if (state.xdg_surface) {
|
|
|
+ xdg_surface_destroy(state.xdg_surface);
|
|
|
+ state.xdg_surface = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_surface) {
|
|
|
+ /* Surface sanity check; these should be unmodified. */
|
|
|
+ if (wl_proxy_get_tag((struct wl_proxy *)state.wl_surface) != &native_surface_tag) {
|
|
|
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "The wl_surface tag was modified, this indicates a problem inside of SDL.");
|
|
|
+ }
|
|
|
+ if (wl_surface_get_user_data(state.wl_surface) != native_userdata_ptr) {
|
|
|
+ SDL_LogError(SDL_LOG_CATEGORY_ERROR, "The wl_surface user data was modified, this indicates a problem inside of SDL.");
|
|
|
+ }
|
|
|
+
|
|
|
+ wl_surface_destroy(state.wl_surface);
|
|
|
+ state.wl_surface = NULL;
|
|
|
+ }
|
|
|
+ if (state.xdg_wm_base) {
|
|
|
+ xdg_wm_base_destroy(state.xdg_wm_base);
|
|
|
+ state.xdg_wm_base = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_compositor) {
|
|
|
+ wl_compositor_destroy(state.wl_compositor);
|
|
|
+ state.wl_compositor = NULL;
|
|
|
+ }
|
|
|
+ if (state.wl_registry) {
|
|
|
+ wl_registry_destroy(state.wl_registry);
|
|
|
+ state.wl_registry = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|