2024-01-10 00:38:38 +08:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2024-07-13 00:39:58 +08:00
|
|
|
state.wl_display = SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, NULL);
|
2024-01-10 00:38:38 +08:00
|
|
|
|
|
|
|
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
|