src/video/wayland/SDL_waylandwindow.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 07 Feb 2018 13:13:55 -0500
changeset 11848 2f157c1ca383
parent 11811 5d94cb6b24d3
child 11944 295cf9910d75
permissions -rw-r--r--
wayland: Add support for xdg-shell protocol (unstable v6).

This is meant to be the desktop-enhanced version of wl_shell. Right now we
just match what the existing wl_shell code does, but there are other areas of
functionality available to us now, that we can fill in later.

This uses the "unstable" API, since this is what ships in Ubuntu 17.10 (as
part of Wayland 1.10), but Wayland 1.12 promotes this to stable with extremely
minor changes. We will add support for the stable version when it makes sense
to do so.
     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 && SDL_VIDEO_OPENGL_EGL
    25 
    26 #include "../SDL_sysvideo.h"
    27 #include "../../events/SDL_windowevents_c.h"
    28 #include "../SDL_egl_c.h"
    29 #include "SDL_waylandevents_c.h"
    30 #include "SDL_waylandwindow.h"
    31 #include "SDL_waylandvideo.h"
    32 #include "SDL_waylandtouch.h"
    33 #include "SDL_waylanddyn.h"
    34 #include "SDL_hints.h"
    35 
    36 #include "xdg-shell-unstable-v6-client-protocol.h"
    37 
    38 /* On modern desktops, we probably will use the xdg-shell protocol instead
    39    of wl_shell, but wl_shell might be useful on older Wayland installs that
    40    don't have the newer protocol, or embedded things that don't have a full
    41    window manager. */
    42 
    43 static void
    44 handle_ping_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface,
    45             uint32_t serial)
    46 {
    47     wl_shell_surface_pong(shell_surface, serial);
    48 }
    49 
    50 static void
    51 handle_configure_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface,
    52                  uint32_t edges, int32_t width, int32_t height)
    53 {
    54     SDL_WindowData *wind = (SDL_WindowData *)data;
    55     SDL_Window *window = wind->sdlwindow;
    56     struct wl_region *region;
    57 
    58     /* wl_shell_surface spec states that this is a suggestion.
    59        Ignore if less than or greater than max/min size. */
    60 
    61     if (width == 0 || height == 0) {
    62         return;
    63     }
    64 
    65     if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
    66         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
    67             if (window->max_w > 0) {
    68                 width = SDL_min(width, window->max_w);
    69             } 
    70             width = SDL_max(width, window->min_w);
    71 
    72             if (window->max_h > 0) {
    73                 height = SDL_min(height, window->max_h);
    74             }
    75             height = SDL_max(height, window->min_h);
    76         } else {
    77             return;
    78         }
    79     }
    80 
    81     if (width == window->w && height == window->h) {
    82         return;
    83     }
    84 
    85     window->w = width;
    86     window->h = height;
    87     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
    88 
    89     region = wl_compositor_create_region(wind->waylandData->compositor);
    90     wl_region_add(region, 0, 0, window->w, window->h);
    91     wl_surface_set_opaque_region(wind->surface, region);
    92     wl_region_destroy(region);
    93     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
    94 }
    95 
    96 static void
    97 handle_popup_done_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface)
    98 {
    99 }
   100 
   101 static const struct wl_shell_surface_listener shell_surface_listener_wl = {
   102     handle_ping_wl_shell_surface,
   103     handle_configure_wl_shell_surface,
   104     handle_popup_done_wl_shell_surface
   105 };
   106 
   107 
   108 
   109 
   110 static void
   111 handle_configure_zxdg_shell_surface(void *data, struct zxdg_surface_v6 *zxdg, uint32_t serial)
   112 {
   113     SDL_WindowData *wind = (SDL_WindowData *)data;
   114     SDL_Window *window = wind->sdlwindow;
   115     struct wl_region *region;
   116     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   117 
   118     region = wl_compositor_create_region(wind->waylandData->compositor);
   119     wl_region_add(region, 0, 0, window->w, window->h);
   120     wl_surface_set_opaque_region(wind->surface, region);
   121     wl_region_destroy(region);
   122     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
   123     zxdg_surface_v6_ack_configure(zxdg, serial);
   124 }
   125 
   126 static const struct zxdg_surface_v6_listener shell_surface_listener_zxdg = {
   127     handle_configure_zxdg_shell_surface
   128 };
   129 
   130 
   131 static void
   132 handle_configure_zxdg_toplevel(void *data,
   133 			  struct zxdg_toplevel_v6 *zxdg_toplevel_v6,
   134 			  int32_t width,
   135 			  int32_t height,
   136 			  struct wl_array *states)
   137 {
   138     SDL_WindowData *wind = (SDL_WindowData *)data;
   139     SDL_Window *window = wind->sdlwindow;
   140 
   141     /* wl_shell_surface spec states that this is a suggestion.
   142        Ignore if less than or greater than max/min size. */
   143 
   144     if (width == 0 || height == 0) {
   145         return;
   146     }
   147 
   148     if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
   149         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
   150             if (window->max_w > 0) {
   151                 width = SDL_min(width, window->max_w);
   152             } 
   153             width = SDL_max(width, window->min_w);
   154 
   155             if (window->max_h > 0) {
   156                 height = SDL_min(height, window->max_h);
   157             }
   158             height = SDL_max(height, window->min_h);
   159         } else {
   160             return;
   161         }
   162     }
   163 
   164     if (width == window->w && height == window->h) {
   165         return;
   166     }
   167 
   168     window->w = width;
   169     window->h = height;
   170 }
   171 
   172 static void
   173 handle_close_zxdg_toplevel(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel_v6)
   174 {
   175     SDL_WindowData *window = (SDL_WindowData *)data;
   176     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   177 }
   178 
   179 static const struct zxdg_toplevel_v6_listener toplevel_listener_zxdg = {
   180     handle_configure_zxdg_toplevel,
   181     handle_close_zxdg_toplevel
   182 };
   183 
   184 
   185 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   186 static void
   187 handle_onscreen_visibility(void *data,
   188         struct qt_extended_surface *qt_extended_surface, int32_t visible)
   189 {
   190 }
   191 
   192 static void
   193 handle_set_generic_property(void *data,
   194         struct qt_extended_surface *qt_extended_surface, const char *name,
   195         struct wl_array *value)
   196 {
   197 }
   198 
   199 static void
   200 handle_close(void *data, struct qt_extended_surface *qt_extended_surface)
   201 {
   202     SDL_WindowData *window = (SDL_WindowData *)data;
   203     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   204 }
   205 
   206 static const struct qt_extended_surface_listener extended_surface_listener = {
   207     handle_onscreen_visibility,
   208     handle_set_generic_property,
   209     handle_close,
   210 };
   211 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   212 
   213 SDL_bool
   214 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   215 {
   216     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   217     const Uint32 version = ((((Uint32) info->version.major) * 1000000) +
   218                             (((Uint32) info->version.minor) * 10000) +
   219                             (((Uint32) info->version.patch)));
   220 
   221     /* Before 2.0.6, it was possible to build an SDL with Wayland support
   222        (SDL_SysWMinfo will be large enough to hold Wayland info), but build
   223        your app against SDL headers that didn't have Wayland support
   224        (SDL_SysWMinfo could be smaller than Wayland needs. This would lead
   225        to an app properly using SDL_GetWindowWMInfo() but we'd accidentally
   226        overflow memory on the stack or heap. To protect against this, we've
   227        padded out the struct unconditionally in the headers and Wayland will
   228        just return an error for older apps using this function. Those apps
   229        will need to be recompiled against newer headers or not use Wayland,
   230        maybe by forcing SDL_VIDEODRIVER=x11. */
   231     if (version < 2000006) {
   232         info->subsystem = SDL_SYSWM_UNKNOWN;
   233         SDL_SetError("Version must be 2.0.6 or newer");
   234         return SDL_FALSE;
   235     }
   236 
   237     info->info.wl.display = data->waylandData->display;
   238     info->info.wl.surface = data->surface;
   239     info->info.wl.shell_surface = data->shell_surface.wl;
   240     info->subsystem = SDL_SYSWM_WAYLAND;
   241 
   242     return SDL_TRUE;
   243 }
   244 
   245 int
   246 Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
   247 {
   248     return 0;  /* just succeed, the real work is done elsewhere. */
   249 }
   250 
   251 static void
   252 SetFullscreen(_THIS, SDL_Window * window, struct wl_output *output)
   253 {
   254     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
   255     SDL_WindowData *wind = window->driverdata;
   256 
   257     if (viddata->shell.zxdg) {
   258         if (output) {
   259             zxdg_toplevel_v6_set_fullscreen(wind->shell_surface.zxdg.roleobj.toplevel, output);
   260         } else {
   261             zxdg_toplevel_v6_unset_fullscreen(wind->shell_surface.zxdg.roleobj.toplevel);
   262         }
   263     } else {
   264         if (output) {
   265             wl_shell_surface_set_fullscreen(wind->shell_surface.wl,
   266                                             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
   267                                             0, output);
   268         } else {
   269             wl_shell_surface_set_toplevel(wind->shell_surface.wl);
   270         }
   271     }
   272 
   273     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   274 }
   275 
   276 void Wayland_ShowWindow(_THIS, SDL_Window *window)
   277 {
   278     struct wl_output *output = (struct wl_output *) window->fullscreen_mode.driverdata;
   279     SetFullscreen(_this, window, (window->flags & SDL_WINDOW_FULLSCREEN) ? output : NULL);
   280 }
   281 
   282 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   283 static void SDLCALL
   284 QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
   285         const char *oldValue, const char *newValue)
   286 {
   287     struct qt_extended_surface *qt_extended_surface = userdata;
   288 
   289     if (name == NULL) {
   290         return;
   291     }
   292 
   293     if (strcmp(name, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION) == 0) {
   294         int32_t orientation = QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION;
   295 
   296         if (newValue != NULL) {
   297             if (strcmp(newValue, "portrait") == 0) {
   298                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION;
   299             } else if (strcmp(newValue, "landscape") == 0) {
   300                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION;
   301             } else if (strcmp(newValue, "inverted-portrait") == 0) {
   302                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION;
   303             } else if (strcmp(newValue, "inverted-landscape") == 0) {
   304                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION;
   305             }
   306         }
   307 
   308         qt_extended_surface_set_content_orientation(qt_extended_surface, orientation);
   309     } else if (strcmp(name, SDL_HINT_QTWAYLAND_WINDOW_FLAGS) == 0) {
   310         uint32_t flags = 0;
   311 
   312         if (newValue != NULL) {
   313             char *tmp = strdup(newValue);
   314             char *saveptr = NULL;
   315 
   316             char *flag = strtok_r(tmp, " ", &saveptr);
   317             while (flag) {
   318                 if (strcmp(flag, "OverridesSystemGestures") == 0) {
   319                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES;
   320                 } else if (strcmp(flag, "StaysOnTop") == 0) {
   321                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
   322                 } else if (strcmp(flag, "BypassWindowManager") == 0) {
   323                     // See https://github.com/qtproject/qtwayland/commit/fb4267103d
   324                     flags |= 4 /* QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER */;
   325                 }
   326 
   327                 flag = strtok_r(NULL, " ", &saveptr);
   328             }
   329 
   330             free(tmp);
   331         }
   332 
   333         qt_extended_surface_set_window_flags(qt_extended_surface, flags);
   334     }
   335 }
   336 
   337 static void QtExtendedSurface_Subscribe(struct qt_extended_surface *surface, const char *name)
   338 {
   339     SDL_AddHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   340 }
   341 
   342 static void QtExtendedSurface_Unsubscribe(struct qt_extended_surface *surface, const char *name)
   343 {
   344     SDL_DelHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   345 }
   346 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   347 
   348 void
   349 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   350                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   351 {
   352     struct wl_output *output = (struct wl_output *) _display->driverdata;
   353     SetFullscreen(_this, window, fullscreen ? output : NULL);
   354 }
   355 
   356 void
   357 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   358 {
   359     SDL_WindowData *wind = window->driverdata;
   360     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
   361 
   362     if (viddata->shell.zxdg) {
   363     } else {
   364         wl_shell_surface_set_toplevel(wind->shell_surface.wl);
   365     }
   366 
   367     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   368 }
   369 
   370 void
   371 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   372 {
   373     SDL_WindowData *wind = window->driverdata;
   374     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   375 
   376     if (viddata->shell.zxdg) {
   377         zxdg_toplevel_v6_set_maximized(wind->shell_surface.zxdg.roleobj.toplevel);
   378     } else {
   379         wl_shell_surface_set_maximized(wind->shell_surface.wl, NULL);
   380     }
   381 
   382     WAYLAND_wl_display_flush( viddata->display );
   383 }
   384 
   385 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   386 {
   387     SDL_WindowData *data;
   388     SDL_VideoData *c;
   389     struct wl_region *region;
   390 
   391     data = calloc(1, sizeof *data);
   392     if (data == NULL)
   393         return SDL_OutOfMemory();
   394 
   395     c = _this->driverdata;
   396     window->driverdata = data;
   397 
   398     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   399         SDL_GL_LoadLibrary(NULL);
   400         window->flags |= SDL_WINDOW_OPENGL;
   401     }
   402 
   403     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   404         window->x = 0;
   405     }
   406     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   407         window->y = 0;
   408     }
   409 
   410     data->waylandData = c;
   411     data->sdlwindow = window;
   412 
   413     data->surface =
   414         wl_compositor_create_surface(c->compositor);
   415     wl_surface_set_user_data(data->surface, data);
   416 
   417     if (c->shell.zxdg) {
   418         data->shell_surface.zxdg.surface = zxdg_shell_v6_get_xdg_surface(c->shell.zxdg, data->surface);
   419         /* !!! FIXME: add popup role */
   420         data->shell_surface.zxdg.roleobj.toplevel = zxdg_surface_v6_get_toplevel(data->shell_surface.zxdg.surface);
   421         zxdg_toplevel_v6_add_listener(data->shell_surface.zxdg.roleobj.toplevel, &toplevel_listener_zxdg, data);
   422         zxdg_toplevel_v6_set_app_id(data->shell_surface.zxdg.roleobj.toplevel, c->classname);
   423     } else {
   424         data->shell_surface.wl = wl_shell_get_shell_surface(c->shell.wl, data->surface);
   425         wl_shell_surface_set_class(data->shell_surface.wl, c->classname);
   426     }
   427 
   428 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   429     if (c->surface_extension) {
   430         data->extended_surface = qt_surface_extension_get_extended_surface(
   431                 c->surface_extension, data->surface);
   432 
   433         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   434         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   435     }
   436 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   437 
   438     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   439                                             window->w, window->h);
   440 
   441     /* Create the GLES window surface */
   442     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   443     
   444     if (data->egl_surface == EGL_NO_SURFACE) {
   445         return SDL_SetError("failed to create a window surface");
   446     }
   447 
   448     if (c->shell.zxdg) {
   449         if (data->shell_surface.zxdg.surface) {
   450             zxdg_surface_v6_set_user_data(data->shell_surface.zxdg.surface, data);
   451             zxdg_surface_v6_add_listener(data->shell_surface.zxdg.surface, &shell_surface_listener_zxdg, data);
   452         }
   453     } else {
   454         if (data->shell_surface.wl) {
   455             wl_shell_surface_set_user_data(data->shell_surface.wl, data);
   456             wl_shell_surface_add_listener(data->shell_surface.wl, &shell_surface_listener_wl, data);
   457         }
   458     }
   459 
   460 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   461     if (data->extended_surface) {
   462         qt_extended_surface_set_user_data(data->extended_surface, data);
   463         qt_extended_surface_add_listener(data->extended_surface,
   464                                          &extended_surface_listener, data);
   465     }
   466 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   467 
   468     region = wl_compositor_create_region(c->compositor);
   469     wl_region_add(region, 0, 0, window->w, window->h);
   470     wl_surface_set_opaque_region(data->surface, region);
   471     wl_region_destroy(region);
   472 
   473     if (c->relative_mouse_mode) {
   474         Wayland_input_lock_pointer(c->input);
   475     }
   476 
   477     wl_surface_commit(data->surface);
   478     WAYLAND_wl_display_flush(c->display);
   479 
   480     return 0;
   481 }
   482 
   483 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   484 {
   485     SDL_VideoData *data = _this->driverdata;
   486     SDL_WindowData *wind = window->driverdata;
   487     struct wl_region *region;
   488 
   489     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   490 
   491     region =wl_compositor_create_region(data->compositor);
   492     wl_region_add(region, 0, 0, window->w, window->h);
   493     wl_surface_set_opaque_region(wind->surface, region);
   494     wl_region_destroy(region);
   495 }
   496 
   497 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   498 {
   499     SDL_WindowData *wind = window->driverdata;
   500     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   501     
   502     if (window->title != NULL) {
   503         if (viddata->shell.zxdg) {
   504             zxdg_toplevel_v6_set_title(wind->shell_surface.zxdg.roleobj.toplevel, window->title);
   505         } else {
   506             wl_shell_surface_set_title(wind->shell_surface.wl, window->title);
   507         }
   508     }
   509 
   510     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   511 }
   512 
   513 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   514 {
   515     SDL_VideoData *data = _this->driverdata;
   516     SDL_WindowData *wind = window->driverdata;
   517 
   518     if (data) {
   519         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   520         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   521 
   522         if (data->shell.zxdg) {
   523             if (wind->shell_surface.zxdg.roleobj.toplevel) {
   524                 zxdg_toplevel_v6_destroy(wind->shell_surface.zxdg.roleobj.toplevel);
   525             }
   526             if (wind->shell_surface.zxdg.surface) {
   527                 zxdg_surface_v6_destroy(wind->shell_surface.zxdg.surface);
   528             }
   529         } else {
   530             if (wind->shell_surface.wl) {
   531                 wl_shell_surface_destroy(wind->shell_surface.wl);
   532             }
   533         }
   534 
   535 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   536         if (wind->extended_surface) {
   537             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   538             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   539             qt_extended_surface_destroy(wind->extended_surface);
   540         }
   541 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   542         wl_surface_destroy(wind->surface);
   543 
   544         SDL_free(wind);
   545         WAYLAND_wl_display_flush(data->display);
   546     }
   547     window->driverdata = NULL;
   548 }
   549 
   550 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   551 
   552 /* vi: set ts=4 sw=4 expandtab: */