mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-10-26 12:27:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			227 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|   Copyright (C) 1997-2025 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_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_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
 | 
