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