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, &registry_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