1521107e33634d7a1247e1bab112b7ffc4929d74
[platform/upstream/SDL.git] / src / video / wayland / SDL_waylandvideo.c
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
25
26 #include "SDL_video.h"
27 #include "SDL_mouse.h"
28 #include "SDL_stdinc.h"
29 #include "../../events/SDL_events_c.h"
30
31 #include "SDL_waylandvideo.h"
32 #include "SDL_waylandevents_c.h"
33 #include "SDL_waylandwindow.h"
34 #include "SDL_waylandopengles.h"
35 #include "SDL_waylandmouse.h"
36 #include "SDL_waylandtouch.h"
37 #include "SDL_waylandvulkan.h"
38
39 #include <fcntl.h>
40 #include <wayland-client.h>
41 #include <wayland-cursor.h>
42 #include <wayland-util.h>
43 #include <xkbcommon/xkbcommon.h>
44
45 #if 0
46 # ifdef USE_IVI_SHELL
47 # include "ivi-application-client-protocol.h"
48 # define IVI_SURFACE_ID 6000
49 # endif
50 #endif
51
52 # include <xdg-shell-client-protocol.h>
53 # include <tizen-extension-client-protocol.h>
54 //# include "subsurface-client-protocol.h"
55
56 #include "SDL_waylanddyn.h"
57
58 #define WAYLANDVID_DRIVER_NAME "wayland"
59 #define XDG_VERSION 5
60
61 /* Initialization/Query functions */
62 static int
63 Wayland_VideoInit(_THIS);
64
65 static void
66 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
67 static int
68 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
69
70 static void
71 Wayland_VideoQuit(_THIS);
72
73 /* Wayland driver bootstrap functions */
74 static int
75 Wayland_Available(void)
76 {
77     struct wl_display *display = NULL;
78     if (SDL_WAYLAND_LoadSymbols()) {
79         display = WAYLAND_wl_display_connect(NULL);
80         if (display != NULL) {
81             WAYLAND_wl_display_disconnect(display);
82         }
83         SDL_WAYLAND_UnloadSymbols();
84     }
85
86     return (display != NULL);
87 }
88
89 static void
90 Wayland_DeleteDevice(SDL_VideoDevice *device)
91 {
92     SDL_free(device);
93     SDL_WAYLAND_UnloadSymbols();
94 }
95
96 static SDL_VideoDevice *
97 Wayland_CreateDevice(int devindex)
98 {
99     SDL_VideoDevice *device;
100
101     if (!SDL_WAYLAND_LoadSymbols()) {
102         return NULL;
103     }
104
105     /* Initialize all variables that we clean on shutdown */
106     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
107     if (!device) {
108         SDL_WAYLAND_UnloadSymbols();
109         SDL_OutOfMemory();
110         return NULL;
111     }
112
113     /* Set the function pointers */
114     device->VideoInit = Wayland_VideoInit;
115     device->VideoQuit = Wayland_VideoQuit;
116     device->SetDisplayMode = Wayland_SetDisplayMode;
117     device->GetDisplayModes = Wayland_GetDisplayModes;
118     device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
119
120     device->PumpEvents = Wayland_PumpEvents;
121
122     device->GL_SwapWindow = Wayland_GLES_SwapWindow;
123     device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
124     device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
125     device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
126     device->GL_CreateContext = Wayland_GLES_CreateContext;
127     device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
128     device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
129     device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
130     device->GL_DeleteContext = Wayland_GLES_DeleteContext;
131
132     device->CreateWindow = Wayland_CreateWindow;
133     device->ShowWindow = Wayland_ShowWindow;
134     device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
135     device->SetWindowSize = Wayland_SetWindowSize;
136     device->DestroyWindow = Wayland_DestroyWindow;
137     device->SetWindowHitTest = Wayland_SetWindowHitTest;
138
139     device->free = Wayland_DeleteDevice;
140
141 #if SDL_VIDEO_VULKAN
142     device->vulkan_GetInstanceExtensions = Wayland_vulkan_GetInstanceExtensions;
143     device->vulkan_CreateSurface = Wayland_vulkan_CreateSurface;
144     device->vulkan_LoadLibrary = Wayland_vulkan_LoadLibrary;
145 #endif
146     return device;
147 }
148
149 VideoBootStrap Wayland_bootstrap = {
150     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
151     Wayland_Available, Wayland_CreateDevice
152 };
153
154 static void
155 display_handle_geometry(void *data,
156                         struct wl_output *output,
157                         int x, int y,
158                         int physical_width,
159                         int physical_height,
160                         int subpixel,
161                         const char *make,
162                         const char *model,
163                         int transform)
164
165 {
166     SDL_VideoDisplay *display = data;
167
168     display->name = strdup(model);
169 }
170
171 static void
172 display_handle_mode(void *data,
173                     struct wl_output *output,
174                     uint32_t flags,
175                     int width,
176                     int height,
177                     int refresh)
178 {
179     SDL_VideoDisplay *display = data;
180     SDL_DisplayMode mode;
181
182     SDL_zero(mode);
183     mode.w = width;
184     mode.h = height;
185     mode.refresh_rate = refresh / 1000; // mHz to Hz
186     SDL_AddDisplayMode(display, &mode);
187
188     if (flags & WL_OUTPUT_MODE_CURRENT) {
189         display->current_mode = mode;
190         display->desktop_mode = mode;
191     }
192 }
193
194 static void
195 display_handle_done(void *data,
196                     struct wl_output *output)
197 {
198     SDL_VideoDisplay *display = data;
199     SDL_AddVideoDisplay(display);
200     SDL_free(display->name);
201     SDL_free(display);
202 }
203
204 static void
205 display_handle_scale(void *data,
206                      struct wl_output *output,
207                      int32_t factor)
208 {
209     // TODO: do HiDPI stuff.
210 }
211
212 static const struct wl_output_listener output_listener = {
213     display_handle_geometry,
214     display_handle_mode,
215     display_handle_done,
216     display_handle_scale
217 };
218
219 static void
220 Wayland_add_display(SDL_VideoData *d, uint32_t id)
221 {
222     struct wl_output *output;
223     SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
224     if (!display) {
225         SDL_OutOfMemory();
226         return;
227     }
228     SDL_zero(*display);
229
230     output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
231     if (!output) {
232         SDL_SetError("Failed to retrieve output.");
233         SDL_free(display);
234         return;
235     }
236
237     wl_output_add_listener(output, &output_listener, display);
238 }
239
240 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
241 static void
242 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
243         int32_t show_is_fullscreen)
244 {
245 }
246
247 static void
248 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
249 {
250     SDL_SendQuit();
251 }
252
253 static const struct qt_windowmanager_listener windowmanager_listener = {
254     windowmanager_hints,
255     windowmanager_quit,
256 };
257 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
258
259 /* xdg shell */
260 static void
261 xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
262 {
263    xdg_shell_pong(shell, serial);
264 }
265
266 static const struct xdg_shell_listener xdg_shell_listener =
267 {
268    xdg_shell_ping,
269 };
270
271 static void
272 _wl_cb_conformant(void *data, struct tizen_policy *tizen_policy, struct wl_surface *surface_resource, uint32_t is_conformant)
273 {
274 #if 0
275    struct wl_surface *surface = surface_resource;
276    Ecore_Wl_Window *win = NULL;
277
278    if (!surface) return;
279    win = ecore_wl_window_surface_find(surface);
280    if (win)
281      win->conformant = is_conformant;
282 #endif
283 }
284
285 static void
286 _wl_cb_conformant_area(void *data, struct tizen_policy *tizen_policy, struct wl_surface *surface_resource, uint32_t conformant_part, uint32_t state, int32_t x, int32_t y, int32_t w, int32_t h)
287 {
288 #if 0
289    struct wl_surface *surface = surface_resource;
290    Ecore_Wl_Window *win = NULL;
291    int org_x, org_y, org_w, org_h;
292    Eina_Bool changed = EINA_FALSE;
293    Ecore_Wl_Indicator_State ind_state;
294    Ecore_Wl_Virtual_Keyboard_State kbd_state;
295    Ecore_Wl_Clipboard_State clip_state;
296
297    if (!surface) return;
298    win = ecore_wl_window_surface_find(surface);
299    if (!win) return;
300
301    if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_INDICATOR)
302      {
303         ecore_wl_window_indicator_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
304         if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
305           {
306              ecore_wl_window_indicator_geometry_set(win, x, y, w, h);
307              changed = EINA_TRUE;
308           }
309
310         /* The given state is based on the visibility value of indicator.
311          * Thus we need to add 1 to it before comparing with indicator state.
312          */
313         ind_state =  ecore_wl_window_indicator_state_get(win);
314         if ((state + 1) != ind_state)
315           {
316              ecore_wl_window_indicator_state_set(win, state + 1);
317              changed = EINA_TRUE;
318           }
319      }
320    else if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_KEYBOARD)
321      {
322         ecore_wl_window_keyboard_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
323         if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
324           {
325              ecore_wl_window_keyboard_geometry_set(win, x, y, w, h);
326              changed = EINA_TRUE;
327           }
328
329         /* The given state is based on the visibility value of virtual keyboard window.
330          * Thus we need to add 1 to it before comparing with keyboard state.
331          */
332         kbd_state = ecore_wl_window_keyboard_state_get(win);
333         if ((state + 1) != (kbd_state))
334           {
335              ecore_wl_window_keyboard_state_set(win, state + 1);
336              changed = EINA_TRUE;
337           }
338      }
339    else if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_CLIPBOARD)
340      {
341         ecore_wl_window_clipboard_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
342         if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
343           {
344              ecore_wl_window_clipboard_geometry_set(win, x, y, w, h);
345              changed = EINA_TRUE;
346           }
347
348         /* The given state is based on the visibility value of clipboard window.
349          * Thus we need to add 1 to it before comparing with clipboard state.
350          */
351         clip_state = ecore_wl_window_clipboard_state_get(win);
352         if ((state + 1) != clip_state)
353           {
354              ecore_wl_window_clipboard_state_set(win, state + 1);
355              changed = EINA_TRUE;
356           }
357      }
358
359    if (changed)
360      _ecore_wl_window_conformant_area_send(win, conformant_part, state);
361 #endif
362 }
363
364 static void
365 _wl_cb_notification_done(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface , int32_t level , uint32_t state )
366 {
367 }
368
369 static void
370 _wl_cb_transient_for_done(void *data , struct tizen_policy *tizen_policy , uint32_t child_id )
371 {
372 }
373
374 static void
375 _wl_cb_scr_mode_done(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface , uint32_t mode , uint32_t state )
376 {
377 }
378
379 static void
380 _wl_cb_iconify_state_changed(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, uint32_t iconified, uint32_t force)
381 {
382 #if 0
383    struct wl_surface *surface = surface_resource;
384    Ecore_Wl_Window *win = NULL;
385    Ecore_Wl_Event_Window_Iconify_State_Change *ev;
386
387    LOGFN(__FILE__, __LINE__, __FUNCTION__);
388
389    if (!surface) return;
390    win = ecore_wl_window_surface_find(surface);
391    if (!win) return;
392
393    if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Window_Iconify_State_Change)))) return;
394    ev->win = win->id;
395    ev->iconified = iconified;
396    ev->force = force;
397
398    ecore_event_add(ECORE_WL_EVENT_WINDOW_ICONIFY_STATE_CHANGE, ev, NULL, NULL);
399 #endif
400 }
401
402 static void
403 _wl_cb_supported_aux_hints(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, struct wl_array *hints, uint32_t num_hints)
404 {
405 #if 0
406    struct wl_surface *surface = surface_resource;
407    Ecore_Wl_Window *win = NULL;
408    char *p = NULL;
409    char **str = NULL;
410    const char *hint = NULL;
411    unsigned int i = 0;
412
413    LOGFN(__FILE__, __LINE__, __FUNCTION__);
414
415    if (!surface) return;
416    win = ecore_wl_window_surface_find(surface);
417    if (!win) return;
418
419    p = hints->data;
420    str = calloc(num_hints, sizeof(char *));
421    if (!str) return;
422
423    _ecore_wl_window_aux_hint_free(win);
424
425    while ((const char *)p < ((const char *)hints->data + hints->size))
426      {
427         str[i] = (char *)eina_stringshare_add(p);
428         p += strlen(p) + 1;
429         i++;
430      }
431    for (i = 0; i < num_hints; i++)
432      {
433         hint = eina_stringshare_add(str[i]);
434         win->supported_aux_hints =
435                eina_list_append(win->supported_aux_hints, hint);
436      }
437    if (str)
438      {
439         for (i = 0; i < num_hints; i++)
440           {
441              if (str[i])
442                {
443                   eina_stringshare_del(str[i]);
444                   str[i] = NULL;
445                }
446           }
447         free(str);
448      }
449 #endif
450 }
451
452 static void
453 _wl_cb_allowed_aux_hint(void *data  , struct tizen_policy *tizen_policy  , struct wl_surface *surface_resource, int id)
454 {
455 #if 0
456    struct wl_surface *surface = surface_resource;
457    Ecore_Wl_Window *win = NULL;
458    Ecore_Wl_Event_Aux_Hint_Allowed *ev;
459
460    LOGFN(__FILE__, __LINE__, __FUNCTION__);
461
462    if (!surface) return;
463    win = ecore_wl_window_surface_find(surface);
464    if (!win) return;
465
466    if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Aux_Hint_Allowed)))) return;
467    ev->win = win->id;
468    ev->id = id;
469    ecore_event_add(ECORE_WL_EVENT_AUX_HINT_ALLOWED, ev, NULL, NULL);
470 #endif
471 }
472
473 static void
474 _wl_cb_aux_message(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, const char *key, const char *val, struct wl_array *options)
475 {
476 #if 0
477    Ecore_Wl_Window *win = NULL;
478    Ecore_Wl_Event_Aux_Message *ev;
479    char *p = NULL, *str = NULL;
480    Eina_List *opt_list = NULL;
481
482    LOGFN(__FILE__, __LINE__, __FUNCTION__);
483
484    if (!surface_resource) return;
485    win = ecore_wl_window_surface_find(surface_resource);
486    if (!win) return;
487
488    if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Aux_Message)))) return;
489
490    if ((options) && (options->size))
491      {
492         p = options->data;
493         while ((const char *)p < ((const char *)options->data + options->size))
494           {
495              str = (char *)eina_stringshare_add(p);
496              opt_list = eina_list_append(opt_list, str);
497              p += strlen(p) + 1;
498           }
499      }
500
501    ev->win = win->id;
502    ev->key = eina_stringshare_add(key);
503    ev->val = eina_stringshare_add(val);
504    ev->options = opt_list;
505
506    ecore_event_add(ECORE_WL_EVENT_AUX_MESSAGE, ev, _cb_aux_message_free, NULL);
507 #endif
508 }
509
510
511 static const struct tizen_policy_listener _tizen_policy_listener =
512 {
513    _wl_cb_conformant,
514    _wl_cb_conformant_area,
515    _wl_cb_notification_done,
516    _wl_cb_transient_for_done,
517    _wl_cb_scr_mode_done,
518    _wl_cb_iconify_state_changed,
519    _wl_cb_supported_aux_hints,
520    _wl_cb_allowed_aux_hint,
521    _wl_cb_aux_message,
522 };
523
524 static void
525 _display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
526                       const char *interface, uint32_t version)
527 {
528     SDL_VideoData *d = data;
529     int client_version = 1;
530
531     if (strcmp(interface, "wl_compositor") == 0) {
532         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 3);
533     } else if (!strcmp(interface, "wl_subcompositor")) {
534        d->subcompositor =
535           wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
536     }
537     else if (strcmp(interface, "wl_output") == 0) {
538         Wayland_add_display(d, id);
539     } else if (strcmp(interface, "wl_seat") == 0) {
540         Wayland_display_add_input(d, id);
541     } else if (!strcmp(interface, "session_recovery")) {
542     } else if (strcmp(interface, "xdg_shell") == 0) {
543
544         d->xdgshell =  wl_registry_bind(d->registry, id, &xdg_shell_interface, 1);
545         xdg_shell_use_unstable_version(d->xdgshell, XDG_VERSION);
546         xdg_shell_add_listener(d->xdgshell, &xdg_shell_listener,d->display);
547
548     } else if (strcmp(interface, "wl_shell") == 0) {
549         d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
550     } else if (strcmp(interface, "wl_shm") == 0) {
551         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
552         d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
553         d->default_cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
554
555 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
556     } else if (strcmp(interface, "qt_touch_extension") == 0) {
557         Wayland_touch_create(d, id);
558     } else if (strcmp(interface, "qt_surface_extension") == 0) {
559         d->surface_extension = wl_registry_bind(registry, id,
560                 &qt_surface_extension_interface, 1);
561     } else if (strcmp(interface, "qt_windowmanager") == 0) {
562         d->windowmanager = wl_registry_bind(registry, id,
563                 &qt_windowmanager_interface, 1);
564         qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
565 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
566     } else if (!strcmp(interface, "wl_data_device_manager")) {
567          d->data_device_manager =
568            wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1);
569     } else if (!strcmp(interface, "tizen_policy")) {
570        if (version >= 3)
571          client_version = 3;
572        else
573          client_version = version;
574
575        d->tz_policy =
576          wl_registry_bind(registry, id, &tizen_policy_interface, client_version);
577        if (d->tz_policy)
578          tizen_policy_add_listener(d->tz_policy, &_tizen_policy_listener, d->display);
579     }
580 }
581
582 static const struct wl_registry_listener registry_listener = {
583     _display_handle_global
584 };
585
586 int
587 Wayland_VideoInit(_THIS)
588 {
589     SDL_VideoData *data = SDL_malloc(sizeof *data);
590     if (data == NULL)
591         return SDL_OutOfMemory();
592     memset(data, 0, sizeof *data);
593
594     _this->driverdata = data;
595
596     data->display = WAYLAND_wl_display_connect(NULL);
597     if (data->display == NULL) {
598         return SDL_SetError("Failed to connect to a Wayland display");
599     }
600
601     data->registry = wl_display_get_registry(data->display);
602     if (data->registry == NULL) {
603         return SDL_SetError("Failed to get the Wayland registry");
604     }
605
606     wl_registry_add_listener(data->registry, &registry_listener, data);
607
608     // First roundtrip to receive all registry objects.
609     WAYLAND_wl_display_roundtrip(data->display);
610
611     // Second roundtrip to receive all output events.
612     WAYLAND_wl_display_roundtrip(data->display);
613
614     data->xkb_context = WAYLAND_xkb_context_new(0);
615     if (!data->xkb_context) {
616         return SDL_SetError("Failed to create XKB context");
617     }
618
619     Wayland_InitMouse();
620
621     WAYLAND_wl_display_flush(data->display);
622
623     return 0;
624 }
625
626 static void
627 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
628 {
629     // Nothing to do here, everything was already done in the wl_output
630     // callbacks.
631 }
632
633 static int
634 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
635 {
636     return SDL_Unsupported();
637 }
638
639 void
640 Wayland_VideoQuit(_THIS)
641 {
642     SDL_VideoData *data = _this->driverdata;
643     int i;
644
645     Wayland_FiniMouse ();
646
647     for (i = 0; i < _this->num_displays; ++i) {
648         SDL_VideoDisplay *display = &_this->displays[i];
649         wl_output_destroy(display->driverdata);
650         display->driverdata = NULL;
651     }
652
653     Wayland_display_destroy_input(data);
654
655     if (data->xkb_context) {
656         WAYLAND_xkb_context_unref(data->xkb_context);
657         data->xkb_context = NULL;
658     }
659 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
660     if (data->windowmanager)
661         qt_windowmanager_destroy(data->windowmanager);
662
663     if (data->surface_extension)
664         qt_surface_extension_destroy(data->surface_extension);
665
666     Wayland_touch_destroy(data);
667 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
668
669     if (data->shm)
670         wl_shm_destroy(data->shm);
671
672     if (data->cursor_theme)
673         WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
674
675     if (data->shell)
676         wl_shell_destroy(data->shell);
677
678     if (data->compositor)
679         wl_compositor_destroy(data->compositor);
680
681     if (data->registry)
682         wl_registry_destroy(data->registry);
683
684     if (data->display) {
685         WAYLAND_wl_display_flush(data->display);
686         WAYLAND_wl_display_disconnect(data->display);
687     }
688
689     free(data);
690     _this->driverdata = NULL;
691 }
692
693 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
694
695 /* vi: set ts=4 sw=4 expandtab: */