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