src/video/wayland/SDL_waylandvideo.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 20 Jan 2014 12:53:44 -0500
changeset 8116 f7c2f71251e5
parent 8104 2e4f1bd21196
child 8149 681eb46b8ac4
permissions -rw-r--r--
Patched to compile if Wayland is disabled via SDL_config.h (thanks, Martin!).

Fixes Bugzilla #2351.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 struct wayland_mode {
    47     SDL_DisplayMode mode;
    48     struct wl_list link;
    49 };
    50 
    51 /* Initialization/Query functions */
    52 static int
    53 Wayland_VideoInit(_THIS);
    54 
    55 static void
    56 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
    57 static int
    58 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
    59 
    60 static void
    61 Wayland_VideoQuit(_THIS);
    62 
    63 /* Wayland driver bootstrap functions */
    64 static int
    65 Wayland_Available(void)
    66 {
    67     struct wl_display *display = NULL;
    68     if (SDL_WAYLAND_LoadSymbols()) {
    69         display = WAYLAND_wl_display_connect(NULL);
    70         if (display != NULL) {
    71             WAYLAND_wl_display_disconnect(display);
    72         }
    73         SDL_WAYLAND_UnloadSymbols();
    74     }
    75 
    76     return (display != NULL);
    77 }
    78 
    79 static void
    80 Wayland_DeleteDevice(SDL_VideoDevice *device)
    81 {
    82     SDL_free(device);
    83     SDL_WAYLAND_UnloadSymbols();
    84 }
    85 
    86 static SDL_VideoDevice *
    87 Wayland_CreateDevice(int devindex)
    88 {
    89     SDL_VideoDevice *device;
    90     
    91     if (!SDL_WAYLAND_LoadSymbols()) {
    92         return NULL;
    93     }
    94 
    95     /* Initialize all variables that we clean on shutdown */
    96     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
    97     if (!device) {
    98         SDL_OutOfMemory();
    99         return NULL;
   100     }
   101 
   102     /* Set the function pointers */
   103     device->VideoInit = Wayland_VideoInit;
   104     device->VideoQuit = Wayland_VideoQuit;
   105     device->SetDisplayMode = Wayland_SetDisplayMode;
   106     device->GetDisplayModes = Wayland_GetDisplayModes;
   107     device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
   108 
   109     device->PumpEvents = Wayland_PumpEvents;
   110 
   111     device->GL_SwapWindow = Wayland_GLES_SwapWindow;
   112     device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
   113     device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
   114     device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
   115     device->GL_CreateContext = Wayland_GLES_CreateContext;
   116     device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
   117     device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
   118     device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
   119     device->GL_DeleteContext = Wayland_GLES_DeleteContext;
   120 
   121     device->CreateWindow = Wayland_CreateWindow;
   122     device->ShowWindow = Wayland_ShowWindow;
   123     device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
   124     device->SetWindowSize = Wayland_SetWindowSize;
   125     device->DestroyWindow = Wayland_DestroyWindow;
   126 
   127     device->free = Wayland_DeleteDevice;
   128 
   129     return device;
   130 }
   131 
   132 VideoBootStrap Wayland_bootstrap = {
   133     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
   134     Wayland_Available, Wayland_CreateDevice
   135 };
   136 
   137 static void
   138 wayland_add_mode(SDL_VideoData *d, SDL_DisplayMode m)
   139 {
   140     struct wayland_mode *mode;
   141 
   142     /* Check for duplicate mode */
   143     wl_list_for_each(mode, &d->modes_list, link)
   144         if (mode->mode.w == m.w && mode->mode.h == m.h &&
   145 	    mode->mode.refresh_rate == m.refresh_rate)
   146 	    return;
   147 
   148     /* Add new mode to the list */
   149     mode = (struct wayland_mode *) SDL_calloc(1, sizeof *mode);
   150 
   151     if (!mode)
   152 	return;
   153 
   154     mode->mode = m;
   155     WAYLAND_wl_list_insert(&d->modes_list, &mode->link);
   156 }
   157 
   158 static void
   159 display_handle_geometry(void *data,
   160                         struct wl_output *output,
   161                         int x, int y,
   162                         int physical_width,
   163                         int physical_height,
   164                         int subpixel,
   165                         const char *make,
   166                         const char *model,
   167                         int transform)
   168 
   169 {
   170     SDL_VideoData *d = data;
   171 
   172     d->screen_allocation.x = x;
   173     d->screen_allocation.y = y;
   174 }
   175 
   176 static void
   177 display_handle_mode(void *data,
   178                     struct wl_output *wl_output,
   179                     uint32_t flags,
   180                     int width,
   181                     int height,
   182                     int refresh)
   183 {
   184     SDL_VideoData *d = data;
   185     SDL_DisplayMode mode;
   186 
   187     SDL_zero(mode);
   188     mode.w = width;
   189     mode.h = height;
   190     mode.refresh_rate = refresh / 1000;
   191 
   192     wayland_add_mode(d, mode);
   193 
   194     if (flags & WL_OUTPUT_MODE_CURRENT) {
   195         d->screen_allocation.width = width;
   196         d->screen_allocation.height = height;
   197     }
   198 }
   199 
   200 static const struct wl_output_listener output_listener = {
   201     display_handle_geometry,
   202     display_handle_mode
   203 };
   204 
   205 static void
   206 shm_handle_format(void *data,
   207                   struct wl_shm *shm,
   208                   uint32_t format)
   209 {
   210     SDL_VideoData *d = data;
   211 
   212     d->shm_formats |= (1 << format);
   213 }
   214 
   215 static const struct wl_shm_listener shm_listener = {
   216     shm_handle_format
   217 };
   218 
   219 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   220 static void
   221 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
   222         int32_t show_is_fullscreen)
   223 {
   224 }
   225 
   226 static void
   227 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
   228 {
   229     SDL_SendQuit();
   230 }
   231 
   232 static const struct qt_windowmanager_listener windowmanager_listener = {
   233     windowmanager_hints,
   234     windowmanager_quit,
   235 };
   236 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   237 
   238 static void
   239 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
   240 					const char *interface, uint32_t version)
   241 {
   242     SDL_VideoData *d = data;
   243     
   244     if (strcmp(interface, "wl_compositor") == 0) {
   245         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
   246     } else if (strcmp(interface, "wl_output") == 0) {
   247         d->output = wl_registry_bind(d->registry, id, &wl_output_interface, 1);
   248         wl_output_add_listener(d->output, &output_listener, d);
   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         d->default_cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   257         wl_shm_add_listener(d->shm, &shm_listener, d);
   258     
   259 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   260     } else if (strcmp(interface, "qt_touch_extension") == 0) {
   261         Wayland_touch_create(d, id);
   262     } else if (strcmp(interface, "qt_surface_extension") == 0) {
   263         d->surface_extension = wl_registry_bind(registry, id,
   264                 &qt_surface_extension_interface, 1);
   265     } else if (strcmp(interface, "qt_windowmanager") == 0) {
   266         d->windowmanager = wl_registry_bind(registry, id,
   267                 &qt_windowmanager_interface, 1);
   268         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
   269 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   270     }
   271 }
   272 
   273 static const struct wl_registry_listener registry_listener = {
   274 	display_handle_global
   275 };
   276 
   277 int
   278 Wayland_VideoInit(_THIS)
   279 {
   280     SDL_VideoData *data;
   281     SDL_VideoDisplay display;
   282     SDL_DisplayMode mode;
   283     int i;
   284     
   285     data = malloc(sizeof *data);
   286     if (data == NULL)
   287         return 0;
   288     memset(data, 0, sizeof *data);
   289 
   290     _this->driverdata = data;
   291 
   292     WAYLAND_wl_list_init(&data->modes_list);
   293     
   294     data->display = WAYLAND_wl_display_connect(NULL);
   295     if (data->display == NULL) {
   296         SDL_SetError("Failed to connect to a Wayland display");
   297         return 0;
   298     }
   299 
   300     data->registry = wl_display_get_registry(data->display);
   301    
   302     if ( data->registry == NULL) {
   303         SDL_SetError("Failed to get the Wayland registry");
   304         return 0;
   305     }
   306     
   307     wl_registry_add_listener(data->registry, &registry_listener, data);
   308 
   309     for (i=0; i < 100; i++) {
   310         if (data->screen_allocation.width != 0 || WAYLAND_wl_display_get_error(data->display) != 0) {
   311             break;
   312         }
   313         WAYLAND_wl_display_dispatch(data->display);
   314     }
   315     
   316     if (data->screen_allocation.width == 0) {
   317         SDL_SetError("Failed while waiting for screen allocation: %d ", WAYLAND_wl_display_get_error(data->display));
   318         return 0;
   319     }
   320 
   321     data->xkb_context = WAYLAND_xkb_context_new(0);
   322     if (!data->xkb_context) {
   323         SDL_SetError("Failed to create XKB context");
   324         return 0;
   325     }
   326 
   327     /* Use a fake 32-bpp desktop mode */
   328     mode.format = SDL_PIXELFORMAT_RGB888;
   329     mode.w = data->screen_allocation.width;
   330     mode.h = data->screen_allocation.height;
   331     mode.refresh_rate = 0;
   332     mode.driverdata = NULL;
   333     wayland_add_mode(data, mode);
   334     SDL_zero(display);
   335     display.desktop_mode = mode;
   336     display.current_mode = mode;
   337     display.driverdata = NULL;
   338     SDL_AddVideoDisplay(&display);
   339 
   340     Wayland_InitMouse ();
   341 
   342     WAYLAND_wl_display_flush(data->display);
   343 
   344     return 0;
   345 }
   346 
   347 static void
   348 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
   349 {
   350     SDL_VideoData *data = _this->driverdata;
   351     SDL_DisplayMode mode;
   352     struct wayland_mode *m;
   353 
   354     Wayland_PumpEvents(_this);
   355 
   356     wl_list_for_each(m, &data->modes_list, link) {
   357         m->mode.format = SDL_PIXELFORMAT_RGB888;
   358         SDL_AddDisplayMode(sdl_display, &m->mode);
   359         m->mode.format = SDL_PIXELFORMAT_RGBA8888;
   360         SDL_AddDisplayMode(sdl_display, &m->mode);
   361     }
   362 
   363     mode.w = data->screen_allocation.width;
   364     mode.h = data->screen_allocation.height;
   365     mode.refresh_rate = 0;
   366     mode.driverdata = NULL;
   367 
   368     mode.format = SDL_PIXELFORMAT_RGB888;
   369     SDL_AddDisplayMode(sdl_display, &mode);
   370     mode.format = SDL_PIXELFORMAT_RGBA8888;
   371     SDL_AddDisplayMode(sdl_display, &mode);
   372 }
   373 
   374 static int
   375 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
   376 {
   377     return 0;
   378 }
   379 
   380 void
   381 Wayland_VideoQuit(_THIS)
   382 {
   383     SDL_VideoData *data = _this->driverdata;
   384     struct wayland_mode *t, *m;
   385 
   386     Wayland_FiniMouse ();
   387 
   388     if (data->output)
   389         wl_output_destroy(data->output);
   390 
   391     Wayland_display_destroy_input(data);
   392 
   393     if (data->xkb_context) {
   394         WAYLAND_xkb_context_unref(data->xkb_context);
   395         data->xkb_context = NULL;
   396     }
   397 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   398     if (data->windowmanager)
   399         qt_windowmanager_destroy(data->windowmanager);
   400 
   401     if (data->surface_extension)
   402         qt_surface_extension_destroy(data->surface_extension);
   403 
   404     Wayland_touch_destroy(data);
   405 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   406 
   407     if (data->shm)
   408         wl_shm_destroy(data->shm);
   409 
   410     if (data->cursor_theme)
   411         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
   412 
   413     if (data->shell)
   414         wl_shell_destroy(data->shell);
   415 
   416     if (data->compositor)
   417         wl_compositor_destroy(data->compositor);
   418 
   419     if (data->display) {
   420         WAYLAND_wl_display_flush(data->display);
   421         WAYLAND_wl_display_disconnect(data->display);
   422     }
   423     
   424     wl_list_for_each_safe(m, t, &data->modes_list, link) {
   425         WAYLAND_wl_list_remove(&m->link);
   426         free(m);
   427     }
   428 
   429 
   430     free(data);
   431     _this->driverdata = NULL;
   432 }
   433 
   434 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   435 
   436 /* vi: set ts=4 sw=4 expandtab: */