src/video/wayland/SDL_waylandwindow.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Jan 2018 10:03:25 -0800
changeset 11811 5d94cb6b24d3
parent 11284 3db78361e751
child 11848 2f157c1ca383
permissions -rw-r--r--
Updated copyright for 2018
     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 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 SDLCALL
   182 QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
   183         const char *oldValue, const char *newValue)
   184 {
   185     struct qt_extended_surface *qt_extended_surface = userdata;
   186 
   187     if (name == NULL) {
   188         return;
   189     }
   190 
   191     if (strcmp(name, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION) == 0) {
   192         int32_t orientation = QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION;
   193 
   194         if (newValue != NULL) {
   195             if (strcmp(newValue, "portrait") == 0) {
   196                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION;
   197             } else if (strcmp(newValue, "landscape") == 0) {
   198                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION;
   199             } else if (strcmp(newValue, "inverted-portrait") == 0) {
   200                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION;
   201             } else if (strcmp(newValue, "inverted-landscape") == 0) {
   202                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION;
   203             }
   204         }
   205 
   206         qt_extended_surface_set_content_orientation(qt_extended_surface, orientation);
   207     } else if (strcmp(name, SDL_HINT_QTWAYLAND_WINDOW_FLAGS) == 0) {
   208         uint32_t flags = 0;
   209 
   210         if (newValue != NULL) {
   211             char *tmp = strdup(newValue);
   212             char *saveptr = NULL;
   213 
   214             char *flag = strtok_r(tmp, " ", &saveptr);
   215             while (flag) {
   216                 if (strcmp(flag, "OverridesSystemGestures") == 0) {
   217                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES;
   218                 } else if (strcmp(flag, "StaysOnTop") == 0) {
   219                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
   220                 } else if (strcmp(flag, "BypassWindowManager") == 0) {
   221                     // See https://github.com/qtproject/qtwayland/commit/fb4267103d
   222                     flags |= 4 /* QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER */;
   223                 }
   224 
   225                 flag = strtok_r(NULL, " ", &saveptr);
   226             }
   227 
   228             free(tmp);
   229         }
   230 
   231         qt_extended_surface_set_window_flags(qt_extended_surface, flags);
   232     }
   233 }
   234 
   235 static void QtExtendedSurface_Subscribe(struct qt_extended_surface *surface, const char *name)
   236 {
   237     SDL_AddHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   238 }
   239 
   240 static void QtExtendedSurface_Unsubscribe(struct qt_extended_surface *surface, const char *name)
   241 {
   242     SDL_DelHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   243 }
   244 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   245 
   246 void
   247 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   248                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   249 {
   250     SDL_WindowData *wind = window->driverdata;
   251 
   252     if (fullscreen)
   253         wl_shell_surface_set_fullscreen(wind->shell_surface,
   254                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
   255                                         0, (struct wl_output *)_display->driverdata);
   256     else
   257         wl_shell_surface_set_toplevel(wind->shell_surface);
   258 
   259     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   260 }
   261 
   262 void
   263 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   264 {
   265     SDL_WindowData *wind = window->driverdata;
   266 
   267     wl_shell_surface_set_toplevel(wind->shell_surface);
   268 
   269     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   270 }
   271 
   272 void
   273 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   274 {
   275     SDL_WindowData *wind = window->driverdata;
   276 
   277     wl_shell_surface_set_maximized(wind->shell_surface, NULL);
   278 
   279     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   280 }
   281 
   282 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   283 {
   284     SDL_WindowData *data;
   285     SDL_VideoData *c;
   286     struct wl_region *region;
   287 
   288     data = calloc(1, sizeof *data);
   289     if (data == NULL)
   290         return SDL_OutOfMemory();
   291 
   292     c = _this->driverdata;
   293     window->driverdata = data;
   294 
   295     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   296         SDL_GL_LoadLibrary(NULL);
   297         window->flags |= SDL_WINDOW_OPENGL;
   298     }
   299 
   300     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   301         window->x = 0;
   302     }
   303     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   304         window->y = 0;
   305     }
   306 
   307     data->waylandData = c;
   308     data->sdlwindow = window;
   309 
   310     data->surface =
   311         wl_compositor_create_surface(c->compositor);
   312     wl_surface_set_user_data(data->surface, data);
   313     data->shell_surface = wl_shell_get_shell_surface(c->shell,
   314                                                      data->surface);
   315     wl_shell_surface_set_class (data->shell_surface, c->classname);
   316 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   317     if (c->surface_extension) {
   318         data->extended_surface = qt_surface_extension_get_extended_surface(
   319                 c->surface_extension, data->surface);
   320 
   321         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   322         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   323     }
   324 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   325 
   326     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   327                                             window->w, window->h);
   328 
   329     /* Create the GLES window surface */
   330     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   331     
   332     if (data->egl_surface == EGL_NO_SURFACE) {
   333         return SDL_SetError("failed to create a window surface");
   334     }
   335 
   336     if (data->shell_surface) {
   337         wl_shell_surface_set_user_data(data->shell_surface, data);
   338         wl_shell_surface_add_listener(data->shell_surface,
   339                                       &shell_surface_listener, data);
   340     }
   341 
   342 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   343     if (data->extended_surface) {
   344         qt_extended_surface_set_user_data(data->extended_surface, data);
   345         qt_extended_surface_add_listener(data->extended_surface,
   346                                          &extended_surface_listener, data);
   347     }
   348 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   349 
   350     region = wl_compositor_create_region(c->compositor);
   351     wl_region_add(region, 0, 0, window->w, window->h);
   352     wl_surface_set_opaque_region(data->surface, region);
   353     wl_region_destroy(region);
   354 
   355     if (c->relative_mouse_mode) {
   356         Wayland_input_lock_pointer(c->input);
   357     }
   358 
   359     WAYLAND_wl_display_flush(c->display);
   360 
   361     return 0;
   362 }
   363 
   364 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   365 {
   366     SDL_VideoData *data = _this->driverdata;
   367     SDL_WindowData *wind = window->driverdata;
   368     struct wl_region *region;
   369 
   370     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   371 
   372     region =wl_compositor_create_region(data->compositor);
   373     wl_region_add(region, 0, 0, window->w, window->h);
   374     wl_surface_set_opaque_region(wind->surface, region);
   375     wl_region_destroy(region);
   376 }
   377 
   378 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   379 {
   380     SDL_WindowData *wind = window->driverdata;
   381     
   382     if (window->title != NULL) {
   383         wl_shell_surface_set_title(wind->shell_surface, window->title);
   384     }
   385 
   386     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   387 }
   388 
   389 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   390 {
   391     SDL_VideoData *data = _this->driverdata;
   392     SDL_WindowData *wind = window->driverdata;
   393 
   394     if (data) {
   395         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   396         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   397 
   398         if (wind->shell_surface)
   399             wl_shell_surface_destroy(wind->shell_surface);
   400 
   401 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   402         if (wind->extended_surface) {
   403             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   404             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   405             qt_extended_surface_destroy(wind->extended_surface);
   406         }
   407 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   408         wl_surface_destroy(wind->surface);
   409 
   410         SDL_free(wind);
   411         WAYLAND_wl_display_flush(data->display);
   412     }
   413     window->driverdata = NULL;
   414 }
   415 
   416 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   417 
   418 /* vi: set ts=4 sw=4 expandtab: */