src/video/wayland/SDL_waylandwindow.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 11 Jun 2017 22:30:39 +0200
changeset 11092 9c9b7b7e46e1
parent 11088 69452f9839d5
child 11284 3db78361e751
permissions -rw-r--r--
Fixed missing error messages for SDL_GetWindowWMInfo().
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 static void
    37 handle_ping(void *data, struct wl_shell_surface *shell_surface,
    38             uint32_t serial)
    39 {
    40     wl_shell_surface_pong(shell_surface, serial);
    41 }
    42 
    43 static void
    44 handle_configure(void *data, struct wl_shell_surface *shell_surface,
    45                  uint32_t edges, int32_t width, int32_t height)
    46 {
    47     SDL_WindowData *wind = (SDL_WindowData *)data;
    48     SDL_Window *window = wind->sdlwindow;
    49     struct wl_region *region;
    50 
    51     /* wl_shell_surface spec states that this is a suggestion.
    52        Ignore if less than or greater than max/min size. */
    53 
    54     if (width == 0 || height == 0) {
    55         return;
    56     }
    57 
    58     if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
    59         if ((window->flags & SDL_WINDOW_RESIZABLE)) {
    60             if (window->max_w > 0) {
    61                 width = SDL_min(width, window->max_w);
    62             } 
    63             width = SDL_max(width, window->min_w);
    64 
    65             if (window->max_h > 0) {
    66                 height = SDL_min(height, window->max_h);
    67             }
    68             height = SDL_max(height, window->min_h);
    69         } else {
    70             return;
    71         }
    72     }
    73 
    74     if (width == window->w && height == window->h) {
    75         return;
    76     }
    77 
    78     window->w = width;
    79     window->h = height;
    80     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
    81 
    82     region = wl_compositor_create_region(wind->waylandData->compositor);
    83     wl_region_add(region, 0, 0, window->w, window->h);
    84     wl_surface_set_opaque_region(wind->surface, region);
    85     wl_region_destroy(region);
    86     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
    87 }
    88 
    89 static void
    90 handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
    91 {
    92 }
    93 
    94 static const struct wl_shell_surface_listener shell_surface_listener = {
    95     handle_ping,
    96     handle_configure,
    97     handle_popup_done
    98 };
    99 
   100 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   101 static void
   102 handle_onscreen_visibility(void *data,
   103         struct qt_extended_surface *qt_extended_surface, int32_t visible)
   104 {
   105 }
   106 
   107 static void
   108 handle_set_generic_property(void *data,
   109         struct qt_extended_surface *qt_extended_surface, const char *name,
   110         struct wl_array *value)
   111 {
   112 }
   113 
   114 static void
   115 handle_close(void *data, struct qt_extended_surface *qt_extended_surface)
   116 {
   117     SDL_WindowData *window = (SDL_WindowData *)data;
   118     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
   119 }
   120 
   121 static const struct qt_extended_surface_listener extended_surface_listener = {
   122     handle_onscreen_visibility,
   123     handle_set_generic_property,
   124     handle_close,
   125 };
   126 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   127 
   128 SDL_bool
   129 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   130 {
   131     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   132     const Uint32 version = ((((Uint32) info->version.major) * 1000000) +
   133                             (((Uint32) info->version.minor) * 10000) +
   134                             (((Uint32) info->version.patch)));
   135 
   136     /* Before 2.0.6, it was possible to build an SDL with Wayland support
   137        (SDL_SysWMinfo will be large enough to hold Wayland info), but build
   138        your app against SDL headers that didn't have Wayland support
   139        (SDL_SysWMinfo could be smaller than Wayland needs. This would lead
   140        to an app properly using SDL_GetWindowWMInfo() but we'd accidentally
   141        overflow memory on the stack or heap. To protect against this, we've
   142        padded out the struct unconditionally in the headers and Wayland will
   143        just return an error for older apps using this function. Those apps
   144        will need to be recompiled against newer headers or not use Wayland,
   145        maybe by forcing SDL_VIDEODRIVER=x11. */
   146     if (version < 2000006) {
   147         info->subsystem = SDL_SYSWM_UNKNOWN;
   148         SDL_SetError("Version must be 2.0.6 or newer");
   149         return SDL_FALSE;
   150     }
   151 
   152     info->info.wl.display = data->waylandData->display;
   153     info->info.wl.surface = data->surface;
   154     info->info.wl.shell_surface = data->shell_surface;
   155     info->subsystem = SDL_SYSWM_WAYLAND;
   156 
   157     return SDL_TRUE;
   158 }
   159 
   160 int
   161 Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
   162 {
   163     return 0;  /* just succeed, the real work is done elsewhere. */
   164 }
   165 
   166 void Wayland_ShowWindow(_THIS, SDL_Window *window)
   167 {
   168     SDL_WindowData *wind = window->driverdata;
   169 
   170     if (window->flags & SDL_WINDOW_FULLSCREEN)
   171         wl_shell_surface_set_fullscreen(wind->shell_surface,
   172                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
   173                                         0, (struct wl_output *)window->fullscreen_mode.driverdata);
   174     else
   175         wl_shell_surface_set_toplevel(wind->shell_surface);
   176 
   177     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   178 }
   179 
   180 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   181 static void QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
   182         const char *oldValue, const char *newValue)
   183 {
   184     struct qt_extended_surface *qt_extended_surface = userdata;
   185 
   186     if (name == NULL) {
   187         return;
   188     }
   189 
   190     if (strcmp(name, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION) == 0) {
   191         int32_t orientation = QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION;
   192 
   193         if (newValue != NULL) {
   194             if (strcmp(newValue, "portrait") == 0) {
   195                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION;
   196             } else if (strcmp(newValue, "landscape") == 0) {
   197                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION;
   198             } else if (strcmp(newValue, "inverted-portrait") == 0) {
   199                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION;
   200             } else if (strcmp(newValue, "inverted-landscape") == 0) {
   201                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION;
   202             }
   203         }
   204 
   205         qt_extended_surface_set_content_orientation(qt_extended_surface, orientation);
   206     } else if (strcmp(name, SDL_HINT_QTWAYLAND_WINDOW_FLAGS) == 0) {
   207         uint32_t flags = 0;
   208 
   209         if (newValue != NULL) {
   210             char *tmp = strdup(newValue);
   211             char *saveptr = NULL;
   212 
   213             char *flag = strtok_r(tmp, " ", &saveptr);
   214             while (flag) {
   215                 if (strcmp(flag, "OverridesSystemGestures") == 0) {
   216                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES;
   217                 } else if (strcmp(flag, "StaysOnTop") == 0) {
   218                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
   219                 } else if (strcmp(flag, "BypassWindowManager") == 0) {
   220                     // See https://github.com/qtproject/qtwayland/commit/fb4267103d
   221                     flags |= 4 /* QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER */;
   222                 }
   223 
   224                 flag = strtok_r(NULL, " ", &saveptr);
   225             }
   226 
   227             free(tmp);
   228         }
   229 
   230         qt_extended_surface_set_window_flags(qt_extended_surface, flags);
   231     }
   232 }
   233 
   234 static void QtExtendedSurface_Subscribe(struct qt_extended_surface *surface, const char *name)
   235 {
   236     SDL_AddHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   237 }
   238 
   239 static void QtExtendedSurface_Unsubscribe(struct qt_extended_surface *surface, const char *name)
   240 {
   241     SDL_DelHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   242 }
   243 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   244 
   245 void
   246 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   247                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   248 {
   249     SDL_WindowData *wind = window->driverdata;
   250 
   251     if (fullscreen)
   252         wl_shell_surface_set_fullscreen(wind->shell_surface,
   253                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
   254                                         0, (struct wl_output *)_display->driverdata);
   255     else
   256         wl_shell_surface_set_toplevel(wind->shell_surface);
   257 
   258     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   259 }
   260 
   261 void
   262 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   263 {
   264     SDL_WindowData *wind = window->driverdata;
   265 
   266     wl_shell_surface_set_toplevel(wind->shell_surface);
   267 
   268     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   269 }
   270 
   271 void
   272 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   273 {
   274     SDL_WindowData *wind = window->driverdata;
   275 
   276     wl_shell_surface_set_maximized(wind->shell_surface, NULL);
   277 
   278     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   279 }
   280 
   281 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   282 {
   283     SDL_WindowData *data;
   284     SDL_VideoData *c;
   285     struct wl_region *region;
   286 
   287     data = calloc(1, sizeof *data);
   288     if (data == NULL)
   289         return SDL_OutOfMemory();
   290 
   291     c = _this->driverdata;
   292     window->driverdata = data;
   293 
   294     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   295         SDL_GL_LoadLibrary(NULL);
   296         window->flags |= SDL_WINDOW_OPENGL;
   297     }
   298 
   299     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   300         window->x = 0;
   301     }
   302     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   303         window->y = 0;
   304     }
   305 
   306     data->waylandData = c;
   307     data->sdlwindow = window;
   308 
   309     data->surface =
   310         wl_compositor_create_surface(c->compositor);
   311     wl_surface_set_user_data(data->surface, data);
   312     data->shell_surface = wl_shell_get_shell_surface(c->shell,
   313                                                      data->surface);
   314     wl_shell_surface_set_class (data->shell_surface, c->classname);
   315 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   316     if (c->surface_extension) {
   317         data->extended_surface = qt_surface_extension_get_extended_surface(
   318                 c->surface_extension, data->surface);
   319 
   320         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   321         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   322     }
   323 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   324 
   325     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   326                                             window->w, window->h);
   327 
   328     /* Create the GLES window surface */
   329     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   330     
   331     if (data->egl_surface == EGL_NO_SURFACE) {
   332         return SDL_SetError("failed to create a window surface");
   333     }
   334 
   335     if (data->shell_surface) {
   336         wl_shell_surface_set_user_data(data->shell_surface, data);
   337         wl_shell_surface_add_listener(data->shell_surface,
   338                                       &shell_surface_listener, data);
   339     }
   340 
   341 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   342     if (data->extended_surface) {
   343         qt_extended_surface_set_user_data(data->extended_surface, data);
   344         qt_extended_surface_add_listener(data->extended_surface,
   345                                          &extended_surface_listener, data);
   346     }
   347 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   348 
   349     region = wl_compositor_create_region(c->compositor);
   350     wl_region_add(region, 0, 0, window->w, window->h);
   351     wl_surface_set_opaque_region(data->surface, region);
   352     wl_region_destroy(region);
   353 
   354     if (c->relative_mouse_mode) {
   355         Wayland_input_lock_pointer(c->input);
   356     }
   357 
   358     WAYLAND_wl_display_flush(c->display);
   359 
   360     return 0;
   361 }
   362 
   363 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   364 {
   365     SDL_VideoData *data = _this->driverdata;
   366     SDL_WindowData *wind = window->driverdata;
   367     struct wl_region *region;
   368 
   369     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   370 
   371     region =wl_compositor_create_region(data->compositor);
   372     wl_region_add(region, 0, 0, window->w, window->h);
   373     wl_surface_set_opaque_region(wind->surface, region);
   374     wl_region_destroy(region);
   375 }
   376 
   377 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   378 {
   379     SDL_WindowData *wind = window->driverdata;
   380     
   381     if (window->title != NULL) {
   382         wl_shell_surface_set_title(wind->shell_surface, window->title);
   383     }
   384 
   385     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   386 }
   387 
   388 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   389 {
   390     SDL_VideoData *data = _this->driverdata;
   391     SDL_WindowData *wind = window->driverdata;
   392 
   393     if (data) {
   394         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   395         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   396 
   397         if (wind->shell_surface)
   398             wl_shell_surface_destroy(wind->shell_surface);
   399 
   400 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   401         if (wind->extended_surface) {
   402             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   403             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   404             qt_extended_surface_destroy(wind->extended_surface);
   405         }
   406 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   407         wl_surface_destroy(wind->surface);
   408 
   409         SDL_free(wind);
   410         WAYLAND_wl_display_flush(data->display);
   411     }
   412     window->driverdata = NULL;
   413 }
   414 
   415 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   416 
   417 /* vi: set ts=4 sw=4 expandtab: */