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, ®istry_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