1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <unistd.h>
18 
19 #include <wayland-client.h>
20 #include <wayland-cursor.h>
21 
22 #include <string/stdstring.h>
23 
24 #ifdef HAVE_CONFIG_H
25 #include "../../config.h"
26 #endif
27 
28 #include "../common/vulkan_common.h"
29 
30 #include "../../frontend/frontend_driver.h"
31 #include "../../input/common/wayland_common.h"
32 #include "../../input/input_driver.h"
33 #include "../../input/input_keymaps.h"
34 #include "../../verbosity.h"
35 
36 /* Generated from idle-inhibit-unstable-v1.xml */
37 #include "../common/wayland/idle-inhibit-unstable-v1.h"
38 
39 /* Generated from xdg-shell-unstable-v6.xml */
40 #include "../common/wayland/xdg-shell-unstable-v6.h"
41 
42 /* Generated from xdg-shell.xml */
43 #include "../common/wayland/xdg-shell.h"
44 
45 /* Generated from xdg-decoration-unstable-v1.h */
46 #include "../common/wayland/xdg-decoration-unstable-v1.h"
47 
48 #include <retro_timers.h>
49 
50 #ifndef EGL_PLATFORM_WAYLAND_KHR
51 #define EGL_PLATFORM_WAYLAND_KHR 0x31D8
52 #endif
53 
handle_toplevel_config_common(void * data,void * toplevel,int32_t width,int32_t height,struct wl_array * states)54 static void handle_toplevel_config_common(void *data,
55       void *toplevel,
56       int32_t width, int32_t height, struct wl_array *states)
57 {
58    const uint32_t *state;
59    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
60 
61    wl->fullscreen             = false;
62    wl->maximized              = false;
63 
64    WL_ARRAY_FOR_EACH(state, states, const uint32_t*)
65    {
66       switch (*state)
67       {
68          case XDG_TOPLEVEL_STATE_FULLSCREEN:
69             wl->fullscreen = true;
70             break;
71          case XDG_TOPLEVEL_STATE_MAXIMIZED:
72             wl->maximized = true;
73             break;
74          case XDG_TOPLEVEL_STATE_RESIZING:
75             wl->resize = true;
76             break;
77          case XDG_TOPLEVEL_STATE_ACTIVATED:
78             wl->activated = true;
79             break;
80       }
81    }
82    if (width > 0 && height > 0)
83    {
84       wl->prev_width  = width;
85       wl->prev_height = height;
86       wl->width       = width;
87       wl->height      = height;
88    }
89 
90    wl->configured = false;
91 }
92 
93 /* Shell surface callbacks. */
handle_toplevel_config(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)94 static void handle_toplevel_config(void *data,
95       struct xdg_toplevel *toplevel,
96       int32_t width, int32_t height, struct wl_array *states)
97 {
98    handle_toplevel_config_common(data, toplevel, width, height, states);
99 }
100 
handle_zxdg_toplevel_config(void * data,struct zxdg_toplevel_v6 * toplevel,int32_t width,int32_t height,struct wl_array * states)101 static void handle_zxdg_toplevel_config(
102       void *data, struct zxdg_toplevel_v6 *toplevel,
103       int32_t width, int32_t height, struct wl_array *states)
104 {
105    handle_toplevel_config_common(data, toplevel, width, height, states);
106 }
107 
108 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
109     handle_toplevel_config,
110     handle_toplevel_close,
111 };
112 
113 static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = {
114     handle_zxdg_toplevel_config,
115     handle_zxdg_toplevel_close,
116 };
117 
gfx_ctx_wl_get_video_size(void * data,unsigned * width,unsigned * height)118 static void gfx_ctx_wl_get_video_size(void *data,
119       unsigned *width, unsigned *height)
120 {
121    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
122 
123    *width  = wl->width  * wl->buffer_scale;
124    *height = wl->height * wl->buffer_scale;
125 }
126 
gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t * wl)127 static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl)
128 {
129    if (!wl)
130       return;
131 
132    vulkan_context_destroy(&wl->vk, wl->surface);
133 
134    if (wl->input.dpy != NULL && wl->input.fd >= 0)
135       close(wl->input.fd);
136 
137 #ifdef HAVE_XKBCOMMON
138    free_xkb();
139 #endif
140 
141    if (wl->wl_keyboard)
142       wl_keyboard_destroy(wl->wl_keyboard);
143    if (wl->wl_pointer)
144       wl_pointer_destroy(wl->wl_pointer);
145    if (wl->wl_touch)
146       wl_touch_destroy(wl->wl_touch);
147 
148    if (wl->cursor.theme)
149       wl_cursor_theme_destroy(wl->cursor.theme);
150    if (wl->cursor.surface)
151       wl_surface_destroy(wl->cursor.surface);
152 
153    if (wl->seat)
154       wl_seat_destroy(wl->seat);
155    if (wl->xdg_shell)
156       xdg_wm_base_destroy(wl->xdg_shell);
157    if (wl->zxdg_shell)
158       zxdg_shell_v6_destroy(wl->zxdg_shell);
159    if (wl->compositor)
160       wl_compositor_destroy(wl->compositor);
161    if (wl->registry)
162       wl_registry_destroy(wl->registry);
163    if (wl->xdg_surface)
164       xdg_surface_destroy(wl->xdg_surface);
165    if (wl->zxdg_surface)
166       zxdg_surface_v6_destroy(wl->zxdg_surface);
167    if (wl->surface)
168       wl_surface_destroy(wl->surface);
169    if (wl->xdg_toplevel)
170       xdg_toplevel_destroy(wl->xdg_toplevel);
171    if (wl->zxdg_toplevel)
172       zxdg_toplevel_v6_destroy(wl->zxdg_toplevel);
173    if (wl->idle_inhibit_manager)
174       zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);
175    if (wl->deco)
176       zxdg_toplevel_decoration_v1_destroy(wl->deco);
177    if (wl->deco_manager)
178       zxdg_decoration_manager_v1_destroy(wl->deco_manager);
179    if (wl->idle_inhibitor)
180       zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
181 
182    if (wl->input.dpy)
183    {
184       wl_display_flush(wl->input.dpy);
185       wl_display_disconnect(wl->input.dpy);
186    }
187 
188    wl->xdg_shell        = NULL;
189    wl->zxdg_shell       = NULL;
190    wl->compositor       = NULL;
191    wl->registry         = NULL;
192    wl->input.dpy        = NULL;
193    wl->xdg_surface      = NULL;
194    wl->surface          = NULL;
195    wl->xdg_toplevel     = NULL;
196    wl->zxdg_toplevel    = NULL;
197 
198    wl->width            = 0;
199    wl->height           = 0;
200 
201 }
202 
gfx_ctx_wl_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)203 static void gfx_ctx_wl_check_window(void *data, bool *quit,
204       bool *resize, unsigned *width, unsigned *height)
205 {
206    /* this function works with SCALED sizes, it's used from the renderer */
207    unsigned new_width, new_height;
208    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
209 
210    flush_wayland_fd(&wl->input);
211 
212    new_width  = *width  * wl->last_buffer_scale;
213    new_height = *height * wl->last_buffer_scale;
214 
215    gfx_ctx_wl_get_video_size(data, &new_width, &new_height);
216 
217    /* Swapchains are recreated in set_resize as a
218     * central place, so use that to trigger swapchain reinit. */
219    *resize = wl->vk.need_new_swapchain;
220 
221    if (new_width != *width * wl->last_buffer_scale || new_height != *height * wl->last_buffer_scale)
222    {
223       *width  = new_width;
224       *height = new_height;
225       *resize = true;
226 
227       wl->last_buffer_scale = wl->buffer_scale;
228    }
229 
230    *quit = (bool)frontend_driver_get_signal_handler_state();
231 }
232 
gfx_ctx_wl_set_resize(void * data,unsigned width,unsigned height)233 static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height)
234 {
235    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
236 
237    if (vulkan_create_swapchain(&wl->vk, width, height, wl->swap_interval))
238    {
239       wl->vk.context.invalid_swapchain = true;
240       if (wl->vk.created_new_swapchain)
241          vulkan_acquire_next_image(&wl->vk);
242    }
243    else
244    {
245       RARCH_ERR("[Wayland/Vulkan]: Failed to update swapchain.\n");
246       return false;
247    }
248 
249    wl->vk.need_new_swapchain = false;
250 
251    wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
252    return true;
253 }
254 
gfx_ctx_wl_update_title(void * data)255 static void gfx_ctx_wl_update_title(void *data)
256 {
257    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
258    char title[128];
259 
260    title[0] = '\0';
261 
262    video_driver_get_window_title(title, sizeof(title));
263 
264    if (wl && title[0])
265    {
266       if (wl->xdg_toplevel || wl->zxdg_toplevel)
267       {
268          if (wl->deco)
269          {
270             zxdg_toplevel_decoration_v1_set_mode(wl->deco,
271                   ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
272          }
273       }
274       if (wl->xdg_toplevel)
275          xdg_toplevel_set_title(wl->xdg_toplevel, title);
276       else if (wl->zxdg_toplevel)
277          zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, title);
278    }
279 }
280 
gfx_ctx_wl_get_metrics(void * data,enum display_metric_types type,float * value)281 static bool gfx_ctx_wl_get_metrics(void *data,
282       enum display_metric_types type, float *value)
283 {
284    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
285 
286    if (!wl || !wl->current_output || wl->current_output->physical_width == 0 || wl->current_output->physical_height == 0)
287       return false;
288 
289    switch (type)
290    {
291       case DISPLAY_METRIC_MM_WIDTH:
292          *value = (float)wl->current_output->physical_width;
293          break;
294 
295       case DISPLAY_METRIC_MM_HEIGHT:
296          *value = (float)wl->current_output->physical_height;
297          break;
298 
299       case DISPLAY_METRIC_DPI:
300          *value = (float)wl->current_output->width * 25.4f / (float)wl->current_output->physical_width;
301          break;
302 
303       default:
304          *value = 0.0f;
305          return false;
306    }
307 
308    return true;
309 }
310 
311 #define DEFAULT_WINDOWED_WIDTH 640
312 #define DEFAULT_WINDOWED_HEIGHT 480
313 
gfx_ctx_wl_init(void * video_driver)314 static void *gfx_ctx_wl_init(void *video_driver)
315 {
316    int i;
317    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)
318       calloc(1, sizeof(gfx_ctx_wayland_data_t));
319 
320    if (!wl)
321       return NULL;
322 
323    (void)video_driver;
324 
325    wl_list_init(&wl->all_outputs);
326 
327    frontend_driver_destroy_signal_handler_state();
328 
329    wl->input.dpy = wl_display_connect(NULL);
330    wl->last_buffer_scale = 1;
331    wl->buffer_scale = 1;
332 
333    if (!wl->input.dpy)
334    {
335       RARCH_ERR("[Wayland]: Failed to connect to Wayland server.\n");
336       goto error;
337    }
338 
339    frontend_driver_install_signal_handler();
340 
341    wl->registry = wl_display_get_registry(wl->input.dpy);
342    wl_registry_add_listener(wl->registry, &registry_listener, wl);
343    wl_display_roundtrip(wl->input.dpy);
344 
345    if (!wl->compositor)
346    {
347       RARCH_ERR("[Wayland]: Failed to create compositor.\n");
348       goto error;
349    }
350 
351    if (!wl->shm)
352    {
353       RARCH_ERR("[Wayland]: Failed to create shm.\n");
354       goto error;
355    }
356 
357    if (!wl->xdg_shell && !!wl->zxdg_shell)
358    {
359       RARCH_LOG("[Wayland]: Using zxdg_shell_v6 interface.\n");
360    }
361 
362    if (!wl->xdg_shell && !wl->zxdg_shell)
363    {
364 	   RARCH_ERR("[Wayland]: Failed to create shell.\n");
365 	   goto error;
366    }
367 
368    if (!wl->idle_inhibit_manager)
369    {
370 	   RARCH_WARN("[Wayland]: Compositor doesn't support zwp_idle_inhibit_manager_v1 protocol!\n");
371    }
372 
373    if (!wl->deco_manager)
374    {
375 	   RARCH_WARN("[Wayland]: Compositor doesn't support zxdg_decoration_manager_v1 protocol!\n");
376    }
377 
378    wl->input.fd = wl_display_get_fd(wl->input.dpy);
379 
380    if (!vulkan_context_init(&wl->vk, VULKAN_WSI_WAYLAND))
381       goto error;
382 
383    wl->input.keyboard_focus = true;
384    wl->input.mouse.focus = true;
385 
386    wl->cursor.surface = wl_compositor_create_surface(wl->compositor);
387    wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm);
388    wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr");
389 
390    wl->num_active_touches = 0;
391 
392    for (i = 0;i < MAX_TOUCHES;i++)
393    {
394        wl->active_touch_positions[i].active = false;
395        wl->active_touch_positions[i].id     = -1;
396        wl->active_touch_positions[i].x      = (unsigned) 0;
397        wl->active_touch_positions[i].y      = (unsigned) 0;
398    }
399 
400    flush_wayland_fd(&wl->input);
401 
402    return wl;
403 
404 error:
405    gfx_ctx_wl_destroy_resources(wl);
406 
407    if (wl)
408       free(wl);
409 
410    return NULL;
411 }
412 
gfx_ctx_wl_destroy(void * data)413 static void gfx_ctx_wl_destroy(void *data)
414 {
415    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
416 
417    if (!wl)
418       return;
419 
420    gfx_ctx_wl_destroy_resources(wl);
421 
422 #if defined(HAVE_THREADS)
423    if (wl->vk.context.queue_lock)
424       slock_free(wl->vk.context.queue_lock);
425 #endif
426 
427    free(wl);
428 }
429 
gfx_ctx_wl_set_swap_interval(void * data,int swap_interval)430 static void gfx_ctx_wl_set_swap_interval(void *data, int swap_interval)
431 {
432    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
433 
434    if (wl->swap_interval != swap_interval)
435    {
436       wl->swap_interval = swap_interval;
437       if (wl->vk.swapchain)
438          wl->vk.need_new_swapchain = true;
439    }
440 }
441 
gfx_ctx_wl_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)442 static bool gfx_ctx_wl_set_video_mode(void *data,
443       unsigned width, unsigned height,
444       bool fullscreen)
445 {
446    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
447 
448    wl->width                  = width  ? width  : DEFAULT_WINDOWED_WIDTH;
449    wl->height                 = height ? height : DEFAULT_WINDOWED_HEIGHT;
450 
451    wl->surface                = wl_compositor_create_surface(wl->compositor);
452 
453    wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
454    wl_surface_add_listener(wl->surface, &wl_surface_listener, wl);
455 
456    if (wl->xdg_shell)
457    {
458       wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_shell, wl->surface);
459       xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl);
460 
461       wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
462       xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
463 
464       xdg_toplevel_set_app_id(wl->xdg_toplevel, "retroarch");
465       xdg_toplevel_set_title(wl->xdg_toplevel, "RetroArch");
466 
467       if (wl->deco_manager)
468       {
469          wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
470                wl->deco_manager, wl->xdg_toplevel);
471       }
472 
473       /* Waiting for xdg_toplevel to be configured before starting to draw */
474       wl_surface_commit(wl->surface);
475       wl->configured = true;
476 
477       while (wl->configured)
478          wl_display_dispatch(wl->input.dpy);
479 
480       wl_display_roundtrip(wl->input.dpy);
481       xdg_wm_base_add_listener(wl->xdg_shell, &xdg_shell_listener, NULL);
482    }
483    else if (wl->zxdg_shell)
484    {
485       wl->zxdg_surface = zxdg_shell_v6_get_xdg_surface(wl->zxdg_shell, wl->surface);
486       zxdg_surface_v6_add_listener(wl->zxdg_surface, &zxdg_surface_v6_listener, wl);
487 
488       wl->zxdg_toplevel = zxdg_surface_v6_get_toplevel(wl->zxdg_surface);
489       zxdg_toplevel_v6_add_listener(wl->zxdg_toplevel, &zxdg_toplevel_v6_listener, wl);
490 
491       zxdg_toplevel_v6_set_app_id(wl->zxdg_toplevel, "retroarch");
492       zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, "RetroArch");
493 
494       if (wl->deco_manager)
495          wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
496                wl->deco_manager, wl->xdg_toplevel);
497 
498       /* Waiting for xdg_toplevel to be configured before starting to draw */
499       wl_surface_commit(wl->surface);
500       wl->configured = true;
501 
502       while (wl->configured)
503          wl_display_dispatch(wl->input.dpy);
504 
505       wl_display_roundtrip(wl->input.dpy);
506       zxdg_shell_v6_add_listener(wl->zxdg_shell, &zxdg_shell_v6_listener, NULL);
507    }
508 
509    if (fullscreen)
510    {
511 	   if (wl->xdg_toplevel)
512 		   xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL);
513 	   else if (wl->zxdg_toplevel)
514 		   zxdg_toplevel_v6_set_fullscreen(wl->zxdg_toplevel, NULL);
515 	}
516 
517    flush_wayland_fd(&wl->input);
518 
519    wl_display_roundtrip(wl->input.dpy);
520 
521    if (!vulkan_surface_create(&wl->vk, VULKAN_WSI_WAYLAND,
522             wl->input.dpy, wl->surface,
523             wl->width * wl->buffer_scale, wl->height * wl->buffer_scale, wl->swap_interval))
524       goto error;
525 
526    if (fullscreen)
527    {
528       wl->cursor.visible = false;
529       gfx_ctx_wl_show_mouse(wl, false);
530    }
531    else
532       wl->cursor.visible = true;
533 
534    return true;
535 
536 error:
537    gfx_ctx_wl_destroy(data);
538    return false;
539 }
540 
541 bool input_wl_init(void *data, const char *joypad_name);
542 
gfx_ctx_wl_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)543 static void gfx_ctx_wl_input_driver(void *data,
544       const char *joypad_name,
545       input_driver_t **input, void **input_data)
546 {
547    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
548    /* Input is heavily tied to the window stuff
549     * on Wayland, so just implement the input driver here. */
550    if (!input_wl_init(&wl->input, joypad_name))
551    {
552       wl->input.gfx = NULL;
553       *input        = NULL;
554       *input_data   = NULL;
555    }
556    else
557    {
558       wl->input.gfx = wl;
559       *input        = &input_wayland;
560       *input_data   = &wl->input;
561       input_driver_init_joypads();
562    }
563 }
564 
gfx_ctx_wl_has_focus(void * data)565 static bool gfx_ctx_wl_has_focus(void *data)
566 {
567    (void)data;
568    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
569    return wl->input.keyboard_focus;
570 }
571 
gfx_ctx_wl_suppress_screensaver(void * data,bool state)572 static bool gfx_ctx_wl_suppress_screensaver(void *data, bool state)
573 {
574 	(void)data;
575 
576 	gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
577 
578     if (!wl->idle_inhibit_manager)
579         return false;
580     if (state == (!!wl->idle_inhibitor))
581         return true;
582     if (state)
583     {
584         RARCH_LOG("[Wayland]: Enabling idle inhibitor\n");
585         struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager;
586         wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface);
587     }
588     else
589     {
590         RARCH_LOG("[Wayland]: Disabling the idle inhibitor\n");
591         zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
592         wl->idle_inhibitor = NULL;
593     }
594     return true;
595 }
596 
gfx_ctx_wl_get_api(void * data)597 static enum gfx_ctx_api gfx_ctx_wl_get_api(void *data) { return GFX_CTX_VULKAN_API; }
598 
gfx_ctx_wl_bind_api(void * video_driver,enum gfx_ctx_api api,unsigned major,unsigned minor)599 static bool gfx_ctx_wl_bind_api(void *video_driver,
600       enum gfx_ctx_api api, unsigned major, unsigned minor)
601 {
602    if (api == GFX_CTX_VULKAN_API)
603          return true;
604    return false;
605 }
606 
gfx_ctx_wl_get_context_data(void * data)607 static void *gfx_ctx_wl_get_context_data(void *data)
608 {
609    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
610    return &wl->vk.context;
611 }
612 
gfx_ctx_wl_swap_buffers(void * data)613 static void gfx_ctx_wl_swap_buffers(void *data)
614 {
615    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
616 
617    if (wl->vk.context.has_acquired_swapchain)
618    {
619       wl->vk.context.has_acquired_swapchain = false;
620       if (wl->vk.swapchain == VK_NULL_HANDLE)
621       {
622          retro_sleep(10);
623       }
624       else
625          vulkan_present(&wl->vk, wl->vk.context.current_swapchain_index);
626    }
627    vulkan_acquire_next_image(&wl->vk);
628    flush_wayland_fd(&wl->input);
629 }
630 
gfx_ctx_wl_get_proc_address(const char * symbol)631 static gfx_ctx_proc_t gfx_ctx_wl_get_proc_address(const char *symbol)
632 {
633    return NULL;
634 }
635 
gfx_ctx_wl_bind_hw_render(void * data,bool enable)636 static void gfx_ctx_wl_bind_hw_render(void *data, bool enable) { }
637 
gfx_ctx_wl_get_flags(void * data)638 static uint32_t gfx_ctx_wl_get_flags(void *data)
639 {
640    uint32_t             flags = 0;
641    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
642 
643 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
644    BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
645 #endif
646 
647    return flags;
648 }
649 
gfx_ctx_wl_set_flags(void * data,uint32_t flags)650 static void gfx_ctx_wl_set_flags(void *data, uint32_t flags) { }
651 
gfx_ctx_wl_get_refresh_rate(void * data)652 static float gfx_ctx_wl_get_refresh_rate(void *data)
653 {
654    gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
655 
656    if (!wl || !wl->current_output)
657       return false;
658 
659    return (float) wl->current_output->refresh_rate / 1000.0f;
660 }
661 
662 const gfx_ctx_driver_t gfx_ctx_vk_wayland = {
663    gfx_ctx_wl_init,
664    gfx_ctx_wl_destroy,
665    gfx_ctx_wl_get_api,
666    gfx_ctx_wl_bind_api,
667    gfx_ctx_wl_set_swap_interval,
668    gfx_ctx_wl_set_video_mode,
669    gfx_ctx_wl_get_video_size,
670    gfx_ctx_wl_get_refresh_rate,
671    NULL, /* get_video_output_size */
672    NULL, /* get_video_output_prev */
673    NULL, /* get_video_output_next */
674    gfx_ctx_wl_get_metrics,
675    NULL,
676    gfx_ctx_wl_update_title,
677    gfx_ctx_wl_check_window,
678    gfx_ctx_wl_set_resize,
679    gfx_ctx_wl_has_focus,
680    gfx_ctx_wl_suppress_screensaver,
681    true, /* has_windowed */
682    gfx_ctx_wl_swap_buffers,
683    gfx_ctx_wl_input_driver,
684    gfx_ctx_wl_get_proc_address,
685    NULL,
686    NULL,
687    gfx_ctx_wl_show_mouse,
688    "vk_wayland",
689    gfx_ctx_wl_get_flags,
690    gfx_ctx_wl_set_flags,
691    gfx_ctx_wl_bind_hw_render,
692    gfx_ctx_wl_get_context_data,
693    NULL,
694 };
695