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 #ifdef HAVE_EGL
29 #include <wayland-egl.h>
30 #include "../common/egl_common.h"
31 #endif
32
33 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
34 #include "../common/gl_common.h"
35 #endif
36
37 #include "../../frontend/frontend_driver.h"
38 #include "../../input/common/wayland_common.h"
39 #include "../../input/input_driver.h"
40 #include "../../input/input_keymaps.h"
41 #include "../../verbosity.h"
42
43 /* Generated from idle-inhibit-unstable-v1.xml */
44 #include "../common/wayland/idle-inhibit-unstable-v1.h"
45
46 /* Generated from xdg-shell-unstable-v6.xml */
47 #include "../common/wayland/xdg-shell-unstable-v6.h"
48
49 /* Generated from xdg-shell.xml */
50 #include "../common/wayland/xdg-shell.h"
51
52 /* Generated from xdg-decoration-unstable-v1.h */
53 #include "../common/wayland/xdg-decoration-unstable-v1.h"
54
55 static enum gfx_ctx_api wl_api = GFX_CTX_NONE;
56
57 #ifndef EGL_OPENGL_ES3_BIT_KHR
58 #define EGL_OPENGL_ES3_BIT_KHR 0x0040
59 #endif
60
61 #ifndef EGL_PLATFORM_WAYLAND_KHR
62 #define EGL_PLATFORM_WAYLAND_KHR 0x31D8
63 #endif
64
handle_toplevel_config_common(void * data,void * toplevel,int32_t width,int32_t height,struct wl_array * states)65 static void handle_toplevel_config_common(void *data,
66 void *toplevel,
67 int32_t width, int32_t height, struct wl_array *states)
68 {
69 const uint32_t *state;
70 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
71
72 wl->fullscreen = false;
73 wl->maximized = false;
74
75 WL_ARRAY_FOR_EACH(state, states, const uint32_t*)
76 {
77 switch (*state)
78 {
79 case XDG_TOPLEVEL_STATE_FULLSCREEN:
80 wl->fullscreen = true;
81 break;
82 case XDG_TOPLEVEL_STATE_MAXIMIZED:
83 wl->maximized = true;
84 break;
85 case XDG_TOPLEVEL_STATE_RESIZING:
86 wl->resize = true;
87 break;
88 case XDG_TOPLEVEL_STATE_ACTIVATED:
89 wl->activated = true;
90 break;
91 }
92 }
93 if (width > 0 && height > 0)
94 {
95 wl->prev_width = width;
96 wl->prev_height = height;
97 wl->width = width;
98 wl->height = height;
99 }
100
101 #ifdef HAVE_EGL
102 if (wl->win)
103 wl_egl_window_resize(wl->win, width, height, 0, 0);
104 else
105 wl->win = wl_egl_window_create(wl->surface,
106 wl->width * wl->buffer_scale,
107 wl->height * wl->buffer_scale);
108 #endif
109
110 wl->configured = false;
111 }
112
113 /* Shell surface callbacks. */
handle_toplevel_config(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)114 static void handle_toplevel_config(void *data,
115 struct xdg_toplevel *toplevel,
116 int32_t width, int32_t height, struct wl_array *states)
117 {
118 handle_toplevel_config_common(data, toplevel, width, height, states);
119 }
120
handle_zxdg_toplevel_config(void * data,struct zxdg_toplevel_v6 * toplevel,int32_t width,int32_t height,struct wl_array * states)121 static void handle_zxdg_toplevel_config(
122 void *data, struct zxdg_toplevel_v6 *toplevel,
123 int32_t width, int32_t height, struct wl_array *states)
124 {
125 handle_toplevel_config_common(data, toplevel, width, height, states);
126 }
127
128 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
129 handle_toplevel_config,
130 handle_toplevel_close,
131 };
132
133 static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = {
134 handle_zxdg_toplevel_config,
135 handle_zxdg_toplevel_close,
136 };
137
gfx_ctx_wl_get_video_size(void * data,unsigned * width,unsigned * height)138 static void gfx_ctx_wl_get_video_size(void *data,
139 unsigned *width, unsigned *height)
140 {
141 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
142
143 *width = wl->width * wl->buffer_scale;
144 *height = wl->height * wl->buffer_scale;
145 }
146
gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t * wl)147 static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl)
148 {
149 if (!wl)
150 return;
151
152 #ifdef HAVE_EGL
153 egl_destroy(&wl->egl);
154
155 if (wl->win)
156 wl_egl_window_destroy(wl->win);
157 #endif
158
159 #ifdef HAVE_XKBCOMMON
160 free_xkb();
161 #endif
162
163 if (wl->wl_keyboard)
164 wl_keyboard_destroy(wl->wl_keyboard);
165 if (wl->wl_pointer)
166 wl_pointer_destroy(wl->wl_pointer);
167 if (wl->wl_touch)
168 wl_touch_destroy(wl->wl_touch);
169
170 if (wl->cursor.theme)
171 wl_cursor_theme_destroy(wl->cursor.theme);
172 if (wl->cursor.surface)
173 wl_surface_destroy(wl->cursor.surface);
174
175 if (wl->seat)
176 wl_seat_destroy(wl->seat);
177 if (wl->xdg_shell)
178 xdg_wm_base_destroy(wl->xdg_shell);
179 if (wl->zxdg_shell)
180 zxdg_shell_v6_destroy(wl->zxdg_shell);
181 if (wl->compositor)
182 wl_compositor_destroy(wl->compositor);
183 if (wl->registry)
184 wl_registry_destroy(wl->registry);
185 if (wl->xdg_surface)
186 xdg_surface_destroy(wl->xdg_surface);
187 if (wl->zxdg_surface)
188 zxdg_surface_v6_destroy(wl->zxdg_surface);
189 if (wl->surface)
190 wl_surface_destroy(wl->surface);
191 if (wl->xdg_toplevel)
192 xdg_toplevel_destroy(wl->xdg_toplevel);
193 if (wl->zxdg_toplevel)
194 zxdg_toplevel_v6_destroy(wl->zxdg_toplevel);
195 if (wl->idle_inhibit_manager)
196 zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);
197 if (wl->deco)
198 zxdg_toplevel_decoration_v1_destroy(wl->deco);
199 if (wl->deco_manager)
200 zxdg_decoration_manager_v1_destroy(wl->deco_manager);
201 if (wl->idle_inhibitor)
202 zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
203
204 if (wl->input.dpy)
205 {
206 wl_display_flush(wl->input.dpy);
207 wl_display_disconnect(wl->input.dpy);
208 }
209
210 #ifdef HAVE_EGL
211 wl->win = NULL;
212 #endif
213 wl->xdg_shell = NULL;
214 wl->zxdg_shell = NULL;
215 wl->compositor = NULL;
216 wl->registry = NULL;
217 wl->input.dpy = NULL;
218 wl->xdg_surface = NULL;
219 wl->surface = NULL;
220 wl->xdg_toplevel = NULL;
221 wl->zxdg_toplevel = NULL;
222
223 wl->width = 0;
224 wl->height = 0;
225
226 }
227
gfx_ctx_wl_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)228 static void gfx_ctx_wl_check_window(void *data, bool *quit,
229 bool *resize, unsigned *width, unsigned *height)
230 {
231 /* this function works with SCALED sizes, it's used from the renderer */
232 unsigned new_width, new_height;
233 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
234
235 flush_wayland_fd(&wl->input);
236
237 new_width = *width * wl->last_buffer_scale;
238 new_height = *height * wl->last_buffer_scale;
239
240 gfx_ctx_wl_get_video_size(data, &new_width, &new_height);
241
242 if ( new_width != *width * wl->last_buffer_scale ||
243 new_height != *height * wl->last_buffer_scale)
244 {
245 *width = new_width;
246 *height = new_height;
247 *resize = true;
248
249 wl->last_buffer_scale = wl->buffer_scale;
250 }
251
252 *quit = (bool)frontend_driver_get_signal_handler_state();
253 }
254
gfx_ctx_wl_set_resize(void * data,unsigned width,unsigned height)255 static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height)
256 {
257 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
258
259 #ifdef HAVE_EGL
260 wl_egl_window_resize(wl->win, width, height, 0, 0);
261 #endif
262
263 wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
264 return true;
265 }
266
gfx_ctx_wl_update_title(void * data)267 static void gfx_ctx_wl_update_title(void *data)
268 {
269 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
270 char title[128];
271
272 title[0] = '\0';
273
274 video_driver_get_window_title(title, sizeof(title));
275
276 if (wl && title[0])
277 {
278 if (wl->xdg_toplevel || wl->zxdg_toplevel)
279 {
280 if (wl->deco)
281 {
282 zxdg_toplevel_decoration_v1_set_mode(wl->deco,
283 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
284 }
285 }
286 if (wl->xdg_toplevel)
287 xdg_toplevel_set_title(wl->xdg_toplevel, title);
288 else if (wl->zxdg_toplevel)
289 zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, title);
290 }
291 }
292
gfx_ctx_wl_get_metrics(void * data,enum display_metric_types type,float * value)293 static bool gfx_ctx_wl_get_metrics(void *data,
294 enum display_metric_types type, float *value)
295 {
296 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
297
298 if (!wl || !wl->current_output || wl->current_output->physical_width == 0 || wl->current_output->physical_height == 0)
299 return false;
300
301 switch (type)
302 {
303 case DISPLAY_METRIC_MM_WIDTH:
304 *value = (float)wl->current_output->physical_width;
305 break;
306
307 case DISPLAY_METRIC_MM_HEIGHT:
308 *value = (float)wl->current_output->physical_height;
309 break;
310
311 case DISPLAY_METRIC_DPI:
312 *value = (float)wl->current_output->width * 25.4f / (float)wl->current_output->physical_width;
313 break;
314
315 default:
316 *value = 0.0f;
317 return false;
318 }
319
320 return true;
321 }
322
323 #define DEFAULT_WINDOWED_WIDTH 640
324 #define DEFAULT_WINDOWED_HEIGHT 480
325
326 #ifdef HAVE_EGL
327 #define WL_EGL_ATTRIBS_BASE \
328 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \
329 EGL_RED_SIZE, 1, \
330 EGL_GREEN_SIZE, 1, \
331 EGL_BLUE_SIZE, 1, \
332 EGL_ALPHA_SIZE, 0, \
333 EGL_DEPTH_SIZE, 0
334 #endif
335
gfx_ctx_wl_init(void * video_driver)336 static void *gfx_ctx_wl_init(void *video_driver)
337 {
338 int i;
339 #ifdef HAVE_EGL
340 static const EGLint egl_attribs_gl[] = {
341 WL_EGL_ATTRIBS_BASE,
342 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
343 EGL_NONE,
344 };
345
346 #ifdef HAVE_OPENGLES
347 #ifdef HAVE_OPENGLES2
348 static const EGLint egl_attribs_gles[] = {
349 WL_EGL_ATTRIBS_BASE,
350 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
351 EGL_NONE,
352 };
353 #endif
354
355 #ifdef HAVE_OPENGLES3
356 #ifdef EGL_KHR_create_context
357 static const EGLint egl_attribs_gles3[] = {
358 WL_EGL_ATTRIBS_BASE,
359 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
360 EGL_NONE,
361 };
362 #endif
363 #endif
364
365 #endif
366
367 static const EGLint egl_attribs_vg[] = {
368 WL_EGL_ATTRIBS_BASE,
369 EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
370 EGL_NONE,
371 };
372
373 EGLint n;
374 EGLint major = 0, minor = 0;
375 const EGLint *attrib_ptr = NULL;
376 #endif
377 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)
378 calloc(1, sizeof(gfx_ctx_wayland_data_t));
379
380 if (!wl)
381 return NULL;
382
383 (void)video_driver;
384
385 wl_list_init(&wl->all_outputs);
386
387 #ifdef HAVE_EGL
388 switch (wl_api)
389 {
390 case GFX_CTX_OPENGL_API:
391 #ifdef HAVE_OPENGL
392 attrib_ptr = egl_attribs_gl;
393 #endif
394 break;
395 case GFX_CTX_OPENGL_ES_API:
396 #ifdef HAVE_OPENGLES
397 #ifdef HAVE_OPENGLES3
398 #ifdef EGL_KHR_create_context
399 if (g_egl_major >= 3)
400 attrib_ptr = egl_attribs_gles3;
401 else
402 #endif
403 #endif
404 #ifdef HAVE_OPENGLES2
405 attrib_ptr = egl_attribs_gles;
406 #endif
407 #endif
408 break;
409 case GFX_CTX_OPENVG_API:
410 #ifdef HAVE_VG
411 attrib_ptr = egl_attribs_vg;
412 #endif
413 break;
414 case GFX_CTX_NONE:
415 default:
416 break;
417 }
418 #endif
419
420 frontend_driver_destroy_signal_handler_state();
421
422 wl->input.dpy = wl_display_connect(NULL);
423 wl->last_buffer_scale = 1;
424 wl->buffer_scale = 1;
425
426 if (!wl->input.dpy)
427 {
428 RARCH_ERR("[Wayland]: Failed to connect to Wayland server.\n");
429 goto error;
430 }
431
432 frontend_driver_install_signal_handler();
433
434 wl->registry = wl_display_get_registry(wl->input.dpy);
435 wl_registry_add_listener(wl->registry, ®istry_listener, wl);
436 wl_display_roundtrip(wl->input.dpy);
437
438 if (!wl->compositor)
439 {
440 RARCH_ERR("[Wayland]: Failed to create compositor.\n");
441 goto error;
442 }
443
444 if (!wl->shm)
445 {
446 RARCH_ERR("[Wayland]: Failed to create shm.\n");
447 goto error;
448 }
449
450 if (!wl->xdg_shell && !!wl->zxdg_shell)
451 {
452 RARCH_LOG("[Wayland]: Using zxdg_shell_v6 interface.\n");
453 }
454
455 if (!wl->xdg_shell && !wl->zxdg_shell)
456 {
457 RARCH_ERR("[Wayland]: Failed to create shell.\n");
458 goto error;
459 }
460
461 if (!wl->idle_inhibit_manager)
462 {
463 RARCH_WARN("[Wayland]: Compositor doesn't support zwp_idle_inhibit_manager_v1 protocol!\n");
464 }
465
466 if (!wl->deco_manager)
467 {
468 RARCH_WARN("[Wayland]: Compositor doesn't support zxdg_decoration_manager_v1 protocol!\n");
469 }
470
471 wl->input.fd = wl_display_get_fd(wl->input.dpy);
472
473 #ifdef HAVE_EGL
474 if (!egl_init_context(&wl->egl,
475 EGL_PLATFORM_WAYLAND_KHR,
476 (EGLNativeDisplayType)wl->input.dpy,
477 &major, &minor, &n, attrib_ptr,
478 egl_default_accept_config_cb))
479 {
480 egl_report_error();
481 goto error;
482 }
483
484 if (n == 0 || !egl_has_config(&wl->egl))
485 goto error;
486 #endif
487
488 wl->input.keyboard_focus = true;
489 wl->input.mouse.focus = true;
490
491 wl->cursor.surface = wl_compositor_create_surface(wl->compositor);
492 wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm);
493 wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr");
494
495 wl->num_active_touches = 0;
496
497 for (i = 0;i < MAX_TOUCHES;i++)
498 {
499 wl->active_touch_positions[i].active = false;
500 wl->active_touch_positions[i].id = -1;
501 wl->active_touch_positions[i].x = (unsigned) 0;
502 wl->active_touch_positions[i].y = (unsigned) 0;
503 }
504
505 flush_wayland_fd(&wl->input);
506
507 return wl;
508
509 error:
510 gfx_ctx_wl_destroy_resources(wl);
511
512 if (wl)
513 free(wl);
514
515 return NULL;
516 }
517
518 #ifdef HAVE_EGL
egl_fill_attribs(gfx_ctx_wayland_data_t * wl,EGLint * attr)519 static EGLint *egl_fill_attribs(gfx_ctx_wayland_data_t *wl, EGLint *attr)
520 {
521 switch (wl_api)
522 {
523 #ifdef EGL_KHR_create_context
524 case GFX_CTX_OPENGL_API:
525 {
526 bool debug = false;
527 #ifdef HAVE_OPENGL
528 unsigned version = wl->egl.major * 1000 + wl->egl.minor;
529 bool core = version >= 3001;
530 #ifndef GL_DEBUG
531 struct retro_hw_render_callback *hwr =
532 video_driver_get_hw_context();
533 #endif
534
535 #ifdef GL_DEBUG
536 debug = true;
537 #else
538 debug = hwr->debug_context;
539 #endif
540
541 if (core)
542 {
543 *attr++ = EGL_CONTEXT_MAJOR_VERSION_KHR;
544 *attr++ = wl->egl.major;
545 *attr++ = EGL_CONTEXT_MINOR_VERSION_KHR;
546 *attr++ = wl->egl.minor;
547 /* Technically, we don't have core/compat until 3.2.
548 * Version 3.1 is either compat or not depending on GL_ARB_compatibility. */
549 if (version >= 3002)
550 {
551 *attr++ = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
552 *attr++ = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
553 }
554 }
555
556 if (debug)
557 {
558 *attr++ = EGL_CONTEXT_FLAGS_KHR;
559 *attr++ = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
560 }
561 #endif
562
563 break;
564 }
565 #endif
566
567 case GFX_CTX_OPENGL_ES_API:
568 #ifdef HAVE_OPENGLES
569 *attr++ = EGL_CONTEXT_CLIENT_VERSION; /* Same as EGL_CONTEXT_MAJOR_VERSION */
570 *attr++ = wl->egl.major ? (EGLint)wl->egl.major : 2;
571 #ifdef EGL_KHR_create_context
572 if (wl->egl.minor > 0)
573 {
574 *attr++ = EGL_CONTEXT_MINOR_VERSION_KHR;
575 *attr++ = wl->egl.minor;
576 }
577 #endif
578 #endif
579 break;
580
581 case GFX_CTX_NONE:
582 default:
583 break;
584 }
585
586 *attr = EGL_NONE;
587 return attr;
588 }
589 #endif
590
gfx_ctx_wl_destroy(void * data)591 static void gfx_ctx_wl_destroy(void *data)
592 {
593 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
594
595 if (!wl)
596 return;
597
598 gfx_ctx_wl_destroy_resources(wl);
599
600 free(wl);
601 }
602
gfx_ctx_wl_set_swap_interval(void * data,int swap_interval)603 static void gfx_ctx_wl_set_swap_interval(void *data, int swap_interval)
604 {
605 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
606
607 #ifdef HAVE_EGL
608 egl_set_swap_interval(&wl->egl, swap_interval);
609 #endif
610 }
611
gfx_ctx_wl_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)612 static bool gfx_ctx_wl_set_video_mode(void *data,
613 unsigned width, unsigned height,
614 bool fullscreen)
615 {
616 #ifdef HAVE_EGL
617 EGLint egl_attribs[16];
618 EGLint *attr = egl_fill_attribs(
619 (gfx_ctx_wayland_data_t*)data, egl_attribs);
620 #endif
621 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
622
623 wl->width = width ? width : DEFAULT_WINDOWED_WIDTH;
624 wl->height = height ? height : DEFAULT_WINDOWED_HEIGHT;
625
626 wl->surface = wl_compositor_create_surface(wl->compositor);
627
628 wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale);
629 wl_surface_add_listener(wl->surface, &wl_surface_listener, wl);
630
631 #ifdef HAVE_EGL
632 wl->win = wl_egl_window_create(wl->surface, wl->width * wl->buffer_scale, wl->height * wl->buffer_scale);
633 #endif
634
635 if (wl->xdg_shell)
636 {
637 wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_shell, wl->surface);
638 xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl);
639
640 wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
641 xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
642
643 xdg_toplevel_set_app_id(wl->xdg_toplevel, "retroarch");
644 xdg_toplevel_set_title(wl->xdg_toplevel, "RetroArch");
645
646 if (wl->deco_manager)
647 {
648 wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
649 wl->deco_manager, wl->xdg_toplevel);
650 }
651
652 /* Waiting for xdg_toplevel to be configured before starting to draw */
653 wl_surface_commit(wl->surface);
654 wl->configured = true;
655
656 while (wl->configured)
657 wl_display_dispatch(wl->input.dpy);
658
659 wl_display_roundtrip(wl->input.dpy);
660 xdg_wm_base_add_listener(wl->xdg_shell, &xdg_shell_listener, NULL);
661 }
662 else if (wl->zxdg_shell)
663 {
664 wl->zxdg_surface = zxdg_shell_v6_get_xdg_surface(wl->zxdg_shell, wl->surface);
665 zxdg_surface_v6_add_listener(wl->zxdg_surface, &zxdg_surface_v6_listener, wl);
666
667 wl->zxdg_toplevel = zxdg_surface_v6_get_toplevel(wl->zxdg_surface);
668 zxdg_toplevel_v6_add_listener(wl->zxdg_toplevel, &zxdg_toplevel_v6_listener, wl);
669
670 zxdg_toplevel_v6_set_app_id(wl->zxdg_toplevel, "retroarch");
671 zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, "RetroArch");
672
673 if (wl->deco_manager)
674 wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
675 wl->deco_manager, wl->xdg_toplevel);
676
677 /* Waiting for xdg_toplevel to be configured before starting to draw */
678 wl_surface_commit(wl->surface);
679 wl->configured = true;
680
681 while (wl->configured)
682 wl_display_dispatch(wl->input.dpy);
683
684 wl_display_roundtrip(wl->input.dpy);
685 zxdg_shell_v6_add_listener(wl->zxdg_shell, &zxdg_shell_v6_listener, NULL);
686 }
687
688 #ifdef HAVE_EGL
689 if (!egl_create_context(&wl->egl, (attr != egl_attribs)
690 ? egl_attribs : NULL))
691 {
692 egl_report_error();
693 goto error;
694 }
695
696 if (!egl_create_surface(&wl->egl, (EGLNativeWindowType)wl->win))
697 goto error;
698 egl_set_swap_interval(&wl->egl, wl->egl.interval);
699 #endif
700
701 if (fullscreen)
702 {
703 if (wl->xdg_toplevel)
704 xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL);
705 else if (wl->zxdg_toplevel)
706 zxdg_toplevel_v6_set_fullscreen(wl->zxdg_toplevel, NULL);
707 }
708
709 flush_wayland_fd(&wl->input);
710
711 if (fullscreen)
712 {
713 wl->cursor.visible = false;
714 gfx_ctx_wl_show_mouse(wl, false);
715 }
716 else
717 wl->cursor.visible = true;
718
719 return true;
720
721 #if defined(HAVE_EGL)
722 error:
723 gfx_ctx_wl_destroy(data);
724 return false;
725 #endif
726 }
727
728 bool input_wl_init(void *data, const char *joypad_name);
729
gfx_ctx_wl_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)730 static void gfx_ctx_wl_input_driver(void *data,
731 const char *joypad_name,
732 input_driver_t **input, void **input_data)
733 {
734 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
735 /* Input is heavily tied to the window stuff
736 * on Wayland, so just implement the input driver here. */
737 if (!input_wl_init(&wl->input, joypad_name))
738 {
739 wl->input.gfx = NULL;
740 *input = NULL;
741 *input_data = NULL;
742 }
743 else
744 {
745 wl->input.gfx = wl;
746 *input = &input_wayland;
747 *input_data = &wl->input;
748 input_driver_init_joypads();
749 }
750 }
751
gfx_ctx_wl_has_focus(void * data)752 static bool gfx_ctx_wl_has_focus(void *data)
753 {
754 (void)data;
755 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
756 return wl->input.keyboard_focus;
757 }
758
gfx_ctx_wl_suppress_screensaver(void * data,bool state)759 static bool gfx_ctx_wl_suppress_screensaver(void *data, bool state)
760 {
761 (void)data;
762
763 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
764
765 if (!wl->idle_inhibit_manager)
766 return false;
767 if (state == (!!wl->idle_inhibitor))
768 return true;
769 if (state)
770 {
771 RARCH_LOG("[Wayland]: Enabling idle inhibitor\n");
772 struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager;
773 wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface);
774 }
775 else
776 {
777 RARCH_LOG("[Wayland]: Disabling the idle inhibitor\n");
778 zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
779 wl->idle_inhibitor = NULL;
780 }
781 return true;
782 }
783
gfx_ctx_wl_get_api(void * data)784 static enum gfx_ctx_api gfx_ctx_wl_get_api(void *data)
785 {
786 return wl_api;
787 }
788
gfx_ctx_wl_bind_api(void * video_driver,enum gfx_ctx_api api,unsigned major,unsigned minor)789 static bool gfx_ctx_wl_bind_api(void *video_driver,
790 enum gfx_ctx_api api, unsigned major, unsigned minor)
791 {
792 #ifdef HAVE_EGL
793 g_egl_major = major;
794 g_egl_minor = minor;
795 #endif
796 wl_api = api;
797
798 switch (api)
799 {
800 case GFX_CTX_OPENGL_API:
801 #ifdef HAVE_OPENGL
802 #ifndef EGL_KHR_create_context
803 if ((major * 1000 + minor) >= 3001)
804 return false;
805 #endif
806 #ifdef HAVE_EGL
807 if (egl_bind_api(EGL_OPENGL_API))
808 return true;
809 #endif
810 #endif
811 break;
812 case GFX_CTX_OPENGL_ES_API:
813 #ifdef HAVE_OPENGLES
814 #ifndef EGL_KHR_create_context
815 if (major >= 3)
816 return false;
817 #endif
818 #ifdef HAVE_EGL
819 if (egl_bind_api(EGL_OPENGL_ES_API))
820 return true;
821 #endif
822 #endif
823 break;
824 case GFX_CTX_OPENVG_API:
825 #ifdef HAVE_VG
826 #ifdef HAVE_EGL
827 if (egl_bind_api(EGL_OPENVG_API))
828 return true;
829 #endif
830 #endif
831 break;
832 case GFX_CTX_NONE:
833 default:
834 break;
835 }
836
837 return false;
838 }
839
gfx_ctx_wl_swap_buffers(void * data)840 static void gfx_ctx_wl_swap_buffers(void *data)
841 {
842 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
843
844 #ifdef HAVE_EGL
845 egl_swap_buffers(&wl->egl);
846 #endif
847 }
848
gfx_ctx_wl_bind_hw_render(void * data,bool enable)849 static void gfx_ctx_wl_bind_hw_render(void *data, bool enable)
850 {
851 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
852 #ifdef HAVE_EGL
853 egl_bind_hw_render(&wl->egl, enable);
854 #endif
855 }
856
gfx_ctx_wl_get_flags(void * data)857 static uint32_t gfx_ctx_wl_get_flags(void *data)
858 {
859 uint32_t flags = 0;
860 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
861
862 if (wl->core_hw_context_enable)
863 BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
864
865 if (string_is_equal(video_driver_get_ident(), "glcore"))
866 {
867 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
868 BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
869 #endif
870 }
871 else if (string_is_equal(video_driver_get_ident(), "gl"))
872 {
873 #ifdef HAVE_GLSL
874 BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
875 #endif
876 }
877
878 return flags;
879 }
880
gfx_ctx_wl_set_flags(void * data,uint32_t flags)881 static void gfx_ctx_wl_set_flags(void *data, uint32_t flags)
882 {
883 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
884 if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
885 wl->core_hw_context_enable = true;
886 }
887
gfx_ctx_wl_get_refresh_rate(void * data)888 static float gfx_ctx_wl_get_refresh_rate(void *data)
889 {
890 gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
891
892 if (!wl || !wl->current_output)
893 return false;
894
895 return (float) wl->current_output->refresh_rate / 1000.0f;
896 }
897
898 const gfx_ctx_driver_t gfx_ctx_wayland = {
899 gfx_ctx_wl_init,
900 gfx_ctx_wl_destroy,
901 gfx_ctx_wl_get_api,
902 gfx_ctx_wl_bind_api,
903 gfx_ctx_wl_set_swap_interval,
904 gfx_ctx_wl_set_video_mode,
905 gfx_ctx_wl_get_video_size,
906 gfx_ctx_wl_get_refresh_rate,
907 NULL, /* get_video_output_size */
908 NULL, /* get_video_output_prev */
909 NULL, /* get_video_output_next */
910 gfx_ctx_wl_get_metrics,
911 NULL,
912 gfx_ctx_wl_update_title,
913 gfx_ctx_wl_check_window,
914 gfx_ctx_wl_set_resize,
915 gfx_ctx_wl_has_focus,
916 gfx_ctx_wl_suppress_screensaver,
917 true, /* has_windowed */
918 gfx_ctx_wl_swap_buffers,
919 gfx_ctx_wl_input_driver,
920 #ifdef HAVE_EGL
921 egl_get_proc_address,
922 #else
923 NULL,
924 #endif
925 NULL,
926 NULL,
927 gfx_ctx_wl_show_mouse,
928 "wayland",
929 gfx_ctx_wl_get_flags,
930 gfx_ctx_wl_set_flags,
931 gfx_ctx_wl_bind_hw_render,
932 NULL,
933 NULL,
934 };
935