src/video/wayland/SDL_waylandvideo.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Wed, 16 Nov 2016 22:09:40 +0100
changeset 10618 01ff51434145
parent 10583 974f8ebcb819
child 10737 3406a0f8b041
permissions -rw-r--r--
Wayland: Fixed memory leak if output retrieval failed.

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