Atlas - SDL_waylandvideo.c
Home / ext / SDL2 / src / video / wayland Lines: 1 | Size: 15526 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "../../SDL_internal.h" 23 24#if SDL_VIDEO_DRIVER_WAYLAND 25 26#include "SDL_video.h" 27#include "SDL_mouse.h" 28#include "SDL_stdinc.h" 29#include "../../events/SDL_events_c.h" 30 31#include "SDL_waylandvideo.h" 32#include "SDL_waylandevents_c.h" 33#include "SDL_waylandwindow.h" 34#include "SDL_waylandopengles.h" 35#include "SDL_waylandmouse.h" 36#include "SDL_waylandtouch.h" 37#include "SDL_waylandclipboard.h" 38#include "SDL_waylandvulkan.h" 39 40#include <sys/types.h> 41#include <unistd.h> 42#include <fcntl.h> 43#include <xkbcommon/xkbcommon.h> 44 45#include "SDL_waylanddyn.h" 46#include <wayland-util.h> 47 48#include "xdg-shell-client-protocol.h" 49#include "xdg-shell-unstable-v6-client-protocol.h" 50 51#define WAYLANDVID_DRIVER_NAME "wayland" 52 53/* Initialization/Query functions */ 54static int 55Wayland_VideoInit(_THIS); 56 57static void 58Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display); 59static int 60Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode); 61 62static void 63Wayland_VideoQuit(_THIS); 64 65/* Find out what class name we should use 66 * Based on src/video/x11/SDL_x11video.c */ 67static char * 68get_classname() 69{ 70/* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec: 71 "The surface class identifies the general class of applications 72 to which the surface belongs. A common convention is to use the 73 file name (or the full path if it is a non-standard location) of 74 the application's .desktop file as the class." */ 75 76 char *spot; 77#if defined(__LINUX__) || defined(__FREEBSD__) 78 char procfile[1024]; 79 char linkfile[1024]; 80 int linksize; 81#endif 82 83 /* First allow environment variable override */ 84 spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS"); 85 if (spot) { 86 return SDL_strdup(spot); 87 } else { 88 /* Fallback to the "old" envvar */ 89 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS"); 90 if (spot) { 91 return SDL_strdup(spot); 92 } 93 } 94 95 /* Next look at the application's executable name */ 96#if defined(__LINUX__) || defined(__FREEBSD__) 97#if defined(__LINUX__) 98 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid()); 99#elif defined(__FREEBSD__) 100 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", 101 getpid()); 102#else 103#error Where can we find the executable name? 104#endif 105 linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1); 106 if (linksize > 0) { 107 linkfile[linksize] = '\0'; 108 spot = SDL_strrchr(linkfile, '/'); 109 if (spot) { 110 return SDL_strdup(spot + 1); 111 } else { 112 return SDL_strdup(linkfile); 113 } 114 } 115#endif /* __LINUX__ || __FREEBSD__ */ 116 117 /* Finally use the default we've used forever */ 118 return SDL_strdup("SDL_App"); 119} 120 121/* Wayland driver bootstrap functions */ 122static int 123Wayland_Available(void) 124{ 125 struct wl_display *display = NULL; 126 if (SDL_WAYLAND_LoadSymbols()) { 127 display = WAYLAND_wl_display_connect(NULL); 128 if (display != NULL) { 129 WAYLAND_wl_display_disconnect(display); 130 } 131 SDL_WAYLAND_UnloadSymbols(); 132 } 133 134 return (display != NULL); 135} 136 137static void 138Wayland_DeleteDevice(SDL_VideoDevice *device) 139{ 140 SDL_free(device); 141 SDL_WAYLAND_UnloadSymbols(); 142} 143 144static SDL_VideoDevice * 145Wayland_CreateDevice(int devindex) 146{ 147 SDL_VideoDevice *device; 148 149 if (!SDL_WAYLAND_LoadSymbols()) { 150 return NULL; 151 } 152 153 /* Initialize all variables that we clean on shutdown */ 154 device = SDL_calloc(1, sizeof(SDL_VideoDevice)); 155 if (!device) { 156 SDL_WAYLAND_UnloadSymbols(); 157 SDL_OutOfMemory(); 158 return NULL; 159 } 160 161 /* Set the function pointers */ 162 device->VideoInit = Wayland_VideoInit; 163 device->VideoQuit = Wayland_VideoQuit; 164 device->SetDisplayMode = Wayland_SetDisplayMode; 165 device->GetDisplayModes = Wayland_GetDisplayModes; 166 device->GetWindowWMInfo = Wayland_GetWindowWMInfo; 167 168 device->PumpEvents = Wayland_PumpEvents; 169 170 device->GL_SwapWindow = Wayland_GLES_SwapWindow; 171 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval; 172 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval; 173 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent; 174 device->GL_CreateContext = Wayland_GLES_CreateContext; 175 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary; 176 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary; 177 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress; 178 device->GL_DeleteContext = Wayland_GLES_DeleteContext; 179 180 device->CreateSDLWindow = Wayland_CreateWindow; 181 device->ShowWindow = Wayland_ShowWindow; 182 device->SetWindowFullscreen = Wayland_SetWindowFullscreen; 183 device->MaximizeWindow = Wayland_MaximizeWindow; 184 device->RestoreWindow = Wayland_RestoreWindow; 185 device->SetWindowSize = Wayland_SetWindowSize; 186 device->SetWindowTitle = Wayland_SetWindowTitle; 187 device->DestroyWindow = Wayland_DestroyWindow; 188 device->SetWindowHitTest = Wayland_SetWindowHitTest; 189 190 device->SetClipboardText = Wayland_SetClipboardText; 191 device->GetClipboardText = Wayland_GetClipboardText; 192 device->HasClipboardText = Wayland_HasClipboardText; 193 194#if SDL_VIDEO_VULKAN 195 device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary; 196 device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary; 197 device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions; 198 device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface; 199#endif 200 201 device->free = Wayland_DeleteDevice; 202 203 return device; 204} 205 206VideoBootStrap Wayland_bootstrap = { 207 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver", 208 Wayland_Available, Wayland_CreateDevice 209}; 210 211static void 212display_handle_geometry(void *data, 213 struct wl_output *output, 214 int x, int y, 215 int physical_width, 216 int physical_height, 217 int subpixel, 218 const char *make, 219 const char *model, 220 int transform) 221 222{ 223 SDL_VideoDisplay *display = data; 224 225 display->name = SDL_strdup(model); 226 display->driverdata = output; 227} 228 229static void 230display_handle_mode(void *data, 231 struct wl_output *output, 232 uint32_t flags, 233 int width, 234 int height, 235 int refresh) 236{ 237 SDL_VideoDisplay *display = data; 238 SDL_DisplayMode mode; 239 240 SDL_zero(mode); 241 mode.format = SDL_PIXELFORMAT_RGB888; 242 mode.w = width; 243 mode.h = height; 244 mode.refresh_rate = refresh / 1000; // mHz to Hz 245 mode.driverdata = display->driverdata; 246 SDL_AddDisplayMode(display, &mode); 247 248 if (flags & WL_OUTPUT_MODE_CURRENT) { 249 display->current_mode = mode; 250 display->desktop_mode = mode; 251 } 252} 253 254static void 255display_handle_done(void *data, 256 struct wl_output *output) 257{ 258 SDL_VideoDisplay *display = data; 259 SDL_AddVideoDisplay(display); 260 SDL_free(display->name); 261 SDL_free(display); 262} 263 264static void 265display_handle_scale(void *data, 266 struct wl_output *output, 267 int32_t factor) 268{ 269 // TODO: do HiDPI stuff. 270} 271 272static const struct wl_output_listener output_listener = { 273 display_handle_geometry, 274 display_handle_mode, 275 display_handle_done, 276 display_handle_scale 277}; 278 279static void 280Wayland_add_display(SDL_VideoData *d, uint32_t id) 281{ 282 struct wl_output *output; 283 SDL_VideoDisplay *display = SDL_malloc(sizeof *display); 284 if (!display) { 285 SDL_OutOfMemory(); 286 return; 287 } 288 SDL_zero(*display); 289 290 output = wl_registry_bind(d->registry, id, &wl_output_interface, 2); 291 if (!output) { 292 SDL_SetError("Failed to retrieve output."); 293 SDL_free(display); 294 return; 295 } 296 297 wl_output_add_listener(output, &output_listener, display); 298} 299 300#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 301static void 302windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager, 303 int32_t show_is_fullscreen) 304{ 305} 306 307static void 308windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager) 309{ 310 SDL_SendQuit(); 311} 312 313static const struct qt_windowmanager_listener windowmanager_listener = { 314 windowmanager_hints, 315 windowmanager_quit, 316}; 317#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 318 319 320static void 321handle_ping_zxdg_shell(void *data, struct zxdg_shell_v6 *zxdg, uint32_t serial) 322{ 323 zxdg_shell_v6_pong(zxdg, serial); 324} 325 326static const struct zxdg_shell_v6_listener shell_listener_zxdg = { 327 handle_ping_zxdg_shell 328}; 329 330 331static void 332handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial) 333{ 334 xdg_wm_base_pong(xdg, serial); 335} 336 337static const struct xdg_wm_base_listener shell_listener_xdg = { 338 handle_ping_xdg_wm_base 339}; 340 341 342static void 343display_handle_global(void *data, struct wl_registry *registry, uint32_t id, 344 const char *interface, uint32_t version) 345{ 346 SDL_VideoData *d = data; 347 348 if (strcmp(interface, "wl_compositor") == 0) { 349 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1); 350 } else if (strcmp(interface, "wl_output") == 0) { 351 Wayland_add_display(d, id); 352 } else if (strcmp(interface, "wl_seat") == 0) { 353 Wayland_display_add_input(d, id); 354 } else if (strcmp(interface, "xdg_wm_base") == 0) { 355 d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1); 356 xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL); 357 } else if (strcmp(interface, "zxdg_shell_v6") == 0) { 358 d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1); 359 zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL); 360 } else if (strcmp(interface, "wl_shell") == 0) { 361 d->shell.wl = wl_registry_bind(d->registry, id, &wl_shell_interface, 1); 362 } else if (strcmp(interface, "wl_shm") == 0) { 363 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); 364 d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm); 365 } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { 366 Wayland_display_add_relative_pointer_manager(d, id); 367 } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { 368 Wayland_display_add_pointer_constraints(d, id); 369 } else if (strcmp(interface, "wl_data_device_manager") == 0) { 370 d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, 3); 371 372#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 373 } else if (strcmp(interface, "qt_touch_extension") == 0) { 374 Wayland_touch_create(d, id); 375 } else if (strcmp(interface, "qt_surface_extension") == 0) { 376 d->surface_extension = wl_registry_bind(registry, id, 377 &qt_surface_extension_interface, 1); 378 } else if (strcmp(interface, "qt_windowmanager") == 0) { 379 d->windowmanager = wl_registry_bind(registry, id, 380 &qt_windowmanager_interface, 1); 381 qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d); 382#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 383 } 384} 385 386static const struct wl_registry_listener registry_listener = { 387 display_handle_global, 388 NULL, /* global_remove */ 389}; 390 391int 392Wayland_VideoInit(_THIS) 393{ 394 SDL_VideoData *data = SDL_malloc(sizeof *data); 395 if (data == NULL) 396 return SDL_OutOfMemory(); 397 memset(data, 0, sizeof *data); 398 399 _this->driverdata = data; 400 401 data->xkb_context = WAYLAND_xkb_context_new(0); 402 if (!data->xkb_context) { 403 return SDL_SetError("Failed to create XKB context"); 404 } 405 406 data->display = WAYLAND_wl_display_connect(NULL); 407 if (data->display == NULL) { 408 return SDL_SetError("Failed to connect to a Wayland display"); 409 } 410 411 data->registry = wl_display_get_registry(data->display); 412 if (data->registry == NULL) { 413 return SDL_SetError("Failed to get the Wayland registry"); 414 } 415 416 wl_registry_add_listener(data->registry, ®istry_listener, data); 417 418 // First roundtrip to receive all registry objects. 419 WAYLAND_wl_display_roundtrip(data->display); 420 421 // Second roundtrip to receive all output events. 422 WAYLAND_wl_display_roundtrip(data->display); 423 424 Wayland_InitMouse(); 425 426 /* Get the surface class name, usually the name of the application */ 427 data->classname = get_classname(); 428 429 WAYLAND_wl_display_flush(data->display); 430 431 return 0; 432} 433 434static void 435Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display) 436{ 437 // Nothing to do here, everything was already done in the wl_output 438 // callbacks. 439} 440 441static int 442Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 443{ 444 return SDL_Unsupported(); 445} 446 447void 448Wayland_VideoQuit(_THIS) 449{ 450 SDL_VideoData *data = _this->driverdata; 451 int i, j; 452 453 Wayland_FiniMouse (); 454 455 for (i = 0; i < _this->num_displays; ++i) { 456 SDL_VideoDisplay *display = &_this->displays[i]; 457 wl_output_destroy(display->driverdata); 458 display->driverdata = NULL; 459 460 for (j = display->num_display_modes; j--;) { 461 display->display_modes[j].driverdata = NULL; 462 } 463 display->desktop_mode.driverdata = NULL; 464 } 465 466 Wayland_display_destroy_input(data); 467 Wayland_display_destroy_pointer_constraints(data); 468 Wayland_display_destroy_relative_pointer_manager(data); 469 470 if (data->xkb_context) { 471 WAYLAND_xkb_context_unref(data->xkb_context); 472 data->xkb_context = NULL; 473 } 474#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 475 if (data->windowmanager) 476 qt_windowmanager_destroy(data->windowmanager); 477 478 if (data->surface_extension) 479 qt_surface_extension_destroy(data->surface_extension); 480 481 Wayland_touch_destroy(data); 482#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 483 484 if (data->shm) 485 wl_shm_destroy(data->shm); 486 487 if (data->cursor_theme) 488 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme); 489 490 if (data->shell.wl) 491 wl_shell_destroy(data->shell.wl); 492 493 if (data->shell.xdg) 494 xdg_wm_base_destroy(data->shell.xdg); 495 496 if (data->shell.zxdg) 497 zxdg_shell_v6_destroy(data->shell.zxdg); 498 499 if (data->compositor) 500 wl_compositor_destroy(data->compositor); 501 502 if (data->registry) 503 wl_registry_destroy(data->registry); 504 505 if (data->display) { 506 WAYLAND_wl_display_flush(data->display); 507 WAYLAND_wl_display_disconnect(data->display); 508 } 509 510 SDL_free(data->classname); 511 SDL_free(data); 512 _this->driverdata = NULL; 513} 514 515#endif /* SDL_VIDEO_DRIVER_WAYLAND */ 516 517/* vi: set ts=4 sw=4 expandtab: */ 518[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.