src/video/wayland/SDL_waylandvideo.c
author Philipp Wiesemann
Tue, 28 Jun 2016 21:08:23 +0200
changeset 10187 1f0f8c95531d
parent 10119 ac9831ff70d1
child 10302 729eff9ee77a
permissions -rw-r--r--
Fixed freeing strings created by strdup() with SDL_free().

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