src/video/wayland/SDL_waylandwindow.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 19 Jun 2019 00:52:34 -0400
changeset 12889 32d98e72ed94
parent 12844 d92b5fbb5ac0
permissions -rw-r--r--
wayland: Fixed C99-style variable declaration inside for-loop.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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-client-protocol.h"
    37 #include "xdg-shell-unstable-v6-client-protocol.h"
    38 #include "xdg-decoration-unstable-v1-client-protocol.h"
    39 #include "org-kde-kwin-server-decoration-manager-client-protocol.h"
    40 
    41 static float get_window_scale_factor(SDL_Window *window) {
    42       return ((SDL_WindowData*)window->driverdata)->scale_factor;
    43 }
    44 
    45 /* On modern desktops, we probably will use the xdg-shell protocol instead
    46    of wl_shell, but wl_shell might be useful on older Wayland installs that
    47    don't have the newer protocol, or embedded things that don't have a full
    48    window manager. */
    49 
    50 static void
    51 handle_ping_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface,
    52             uint32_t serial)
    53 {
    54     wl_shell_surface_pong(shell_surface, serial);
    55 }
    56 
    57 static void
    58 handle_configure_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface,
    59                  uint32_t edges, int32_t width, int32_t height)
    60 {
    61     SDL_WindowData *wind = (SDL_WindowData *)data;
    62     SDL_Window *window = wind->sdlwindow;
    63 
    64     /* wl_shell_surface spec states that this is a suggestion.
    65        Ignore if less than or greater than max/min size. */
    66 
    67     if (width == 0 || height == 0) {
    68         return;
    69     }
    70 
    71     if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
    72         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
    73             if (window->max_w > 0) {
    74                 width = SDL_min(width, window->max_w);
    75             }
    76             width = SDL_max(width, window->min_w);
    77 
    78             if (window->max_h > 0) {
    79                 height = SDL_min(height, window->max_h);
    80             }
    81             height = SDL_max(height, window->min_h);
    82         } else {
    83             return;
    84         }
    85     }
    86 
    87     wind->resize.width = width;
    88     wind->resize.height = height;
    89     wind->resize.pending = SDL_TRUE;
    90 }
    91 
    92 static void
    93 handle_popup_done_wl_shell_surface(void *data, struct wl_shell_surface *shell_surface)
    94 {
    95 }
    96 
    97 static const struct wl_shell_surface_listener shell_surface_listener_wl = {
    98     handle_ping_wl_shell_surface,
    99     handle_configure_wl_shell_surface,
   100     handle_popup_done_wl_shell_surface
   101 };
   102 
   103 
   104 
   105 
   106 static void
   107 handle_configure_zxdg_shell_surface(void *data, struct zxdg_surface_v6 *zxdg, uint32_t serial)
   108 {
   109     SDL_WindowData *wind = (SDL_WindowData *)data;
   110     SDL_Window *window = wind->sdlwindow;
   111     struct wl_region *region;
   112 
   113     if (!wind->shell_surface.zxdg.initial_configure_seen) {
   114         window->w = 0;
   115         window->h = 0;
   116         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, wind->resize.width, wind->resize.height);
   117         window->w = wind->resize.width;
   118         window->h = wind->resize.height;
   119 
   120         wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
   121         WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
   122 
   123         zxdg_surface_v6_ack_configure(zxdg, serial);
   124 
   125         region = wl_compositor_create_region(wind->waylandData->compositor);
   126         wl_region_add(region, 0, 0, window->w, window->h);
   127         wl_surface_set_opaque_region(wind->surface, region);
   128         wl_region_destroy(region);
   129 
   130         wind->shell_surface.zxdg.initial_configure_seen = SDL_TRUE;
   131     } else {
   132         wind->resize.pending = SDL_TRUE;
   133         wind->resize.configure = SDL_TRUE;
   134         wind->resize.serial = serial;
   135     }
   136 }
   137 
   138 static const struct zxdg_surface_v6_listener shell_surface_listener_zxdg = {
   139     handle_configure_zxdg_shell_surface
   140 };
   141 
   142 
   143 static void
   144 handle_configure_zxdg_toplevel(void *data,
   145               struct zxdg_toplevel_v6 *zxdg_toplevel_v6,
   146               int32_t width,
   147               int32_t height,
   148               struct wl_array *states)
   149 {
   150     SDL_WindowData *wind = (SDL_WindowData *)data;
   151     SDL_Window *window = wind->sdlwindow;
   152 
   153     enum zxdg_toplevel_v6_state *state;
   154     SDL_bool fullscreen = SDL_FALSE;
   155     wl_array_for_each(state, states) {
   156         if (*state == ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN) {
   157             fullscreen = SDL_TRUE;
   158         }
   159     }
   160 
   161     if (!fullscreen) {
   162         if (width == 0 || height == 0) {
   163             width = window->windowed.w;
   164             height = window->windowed.h;
   165         }
   166 
   167         /* zxdg_toplevel spec states that this is a suggestion.
   168            Ignore if less than or greater than max/min size. */
   169 
   170         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
   171             if (window->max_w > 0) {
   172                 width = SDL_min(width, window->max_w);
   173             }
   174             width = SDL_max(width, window->min_w);
   175 
   176             if (window->max_h > 0) {
   177                 height = SDL_min(height, window->max_h);
   178             }
   179             height = SDL_max(height, window->min_h);
   180         } else {
   181             wind->resize.width = window->w;
   182             wind->resize.height = window->h;
   183             return;
   184         }
   185     }
   186 
   187     if (width == 0 || height == 0) {
   188         wind->resize.width = window->w;
   189         wind->resize.height = window->h;
   190         return;
   191     }
   192 
   193     wind->resize.width = width;
   194     wind->resize.height = height;
   195 }
   196 
   197 static void
   198 handle_close_zxdg_toplevel(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel_v6)
   199 {
   200     SDL_WindowData *window = (SDL_WindowData *)data;
   201     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   202 }
   203 
   204 static const struct zxdg_toplevel_v6_listener toplevel_listener_zxdg = {
   205     handle_configure_zxdg_toplevel,
   206     handle_close_zxdg_toplevel
   207 };
   208 
   209 
   210 
   211 static void
   212 handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t serial)
   213 {
   214     SDL_WindowData *wind = (SDL_WindowData *)data;
   215     SDL_Window *window = wind->sdlwindow;
   216     struct wl_region *region;
   217 
   218     if (!wind->shell_surface.xdg.initial_configure_seen) {
   219         window->w = 0;
   220         window->h = 0;
   221         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, wind->resize.width, wind->resize.height);
   222         window->w = wind->resize.width;
   223         window->h = wind->resize.height;
   224 
   225         wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
   226         WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
   227 
   228         xdg_surface_ack_configure(xdg, serial);
   229 
   230         region = wl_compositor_create_region(wind->waylandData->compositor);
   231         wl_region_add(region, 0, 0, window->w, window->h);
   232         wl_surface_set_opaque_region(wind->surface, region);
   233         wl_region_destroy(region);
   234 
   235         wind->shell_surface.xdg.initial_configure_seen = SDL_TRUE;
   236     } else {
   237         wind->resize.pending = SDL_TRUE;
   238         wind->resize.configure = SDL_TRUE;
   239         wind->resize.serial = serial;
   240     }
   241 }
   242 
   243 static const struct xdg_surface_listener shell_surface_listener_xdg = {
   244     handle_configure_xdg_shell_surface
   245 };
   246 
   247 
   248 static void
   249 handle_configure_xdg_toplevel(void *data,
   250               struct xdg_toplevel *xdg_toplevel,
   251               int32_t width,
   252               int32_t height,
   253               struct wl_array *states)
   254 {
   255     SDL_WindowData *wind = (SDL_WindowData *)data;
   256     SDL_Window *window = wind->sdlwindow;
   257 
   258     enum xdg_toplevel_state *state;
   259     SDL_bool fullscreen = SDL_FALSE;
   260     wl_array_for_each(state, states) {
   261         if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN) {
   262             fullscreen = SDL_TRUE;
   263         }
   264      }
   265 
   266     if (!fullscreen) {
   267         if (width == 0 || height == 0) {
   268             width = window->windowed.w;
   269             height = window->windowed.h;
   270         }
   271 
   272         /* xdg_toplevel spec states that this is a suggestion.
   273            Ignore if less than or greater than max/min size. */
   274 
   275         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
   276             if (window->max_w > 0) {
   277                 width = SDL_min(width, window->max_w);
   278             }
   279             width = SDL_max(width, window->min_w);
   280 
   281             if (window->max_h > 0) {
   282                 height = SDL_min(height, window->max_h);
   283             }
   284             height = SDL_max(height, window->min_h);
   285         } else {
   286             wind->resize.width = window->w;
   287             wind->resize.height = window->h;
   288             return;
   289         }
   290     }
   291 
   292     if (width == 0 || height == 0) {
   293         wind->resize.width = window->w;
   294         wind->resize.height = window->h;
   295         return;
   296     }
   297 
   298     wind->resize.width = width;
   299     wind->resize.height = height;
   300 }
   301 
   302 static void
   303 handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel)
   304 {
   305     SDL_WindowData *window = (SDL_WindowData *)data;
   306     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   307 }
   308 
   309 static const struct xdg_toplevel_listener toplevel_listener_xdg = {
   310     handle_configure_xdg_toplevel,
   311     handle_close_xdg_toplevel
   312 };
   313 
   314 
   315 
   316 
   317 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   318 static void
   319 handle_onscreen_visibility(void *data,
   320         struct qt_extended_surface *qt_extended_surface, int32_t visible)
   321 {
   322 }
   323 
   324 static void
   325 handle_set_generic_property(void *data,
   326         struct qt_extended_surface *qt_extended_surface, const char *name,
   327         struct wl_array *value)
   328 {
   329 }
   330 
   331 static void
   332 handle_close(void *data, struct qt_extended_surface *qt_extended_surface)
   333 {
   334     SDL_WindowData *window = (SDL_WindowData *)data;
   335     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   336 }
   337 
   338 static const struct qt_extended_surface_listener extended_surface_listener = {
   339     handle_onscreen_visibility,
   340     handle_set_generic_property,
   341     handle_close,
   342 };
   343 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   344 
   345 static void
   346 update_scale_factor(SDL_WindowData *window) {
   347    float old_factor = window->scale_factor, new_factor = 0.0;
   348    int i;
   349 
   350    if (!(window->sdlwindow->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
   351        return;
   352    }
   353 
   354    if (!window->num_outputs) {
   355        new_factor = old_factor;
   356    }
   357 
   358    if (FULLSCREEN_VISIBLE(window->sdlwindow) && window->sdlwindow->fullscreen_mode.driverdata) {
   359        new_factor = ((SDL_WaylandOutputData*)(wl_output_get_user_data(window->sdlwindow->fullscreen_mode.driverdata)))->scale_factor;
   360    }
   361 
   362    for (i = 0; i < window->num_outputs; i++) {
   363        float factor = ((SDL_WaylandOutputData*)(wl_output_get_user_data(window->outputs[i])))->scale_factor;
   364        if (factor > new_factor) {
   365            new_factor = factor;
   366        }
   367    }
   368 
   369    if (new_factor != old_factor) {
   370        /* force the resize event to trigger, as the logical size didn't change */
   371        window->resize.width = window->sdlwindow->w;
   372        window->resize.height = window->sdlwindow->h;
   373        window->resize.scale_factor = new_factor;
   374        window->resize.pending = SDL_TRUE;
   375    }
   376 }
   377 
   378 static void
   379 handle_surface_enter(void *data, struct wl_surface *surface,
   380         struct wl_output *output) {
   381     SDL_WindowData *window = data;
   382 
   383     window->outputs = SDL_realloc(window->outputs, (window->num_outputs + 1) * sizeof *window->outputs);
   384     window->outputs[window->num_outputs++] = output;
   385     update_scale_factor(window);
   386 }
   387 
   388 static void
   389 handle_surface_leave(void *data, struct wl_surface *surface,
   390         struct wl_output *output) {
   391     SDL_WindowData *window = data;
   392     int i;
   393 
   394     if (window->num_outputs > 1) {
   395        struct wl_output **new_outputs = SDL_malloc((window->num_outputs - 1) * sizeof *window->outputs), **iter = new_outputs;
   396        for (i=0; i < window->num_outputs; i++) {
   397            if (window->outputs[i] != output) {
   398                *iter = window->outputs[i];
   399                iter++;
   400            }
   401        }
   402        SDL_free(window->outputs);
   403        window->outputs = new_outputs;
   404        window->num_outputs--;
   405     } else {
   406        window->num_outputs = 0;
   407        SDL_free(window->outputs);
   408        window->outputs = NULL;
   409     }
   410 
   411     update_scale_factor(window);
   412 }
   413 
   414 static const struct wl_surface_listener surface_listener = {
   415     handle_surface_enter,
   416     handle_surface_leave
   417 };
   418 
   419 SDL_bool
   420 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   421 {
   422     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   423     const Uint32 version = ((((Uint32) info->version.major) * 1000000) +
   424                             (((Uint32) info->version.minor) * 10000) +
   425                             (((Uint32) info->version.patch)));
   426 
   427     /* Before 2.0.6, it was possible to build an SDL with Wayland support
   428        (SDL_SysWMinfo will be large enough to hold Wayland info), but build
   429        your app against SDL headers that didn't have Wayland support
   430        (SDL_SysWMinfo could be smaller than Wayland needs. This would lead
   431        to an app properly using SDL_GetWindowWMInfo() but we'd accidentally
   432        overflow memory on the stack or heap. To protect against this, we've
   433        padded out the struct unconditionally in the headers and Wayland will
   434        just return an error for older apps using this function. Those apps
   435        will need to be recompiled against newer headers or not use Wayland,
   436        maybe by forcing SDL_VIDEODRIVER=x11. */
   437     if (version < 2000006) {
   438         info->subsystem = SDL_SYSWM_UNKNOWN;
   439         SDL_SetError("Version must be 2.0.6 or newer");
   440         return SDL_FALSE;
   441     }
   442 
   443     info->info.wl.display = data->waylandData->display;
   444     info->info.wl.surface = data->surface;
   445     info->info.wl.shell_surface = data->shell_surface.wl;
   446     info->subsystem = SDL_SYSWM_WAYLAND;
   447 
   448     return SDL_TRUE;
   449 }
   450 
   451 int
   452 Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
   453 {
   454     return 0;  /* just succeed, the real work is done elsewhere. */
   455 }
   456 
   457 static void
   458 SetFullscreen(_THIS, SDL_Window * window, struct wl_output *output)
   459 {
   460     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
   461     SDL_WindowData *wind = window->driverdata;
   462 
   463     if (viddata->shell.xdg) {
   464         if (output) {
   465             xdg_toplevel_set_fullscreen(wind->shell_surface.xdg.roleobj.toplevel, output);
   466         } else {
   467             xdg_toplevel_unset_fullscreen(wind->shell_surface.xdg.roleobj.toplevel);
   468         }
   469     } else if (viddata->shell.zxdg) {
   470         if (output) {
   471             zxdg_toplevel_v6_set_fullscreen(wind->shell_surface.zxdg.roleobj.toplevel, output);
   472         } else {
   473             zxdg_toplevel_v6_unset_fullscreen(wind->shell_surface.zxdg.roleobj.toplevel);
   474         }
   475     } else {
   476         if (output) {
   477             wl_shell_surface_set_fullscreen(wind->shell_surface.wl,
   478                                             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
   479                                             0, output);
   480         } else {
   481             wl_shell_surface_set_toplevel(wind->shell_surface.wl);
   482         }
   483     }
   484 
   485     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   486 }
   487 
   488 void Wayland_ShowWindow(_THIS, SDL_Window *window)
   489 {
   490     struct wl_output *output = (struct wl_output *) window->fullscreen_mode.driverdata;
   491     SetFullscreen(_this, window, (window->flags & SDL_WINDOW_FULLSCREEN) ? output : NULL);
   492 }
   493 
   494 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   495 static void SDLCALL
   496 QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
   497         const char *oldValue, const char *newValue)
   498 {
   499     struct qt_extended_surface *qt_extended_surface = userdata;
   500 
   501     if (name == NULL) {
   502         return;
   503     }
   504 
   505     if (strcmp(name, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION) == 0) {
   506         int32_t orientation = QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION;
   507 
   508         if (newValue != NULL) {
   509             if (strcmp(newValue, "portrait") == 0) {
   510                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION;
   511             } else if (strcmp(newValue, "landscape") == 0) {
   512                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION;
   513             } else if (strcmp(newValue, "inverted-portrait") == 0) {
   514                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION;
   515             } else if (strcmp(newValue, "inverted-landscape") == 0) {
   516                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION;
   517             }
   518         }
   519 
   520         qt_extended_surface_set_content_orientation(qt_extended_surface, orientation);
   521     } else if (strcmp(name, SDL_HINT_QTWAYLAND_WINDOW_FLAGS) == 0) {
   522         uint32_t flags = 0;
   523 
   524         if (newValue != NULL) {
   525             char *tmp = strdup(newValue);
   526             char *saveptr = NULL;
   527 
   528             char *flag = strtok_r(tmp, " ", &saveptr);
   529             while (flag) {
   530                 if (strcmp(flag, "OverridesSystemGestures") == 0) {
   531                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES;
   532                 } else if (strcmp(flag, "StaysOnTop") == 0) {
   533                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
   534                 } else if (strcmp(flag, "BypassWindowManager") == 0) {
   535                     // See https://github.com/qtproject/qtwayland/commit/fb4267103d
   536                     flags |= 4 /* QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER */;
   537                 }
   538 
   539                 flag = strtok_r(NULL, " ", &saveptr);
   540             }
   541 
   542             free(tmp);
   543         }
   544 
   545         qt_extended_surface_set_window_flags(qt_extended_surface, flags);
   546     }
   547 }
   548 
   549 static void QtExtendedSurface_Subscribe(struct qt_extended_surface *surface, const char *name)
   550 {
   551     SDL_AddHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   552 }
   553 
   554 static void QtExtendedSurface_Unsubscribe(struct qt_extended_surface *surface, const char *name)
   555 {
   556     SDL_DelHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   557 }
   558 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   559 
   560 void
   561 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   562                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   563 {
   564     struct wl_output *output = ((SDL_WaylandOutputData*) _display->driverdata)->output;
   565     SetFullscreen(_this, window, fullscreen ? output : NULL);
   566 }
   567 
   568 void
   569 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   570 {
   571     SDL_WindowData *wind = window->driverdata;
   572     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
   573 
   574     if (viddata->shell.xdg) {
   575     } else if (viddata->shell.zxdg) {
   576     } else {
   577         wl_shell_surface_set_toplevel(wind->shell_surface.wl);
   578     }
   579 
   580     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   581 }
   582 
   583 void
   584 Wayland_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
   585 {
   586     SDL_WindowData *wind = window->driverdata;
   587     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
   588     if ((viddata->decoration_manager) && (wind->server_decoration)) {
   589         const enum zxdg_toplevel_decoration_v1_mode mode = bordered ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
   590         zxdg_toplevel_decoration_v1_set_mode(wind->server_decoration, mode);
   591     } else if ((viddata->kwin_server_decoration_manager) && (wind->kwin_server_decoration)) {
   592         const enum org_kde_kwin_server_decoration_mode mode = bordered ? ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER : ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE;
   593         org_kde_kwin_server_decoration_request_mode(wind->kwin_server_decoration, mode);
   594     }
   595 }
   596 
   597 void
   598 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   599 {
   600     SDL_WindowData *wind = window->driverdata;
   601     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   602 
   603     if (viddata->shell.xdg) {
   604         xdg_toplevel_set_maximized(wind->shell_surface.xdg.roleobj.toplevel);
   605     } else if (viddata->shell.zxdg) {
   606         zxdg_toplevel_v6_set_maximized(wind->shell_surface.zxdg.roleobj.toplevel);
   607     } else {
   608         wl_shell_surface_set_maximized(wind->shell_surface.wl, NULL);
   609     }
   610 
   611     WAYLAND_wl_display_flush( viddata->display );
   612 }
   613 
   614 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   615 {
   616     SDL_WindowData *data;
   617     SDL_VideoData *c;
   618     struct wl_region *region;
   619 
   620     data = calloc(1, sizeof *data);
   621     if (data == NULL)
   622         return SDL_OutOfMemory();
   623 
   624     c = _this->driverdata;
   625     window->driverdata = data;
   626 
   627     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   628         SDL_GL_LoadLibrary(NULL);
   629         window->flags |= SDL_WINDOW_OPENGL;
   630     }
   631 
   632     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   633         window->x = 0;
   634     }
   635     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   636         window->y = 0;
   637     }
   638 
   639     data->waylandData = c;
   640     data->sdlwindow = window;
   641 
   642     data->scale_factor = 1.0;
   643 
   644     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   645         int i;
   646         for (i=0; i < SDL_GetVideoDevice()->num_displays; i++) {
   647             float scale = ((SDL_WaylandOutputData*)SDL_GetVideoDevice()->displays[i].driverdata)->scale_factor;
   648             if (scale > data->scale_factor) {
   649                 data->scale_factor = scale;
   650             }
   651         }
   652     }
   653 
   654     data->resize.pending = SDL_FALSE;
   655     data->resize.width = window->w;
   656     data->resize.height = window->h;
   657     data->resize.scale_factor = data->scale_factor;
   658 
   659     data->outputs = NULL;
   660     data->num_outputs = 0;
   661 
   662     data->surface =
   663         wl_compositor_create_surface(c->compositor);
   664     wl_surface_add_listener(data->surface, &surface_listener, data);
   665 
   666     if (c->shell.xdg) {
   667         data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface);
   668         /* !!! FIXME: add popup role */
   669         data->shell_surface.xdg.roleobj.toplevel = xdg_surface_get_toplevel(data->shell_surface.xdg.surface);
   670         xdg_toplevel_add_listener(data->shell_surface.xdg.roleobj.toplevel, &toplevel_listener_xdg, data);
   671         xdg_toplevel_set_app_id(data->shell_surface.xdg.roleobj.toplevel, c->classname);
   672     } else if (c->shell.zxdg) {
   673         data->shell_surface.zxdg.surface = zxdg_shell_v6_get_xdg_surface(c->shell.zxdg, data->surface);
   674         /* !!! FIXME: add popup role */
   675         data->shell_surface.zxdg.roleobj.toplevel = zxdg_surface_v6_get_toplevel(data->shell_surface.zxdg.surface);
   676         zxdg_toplevel_v6_add_listener(data->shell_surface.zxdg.roleobj.toplevel, &toplevel_listener_zxdg, data);
   677         zxdg_toplevel_v6_set_app_id(data->shell_surface.zxdg.roleobj.toplevel, c->classname);
   678     } else {
   679         data->shell_surface.wl = wl_shell_get_shell_surface(c->shell.wl, data->surface);
   680         wl_shell_surface_set_class(data->shell_surface.wl, c->classname);
   681     }
   682 
   683 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   684     if (c->surface_extension) {
   685         data->extended_surface = qt_surface_extension_get_extended_surface(
   686                 c->surface_extension, data->surface);
   687 
   688         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   689         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   690     }
   691 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   692 
   693     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   694                                             window->w * data->scale_factor, window->h * data->scale_factor);
   695 
   696     /* Create the GLES window surface */
   697     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   698     
   699     if (data->egl_surface == EGL_NO_SURFACE) {
   700         return SDL_SetError("failed to create a window surface");
   701     }
   702 
   703     if (c->shell.xdg) {
   704         if (data->shell_surface.xdg.surface) {
   705             xdg_surface_set_user_data(data->shell_surface.xdg.surface, data);
   706             xdg_surface_add_listener(data->shell_surface.xdg.surface, &shell_surface_listener_xdg, data);
   707         }
   708     } else if (c->shell.zxdg) {
   709         if (data->shell_surface.zxdg.surface) {
   710             zxdg_surface_v6_set_user_data(data->shell_surface.zxdg.surface, data);
   711             zxdg_surface_v6_add_listener(data->shell_surface.zxdg.surface, &shell_surface_listener_zxdg, data);
   712         }
   713     } else {
   714         if (data->shell_surface.wl) {
   715             wl_shell_surface_set_user_data(data->shell_surface.wl, data);
   716             wl_shell_surface_add_listener(data->shell_surface.wl, &shell_surface_listener_wl, data);
   717         }
   718     }
   719 
   720 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   721     if (data->extended_surface) {
   722         qt_extended_surface_set_user_data(data->extended_surface, data);
   723         qt_extended_surface_add_listener(data->extended_surface,
   724                                          &extended_surface_listener, data);
   725     }
   726 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   727 
   728     if (c->decoration_manager && c->shell.xdg && data->shell_surface.xdg.surface) {
   729         data->server_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(c->decoration_manager, data->shell_surface.xdg.roleobj.toplevel);
   730         if (data->server_decoration) {
   731             const SDL_bool bordered = (window->flags & SDL_WINDOW_BORDERLESS) == 0;
   732             const enum zxdg_toplevel_decoration_v1_mode mode = bordered ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
   733             zxdg_toplevel_decoration_v1_set_mode(data->server_decoration, mode);
   734         }
   735     } else if (c->kwin_server_decoration_manager) {
   736         data->kwin_server_decoration = org_kde_kwin_server_decoration_manager_create(c->kwin_server_decoration_manager, data->surface);
   737         if (data->kwin_server_decoration) {
   738             const SDL_bool bordered = (window->flags & SDL_WINDOW_BORDERLESS) == 0;
   739             const enum org_kde_kwin_server_decoration_mode mode = bordered ? ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER : ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE;
   740             org_kde_kwin_server_decoration_request_mode(data->kwin_server_decoration, mode);
   741         }
   742     }
   743 
   744     region = wl_compositor_create_region(c->compositor);
   745     wl_region_add(region, 0, 0, window->w, window->h);
   746     wl_surface_set_opaque_region(data->surface, region);
   747     wl_region_destroy(region);
   748 
   749     if (c->relative_mouse_mode) {
   750         Wayland_input_lock_pointer(c->input);
   751     }
   752 
   753     wl_surface_commit(data->surface);
   754     WAYLAND_wl_display_flush(c->display);
   755 
   756     /* we have to wait until the surface gets a "configure" event, or
   757        use of this surface will fail. This is a new rule for xdg_shell. */
   758     if (c->shell.xdg) {
   759         if (data->shell_surface.xdg.surface) {
   760             while (!data->shell_surface.xdg.initial_configure_seen) {
   761                 WAYLAND_wl_display_flush(c->display);
   762                 WAYLAND_wl_display_dispatch(c->display);
   763             }
   764         }
   765     } else if (c->shell.zxdg) {
   766         if (data->shell_surface.zxdg.surface) {
   767             while (!data->shell_surface.zxdg.initial_configure_seen) {
   768                 WAYLAND_wl_display_flush(c->display);
   769                 WAYLAND_wl_display_dispatch(c->display);
   770             }
   771         }
   772     }
   773 
   774     return 0;
   775 }
   776 
   777 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   778 {
   779     SDL_VideoData *data = _this->driverdata;
   780     SDL_WindowData *wind = window->driverdata;
   781     struct wl_region *region;
   782 
   783     wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
   784     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
   785 
   786     region = wl_compositor_create_region(data->compositor);
   787     wl_region_add(region, 0, 0, window->w, window->h);
   788     wl_surface_set_opaque_region(wind->surface, region);
   789     wl_region_destroy(region);
   790 }
   791 
   792 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   793 {
   794     SDL_WindowData *wind = window->driverdata;
   795     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   796     
   797     if (window->title != NULL) {
   798         if (viddata->shell.xdg) {
   799             xdg_toplevel_set_title(wind->shell_surface.xdg.roleobj.toplevel, window->title);
   800         } else if (viddata->shell.zxdg) {
   801             zxdg_toplevel_v6_set_title(wind->shell_surface.zxdg.roleobj.toplevel, window->title);
   802         } else {
   803             wl_shell_surface_set_title(wind->shell_surface.wl, window->title);
   804         }
   805     }
   806 
   807     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   808 }
   809 
   810 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   811 {
   812     SDL_VideoData *data = _this->driverdata;
   813     SDL_WindowData *wind = window->driverdata;
   814 
   815     if (data) {
   816         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   817         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   818 
   819         if (wind->server_decoration) {
   820            zxdg_toplevel_decoration_v1_destroy(wind->server_decoration);
   821         }
   822 
   823         if (wind->kwin_server_decoration) {
   824             org_kde_kwin_server_decoration_release(wind->kwin_server_decoration);
   825         }
   826 
   827         if (data->shell.xdg) {
   828             if (wind->shell_surface.xdg.roleobj.toplevel) {
   829                 xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel);
   830             }
   831             if (wind->shell_surface.zxdg.surface) {
   832                 xdg_surface_destroy(wind->shell_surface.xdg.surface);
   833             }
   834         } else if (data->shell.zxdg) {
   835             if (wind->shell_surface.zxdg.roleobj.toplevel) {
   836                 zxdg_toplevel_v6_destroy(wind->shell_surface.zxdg.roleobj.toplevel);
   837             }
   838             if (wind->shell_surface.zxdg.surface) {
   839                 zxdg_surface_v6_destroy(wind->shell_surface.zxdg.surface);
   840             }
   841         } else {
   842             if (wind->shell_surface.wl) {
   843                 wl_shell_surface_destroy(wind->shell_surface.wl);
   844             }
   845         }
   846 
   847 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   848         if (wind->extended_surface) {
   849             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   850             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   851             qt_extended_surface_destroy(wind->extended_surface);
   852         }
   853 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   854         wl_surface_destroy(wind->surface);
   855 
   856         SDL_free(wind);
   857         WAYLAND_wl_display_flush(data->display);
   858     }
   859     window->driverdata = NULL;
   860 }
   861 
   862 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   863 
   864 /* vi: set ts=4 sw=4 expandtab: */