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