src/video/wayland/SDL_waylandvideo.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 30 Oct 2016 21:01:33 +0100
changeset 10563 e3d84016cb79
parent 10530 2a3f8bc23daa
child 10583 974f8ebcb819
permissions -rw-r--r--
Fixed outdated info in README.
     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->MaximizeWindow = Wayland_MaximizeWindow;
   173     device->RestoreWindow = Wayland_RestoreWindow;
   174     device->SetWindowSize = Wayland_SetWindowSize;
   175     device->SetWindowTitle = Wayland_SetWindowTitle;
   176     device->DestroyWindow = Wayland_DestroyWindow;
   177     device->SetWindowHitTest = Wayland_SetWindowHitTest;
   178 
   179     device->free = Wayland_DeleteDevice;
   180 
   181     return device;
   182 }
   183 
   184 VideoBootStrap Wayland_bootstrap = {
   185     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
   186     Wayland_Available, Wayland_CreateDevice
   187 };
   188 
   189 static void
   190 display_handle_geometry(void *data,
   191                         struct wl_output *output,
   192                         int x, int y,
   193                         int physical_width,
   194                         int physical_height,
   195                         int subpixel,
   196                         const char *make,
   197                         const char *model,
   198                         int transform)
   199 
   200 {
   201     SDL_VideoDisplay *display = data;
   202 
   203     display->name = SDL_strdup(model);
   204     display->driverdata = output;
   205 }
   206 
   207 static void
   208 display_handle_mode(void *data,
   209                     struct wl_output *output,
   210                     uint32_t flags,
   211                     int width,
   212                     int height,
   213                     int refresh)
   214 {
   215     SDL_VideoDisplay *display = data;
   216     SDL_DisplayMode mode;
   217 
   218     SDL_zero(mode);
   219     mode.w = width;
   220     mode.h = height;
   221     mode.refresh_rate = refresh / 1000; // mHz to Hz
   222     SDL_AddDisplayMode(display, &mode);
   223 
   224     if (flags & WL_OUTPUT_MODE_CURRENT) {
   225         display->current_mode = mode;
   226         display->desktop_mode = mode;
   227     }
   228 }
   229 
   230 static void
   231 display_handle_done(void *data,
   232                     struct wl_output *output)
   233 {
   234     SDL_VideoDisplay *display = data;
   235     SDL_AddVideoDisplay(display);
   236     SDL_free(display->name);
   237     SDL_free(display);
   238 }
   239 
   240 static void
   241 display_handle_scale(void *data,
   242                      struct wl_output *output,
   243                      int32_t factor)
   244 {
   245     // TODO: do HiDPI stuff.
   246 }
   247 
   248 static const struct wl_output_listener output_listener = {
   249     display_handle_geometry,
   250     display_handle_mode,
   251     display_handle_done,
   252     display_handle_scale
   253 };
   254 
   255 static void
   256 Wayland_add_display(SDL_VideoData *d, uint32_t id)
   257 {
   258     struct wl_output *output;
   259     SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
   260     if (!display) {
   261         SDL_OutOfMemory();
   262         return;
   263     }
   264     SDL_zero(*display);
   265 
   266     output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
   267     if (!output) {
   268         SDL_SetError("Failed to retrieve output.");
   269         return;
   270     }
   271 
   272     wl_output_add_listener(output, &output_listener, display);
   273 }
   274 
   275 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   276 static void
   277 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
   278         int32_t show_is_fullscreen)
   279 {
   280 }
   281 
   282 static void
   283 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
   284 {
   285     SDL_SendQuit();
   286 }
   287 
   288 static const struct qt_windowmanager_listener windowmanager_listener = {
   289     windowmanager_hints,
   290     windowmanager_quit,
   291 };
   292 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   293 
   294 static void
   295 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
   296                       const char *interface, uint32_t version)
   297 {
   298     SDL_VideoData *d = data;
   299 
   300     if (strcmp(interface, "wl_compositor") == 0) {
   301         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
   302     } else if (strcmp(interface, "wl_output") == 0) {
   303         Wayland_add_display(d, id);
   304     } else if (strcmp(interface, "wl_seat") == 0) {
   305         Wayland_display_add_input(d, id);
   306     } else if (strcmp(interface, "wl_shell") == 0) {
   307         d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
   308     } else if (strcmp(interface, "wl_shm") == 0) {
   309         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
   310         d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
   311     } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
   312         Wayland_display_add_relative_pointer_manager(d, id);
   313     } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
   314         Wayland_display_add_pointer_constraints(d, id);
   315 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   316     } else if (strcmp(interface, "qt_touch_extension") == 0) {
   317         Wayland_touch_create(d, id);
   318     } else if (strcmp(interface, "qt_surface_extension") == 0) {
   319         d->surface_extension = wl_registry_bind(registry, id,
   320                 &qt_surface_extension_interface, 1);
   321     } else if (strcmp(interface, "qt_windowmanager") == 0) {
   322         d->windowmanager = wl_registry_bind(registry, id,
   323                 &qt_windowmanager_interface, 1);
   324         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
   325 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   326     }
   327 }
   328 
   329 static const struct wl_registry_listener registry_listener = {
   330     display_handle_global
   331 };
   332 
   333 int
   334 Wayland_VideoInit(_THIS)
   335 {
   336     SDL_VideoData *data = SDL_malloc(sizeof *data);
   337     if (data == NULL)
   338         return SDL_OutOfMemory();
   339     memset(data, 0, sizeof *data);
   340 
   341     _this->driverdata = data;
   342 
   343     data->xkb_context = WAYLAND_xkb_context_new(0);
   344     if (!data->xkb_context) {
   345         return SDL_SetError("Failed to create XKB context");
   346     }
   347 
   348     data->display = WAYLAND_wl_display_connect(NULL);
   349     if (data->display == NULL) {
   350         return SDL_SetError("Failed to connect to a Wayland display");
   351     }
   352 
   353     data->registry = wl_display_get_registry(data->display);
   354     if (data->registry == NULL) {
   355         return SDL_SetError("Failed to get the Wayland registry");
   356     }
   357 
   358     wl_registry_add_listener(data->registry, &registry_listener, data);
   359 
   360     // First roundtrip to receive all registry objects.
   361     WAYLAND_wl_display_roundtrip(data->display);
   362 
   363     // Second roundtrip to receive all output events.
   364     WAYLAND_wl_display_roundtrip(data->display);
   365 
   366     Wayland_InitMouse();
   367 
   368     /* Get the surface class name, usually the name of the application */
   369     data->classname = get_classname();
   370 
   371     WAYLAND_wl_display_flush(data->display);
   372 
   373     return 0;
   374 }
   375 
   376 static void
   377 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
   378 {
   379     // Nothing to do here, everything was already done in the wl_output
   380     // callbacks.
   381 }
   382 
   383 static int
   384 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
   385 {
   386     return SDL_Unsupported();
   387 }
   388 
   389 void
   390 Wayland_VideoQuit(_THIS)
   391 {
   392     SDL_VideoData *data = _this->driverdata;
   393     int i;
   394 
   395     Wayland_FiniMouse ();
   396 
   397     for (i = 0; i < _this->num_displays; ++i) {
   398         SDL_VideoDisplay *display = &_this->displays[i];
   399         wl_output_destroy(display->driverdata);
   400         display->driverdata = NULL;
   401     }
   402 
   403     Wayland_display_destroy_input(data);
   404     Wayland_display_destroy_pointer_constraints(data);
   405     Wayland_display_destroy_relative_pointer_manager(data);
   406 
   407     if (data->xkb_context) {
   408         WAYLAND_xkb_context_unref(data->xkb_context);
   409         data->xkb_context = NULL;
   410     }
   411 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   412     if (data->windowmanager)
   413         qt_windowmanager_destroy(data->windowmanager);
   414 
   415     if (data->surface_extension)
   416         qt_surface_extension_destroy(data->surface_extension);
   417 
   418     Wayland_touch_destroy(data);
   419 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   420 
   421     if (data->shm)
   422         wl_shm_destroy(data->shm);
   423 
   424     if (data->cursor_theme)
   425         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
   426 
   427     if (data->shell)
   428         wl_shell_destroy(data->shell);
   429 
   430     if (data->compositor)
   431         wl_compositor_destroy(data->compositor);
   432 
   433     if (data->registry)
   434         wl_registry_destroy(data->registry);
   435 
   436     if (data->display) {
   437         WAYLAND_wl_display_flush(data->display);
   438         WAYLAND_wl_display_disconnect(data->display);
   439     }
   440 
   441     SDL_free(data->classname);
   442     free(data);
   443     _this->driverdata = NULL;
   444 }
   445 
   446 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   447 
   448 /* vi: set ts=4 sw=4 expandtab: */