src/video/wayland/SDL_waylandvideo.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 24 Sep 2018 11:49:25 -0700
changeset 12201 8bdc4d340419
parent 12033 bc702c5a7c3a
child 12381 dc9108cd4340
permissions -rw-r--r--
Fixed whitespace
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 
    51 #define WAYLANDVID_DRIVER_NAME "wayland"
    52 
    53 /* Initialization/Query functions */
    54 static int
    55 Wayland_VideoInit(_THIS);
    56 
    57 static void
    58 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
    59 static int
    60 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
    61 
    62 static void
    63 Wayland_VideoQuit(_THIS);
    64 
    65 /* Find out what class name we should use
    66  * Based on src/video/x11/SDL_x11video.c */
    67 static char *
    68 get_classname()
    69 {
    70 /* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec:
    71     "The surface class identifies the general class of applications
    72     to which the surface belongs. A common convention is to use the
    73     file name (or the full path if it is a non-standard location) of
    74     the application's .desktop file as the class." */
    75 
    76     char *spot;
    77 #if defined(__LINUX__) || defined(__FREEBSD__)
    78     char procfile[1024];
    79     char linkfile[1024];
    80     int linksize;
    81 #endif
    82 
    83     /* First allow environment variable override */
    84     spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
    85     if (spot) {
    86         return SDL_strdup(spot);
    87     } else {
    88         /* Fallback to the "old" envvar */
    89         spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
    90         if (spot) {
    91             return SDL_strdup(spot);
    92         }
    93     }
    94 
    95     /* Next look at the application's executable name */
    96 #if defined(__LINUX__) || defined(__FREEBSD__)
    97 #if defined(__LINUX__)
    98     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
    99 #elif defined(__FREEBSD__)
   100     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
   101                  getpid());
   102 #else
   103 #error Where can we find the executable name?
   104 #endif
   105     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
   106     if (linksize > 0) {
   107         linkfile[linksize] = '\0';
   108         spot = SDL_strrchr(linkfile, '/');
   109         if (spot) {
   110             return SDL_strdup(spot + 1);
   111         } else {
   112             return SDL_strdup(linkfile);
   113         }
   114     }
   115 #endif /* __LINUX__ || __FREEBSD__ */
   116 
   117     /* Finally use the default we've used forever */
   118     return SDL_strdup("SDL_App");
   119 }
   120 
   121 /* Wayland driver bootstrap functions */
   122 static int
   123 Wayland_Available(void)
   124 {
   125     struct wl_display *display = NULL;
   126     if (SDL_WAYLAND_LoadSymbols()) {
   127         display = WAYLAND_wl_display_connect(NULL);
   128         if (display != NULL) {
   129             WAYLAND_wl_display_disconnect(display);
   130         }
   131         SDL_WAYLAND_UnloadSymbols();
   132     }
   133 
   134     return (display != NULL);
   135 }
   136 
   137 static void
   138 Wayland_DeleteDevice(SDL_VideoDevice *device)
   139 {
   140     SDL_free(device);
   141     SDL_WAYLAND_UnloadSymbols();
   142 }
   143 
   144 static SDL_VideoDevice *
   145 Wayland_CreateDevice(int devindex)
   146 {
   147     SDL_VideoDevice *device;
   148 
   149     if (!SDL_WAYLAND_LoadSymbols()) {
   150         return NULL;
   151     }
   152 
   153     /* Initialize all variables that we clean on shutdown */
   154     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
   155     if (!device) {
   156         SDL_WAYLAND_UnloadSymbols();
   157         SDL_OutOfMemory();
   158         return NULL;
   159     }
   160 
   161     /* Set the function pointers */
   162     device->VideoInit = Wayland_VideoInit;
   163     device->VideoQuit = Wayland_VideoQuit;
   164     device->SetDisplayMode = Wayland_SetDisplayMode;
   165     device->GetDisplayModes = Wayland_GetDisplayModes;
   166     device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
   167 
   168     device->PumpEvents = Wayland_PumpEvents;
   169 
   170     device->GL_SwapWindow = Wayland_GLES_SwapWindow;
   171     device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
   172     device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
   173     device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
   174     device->GL_CreateContext = Wayland_GLES_CreateContext;
   175     device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
   176     device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
   177     device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
   178     device->GL_DeleteContext = Wayland_GLES_DeleteContext;
   179 
   180     device->CreateSDLWindow = Wayland_CreateWindow;
   181     device->ShowWindow = Wayland_ShowWindow;
   182     device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
   183     device->MaximizeWindow = Wayland_MaximizeWindow;
   184     device->RestoreWindow = Wayland_RestoreWindow;
   185     device->SetWindowSize = Wayland_SetWindowSize;
   186     device->SetWindowTitle = Wayland_SetWindowTitle;
   187     device->DestroyWindow = Wayland_DestroyWindow;
   188     device->SetWindowHitTest = Wayland_SetWindowHitTest;
   189 
   190     device->SetClipboardText = Wayland_SetClipboardText;
   191     device->GetClipboardText = Wayland_GetClipboardText;
   192     device->HasClipboardText = Wayland_HasClipboardText;
   193 
   194 #if SDL_VIDEO_VULKAN
   195     device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary;
   196     device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
   197     device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions;
   198     device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface;
   199 #endif
   200 
   201     device->free = Wayland_DeleteDevice;
   202 
   203     return device;
   204 }
   205 
   206 VideoBootStrap Wayland_bootstrap = {
   207     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
   208     Wayland_Available, Wayland_CreateDevice
   209 };
   210 
   211 static void
   212 display_handle_geometry(void *data,
   213                         struct wl_output *output,
   214                         int x, int y,
   215                         int physical_width,
   216                         int physical_height,
   217                         int subpixel,
   218                         const char *make,
   219                         const char *model,
   220                         int transform)
   221 
   222 {
   223     SDL_VideoDisplay *display = data;
   224 
   225     display->name = SDL_strdup(model);
   226     display->driverdata = output;
   227 }
   228 
   229 static void
   230 display_handle_mode(void *data,
   231                     struct wl_output *output,
   232                     uint32_t flags,
   233                     int width,
   234                     int height,
   235                     int refresh)
   236 {
   237     SDL_VideoDisplay *display = data;
   238     SDL_DisplayMode mode;
   239 
   240     SDL_zero(mode);
   241     mode.format = SDL_PIXELFORMAT_RGB888;
   242     mode.w = width;
   243     mode.h = height;
   244     mode.refresh_rate = refresh / 1000; // mHz to Hz
   245     mode.driverdata = display->driverdata;
   246     SDL_AddDisplayMode(display, &mode);
   247 
   248     if (flags & WL_OUTPUT_MODE_CURRENT) {
   249         display->current_mode = mode;
   250         display->desktop_mode = mode;
   251     }
   252 }
   253 
   254 static void
   255 display_handle_done(void *data,
   256                     struct wl_output *output)
   257 {
   258     SDL_VideoDisplay *display = data;
   259     SDL_AddVideoDisplay(display);
   260     SDL_free(display->name);
   261     SDL_free(display);
   262 }
   263 
   264 static void
   265 display_handle_scale(void *data,
   266                      struct wl_output *output,
   267                      int32_t factor)
   268 {
   269     // TODO: do HiDPI stuff.
   270 }
   271 
   272 static const struct wl_output_listener output_listener = {
   273     display_handle_geometry,
   274     display_handle_mode,
   275     display_handle_done,
   276     display_handle_scale
   277 };
   278 
   279 static void
   280 Wayland_add_display(SDL_VideoData *d, uint32_t id)
   281 {
   282     struct wl_output *output;
   283     SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
   284     if (!display) {
   285         SDL_OutOfMemory();
   286         return;
   287     }
   288     SDL_zero(*display);
   289 
   290     output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
   291     if (!output) {
   292         SDL_SetError("Failed to retrieve output.");
   293         SDL_free(display);
   294         return;
   295     }
   296 
   297     wl_output_add_listener(output, &output_listener, display);
   298 }
   299 
   300 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   301 static void
   302 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
   303         int32_t show_is_fullscreen)
   304 {
   305 }
   306 
   307 static void
   308 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
   309 {
   310     SDL_SendQuit();
   311 }
   312 
   313 static const struct qt_windowmanager_listener windowmanager_listener = {
   314     windowmanager_hints,
   315     windowmanager_quit,
   316 };
   317 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   318 
   319 
   320 static void
   321 handle_ping_zxdg_shell(void *data, struct zxdg_shell_v6 *zxdg, uint32_t serial)
   322 {
   323     zxdg_shell_v6_pong(zxdg, serial);
   324 }
   325 
   326 static const struct zxdg_shell_v6_listener shell_listener_zxdg = {
   327     handle_ping_zxdg_shell
   328 };
   329 
   330 
   331 static void
   332 handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
   333 {
   334     xdg_wm_base_pong(xdg, serial);
   335 }
   336 
   337 static const struct xdg_wm_base_listener shell_listener_xdg = {
   338     handle_ping_xdg_wm_base
   339 };
   340 
   341 
   342 static void
   343 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
   344                       const char *interface, uint32_t version)
   345 {
   346     SDL_VideoData *d = data;
   347 
   348     if (strcmp(interface, "wl_compositor") == 0) {
   349         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
   350     } else if (strcmp(interface, "wl_output") == 0) {
   351         Wayland_add_display(d, id);
   352     } else if (strcmp(interface, "wl_seat") == 0) {
   353         Wayland_display_add_input(d, id);
   354     } else if (strcmp(interface, "xdg_wm_base") == 0) {
   355         d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1);
   356         xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL);
   357     } else if (strcmp(interface, "zxdg_shell_v6") == 0) {
   358         d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1);
   359         zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL);
   360     } else if (strcmp(interface, "wl_shell") == 0) {
   361         d->shell.wl = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
   362     } else if (strcmp(interface, "wl_shm") == 0) {
   363         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
   364         d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
   365     } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
   366         Wayland_display_add_relative_pointer_manager(d, id);
   367     } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
   368         Wayland_display_add_pointer_constraints(d, id);
   369     } else if (strcmp(interface, "wl_data_device_manager") == 0) {
   370         d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, 3);
   371 
   372 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   373     } else if (strcmp(interface, "qt_touch_extension") == 0) {
   374         Wayland_touch_create(d, id);
   375     } else if (strcmp(interface, "qt_surface_extension") == 0) {
   376         d->surface_extension = wl_registry_bind(registry, id,
   377                 &qt_surface_extension_interface, 1);
   378     } else if (strcmp(interface, "qt_windowmanager") == 0) {
   379         d->windowmanager = wl_registry_bind(registry, id,
   380                 &qt_windowmanager_interface, 1);
   381         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
   382 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   383     }
   384 }
   385 
   386 static const struct wl_registry_listener registry_listener = {
   387     display_handle_global,
   388     NULL, /* global_remove */
   389 };
   390 
   391 int
   392 Wayland_VideoInit(_THIS)
   393 {
   394     SDL_VideoData *data = SDL_malloc(sizeof *data);
   395     if (data == NULL)
   396         return SDL_OutOfMemory();
   397     memset(data, 0, sizeof *data);
   398 
   399     _this->driverdata = data;
   400 
   401     data->xkb_context = WAYLAND_xkb_context_new(0);
   402     if (!data->xkb_context) {
   403         return SDL_SetError("Failed to create XKB context");
   404     }
   405 
   406     data->display = WAYLAND_wl_display_connect(NULL);
   407     if (data->display == NULL) {
   408         return SDL_SetError("Failed to connect to a Wayland display");
   409     }
   410 
   411     data->registry = wl_display_get_registry(data->display);
   412     if (data->registry == NULL) {
   413         return SDL_SetError("Failed to get the Wayland registry");
   414     }
   415 
   416     wl_registry_add_listener(data->registry, &registry_listener, data);
   417 
   418     // First roundtrip to receive all registry objects.
   419     WAYLAND_wl_display_roundtrip(data->display);
   420 
   421     // Second roundtrip to receive all output events.
   422     WAYLAND_wl_display_roundtrip(data->display);
   423 
   424     Wayland_InitMouse();
   425 
   426     /* Get the surface class name, usually the name of the application */
   427     data->classname = get_classname();
   428 
   429     WAYLAND_wl_display_flush(data->display);
   430 
   431     return 0;
   432 }
   433 
   434 static void
   435 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
   436 {
   437     // Nothing to do here, everything was already done in the wl_output
   438     // callbacks.
   439 }
   440 
   441 static int
   442 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
   443 {
   444     return SDL_Unsupported();
   445 }
   446 
   447 void
   448 Wayland_VideoQuit(_THIS)
   449 {
   450     SDL_VideoData *data = _this->driverdata;
   451     int i, j;
   452 
   453     Wayland_FiniMouse ();
   454 
   455     for (i = 0; i < _this->num_displays; ++i) {
   456         SDL_VideoDisplay *display = &_this->displays[i];
   457         wl_output_destroy(display->driverdata);
   458         display->driverdata = NULL;
   459 
   460         for (j = display->num_display_modes; j--;) {
   461             display->display_modes[j].driverdata = NULL;
   462         }
   463         display->desktop_mode.driverdata = NULL;
   464     }
   465 
   466     Wayland_display_destroy_input(data);
   467     Wayland_display_destroy_pointer_constraints(data);
   468     Wayland_display_destroy_relative_pointer_manager(data);
   469 
   470     if (data->xkb_context) {
   471         WAYLAND_xkb_context_unref(data->xkb_context);
   472         data->xkb_context = NULL;
   473     }
   474 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   475     if (data->windowmanager)
   476         qt_windowmanager_destroy(data->windowmanager);
   477 
   478     if (data->surface_extension)
   479         qt_surface_extension_destroy(data->surface_extension);
   480 
   481     Wayland_touch_destroy(data);
   482 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   483 
   484     if (data->shm)
   485         wl_shm_destroy(data->shm);
   486 
   487     if (data->cursor_theme)
   488         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
   489 
   490     if (data->shell.wl)
   491         wl_shell_destroy(data->shell.wl);
   492 
   493     if (data->shell.xdg)
   494         xdg_wm_base_destroy(data->shell.xdg);
   495 
   496     if (data->shell.zxdg)
   497         zxdg_shell_v6_destroy(data->shell.zxdg);
   498 
   499     if (data->compositor)
   500         wl_compositor_destroy(data->compositor);
   501 
   502     if (data->registry)
   503         wl_registry_destroy(data->registry);
   504 
   505     if (data->display) {
   506         WAYLAND_wl_display_flush(data->display);
   507         WAYLAND_wl_display_disconnect(data->display);
   508     }
   509 
   510     SDL_free(data->classname);
   511     SDL_free(data);
   512     _this->driverdata = NULL;
   513 }
   514 
   515 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   516 
   517 /* vi: set ts=4 sw=4 expandtab: */