src/video/wayland/SDL_waylandvideo.c
author Sylvain Becker
Tue, 27 Oct 2020 21:14:49 +0100
changeset 14207 7225f02f0055
parent 14098 cf4753b7c95a
permissions -rw-r--r--
Android: apply code simplications found with lint / Android Studio
     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: */