src/video/wayland/SDL_waylandvideo.c
author Bastien Nocera
Thu, 01 Sep 2016 01:22:58 -0700
changeset 10302 729eff9ee77a
parent 10187 1f0f8c95531d
child 10304 ee83e0b4a36f
permissions -rw-r--r--
Wayland: Set "class" for each window we create
This will be used by Wayland compositors to match the application ID and
.desktop file to the SDL window(s).

Applications can set the SDL_VIDEO_WAYLAND_WMCLASS environemnt variable
early in the process to override using the binary name as a fallback.

Note that we also support the SDL_VIDEO_X11_WMCLASS in the Wayland
backend so that if a program correctly associated the desktop file with
the window under X11, only a newer SDL would be needed for it to work
under Wayland.

https://bugzilla.libsdl.org/show_bug.cgi?id=3376
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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 
    38 #include <sys/types.h>
    39 #include <unistd.h>
    40 #include <fcntl.h>
    41 #include <xkbcommon/xkbcommon.h>
    42 
    43 #include "SDL_waylanddyn.h"
    44 #include <wayland-util.h>
    45 
    46 #define WAYLANDVID_DRIVER_NAME "wayland"
    47 
    48 /* Initialization/Query functions */
    49 static int
    50 Wayland_VideoInit(_THIS);
    51 
    52 static void
    53 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
    54 static int
    55 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
    56 
    57 static void
    58 Wayland_VideoQuit(_THIS);
    59 
    60 /* Find out what class name we should use
    61  * Based on src/video/x11/SDL_x11video.c */
    62 static char *
    63 get_classname()
    64 {
    65     char *spot;
    66 #if defined(__LINUX__) || defined(__FREEBSD__)
    67     char procfile[1024];
    68     char linkfile[1024];
    69     int linksize;
    70 #endif
    71 
    72     /* First allow environment variable override */
    73     spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
    74     if (spot) {
    75         return SDL_strdup(spot);
    76     } else {
    77         /* Fallback to the "old" envvar */
    78         spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
    79         if (spot) {
    80             return SDL_strdup(spot);
    81         }
    82     }
    83 
    84     /* Next look at the application's executable name */
    85 #if defined(__LINUX__) || defined(__FREEBSD__)
    86 #if defined(__LINUX__)
    87     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
    88 #elif defined(__FREEBSD__)
    89     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
    90                  getpid());
    91 #else
    92 #error Where can we find the executable name?
    93 #endif
    94     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
    95     if (linksize > 0) {
    96         linkfile[linksize] = '\0';
    97         spot = SDL_strrchr(linkfile, '/');
    98         if (spot) {
    99             return SDL_strdup(spot + 1);
   100         } else {
   101             return SDL_strdup(linkfile);
   102         }
   103     }
   104 #endif /* __LINUX__ || __FREEBSD__ */
   105 
   106     /* Finally use the default we've used forever */
   107     return SDL_strdup("SDL_App");
   108 }
   109 
   110 /* Wayland driver bootstrap functions */
   111 static int
   112 Wayland_Available(void)
   113 {
   114     struct wl_display *display = NULL;
   115     if (SDL_WAYLAND_LoadSymbols()) {
   116         display = WAYLAND_wl_display_connect(NULL);
   117         if (display != NULL) {
   118             WAYLAND_wl_display_disconnect(display);
   119         }
   120         SDL_WAYLAND_UnloadSymbols();
   121     }
   122 
   123     return (display != NULL);
   124 }
   125 
   126 static void
   127 Wayland_DeleteDevice(SDL_VideoDevice *device)
   128 {
   129     SDL_free(device);
   130     SDL_WAYLAND_UnloadSymbols();
   131 }
   132 
   133 static SDL_VideoDevice *
   134 Wayland_CreateDevice(int devindex)
   135 {
   136     SDL_VideoDevice *device;
   137 
   138     if (!SDL_WAYLAND_LoadSymbols()) {
   139         return NULL;
   140     }
   141 
   142     /* Initialize all variables that we clean on shutdown */
   143     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
   144     if (!device) {
   145         SDL_WAYLAND_UnloadSymbols();
   146         SDL_OutOfMemory();
   147         return NULL;
   148     }
   149 
   150     /* Set the function pointers */
   151     device->VideoInit = Wayland_VideoInit;
   152     device->VideoQuit = Wayland_VideoQuit;
   153     device->SetDisplayMode = Wayland_SetDisplayMode;
   154     device->GetDisplayModes = Wayland_GetDisplayModes;
   155     device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
   156 
   157     device->PumpEvents = Wayland_PumpEvents;
   158 
   159     device->GL_SwapWindow = Wayland_GLES_SwapWindow;
   160     device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
   161     device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
   162     device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
   163     device->GL_CreateContext = Wayland_GLES_CreateContext;
   164     device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
   165     device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
   166     device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
   167     device->GL_DeleteContext = Wayland_GLES_DeleteContext;
   168 
   169     device->CreateWindow = Wayland_CreateWindow;
   170     device->ShowWindow = Wayland_ShowWindow;
   171     device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
   172     device->SetWindowSize = Wayland_SetWindowSize;
   173     device->DestroyWindow = Wayland_DestroyWindow;
   174     device->SetWindowHitTest = Wayland_SetWindowHitTest;
   175 
   176     device->free = Wayland_DeleteDevice;
   177 
   178     return device;
   179 }
   180 
   181 VideoBootStrap Wayland_bootstrap = {
   182     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
   183     Wayland_Available, Wayland_CreateDevice
   184 };
   185 
   186 static void
   187 display_handle_geometry(void *data,
   188                         struct wl_output *output,
   189                         int x, int y,
   190                         int physical_width,
   191                         int physical_height,
   192                         int subpixel,
   193                         const char *make,
   194                         const char *model,
   195                         int transform)
   196 
   197 {
   198     SDL_VideoDisplay *display = data;
   199 
   200     display->name = SDL_strdup(model);
   201     display->driverdata = output;
   202 }
   203 
   204 static void
   205 display_handle_mode(void *data,
   206                     struct wl_output *output,
   207                     uint32_t flags,
   208                     int width,
   209                     int height,
   210                     int refresh)
   211 {
   212     SDL_VideoDisplay *display = data;
   213     SDL_DisplayMode mode;
   214 
   215     SDL_zero(mode);
   216     mode.w = width;
   217     mode.h = height;
   218     mode.refresh_rate = refresh / 1000; // mHz to Hz
   219     SDL_AddDisplayMode(display, &mode);
   220 
   221     if (flags & WL_OUTPUT_MODE_CURRENT) {
   222         display->current_mode = mode;
   223         display->desktop_mode = mode;
   224     }
   225 }
   226 
   227 static void
   228 display_handle_done(void *data,
   229                     struct wl_output *output)
   230 {
   231     SDL_VideoDisplay *display = data;
   232     SDL_AddVideoDisplay(display);
   233     SDL_free(display->name);
   234     SDL_free(display);
   235 }
   236 
   237 static void
   238 display_handle_scale(void *data,
   239                      struct wl_output *output,
   240                      int32_t factor)
   241 {
   242     // TODO: do HiDPI stuff.
   243 }
   244 
   245 static const struct wl_output_listener output_listener = {
   246     display_handle_geometry,
   247     display_handle_mode,
   248     display_handle_done,
   249     display_handle_scale
   250 };
   251 
   252 static void
   253 Wayland_add_display(SDL_VideoData *d, uint32_t id)
   254 {
   255     struct wl_output *output;
   256     SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
   257     if (!display) {
   258         SDL_OutOfMemory();
   259         return;
   260     }
   261     SDL_zero(*display);
   262 
   263     output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
   264     if (!output) {
   265         SDL_SetError("Failed to retrieve output.");
   266         return;
   267     }
   268 
   269     wl_output_add_listener(output, &output_listener, display);
   270 }
   271 
   272 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   273 static void
   274 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
   275         int32_t show_is_fullscreen)
   276 {
   277 }
   278 
   279 static void
   280 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
   281 {
   282     SDL_SendQuit();
   283 }
   284 
   285 static const struct qt_windowmanager_listener windowmanager_listener = {
   286     windowmanager_hints,
   287     windowmanager_quit,
   288 };
   289 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   290 
   291 static void
   292 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
   293                       const char *interface, uint32_t version)
   294 {
   295     SDL_VideoData *d = data;
   296 
   297     if (strcmp(interface, "wl_compositor") == 0) {
   298         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
   299     } else if (strcmp(interface, "wl_output") == 0) {
   300         Wayland_add_display(d, id);
   301     } else if (strcmp(interface, "wl_seat") == 0) {
   302         Wayland_display_add_input(d, id);
   303     } else if (strcmp(interface, "wl_shell") == 0) {
   304         d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
   305     } else if (strcmp(interface, "wl_shm") == 0) {
   306         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
   307         d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
   308 
   309 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   310     } else if (strcmp(interface, "qt_touch_extension") == 0) {
   311         Wayland_touch_create(d, id);
   312     } else if (strcmp(interface, "qt_surface_extension") == 0) {
   313         d->surface_extension = wl_registry_bind(registry, id,
   314                 &qt_surface_extension_interface, 1);
   315     } else if (strcmp(interface, "qt_windowmanager") == 0) {
   316         d->windowmanager = wl_registry_bind(registry, id,
   317                 &qt_windowmanager_interface, 1);
   318         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
   319 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   320     }
   321 }
   322 
   323 static const struct wl_registry_listener registry_listener = {
   324     display_handle_global
   325 };
   326 
   327 int
   328 Wayland_VideoInit(_THIS)
   329 {
   330     SDL_VideoData *data = SDL_malloc(sizeof *data);
   331     if (data == NULL)
   332         return SDL_OutOfMemory();
   333     memset(data, 0, sizeof *data);
   334 
   335     _this->driverdata = data;
   336 
   337     data->display = WAYLAND_wl_display_connect(NULL);
   338     if (data->display == NULL) {
   339         return SDL_SetError("Failed to connect to a Wayland display");
   340     }
   341 
   342     data->registry = wl_display_get_registry(data->display);
   343     if (data->registry == NULL) {
   344         return SDL_SetError("Failed to get the Wayland registry");
   345     }
   346 
   347     wl_registry_add_listener(data->registry, &registry_listener, data);
   348 
   349     // First roundtrip to receive all registry objects.
   350     WAYLAND_wl_display_roundtrip(data->display);
   351 
   352     // Second roundtrip to receive all output events.
   353     WAYLAND_wl_display_roundtrip(data->display);
   354 
   355     data->xkb_context = WAYLAND_xkb_context_new(0);
   356     if (!data->xkb_context) {
   357         return SDL_SetError("Failed to create XKB context");
   358     }
   359 
   360     Wayland_InitMouse();
   361 
   362     /* Get the surface class name, usually the name of the application */
   363     data->classname = get_classname();
   364 
   365     WAYLAND_wl_display_flush(data->display);
   366 
   367     return 0;
   368 }
   369 
   370 static void
   371 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
   372 {
   373     // Nothing to do here, everything was already done in the wl_output
   374     // callbacks.
   375 }
   376 
   377 static int
   378 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
   379 {
   380     return SDL_Unsupported();
   381 }
   382 
   383 void
   384 Wayland_VideoQuit(_THIS)
   385 {
   386     SDL_VideoData *data = _this->driverdata;
   387     int i;
   388 
   389     Wayland_FiniMouse ();
   390 
   391     for (i = 0; i < _this->num_displays; ++i) {
   392         SDL_VideoDisplay *display = &_this->displays[i];
   393         wl_output_destroy(display->driverdata);
   394         display->driverdata = NULL;
   395     }
   396 
   397     Wayland_display_destroy_input(data);
   398 
   399     if (data->xkb_context) {
   400         WAYLAND_xkb_context_unref(data->xkb_context);
   401         data->xkb_context = NULL;
   402     }
   403 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   404     if (data->windowmanager)
   405         qt_windowmanager_destroy(data->windowmanager);
   406 
   407     if (data->surface_extension)
   408         qt_surface_extension_destroy(data->surface_extension);
   409 
   410     Wayland_touch_destroy(data);
   411 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   412 
   413     if (data->shm)
   414         wl_shm_destroy(data->shm);
   415 
   416     if (data->cursor_theme)
   417         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
   418 
   419     if (data->shell)
   420         wl_shell_destroy(data->shell);
   421 
   422     if (data->compositor)
   423         wl_compositor_destroy(data->compositor);
   424 
   425     if (data->registry)
   426         wl_registry_destroy(data->registry);
   427 
   428     if (data->display) {
   429         WAYLAND_wl_display_flush(data->display);
   430         WAYLAND_wl_display_disconnect(data->display);
   431     }
   432 
   433     SDL_free(data->classname);
   434     free(data);
   435     _this->driverdata = NULL;
   436 }
   437 
   438 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   439 
   440 /* vi: set ts=4 sw=4 expandtab: */