SDL_waylandvideo.c (16838B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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 #include "xdg-decoration-unstable-v1-client-protocol.h" 51 #include "org-kde-kwin-server-decoration-manager-client-protocol.h" 52 53 #define WAYLANDVID_DRIVER_NAME "wayland" 54 55 /* Initialization/Query functions */ 56 static int 57 Wayland_VideoInit(_THIS); 58 59 static void 60 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display); 61 static int 62 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode); 63 64 static void 65 Wayland_VideoQuit(_THIS); 66 67 /* Find out what class name we should use 68 * Based on src/video/x11/SDL_x11video.c */ 69 static char * 70 get_classname() 71 { 72 /* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec: 73 "The surface class identifies the general class of applications 74 to which the surface belongs. A common convention is to use the 75 file name (or the full path if it is a non-standard location) of 76 the application's .desktop file as the class." */ 77 78 char *spot; 79 #if defined(__LINUX__) || defined(__FREEBSD__) 80 char procfile[1024]; 81 char linkfile[1024]; 82 int linksize; 83 #endif 84 85 /* First allow environment variable override */ 86 spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS"); 87 if (spot) { 88 return SDL_strdup(spot); 89 } else { 90 /* Fallback to the "old" envvar */ 91 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS"); 92 if (spot) { 93 return SDL_strdup(spot); 94 } 95 } 96 97 /* Next look at the application's executable name */ 98 #if defined(__LINUX__) || defined(__FREEBSD__) 99 #if defined(__LINUX__) 100 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid()); 101 #elif defined(__FREEBSD__) 102 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", 103 getpid()); 104 #else 105 #error Where can we find the executable name? 106 #endif 107 linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1); 108 if (linksize > 0) { 109 linkfile[linksize] = '\0'; 110 spot = SDL_strrchr(linkfile, '/'); 111 if (spot) { 112 return SDL_strdup(spot + 1); 113 } else { 114 return SDL_strdup(linkfile); 115 } 116 } 117 #endif /* __LINUX__ || __FREEBSD__ */ 118 119 /* Finally use the default we've used forever */ 120 return SDL_strdup("SDL_App"); 121 } 122 123 static void 124 Wayland_DeleteDevice(SDL_VideoDevice *device) 125 { 126 SDL_VideoData *data = (SDL_VideoData *)device->driverdata; 127 if (data->display) { 128 WAYLAND_wl_display_flush(data->display); 129 WAYLAND_wl_display_disconnect(data->display); 130 } 131 SDL_free(data); 132 SDL_free(device); 133 SDL_WAYLAND_UnloadSymbols(); 134 } 135 136 static SDL_VideoDevice * 137 Wayland_CreateDevice(int devindex) 138 { 139 SDL_VideoDevice *device; 140 SDL_VideoData *data; 141 struct wl_display *display; 142 143 if (!SDL_WAYLAND_LoadSymbols()) { 144 return NULL; 145 } 146 147 display = WAYLAND_wl_display_connect(NULL); 148 if (display == NULL) { 149 SDL_WAYLAND_UnloadSymbols(); 150 return NULL; 151 } 152 153 data = SDL_calloc(1, sizeof(*data)); 154 if (data == NULL) { 155 WAYLAND_wl_display_disconnect(display); 156 SDL_WAYLAND_UnloadSymbols(); 157 SDL_OutOfMemory(); 158 return NULL; 159 } 160 161 data->display = display; 162 163 /* Initialize all variables that we clean on shutdown */ 164 device = SDL_calloc(1, sizeof(SDL_VideoDevice)); 165 if (!device) { 166 SDL_free(data); 167 WAYLAND_wl_display_disconnect(display); 168 SDL_WAYLAND_UnloadSymbols(); 169 SDL_OutOfMemory(); 170 return NULL; 171 } 172 173 device->driverdata = data; 174 175 /* Set the function pointers */ 176 device->VideoInit = Wayland_VideoInit; 177 device->VideoQuit = Wayland_VideoQuit; 178 device->SetDisplayMode = Wayland_SetDisplayMode; 179 device->GetDisplayModes = Wayland_GetDisplayModes; 180 device->GetWindowWMInfo = Wayland_GetWindowWMInfo; 181 182 device->PumpEvents = Wayland_PumpEvents; 183 184 device->GL_SwapWindow = Wayland_GLES_SwapWindow; 185 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval; 186 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval; 187 device->GL_GetDrawableSize = Wayland_GLES_GetDrawableSize; 188 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent; 189 device->GL_CreateContext = Wayland_GLES_CreateContext; 190 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary; 191 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary; 192 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress; 193 device->GL_DeleteContext = Wayland_GLES_DeleteContext; 194 195 device->CreateSDLWindow = Wayland_CreateWindow; 196 device->ShowWindow = Wayland_ShowWindow; 197 device->SetWindowFullscreen = Wayland_SetWindowFullscreen; 198 device->MaximizeWindow = Wayland_MaximizeWindow; 199 device->SetWindowGrab = Wayland_SetWindowGrab; 200 device->RestoreWindow = Wayland_RestoreWindow; 201 device->SetWindowBordered = Wayland_SetWindowBordered; 202 device->SetWindowSize = Wayland_SetWindowSize; 203 device->SetWindowTitle = Wayland_SetWindowTitle; 204 device->DestroyWindow = Wayland_DestroyWindow; 205 device->SetWindowHitTest = Wayland_SetWindowHitTest; 206 207 device->SetClipboardText = Wayland_SetClipboardText; 208 device->GetClipboardText = Wayland_GetClipboardText; 209 device->HasClipboardText = Wayland_HasClipboardText; 210 211 #if SDL_VIDEO_VULKAN 212 device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary; 213 device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary; 214 device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions; 215 device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface; 216 device->Vulkan_GetDrawableSize = Wayland_Vulkan_GetDrawableSize; 217 #endif 218 219 device->free = Wayland_DeleteDevice; 220 221 return device; 222 } 223 224 VideoBootStrap Wayland_bootstrap = { 225 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver", 226 Wayland_CreateDevice 227 }; 228 229 static void 230 display_handle_geometry(void *data, 231 struct wl_output *output, 232 int x, int y, 233 int physical_width, 234 int physical_height, 235 int subpixel, 236 const char *make, 237 const char *model, 238 int transform) 239 240 { 241 SDL_VideoDisplay *display = data; 242 243 display->name = SDL_strdup(model); 244 } 245 246 static void 247 display_handle_mode(void *data, 248 struct wl_output *output, 249 uint32_t flags, 250 int width, 251 int height, 252 int refresh) 253 { 254 SDL_DisplayMode mode; 255 SDL_VideoDisplay *display = data; 256 257 SDL_zero(mode); 258 mode.format = SDL_PIXELFORMAT_RGB888; 259 mode.w = width; 260 mode.h = height; 261 mode.refresh_rate = refresh / 1000; // mHz to Hz 262 mode.driverdata = ((SDL_WaylandOutputData*)display->driverdata)->output; 263 SDL_AddDisplayMode(display, &mode); 264 265 if (flags & WL_OUTPUT_MODE_CURRENT) { 266 display->current_mode = mode; 267 display->desktop_mode = mode; 268 } 269 } 270 271 static void 272 display_handle_done(void *data, 273 struct wl_output *output) 274 { 275 /* !!! FIXME: this will fail on any further property changes! */ 276 SDL_VideoDisplay *display = data; 277 SDL_AddVideoDisplay(display, SDL_FALSE); 278 wl_output_set_user_data(output, display->driverdata); 279 SDL_free(display->name); 280 SDL_free(display); 281 } 282 283 static void 284 display_handle_scale(void *data, 285 struct wl_output *output, 286 int32_t factor) 287 { 288 SDL_VideoDisplay *display = data; 289 ((SDL_WaylandOutputData*)display->driverdata)->scale_factor = factor; 290 } 291 292 static const struct wl_output_listener output_listener = { 293 display_handle_geometry, 294 display_handle_mode, 295 display_handle_done, 296 display_handle_scale 297 }; 298 299 static void 300 Wayland_add_display(SDL_VideoData *d, uint32_t id) 301 { 302 struct wl_output *output; 303 SDL_WaylandOutputData *data; 304 SDL_VideoDisplay *display = SDL_malloc(sizeof *display); 305 if (!display) { 306 SDL_OutOfMemory(); 307 return; 308 } 309 SDL_zero(*display); 310 311 output = wl_registry_bind(d->registry, id, &wl_output_interface, 2); 312 if (!output) { 313 SDL_SetError("Failed to retrieve output."); 314 SDL_free(display); 315 return; 316 } 317 data = SDL_malloc(sizeof *data); 318 data->output = output; 319 data->scale_factor = 1.0; 320 display->driverdata = data; 321 322 wl_output_add_listener(output, &output_listener, display); 323 } 324 325 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 326 static void 327 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager, 328 int32_t show_is_fullscreen) 329 { 330 } 331 332 static void 333 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager) 334 { 335 SDL_SendQuit(); 336 } 337 338 static const struct qt_windowmanager_listener windowmanager_listener = { 339 windowmanager_hints, 340 windowmanager_quit, 341 }; 342 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 343 344 345 static void 346 handle_ping_zxdg_shell(void *data, struct zxdg_shell_v6 *zxdg, uint32_t serial) 347 { 348 zxdg_shell_v6_pong(zxdg, serial); 349 } 350 351 static const struct zxdg_shell_v6_listener shell_listener_zxdg = { 352 handle_ping_zxdg_shell 353 }; 354 355 356 static void 357 handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial) 358 { 359 xdg_wm_base_pong(xdg, serial); 360 } 361 362 static const struct xdg_wm_base_listener shell_listener_xdg = { 363 handle_ping_xdg_wm_base 364 }; 365 366 367 static void 368 display_handle_global(void *data, struct wl_registry *registry, uint32_t id, 369 const char *interface, uint32_t version) 370 { 371 SDL_VideoData *d = data; 372 373 /*printf("WAYLAND INTERFACE: %s\n", interface);*/ 374 375 if (strcmp(interface, "wl_compositor") == 0) { 376 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(3, version)); 377 } else if (strcmp(interface, "wl_output") == 0) { 378 Wayland_add_display(d, id); 379 } else if (strcmp(interface, "wl_seat") == 0) { 380 Wayland_display_add_input(d, id, version); 381 } else if (strcmp(interface, "xdg_wm_base") == 0) { 382 d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1); 383 xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL); 384 } else if (strcmp(interface, "zxdg_shell_v6") == 0) { 385 d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1); 386 zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL); 387 } else if (strcmp(interface, "wl_shell") == 0) { 388 d->shell.wl = wl_registry_bind(d->registry, id, &wl_shell_interface, 1); 389 } else if (strcmp(interface, "wl_shm") == 0) { 390 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); 391 d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm); 392 } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { 393 Wayland_display_add_relative_pointer_manager(d, id); 394 } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { 395 Wayland_display_add_pointer_constraints(d, id); 396 } else if (strcmp(interface, "wl_data_device_manager") == 0) { 397 d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version)); 398 } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { 399 d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1); 400 } else if (strcmp(interface, "org_kde_kwin_server_decoration_manager") == 0) { 401 d->kwin_server_decoration_manager = wl_registry_bind(d->registry, id, &org_kde_kwin_server_decoration_manager_interface, 1); 402 403 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 404 } else if (strcmp(interface, "qt_touch_extension") == 0) { 405 Wayland_touch_create(d, id); 406 } else if (strcmp(interface, "qt_surface_extension") == 0) { 407 d->surface_extension = wl_registry_bind(registry, id, 408 &qt_surface_extension_interface, 1); 409 } else if (strcmp(interface, "qt_windowmanager") == 0) { 410 d->windowmanager = wl_registry_bind(registry, id, 411 &qt_windowmanager_interface, 1); 412 qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d); 413 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 414 } 415 } 416 417 static void 418 display_remove_global(void *data, struct wl_registry *registry, uint32_t id) {} 419 420 static const struct wl_registry_listener registry_listener = { 421 display_handle_global, 422 display_remove_global 423 }; 424 425 int 426 Wayland_VideoInit(_THIS) 427 { 428 SDL_VideoData *data = (SDL_VideoData*)_this->driverdata; 429 430 data->xkb_context = WAYLAND_xkb_context_new(0); 431 if (!data->xkb_context) { 432 return SDL_SetError("Failed to create XKB context"); 433 } 434 435 data->registry = wl_display_get_registry(data->display); 436 if (data->registry == NULL) { 437 return SDL_SetError("Failed to get the Wayland registry"); 438 } 439 440 wl_registry_add_listener(data->registry, ®istry_listener, data); 441 442 // First roundtrip to receive all registry objects. 443 WAYLAND_wl_display_roundtrip(data->display); 444 445 // Second roundtrip to receive all output events. 446 WAYLAND_wl_display_roundtrip(data->display); 447 448 Wayland_InitMouse(); 449 450 /* Get the surface class name, usually the name of the application */ 451 data->classname = get_classname(); 452 453 WAYLAND_wl_display_flush(data->display); 454 455 return 0; 456 } 457 458 static void 459 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display) 460 { 461 // Nothing to do here, everything was already done in the wl_output 462 // callbacks. 463 } 464 465 static int 466 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 467 { 468 return SDL_Unsupported(); 469 } 470 471 void 472 Wayland_VideoQuit(_THIS) 473 { 474 SDL_VideoData *data = _this->driverdata; 475 int i, j; 476 477 Wayland_FiniMouse (); 478 479 for (i = 0; i < _this->num_displays; ++i) { 480 SDL_VideoDisplay *display = &_this->displays[i]; 481 482 wl_output_destroy(((SDL_WaylandOutputData*)display->driverdata)->output); 483 SDL_free(display->driverdata); 484 display->driverdata = NULL; 485 486 for (j = display->num_display_modes; j--;) { 487 display->display_modes[j].driverdata = NULL; 488 } 489 display->desktop_mode.driverdata = NULL; 490 } 491 492 Wayland_display_destroy_input(data); 493 Wayland_display_destroy_pointer_constraints(data); 494 Wayland_display_destroy_relative_pointer_manager(data); 495 496 if (data->xkb_context) { 497 WAYLAND_xkb_context_unref(data->xkb_context); 498 data->xkb_context = NULL; 499 } 500 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 501 if (data->windowmanager) 502 qt_windowmanager_destroy(data->windowmanager); 503 504 if (data->surface_extension) 505 qt_surface_extension_destroy(data->surface_extension); 506 507 Wayland_touch_destroy(data); 508 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 509 510 if (data->shm) 511 wl_shm_destroy(data->shm); 512 513 if (data->cursor_theme) 514 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme); 515 516 if (data->shell.wl) 517 wl_shell_destroy(data->shell.wl); 518 519 if (data->shell.xdg) 520 xdg_wm_base_destroy(data->shell.xdg); 521 522 if (data->shell.zxdg) 523 zxdg_shell_v6_destroy(data->shell.zxdg); 524 525 if (data->compositor) 526 wl_compositor_destroy(data->compositor); 527 528 if (data->registry) 529 wl_registry_destroy(data->registry); 530 531 SDL_free(data->classname); 532 } 533 534 #endif /* SDL_VIDEO_DRIVER_WAYLAND */ 535 536 /* vi: set ts=4 sw=4 expandtab: */