2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #include "../../SDL_internal.h"
24 #if SDL_VIDEO_DRIVER_WAYLAND
26 #include "SDL_video.h"
27 #include "SDL_mouse.h"
28 #include "SDL_stdinc.h"
29 #include "../../events/SDL_events_c.h"
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"
40 #include <sys/types.h>
43 #include <xkbcommon/xkbcommon.h>
45 #include "SDL_waylanddyn.h"
46 #include <wayland-util.h>
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"
53 #define WAYLANDVID_DRIVER_NAME "wayland"
55 /* Initialization/Query functions */
57 Wayland_VideoInit(_THIS);
60 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
62 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
65 Wayland_VideoQuit(_THIS);
67 /* Find out what class name we should use
68 * Based on src/video/x11/SDL_x11video.c */
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." */
79 #if defined(__LINUX__) || defined(__FREEBSD__)
85 /* First allow environment variable override */
86 spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
88 return SDL_strdup(spot);
90 /* Fallback to the "old" envvar */
91 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
93 return SDL_strdup(spot);
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",
105 #error Where can we find the executable name?
107 linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
109 linkfile[linksize] = '\0';
110 spot = SDL_strrchr(linkfile, '/');
112 return SDL_strdup(spot + 1);
114 return SDL_strdup(linkfile);
117 #endif /* __LINUX__ || __FREEBSD__ */
119 /* Finally use the default we've used forever */
120 return SDL_strdup("SDL_App");
124 Wayland_DeleteDevice(SDL_VideoDevice *device)
126 SDL_VideoData *data = (SDL_VideoData *)device->driverdata;
128 WAYLAND_wl_display_flush(data->display);
129 WAYLAND_wl_display_disconnect(data->display);
133 SDL_WAYLAND_UnloadSymbols();
136 static SDL_VideoDevice *
137 Wayland_CreateDevice(int devindex)
139 SDL_VideoDevice *device;
141 struct wl_display *display;
143 if (!SDL_WAYLAND_LoadSymbols()) {
147 display = WAYLAND_wl_display_connect(NULL);
148 if (display == NULL) {
149 SDL_WAYLAND_UnloadSymbols();
153 data = SDL_calloc(1, sizeof(*data));
155 WAYLAND_wl_display_disconnect(display);
156 SDL_WAYLAND_UnloadSymbols();
161 data->display = display;
163 /* Initialize all variables that we clean on shutdown */
164 device = SDL_calloc(1, sizeof(SDL_VideoDevice));
167 WAYLAND_wl_display_disconnect(display);
168 SDL_WAYLAND_UnloadSymbols();
173 device->driverdata = data;
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;
182 device->PumpEvents = Wayland_PumpEvents;
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;
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;
207 device->SetClipboardText = Wayland_SetClipboardText;
208 device->GetClipboardText = Wayland_GetClipboardText;
209 device->HasClipboardText = Wayland_HasClipboardText;
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;
219 device->free = Wayland_DeleteDevice;
224 VideoBootStrap Wayland_bootstrap = {
225 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
230 display_handle_geometry(void *data,
231 struct wl_output *output,
241 SDL_VideoDisplay *display = data;
243 display->name = SDL_strdup(model);
247 display_handle_mode(void *data,
248 struct wl_output *output,
254 SDL_DisplayMode mode;
255 SDL_VideoDisplay *display = data;
258 mode.format = SDL_PIXELFORMAT_RGB888;
261 mode.refresh_rate = refresh / 1000; // mHz to Hz
262 mode.driverdata = ((SDL_WaylandOutputData*)display->driverdata)->output;
263 SDL_AddDisplayMode(display, &mode);
265 if (flags & WL_OUTPUT_MODE_CURRENT) {
266 display->current_mode = mode;
267 display->desktop_mode = mode;
272 display_handle_done(void *data,
273 struct wl_output *output)
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);
284 display_handle_scale(void *data,
285 struct wl_output *output,
288 SDL_VideoDisplay *display = data;
289 ((SDL_WaylandOutputData*)display->driverdata)->scale_factor = factor;
292 static const struct wl_output_listener output_listener = {
293 display_handle_geometry,
300 Wayland_add_display(SDL_VideoData *d, uint32_t id)
302 struct wl_output *output;
303 SDL_WaylandOutputData *data;
304 SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
311 output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
313 SDL_SetError("Failed to retrieve output.");
317 data = SDL_malloc(sizeof *data);
318 data->output = output;
319 data->scale_factor = 1.0;
320 display->driverdata = data;
322 wl_output_add_listener(output, &output_listener, display);
325 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
327 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
328 int32_t show_is_fullscreen)
333 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
338 static const struct qt_windowmanager_listener windowmanager_listener = {
342 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
346 handle_ping_zxdg_shell(void *data, struct zxdg_shell_v6 *zxdg, uint32_t serial)
348 zxdg_shell_v6_pong(zxdg, serial);
351 static const struct zxdg_shell_v6_listener shell_listener_zxdg = {
352 handle_ping_zxdg_shell
357 handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
359 xdg_wm_base_pong(xdg, serial);
362 static const struct xdg_wm_base_listener shell_listener_xdg = {
363 handle_ping_xdg_wm_base
368 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
369 const char *interface, uint32_t version)
371 SDL_VideoData *d = data;
373 /*printf("WAYLAND INTERFACE: %s\n", interface);*/
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);
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 */
418 display_remove_global(void *data, struct wl_registry *registry, uint32_t id) {}
420 static const struct wl_registry_listener registry_listener = {
421 display_handle_global,
422 display_remove_global
426 Wayland_VideoInit(_THIS)
428 SDL_VideoData *data = (SDL_VideoData*)_this->driverdata;
430 data->xkb_context = WAYLAND_xkb_context_new(0);
431 if (!data->xkb_context) {
432 return SDL_SetError("Failed to create XKB context");
435 data->registry = wl_display_get_registry(data->display);
436 if (data->registry == NULL) {
437 return SDL_SetError("Failed to get the Wayland registry");
440 wl_registry_add_listener(data->registry, ®istry_listener, data);
442 // First roundtrip to receive all registry objects.
443 WAYLAND_wl_display_roundtrip(data->display);
445 // Second roundtrip to receive all output events.
446 WAYLAND_wl_display_roundtrip(data->display);
450 /* Get the surface class name, usually the name of the application */
451 data->classname = get_classname();
453 WAYLAND_wl_display_flush(data->display);
459 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
461 // Nothing to do here, everything was already done in the wl_output
466 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
468 return SDL_Unsupported();
472 Wayland_VideoQuit(_THIS)
474 SDL_VideoData *data = _this->driverdata;
477 Wayland_FiniMouse ();
479 for (i = 0; i < _this->num_displays; ++i) {
480 SDL_VideoDisplay *display = &_this->displays[i];
482 wl_output_destroy(((SDL_WaylandOutputData*)display->driverdata)->output);
483 SDL_free(display->driverdata);
484 display->driverdata = NULL;
486 for (j = display->num_display_modes; j--;) {
487 display->display_modes[j].driverdata = NULL;
489 display->desktop_mode.driverdata = NULL;
492 Wayland_display_destroy_input(data);
493 Wayland_display_destroy_pointer_constraints(data);
494 Wayland_display_destroy_relative_pointer_manager(data);
496 if (data->xkb_context) {
497 WAYLAND_xkb_context_unref(data->xkb_context);
498 data->xkb_context = NULL;
500 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
501 if (data->windowmanager)
502 qt_windowmanager_destroy(data->windowmanager);
504 if (data->surface_extension)
505 qt_surface_extension_destroy(data->surface_extension);
507 Wayland_touch_destroy(data);
508 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
511 wl_shm_destroy(data->shm);
513 if (data->cursor_theme)
514 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
517 wl_shell_destroy(data->shell.wl);
520 xdg_wm_base_destroy(data->shell.xdg);
522 if (data->shell.zxdg)
523 zxdg_shell_v6_destroy(data->shell.zxdg);
525 if (data->compositor)
526 wl_compositor_destroy(data->compositor);
529 wl_registry_destroy(data->registry);
531 SDL_free(data->classname);
534 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
536 /* vi: set ts=4 sw=4 expandtab: */