Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / wayland / SDL_waylandvideo.c
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, &registry_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: */