src/video/wayland/SDL_waylandwindow.c
author Thomas Perl <m@thp.io>
Sun, 13 Nov 2016 10:39:04 +0100
changeset 10614 63c2c6c8762c
parent 10492 786e10ab72d8
child 10737 3406a0f8b041
permissions -rw-r--r--
[qtwayland] Set orientation and window flags via SDL hints
     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 #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 
   133     info->info.wl.display = data->waylandData->display;
   134     info->info.wl.surface = data->surface;
   135     info->info.wl.shell_surface = data->shell_surface;
   136     info->subsystem = SDL_SYSWM_WAYLAND;
   137 
   138     return SDL_TRUE;
   139 }
   140 
   141 int
   142 Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
   143 {
   144     return 0;  /* just succeed, the real work is done elsewhere. */
   145 }
   146 
   147 void Wayland_ShowWindow(_THIS, SDL_Window *window)
   148 {
   149     SDL_WindowData *wind = window->driverdata;
   150 
   151     if (window->flags & SDL_WINDOW_FULLSCREEN)
   152         wl_shell_surface_set_fullscreen(wind->shell_surface,
   153                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
   154                                         0, (struct wl_output *)window->fullscreen_mode.driverdata);
   155     else
   156         wl_shell_surface_set_toplevel(wind->shell_surface);
   157 
   158     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   159 }
   160 
   161 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   162 static void QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
   163         const char *oldValue, const char *newValue)
   164 {
   165     struct qt_extended_surface *qt_extended_surface = userdata;
   166 
   167     if (name == NULL) {
   168         return;
   169     }
   170 
   171     if (strcmp(name, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION) == 0) {
   172         int32_t orientation = QT_EXTENDED_SURFACE_ORIENTATION_PRIMARYORIENTATION;
   173 
   174         if (newValue != NULL) {
   175             if (strcmp(newValue, "portrait") == 0) {
   176                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_PORTRAITORIENTATION;
   177             } else if (strcmp(newValue, "landscape") == 0) {
   178                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_LANDSCAPEORIENTATION;
   179             } else if (strcmp(newValue, "inverted-portrait") == 0) {
   180                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDPORTRAITORIENTATION;
   181             } else if (strcmp(newValue, "inverted-landscape") == 0) {
   182                 orientation = QT_EXTENDED_SURFACE_ORIENTATION_INVERTEDLANDSCAPEORIENTATION;
   183             }
   184         }
   185 
   186         qt_extended_surface_set_content_orientation(qt_extended_surface, orientation);
   187     } else if (strcmp(name, SDL_HINT_QTWAYLAND_WINDOW_FLAGS) == 0) {
   188         uint32_t flags = 0;
   189 
   190         if (newValue != NULL) {
   191             char *tmp = strdup(newValue);
   192             char *saveptr = NULL;
   193 
   194             char *flag = strtok_r(tmp, " ", &saveptr);
   195             while (flag) {
   196                 if (strcmp(flag, "OverridesSystemGestures") == 0) {
   197                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_OVERRIDESSYSTEMGESTURES;
   198                 } else if (strcmp(flag, "StaysOnTop") == 0) {
   199                     flags |= QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
   200                 } else if (strcmp(flag, "BypassWindowManager") == 0) {
   201                     // See https://github.com/qtproject/qtwayland/commit/fb4267103d
   202                     flags |= 4 /* QT_EXTENDED_SURFACE_WINDOWFLAG_BYPASSWINDOWMANAGER */;
   203                 }
   204 
   205                 flag = strtok_r(NULL, " ", &saveptr);
   206             }
   207 
   208             free(tmp);
   209         }
   210 
   211         qt_extended_surface_set_window_flags(qt_extended_surface, flags);
   212     }
   213 }
   214 
   215 static void QtExtendedSurface_Subscribe(struct qt_extended_surface *surface, const char *name)
   216 {
   217     SDL_AddHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   218 }
   219 
   220 static void QtExtendedSurface_Unsubscribe(struct qt_extended_surface *surface, const char *name)
   221 {
   222     SDL_DelHintCallback(name, QtExtendedSurface_OnHintChanged, surface);
   223 }
   224 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   225 
   226 void
   227 Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   228                             SDL_VideoDisplay * _display, SDL_bool fullscreen)
   229 {
   230     SDL_WindowData *wind = window->driverdata;
   231 
   232     if (fullscreen)
   233         wl_shell_surface_set_fullscreen(wind->shell_surface,
   234                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE,
   235                                         0, (struct wl_output *)_display->driverdata);
   236     else
   237         wl_shell_surface_set_toplevel(wind->shell_surface);
   238 
   239     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   240 }
   241 
   242 void
   243 Wayland_RestoreWindow(_THIS, SDL_Window * window)
   244 {
   245     SDL_WindowData *wind = window->driverdata;
   246 
   247     wl_shell_surface_set_toplevel(wind->shell_surface);
   248 
   249     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   250 }
   251 
   252 void
   253 Wayland_MaximizeWindow(_THIS, SDL_Window * window)
   254 {
   255     SDL_WindowData *wind = window->driverdata;
   256 
   257     wl_shell_surface_set_maximized(wind->shell_surface, NULL);
   258 
   259     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   260 }
   261 
   262 int Wayland_CreateWindow(_THIS, SDL_Window *window)
   263 {
   264     SDL_WindowData *data;
   265     SDL_VideoData *c;
   266     struct wl_region *region;
   267 
   268     data = calloc(1, sizeof *data);
   269     if (data == NULL)
   270         return SDL_OutOfMemory();
   271 
   272     c = _this->driverdata;
   273     window->driverdata = data;
   274 
   275     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   276         SDL_GL_LoadLibrary(NULL);
   277         window->flags |= SDL_WINDOW_OPENGL;
   278     }
   279 
   280     if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   281         window->x = 0;
   282     }
   283     if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   284         window->y = 0;
   285     }
   286 
   287     data->waylandData = c;
   288     data->sdlwindow = window;
   289 
   290     data->surface =
   291         wl_compositor_create_surface(c->compositor);
   292     wl_surface_set_user_data(data->surface, data);
   293     data->shell_surface = wl_shell_get_shell_surface(c->shell,
   294                                                      data->surface);
   295     wl_shell_surface_set_class (data->shell_surface, c->classname);
   296 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   297     if (c->surface_extension) {
   298         data->extended_surface = qt_surface_extension_get_extended_surface(
   299                 c->surface_extension, data->surface);
   300 
   301         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   302         QtExtendedSurface_Subscribe(data->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   303     }
   304 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   305 
   306     data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   307                                             window->w, window->h);
   308 
   309     /* Create the GLES window surface */
   310     data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   311     
   312     if (data->egl_surface == EGL_NO_SURFACE) {
   313         return SDL_SetError("failed to create a window surface");
   314     }
   315 
   316     if (data->shell_surface) {
   317         wl_shell_surface_set_user_data(data->shell_surface, data);
   318         wl_shell_surface_add_listener(data->shell_surface,
   319                                       &shell_surface_listener, data);
   320     }
   321 
   322 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   323     if (data->extended_surface) {
   324         qt_extended_surface_set_user_data(data->extended_surface, data);
   325         qt_extended_surface_add_listener(data->extended_surface,
   326                                          &extended_surface_listener, data);
   327     }
   328 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   329 
   330     region = wl_compositor_create_region(c->compositor);
   331     wl_region_add(region, 0, 0, window->w, window->h);
   332     wl_surface_set_opaque_region(data->surface, region);
   333     wl_region_destroy(region);
   334 
   335     if (c->relative_mouse_mode) {
   336         Wayland_input_lock_pointer(c->input);
   337     }
   338 
   339     WAYLAND_wl_display_flush(c->display);
   340 
   341     return 0;
   342 }
   343 
   344 void Wayland_SetWindowSize(_THIS, SDL_Window * window)
   345 {
   346     SDL_VideoData *data = _this->driverdata;
   347     SDL_WindowData *wind = window->driverdata;
   348     struct wl_region *region;
   349 
   350     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   351 
   352     region =wl_compositor_create_region(data->compositor);
   353     wl_region_add(region, 0, 0, window->w, window->h);
   354     wl_surface_set_opaque_region(wind->surface, region);
   355     wl_region_destroy(region);
   356 }
   357 
   358 void Wayland_SetWindowTitle(_THIS, SDL_Window * window)
   359 {
   360     SDL_WindowData *wind = window->driverdata;
   361     
   362     if (window->title != NULL) {
   363         wl_shell_surface_set_title(wind->shell_surface, window->title);
   364     }
   365 
   366     WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display );
   367 }
   368 
   369 void Wayland_DestroyWindow(_THIS, SDL_Window *window)
   370 {
   371     SDL_VideoData *data = _this->driverdata;
   372     SDL_WindowData *wind = window->driverdata;
   373 
   374     if (data) {
   375         SDL_EGL_DestroySurface(_this, wind->egl_surface);
   376         WAYLAND_wl_egl_window_destroy(wind->egl_window);
   377 
   378         if (wind->shell_surface)
   379             wl_shell_surface_destroy(wind->shell_surface);
   380 
   381 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
   382         if (wind->extended_surface) {
   383             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION);
   384             QtExtendedSurface_Unsubscribe(wind->extended_surface, SDL_HINT_QTWAYLAND_WINDOW_FLAGS);
   385             qt_extended_surface_destroy(wind->extended_surface);
   386         }
   387 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   388         wl_surface_destroy(wind->surface);
   389 
   390         SDL_free(wind);
   391         WAYLAND_wl_display_flush(data->display);
   392     }
   393     window->driverdata = NULL;
   394 }
   395 
   396 #endif /* SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL */
   397 
   398 /* vi: set ts=4 sw=4 expandtab: */