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, &registry_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