src/video/wayland/SDL_waylandvideo.c
author Sam Lantinga
Thu, 01 Sep 2016 01:26:56 -0700
changeset 10304 ee83e0b4a36f
parent 10302 729eff9ee77a
child 10492 786e10ab72d8
permissions -rw-r--r--
wayland: Add support for relative mouse mode, by Jonas Ã…dahl <jadahl@gmail.com>

Generate the C protocol files from the protocol XML files installed by
wayland-protocols, and use them to implement support for relative pointer
motions and pointer locking.

Note that at the time, the protocol is unstable and may change in the future.
Any future breaking changes will, however, fail gracefully and result in no
regressions compared to before this patch.
     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     } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
   309         Wayland_display_add_relative_pointer_manager(d, id);
   310     } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
   311         Wayland_display_add_pointer_constraints(d, id);
   312 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   313     } else if (strcmp(interface, "qt_touch_extension") == 0) {
   314         Wayland_touch_create(d, id);
   315     } else if (strcmp(interface, "qt_surface_extension") == 0) {
   316         d->surface_extension = wl_registry_bind(registry, id,
   317                 &qt_surface_extension_interface, 1);
   318     } else if (strcmp(interface, "qt_windowmanager") == 0) {
   319         d->windowmanager = wl_registry_bind(registry, id,
   320                 &qt_windowmanager_interface, 1);
   321         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
   322 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   323     }
   324 }
   325 
   326 static const struct wl_registry_listener registry_listener = {
   327     display_handle_global
   328 };
   329 
   330 int
   331 Wayland_VideoInit(_THIS)
   332 {
   333     SDL_VideoData *data = SDL_malloc(sizeof *data);
   334     if (data == NULL)
   335         return SDL_OutOfMemory();
   336     memset(data, 0, sizeof *data);
   337 
   338     _this->driverdata = data;
   339 
   340     data->display = WAYLAND_wl_display_connect(NULL);
   341     if (data->display == NULL) {
   342         return SDL_SetError("Failed to connect to a Wayland display");
   343     }
   344 
   345     data->registry = wl_display_get_registry(data->display);
   346     if (data->registry == NULL) {
   347         return SDL_SetError("Failed to get the Wayland registry");
   348     }
   349 
   350     wl_registry_add_listener(data->registry, &registry_listener, data);
   351 
   352     // First roundtrip to receive all registry objects.
   353     WAYLAND_wl_display_roundtrip(data->display);
   354 
   355     // Second roundtrip to receive all output events.
   356     WAYLAND_wl_display_roundtrip(data->display);
   357 
   358     data->xkb_context = WAYLAND_xkb_context_new(0);
   359     if (!data->xkb_context) {
   360         return SDL_SetError("Failed to create XKB context");
   361     }
   362 
   363     Wayland_InitMouse();
   364 
   365     /* Get the surface class name, usually the name of the application */
   366     data->classname = get_classname();
   367 
   368     WAYLAND_wl_display_flush(data->display);
   369 
   370     return 0;
   371 }
   372 
   373 static void
   374 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
   375 {
   376     // Nothing to do here, everything was already done in the wl_output
   377     // callbacks.
   378 }
   379 
   380 static int
   381 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
   382 {
   383     return SDL_Unsupported();
   384 }
   385 
   386 void
   387 Wayland_VideoQuit(_THIS)
   388 {
   389     SDL_VideoData *data = _this->driverdata;
   390     int i;
   391 
   392     Wayland_FiniMouse ();
   393 
   394     for (i = 0; i < _this->num_displays; ++i) {
   395         SDL_VideoDisplay *display = &_this->displays[i];
   396         wl_output_destroy(display->driverdata);
   397         display->driverdata = NULL;
   398     }
   399 
   400     Wayland_display_destroy_input(data);
   401     Wayland_display_destroy_pointer_constraints(data);
   402     Wayland_display_destroy_relative_pointer_manager(data);
   403 
   404     if (data->xkb_context) {
   405         WAYLAND_xkb_context_unref(data->xkb_context);
   406         data->xkb_context = NULL;
   407     }
   408 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   409     if (data->windowmanager)
   410         qt_windowmanager_destroy(data->windowmanager);
   411 
   412     if (data->surface_extension)
   413         qt_surface_extension_destroy(data->surface_extension);
   414 
   415     Wayland_touch_destroy(data);
   416 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   417 
   418     if (data->shm)
   419         wl_shm_destroy(data->shm);
   420 
   421     if (data->cursor_theme)
   422         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
   423 
   424     if (data->shell)
   425         wl_shell_destroy(data->shell);
   426 
   427     if (data->compositor)
   428         wl_compositor_destroy(data->compositor);
   429 
   430     if (data->registry)
   431         wl_registry_destroy(data->registry);
   432 
   433     if (data->display) {
   434         WAYLAND_wl_display_flush(data->display);
   435         WAYLAND_wl_display_disconnect(data->display);
   436     }
   437 
   438     SDL_free(data->classname);
   439     free(data);
   440     _this->driverdata = NULL;
   441 }
   442 
   443 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   444 
   445 /* vi: set ts=4 sw=4 expandtab: */