src/video/wayland/SDL_waylandwindow.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 07 Oct 2016 18:11:03 -0700
changeset 10492 786e10ab72d8
parent 10304 ee83e0b4a36f
child 10614 63c2c6c8762c
permissions -rw-r--r--
Fixed bug 3063 - Wayland: SDL resizes EGL surface to 0x0.

x414e54

Wayland will sometimes send empty resize events (0 width and 0 height) to the client. I have not worked out the exact conditions a client would receive these but I can assume it might be if the window is offscreen or not mapped yet.

This causes issues with some SDL clients as they receive the 0x0 event and unexpected resize event or might not request to resize back to the correct size.

As per the wl_shell Wayland spec configure events are only a suggestion and the client is free to ignore or pick a different size (this is how min/max and fixed aspect ratio is supped to be implemented).

A patch is attached but is just the first iteration and I will fix any issues such as checking for FULLSCREEN/MAXIMIZED or RESIZABLE flags unless someone else fixes this first.

I have update to take into account non resizable and fullscreen windows. Also adding in maximize/restore and title functions for Wayland.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #include "../../SDL_internal.h"
    23 
    24 #if SDL_VIDEO_DRIVER_WAYLAND && 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 
    35 static void
    36 handle_ping(void *data, struct wl_shell_surface *shell_surface,
    37             uint32_t serial)
    38 {
    39     wl_shell_surface_pong(shell_surface, serial);
    40 }
    41 
    42 static void
    43 handle_configure(void *data, struct wl_shell_surface *shell_surface,
    44                  uint32_t edges, int32_t width, int32_t height)
    45 {
    46     SDL_WindowData *wind = (SDL_WindowData *)data;
    47     SDL_Window *window = wind->sdlwindow;
    48     struct wl_region *region;
    49 
    50     /* wl_shell_surface spec states that this is a suggestion.
    51        Ignore if less than or greater than max/min size. */
    52 
    53     if (width == 0 || height == 0) {
    54         return;
    55     }
    56 
    57     if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
    58         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
    59             if (window->max_w > 0) {
    60                 width = SDL_min(width, window->max_w);
    61             } 
    62             width = SDL_max(width, window->min_w);
    63 
    64             if (window->max_h > 0) {
    65                 height = SDL_min(height, window->max_h);
    66             }
    67             height = SDL_max(height, window->min_h);
    68         } else {
    69             return;
    70         }
    71     }
    72 
    73     if (width == window->w && height == window->h) {
    74         return;
    75     }
    76 
    77     window->w = width;
    78     window->h = height;
    79     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
    80 
    81     region = wl_compositor_create_region(wind->waylandData->compositor);
    82     wl_region_add(region, 0, 0, window->w, window->h);
    83     wl_surface_set_opaque_region(wind->surface, region);
    84     wl_region_destroy(region);
    85     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
    86 }
    87 
    88 static void
    89 handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
    90 {
    91 }
    92 
    93 static const struct wl_shell_surface_listener shell_surface_listener = {
    94     handle_ping,
    95     handle_configure,
    96     handle_popup_done
    97 };
    98 
    99 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   100 static void
   101 handle_onscreen_visibility(void *data,
   102         struct qt_extended_surface *qt_extended_surface, int32_t visible)
   103 {
   104 }
   105 
   106 static void
   107 handle_set_generic_property(void *data,
   108         struct qt_extended_surface *qt_extended_surface, const char *name,
   109         struct wl_array *value)
   110 {
   111 }
   112 
   113 static void
   114 handle_close(void *data, struct qt_extended_surface *qt_extended_surface)
   115 {
   116     SDL_WindowData *window = (SDL_WindowData *)data;
   117     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   118 }
   119 
   120 static const struct qt_extended_surface_listener extended_surface_listener = {
   121     handle_onscreen_visibility,
   122     handle_set_generic_property,
   123     handle_close,
   124 };
   125 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   126 
   127 SDL_bool
   128 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   129 {
   130     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   131 
   132     info->info.wl.display = data->waylandData->display;
   133     info->info.wl.surface = data->surface;
   134     info->info.wl.shell_surface = data->shell_surface;
   135     info->subsystem = SDL_SYSWM_WAYLAND;
   136 
   137     return SDL_TRUE;
   138 }
   139 
   140 int
   141 Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
   142 {
   143     return 0;  /* just succeed, the real work is done elsewhere. */
   144 }
   145 
   146 void Wayland_ShowWindow(_THIS, SDL_Window *window)
   147 {
   148     SDL_WindowData *wind = window->driverdata;
   149 
   150     if (window->flags & SDL_WINDOW_FULLSCREEN)
   151         wl_shell_surface_set_fullscreen(wind->shell_surface,
   152                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
   153                                         0, (struct wl_output *)window->fullscreen_mode.driverdata);
   154     else
   155         wl_shell_surface_set_toplevel(wind->shell_surface);
   156 
   157     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   158 }
   159 
   160 void
   161 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   162                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   163 {
   164     SDL_WindowData *wind = window->driverdata;
   165 
   166     if (fullscreen)
   167         wl_shell_surface_set_fullscreen(wind->shell_surface,
   168                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
   169                                         0, (struct wl_output *)_display->driverdata);
   170     else
   171         wl_shell_surface_set_toplevel(wind->shell_surface);
   172 
   173     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   174 }
   175 
   176 void
   177 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   178 {
   179     SDL_WindowData *wind = window->driverdata;
   180 
   181     wl_shell_surface_set_toplevel(wind->shell_surface);
   182 
   183     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   184 }
   185 
   186 void
   187 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   188 {
   189     SDL_WindowData *wind = window->driverdata;
   190 
   191     wl_shell_surface_set_maximized(wind->shell_surface, NULL);
   192 
   193     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   194 }
   195 
   196 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   197 {
   198     SDL_WindowData *data;
   199     SDL_VideoData *c;
   200     struct wl_region *region;
   201 
   202     data = calloc(1, sizeof *data);
   203     if (data == NULL)
   204         return SDL_OutOfMemory();
   205 
   206     c = _this->driverdata;
   207     window->driverdata = data;
   208 
   209     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   210         SDL_GL_LoadLibrary(NULL);
   211         window->flags |= SDL_WINDOW_OPENGL;
   212     }
   213 
   214     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   215         window->x = 0;
   216     }
   217     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   218         window->y = 0;
   219     }
   220 
   221     data->waylandData = c;
   222     data->sdlwindow = window;
   223 
   224     data->surface =
   225         wl_compositor_create_surface(c->compositor);
   226     wl_surface_set_user_data(data->surface, data);
   227     data->shell_surface = wl_shell_get_shell_surface(c->shell,
   228                                                      data->surface);
   229     wl_shell_surface_set_class (data->shell_surface, c->classname);
   230 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH    
   231     if (c->surface_extension) {
   232         data->extended_surface = qt_surface_extension_get_extended_surface(
   233                 c->surface_extension, data->surface);
   234     }
   235 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   236 
   237     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   238                                             window->w, window->h);
   239 
   240     /* Create the GLES window surface */
   241     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   242     
   243     if (data->egl_surface == EGL_NO_SURFACE) {
   244         return SDL_SetError("failed to create a window surface");
   245     }
   246 
   247     if (data->shell_surface) {
   248         wl_shell_surface_set_user_data(data->shell_surface, data);
   249         wl_shell_surface_add_listener(data->shell_surface,
   250                                       &shell_surface_listener, data);
   251     }
   252 
   253 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   254     if (data->extended_surface) {
   255         qt_extended_surface_set_user_data(data->extended_surface, data);
   256         qt_extended_surface_add_listener(data->extended_surface,
   257                                          &extended_surface_listener, data);
   258     }
   259 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   260 
   261     region = wl_compositor_create_region(c->compositor);
   262     wl_region_add(region, 0, 0, window->w, window->h);
   263     wl_surface_set_opaque_region(data->surface, region);
   264     wl_region_destroy(region);
   265 
   266     if (c->relative_mouse_mode) {
   267         Wayland_input_lock_pointer(c->input);
   268     }
   269 
   270     WAYLAND_wl_display_flush(c->display);
   271 
   272     return 0;
   273 }
   274 
   275 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   276 {
   277     SDL_VideoData *data = _this->driverdata;
   278     SDL_WindowData *wind = window->driverdata;
   279     struct wl_region *region;
   280 
   281     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   282 
   283     region =wl_compositor_create_region(data->compositor);
   284     wl_region_add(region, 0, 0, window->w, window->h);
   285     wl_surface_set_opaque_region(wind->surface, region);
   286     wl_region_destroy(region);
   287 }
   288 
   289 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   290 {
   291     SDL_WindowData *wind = window->driverdata;
   292     
   293     if (window->title != NULL) {
   294         wl_shell_surface_set_title(wind->shell_surface, window->title);
   295     }
   296 
   297     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   298 }
   299 
   300 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   301 {
   302     SDL_VideoData *data = _this->driverdata;
   303     SDL_WindowData *wind = window->driverdata;
   304 
   305     if (data) {
   306         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   307         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   308 
   309         if (wind->shell_surface)
   310             wl_shell_surface_destroy(wind->shell_surface);
   311 
   312 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   313         if (wind->extended_surface)
   314             qt_extended_surface_destroy(wind->extended_surface);
   315 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   316         wl_surface_destroy(wind->surface);
   317 
   318         SDL_free(wind);
   319         WAYLAND_wl_display_flush(data->display);
   320     }
   321     window->driverdata = NULL;
   322 }
   323 
   324 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   325 
   326 /* vi: set ts=4 sw=4 expandtab: */