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