diff --git a/gfx/drivers_context/wayland_ctx.c b/gfx/drivers_context/wayland_ctx.c index 547baed953..14dec3e3e9 100644 --- a/gfx/drivers_context/wayland_ctx.c +++ b/gfx/drivers_context/wayland_ctx.c @@ -27,13 +27,6 @@ #ifdef HAVE_EGL #include -#endif - -#ifdef HAVE_VULKAN -#include "../common/vulkan_common.h" -#endif - -#ifdef HAVE_EGL #include "../common/egl_common.h" #endif @@ -156,30 +149,12 @@ static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl) if (!wl) return; - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - egl_destroy(&wl->egl); + egl_destroy(&wl->egl); - if (wl->win) - wl_egl_window_destroy(wl->win); + if (wl->win) + wl_egl_window_destroy(wl->win); #endif - break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - vulkan_context_destroy(&wl->vk, wl->surface); - - if (wl->input.dpy != NULL && wl->input.fd >= 0) - close(wl->input.fd); -#endif - break; - case GFX_CTX_NONE: - default: - break; - } #ifdef HAVE_XKBCOMMON free_xkb(); @@ -264,21 +239,8 @@ static void gfx_ctx_wl_check_window(void *data, bool *quit, gfx_ctx_wl_get_video_size(data, &new_width, &new_height); - switch (wl_api) - { - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - /* Swapchains are recreated in set_resize as a - * central place, so use that to trigger swapchain reinit. */ - *resize = wl->vk.need_new_swapchain; -#endif - break; - case GFX_CTX_NONE: - default: - break; - } - - if (new_width != *width * wl->last_buffer_scale || new_height != *height * wl->last_buffer_scale) + if ( new_width != *width * wl->last_buffer_scale || + new_height != *height * wl->last_buffer_scale) { *width = new_width; *height = new_height; @@ -294,36 +256,9 @@ static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height) { gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - wl_egl_window_resize(wl->win, width, height, 0, 0); + wl_egl_window_resize(wl->win, width, height, 0, 0); #endif - break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - if (vulkan_create_swapchain(&wl->vk, width, height, wl->swap_interval)) - { - wl->vk.context.invalid_swapchain = true; - if (wl->vk.created_new_swapchain) - vulkan_acquire_next_image(&wl->vk); - } - else - { - RARCH_ERR("[Wayland/Vulkan]: Failed to update swapchain.\n"); - return false; - } - - wl->vk.need_new_swapchain = false; -#endif - break; - case GFX_CTX_NONE: - default: - break; - } wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale); return true; @@ -535,37 +470,21 @@ static void *gfx_ctx_wl_init(void *video_driver) wl->input.fd = wl_display_get_fd(wl->input.dpy); - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - if (!egl_init_context(&wl->egl, - EGL_PLATFORM_WAYLAND_KHR, - (EGLNativeDisplayType)wl->input.dpy, - &major, &minor, &n, attrib_ptr, - egl_default_accept_config_cb)) - { - egl_report_error(); - goto error; - } - - if (n == 0 || !egl_has_config(&wl->egl)) - goto error; -#endif - break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - if (!vulkan_context_init(&wl->vk, VULKAN_WSI_WAYLAND)) - goto error; -#endif - break; - case GFX_CTX_NONE: - default: - break; + if (!egl_init_context(&wl->egl, + EGL_PLATFORM_WAYLAND_KHR, + (EGLNativeDisplayType)wl->input.dpy, + &major, &minor, &n, attrib_ptr, + egl_default_accept_config_cb)) + { + egl_report_error(); + goto error; } + if (n == 0 || !egl_has_config(&wl->egl)) + goto error; +#endif + wl->input.keyboard_focus = true; wl->input.mouse.focus = true; @@ -678,19 +597,6 @@ static void gfx_ctx_wl_destroy(void *data) gfx_ctx_wl_destroy_resources(wl); - switch (wl_api) - { - case GFX_CTX_VULKAN_API: -#if defined(HAVE_VULKAN) && defined(HAVE_THREADS) - if (wl->vk.context.queue_lock) - slock_free(wl->vk.context.queue_lock); -#endif - break; - case GFX_CTX_NONE: - default: - break; - } - free(wl); } @@ -698,29 +604,9 @@ static void gfx_ctx_wl_set_swap_interval(void *data, int swap_interval) { gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - egl_set_swap_interval(&wl->egl, swap_interval); + egl_set_swap_interval(&wl->egl, swap_interval); #endif - break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - if (wl->swap_interval != swap_interval) - { - wl->swap_interval = swap_interval; - if (wl->vk.swapchain) - wl->vk.need_new_swapchain = true; - } -#endif - break; - case GFX_CTX_NONE: - default: - break; - } } /* Forward declaration */ @@ -745,19 +631,9 @@ static bool gfx_ctx_wl_set_video_mode(void *data, wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale); wl_surface_add_listener(wl->surface, &wl_surface_listener, wl); - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - wl->win = wl_egl_window_create(wl->surface, wl->width * wl->buffer_scale, wl->height * wl->buffer_scale); + wl->win = wl_egl_window_create(wl->surface, wl->width * wl->buffer_scale, wl->height * wl->buffer_scale); #endif - break; - case GFX_CTX_NONE: - default: - break; - } if (wl->xdg_shell) { @@ -812,29 +688,19 @@ static bool gfx_ctx_wl_set_video_mode(void *data, zxdg_shell_v6_add_listener(wl->zxdg_shell, &zxdg_shell_v6_listener, NULL); } - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - - if (!egl_create_context(&wl->egl, (attr != egl_attribs) ? egl_attribs : NULL)) - { - egl_report_error(); - goto error; - } - - if (!egl_create_surface(&wl->egl, (EGLNativeWindowType)wl->win)) - goto error; - egl_set_swap_interval(&wl->egl, wl->egl.interval); -#endif - break; - case GFX_CTX_NONE: - default: - break; + if (!egl_create_context(&wl->egl, (attr != egl_attribs) + ? egl_attribs : NULL)) + { + egl_report_error(); + goto error; } + if (!egl_create_surface(&wl->egl, (EGLNativeWindowType)wl->win)) + goto error; + egl_set_swap_interval(&wl->egl, wl->egl.interval); +#endif + if (fullscreen) { if (wl->xdg_toplevel) @@ -845,23 +711,6 @@ static bool gfx_ctx_wl_set_video_mode(void *data, flush_wayland_fd(&wl->input); - switch (wl_api) - { - case GFX_CTX_VULKAN_API: - wl_display_roundtrip(wl->input.dpy); - -#ifdef HAVE_VULKAN - if (!vulkan_surface_create(&wl->vk, VULKAN_WSI_WAYLAND, - wl->input.dpy, wl->surface, - wl->width * wl->buffer_scale, wl->height * wl->buffer_scale, wl->swap_interval)) - goto error; -#endif - break; - case GFX_CTX_NONE: - default: - break; - } - if (fullscreen) { wl->cursor.visible = false; @@ -872,7 +721,7 @@ static bool gfx_ctx_wl_set_video_mode(void *data, return true; -#if defined(HAVE_EGL) || defined(HAVE_VULKAN) +#if defined(HAVE_EGL) error: gfx_ctx_wl_destroy(data); return false; @@ -980,12 +829,6 @@ static bool gfx_ctx_wl_bind_api(void *video_driver, #endif #endif break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - return true; -#else - break; -#endif case GFX_CTX_NONE: default: break; @@ -994,77 +837,30 @@ static bool gfx_ctx_wl_bind_api(void *video_driver, return false; } -#ifdef HAVE_VULKAN -static void *gfx_ctx_wl_get_context_data(void *data) -{ - gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - return &wl->vk.context; -} -#endif - static void gfx_ctx_wl_swap_buffers(void *data) { gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - egl_swap_buffers(&wl->egl); + egl_swap_buffers(&wl->egl); #endif - break; - case GFX_CTX_VULKAN_API: -#ifdef HAVE_VULKAN - vulkan_present(&wl->vk, wl->vk.context.current_swapchain_index); - vulkan_acquire_next_image(&wl->vk); - flush_wayland_fd(&wl->input); -#endif - break; - case GFX_CTX_NONE: - default: - break; - } } static gfx_ctx_proc_t gfx_ctx_wl_get_proc_address(const char *symbol) { - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - return egl_get_proc_address(symbol); + return egl_get_proc_address(symbol); #else - break; -#endif - case GFX_CTX_NONE: - default: - break; - } - return NULL; +#endif } static void gfx_ctx_wl_bind_hw_render(void *data, bool enable) { gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; - - switch (wl_api) - { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - case GFX_CTX_OPENVG_API: #ifdef HAVE_EGL - egl_bind_hw_render(&wl->egl, enable); + egl_bind_hw_render(&wl->egl, enable); #endif - break; - case GFX_CTX_NONE: - default: - break; - } } static uint32_t gfx_ctx_wl_get_flags(void *data) @@ -1075,31 +871,17 @@ static uint32_t gfx_ctx_wl_get_flags(void *data) if (wl->core_hw_context_enable) BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT); - switch (wl_api) + if (string_is_equal(video_driver_get_ident(), "glcore")) { - case GFX_CTX_OPENGL_API: - case GFX_CTX_OPENGL_ES_API: - if (string_is_equal(video_driver_get_ident(), "glcore")) - { #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) - BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); + BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); #endif - } - else if (string_is_equal(video_driver_get_ident(), "gl")) - { + } + else if (string_is_equal(video_driver_get_ident(), "gl")) + { #ifdef HAVE_GLSL - BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL); + BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL); #endif - } - break; - case GFX_CTX_VULKAN_API: -#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) - BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); -#endif - break; - case GFX_CTX_NONE: - default: - break; } return flags; @@ -1172,10 +954,6 @@ const gfx_ctx_driver_t gfx_ctx_wayland = { gfx_ctx_wl_get_flags, gfx_ctx_wl_set_flags, gfx_ctx_wl_bind_hw_render, -#ifdef HAVE_VULKAN - gfx_ctx_wl_get_context_data, -#else NULL, -#endif NULL, }; diff --git a/gfx/drivers_context/wayland_vk_ctx.c b/gfx/drivers_context/wayland_vk_ctx.c new file mode 100644 index 0000000000..48c85d266f --- /dev/null +++ b/gfx/drivers_context/wayland_vk_ctx.c @@ -0,0 +1,753 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include "../common/vulkan_common.h" + +#include "../../frontend/frontend_driver.h" +#include "../../input/common/wayland_common.h" +#include "../../input/input_driver.h" +#include "../../input/input_keymaps.h" +#include "../../verbosity.h" + +/* Generated from idle-inhibit-unstable-v1.xml */ +#include "../common/wayland/idle-inhibit-unstable-v1.h" + +/* Generated from xdg-shell-unstable-v6.xml */ +#include "../common/wayland/xdg-shell-unstable-v6.h" + +/* Generated from xdg-shell.xml */ +#include "../common/wayland/xdg-shell.h" + +/* Generated from xdg-decoration-unstable-v1.h */ +#include "../common/wayland/xdg-decoration-unstable-v1.h" + +static enum gfx_ctx_api wl_api = GFX_CTX_NONE; + +#ifndef EGL_OPENGL_ES3_BIT_KHR +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#ifndef EGL_PLATFORM_WAYLAND_KHR +#define EGL_PLATFORM_WAYLAND_KHR 0x31D8 +#endif + +static void handle_toplevel_config_common(void *data, + void *toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + const uint32_t *state; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + wl->fullscreen = false; + wl->maximized = false; + + WL_ARRAY_FOR_EACH(state, states, const uint32_t*) + { + switch (*state) + { + case XDG_TOPLEVEL_STATE_FULLSCREEN: + wl->fullscreen = true; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + wl->maximized = true; + break; + case XDG_TOPLEVEL_STATE_RESIZING: + wl->resize = true; + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + wl->activated = true; + break; + } + } + if (width > 0 && height > 0) + { + wl->prev_width = width; + wl->prev_height = height; + wl->width = width; + wl->height = height; + } + + wl->configured = false; +} + +/* Shell surface callbacks. */ +static void handle_toplevel_config(void *data, + struct xdg_toplevel *toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + handle_toplevel_config_common(data, toplevel, width, height, states); +} + +static void handle_zxdg_toplevel_config( + void *data, struct zxdg_toplevel_v6 *toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + handle_toplevel_config_common(data, toplevel, width, height, states); +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + handle_toplevel_config, + handle_toplevel_close, +}; + +static const struct zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = { + handle_zxdg_toplevel_config, + handle_zxdg_toplevel_close, +}; + +static void gfx_ctx_wl_get_video_size(void *data, + unsigned *width, unsigned *height) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + *width = wl->width * wl->buffer_scale; + *height = wl->height * wl->buffer_scale; +} + +static void gfx_ctx_wl_destroy_resources(gfx_ctx_wayland_data_t *wl) +{ + if (!wl) + return; + + vulkan_context_destroy(&wl->vk, wl->surface); + + if (wl->input.dpy != NULL && wl->input.fd >= 0) + close(wl->input.fd); + +#ifdef HAVE_XKBCOMMON + free_xkb(); +#endif + + if (wl->wl_keyboard) + wl_keyboard_destroy(wl->wl_keyboard); + if (wl->wl_pointer) + wl_pointer_destroy(wl->wl_pointer); + if (wl->wl_touch) + wl_touch_destroy(wl->wl_touch); + + if (wl->cursor.theme) + wl_cursor_theme_destroy(wl->cursor.theme); + if (wl->cursor.surface) + wl_surface_destroy(wl->cursor.surface); + + if (wl->seat) + wl_seat_destroy(wl->seat); + if (wl->xdg_shell) + xdg_wm_base_destroy(wl->xdg_shell); + if (wl->zxdg_shell) + zxdg_shell_v6_destroy(wl->zxdg_shell); + if (wl->compositor) + wl_compositor_destroy(wl->compositor); + if (wl->registry) + wl_registry_destroy(wl->registry); + if (wl->xdg_surface) + xdg_surface_destroy(wl->xdg_surface); + if (wl->zxdg_surface) + zxdg_surface_v6_destroy(wl->zxdg_surface); + if (wl->surface) + wl_surface_destroy(wl->surface); + if (wl->xdg_toplevel) + xdg_toplevel_destroy(wl->xdg_toplevel); + if (wl->zxdg_toplevel) + zxdg_toplevel_v6_destroy(wl->zxdg_toplevel); + if (wl->idle_inhibit_manager) + zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager); + if (wl->deco) + zxdg_toplevel_decoration_v1_destroy(wl->deco); + if (wl->deco_manager) + zxdg_decoration_manager_v1_destroy(wl->deco_manager); + if (wl->idle_inhibitor) + zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor); + + if (wl->input.dpy) + { + wl_display_flush(wl->input.dpy); + wl_display_disconnect(wl->input.dpy); + } + + wl->xdg_shell = NULL; + wl->zxdg_shell = NULL; + wl->compositor = NULL; + wl->registry = NULL; + wl->input.dpy = NULL; + wl->xdg_surface = NULL; + wl->surface = NULL; + wl->xdg_toplevel = NULL; + wl->zxdg_toplevel = NULL; + + wl->width = 0; + wl->height = 0; + +} + +static void gfx_ctx_wl_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height) +{ + /* this function works with SCALED sizes, it's used from the renderer */ + unsigned new_width, new_height; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + flush_wayland_fd(&wl->input); + + new_width = *width * wl->last_buffer_scale; + new_height = *height * wl->last_buffer_scale; + + gfx_ctx_wl_get_video_size(data, &new_width, &new_height); + + /* Swapchains are recreated in set_resize as a + * central place, so use that to trigger swapchain reinit. */ + *resize = wl->vk.need_new_swapchain; + + if (new_width != *width * wl->last_buffer_scale || new_height != *height * wl->last_buffer_scale) + { + *width = new_width; + *height = new_height; + *resize = true; + + wl->last_buffer_scale = wl->buffer_scale; + } + + *quit = (bool)frontend_driver_get_signal_handler_state(); +} + +static bool gfx_ctx_wl_set_resize(void *data, unsigned width, unsigned height) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (vulkan_create_swapchain(&wl->vk, width, height, wl->swap_interval)) + { + wl->vk.context.invalid_swapchain = true; + if (wl->vk.created_new_swapchain) + vulkan_acquire_next_image(&wl->vk); + } + else + { + RARCH_ERR("[Wayland/Vulkan]: Failed to update swapchain.\n"); + return false; + } + + wl->vk.need_new_swapchain = false; + + wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale); + return true; +} + +static void gfx_ctx_wl_update_title(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + char title[128]; + + title[0] = '\0'; + + video_driver_get_window_title(title, sizeof(title)); + + if (wl && title[0]) + { + if (wl->xdg_toplevel || wl->zxdg_toplevel) + { + if (wl->deco) + { + zxdg_toplevel_decoration_v1_set_mode(wl->deco, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + } + if (wl->xdg_toplevel) + xdg_toplevel_set_title(wl->xdg_toplevel, title); + else if (wl->zxdg_toplevel) + zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, title); + } +} + +static bool gfx_ctx_wl_get_metrics(void *data, + enum display_metric_types type, float *value) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (!wl || !wl->current_output || wl->current_output->physical_width == 0 || wl->current_output->physical_height == 0) + return false; + + switch (type) + { + case DISPLAY_METRIC_MM_WIDTH: + *value = (float)wl->current_output->physical_width; + break; + + case DISPLAY_METRIC_MM_HEIGHT: + *value = (float)wl->current_output->physical_height; + break; + + case DISPLAY_METRIC_DPI: + *value = (float)wl->current_output->width * 25.4f / (float)wl->current_output->physical_width; + break; + + default: + *value = 0.0f; + return false; + } + + return true; +} + +#define DEFAULT_WINDOWED_WIDTH 640 +#define DEFAULT_WINDOWED_HEIGHT 480 + +static void *gfx_ctx_wl_init(void *video_driver) +{ + int i; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*) + calloc(1, sizeof(gfx_ctx_wayland_data_t)); + + if (!wl) + return NULL; + + (void)video_driver; + + wl_list_init(&wl->all_outputs); + + frontend_driver_destroy_signal_handler_state(); + + wl->input.dpy = wl_display_connect(NULL); + wl->last_buffer_scale = 1; + wl->buffer_scale = 1; + + if (!wl->input.dpy) + { + RARCH_ERR("[Wayland]: Failed to connect to Wayland server.\n"); + goto error; + } + + frontend_driver_install_signal_handler(); + + wl->registry = wl_display_get_registry(wl->input.dpy); + wl_registry_add_listener(wl->registry, ®istry_listener, wl); + wl_display_roundtrip(wl->input.dpy); + + if (!wl->compositor) + { + RARCH_ERR("[Wayland]: Failed to create compositor.\n"); + goto error; + } + + if (!wl->shm) + { + RARCH_ERR("[Wayland]: Failed to create shm.\n"); + goto error; + } + + if (!wl->xdg_shell && !!wl->zxdg_shell) + { + RARCH_LOG("[Wayland]: Using zxdg_shell_v6 interface.\n"); + } + + if (!wl->xdg_shell && !wl->zxdg_shell) + { + RARCH_ERR("[Wayland]: Failed to create shell.\n"); + goto error; + } + + if (!wl->idle_inhibit_manager) + { + RARCH_WARN("[Wayland]: Compositor doesn't support zwp_idle_inhibit_manager_v1 protocol!\n"); + } + + if (!wl->deco_manager) + { + RARCH_WARN("[Wayland]: Compositor doesn't support zxdg_decoration_manager_v1 protocol!\n"); + } + + wl->input.fd = wl_display_get_fd(wl->input.dpy); + + if (!vulkan_context_init(&wl->vk, VULKAN_WSI_WAYLAND)) + goto error; + + wl->input.keyboard_focus = true; + wl->input.mouse.focus = true; + + wl->cursor.surface = wl_compositor_create_surface(wl->compositor); + wl->cursor.theme = wl_cursor_theme_load(NULL, 16, wl->shm); + wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr"); + + wl->num_active_touches = 0; + + for (i = 0;i < MAX_TOUCHES;i++) + { + wl->active_touch_positions[i].active = false; + wl->active_touch_positions[i].id = -1; + wl->active_touch_positions[i].x = (unsigned) 0; + wl->active_touch_positions[i].y = (unsigned) 0; + } + + flush_wayland_fd(&wl->input); + + return wl; + +error: + gfx_ctx_wl_destroy_resources(wl); + + if (wl) + free(wl); + + return NULL; +} + +static void gfx_ctx_wl_destroy(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (!wl) + return; + + gfx_ctx_wl_destroy_resources(wl); + + switch (wl_api) + { + case GFX_CTX_VULKAN_API: +#if defined(HAVE_THREADS) + if (wl->vk.context.queue_lock) + slock_free(wl->vk.context.queue_lock); +#endif + break; + case GFX_CTX_NONE: + default: + break; + } + + free(wl); +} + +static void gfx_ctx_wl_set_swap_interval(void *data, int swap_interval) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (wl->swap_interval != swap_interval) + { + wl->swap_interval = swap_interval; + if (wl->vk.swapchain) + wl->vk.need_new_swapchain = true; + } +} + +/* Forward declaration */ +void gfx_ctx_wl_show_mouse(void *data, bool state); + +static bool gfx_ctx_wl_set_video_mode(void *data, + unsigned width, unsigned height, + bool fullscreen) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + wl->width = width ? width : DEFAULT_WINDOWED_WIDTH; + wl->height = height ? height : DEFAULT_WINDOWED_HEIGHT; + + wl->surface = wl_compositor_create_surface(wl->compositor); + + wl_surface_set_buffer_scale(wl->surface, wl->buffer_scale); + wl_surface_add_listener(wl->surface, &wl_surface_listener, wl); + + if (wl->xdg_shell) + { + wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_shell, wl->surface); + xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl); + + wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface); + xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl); + + xdg_toplevel_set_app_id(wl->xdg_toplevel, "retroarch"); + xdg_toplevel_set_title(wl->xdg_toplevel, "RetroArch"); + + if (wl->deco_manager) + { + wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration( + wl->deco_manager, wl->xdg_toplevel); + } + + /* Waiting for xdg_toplevel to be configured before starting to draw */ + wl_surface_commit(wl->surface); + wl->configured = true; + + while (wl->configured) + wl_display_dispatch(wl->input.dpy); + + wl_display_roundtrip(wl->input.dpy); + xdg_wm_base_add_listener(wl->xdg_shell, &xdg_shell_listener, NULL); + } + else if (wl->zxdg_shell) + { + wl->zxdg_surface = zxdg_shell_v6_get_xdg_surface(wl->zxdg_shell, wl->surface); + zxdg_surface_v6_add_listener(wl->zxdg_surface, &zxdg_surface_v6_listener, wl); + + wl->zxdg_toplevel = zxdg_surface_v6_get_toplevel(wl->zxdg_surface); + zxdg_toplevel_v6_add_listener(wl->zxdg_toplevel, &zxdg_toplevel_v6_listener, wl); + + zxdg_toplevel_v6_set_app_id(wl->zxdg_toplevel, "retroarch"); + zxdg_toplevel_v6_set_title(wl->zxdg_toplevel, "RetroArch"); + + if (wl->deco_manager) + wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration( + wl->deco_manager, wl->xdg_toplevel); + + /* Waiting for xdg_toplevel to be configured before starting to draw */ + wl_surface_commit(wl->surface); + wl->configured = true; + + while (wl->configured) + wl_display_dispatch(wl->input.dpy); + + wl_display_roundtrip(wl->input.dpy); + zxdg_shell_v6_add_listener(wl->zxdg_shell, &zxdg_shell_v6_listener, NULL); + } + + if (fullscreen) + { + if (wl->xdg_toplevel) + xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL); + else if (wl->zxdg_toplevel) + zxdg_toplevel_v6_set_fullscreen(wl->zxdg_toplevel, NULL); + } + + flush_wayland_fd(&wl->input); + + wl_display_roundtrip(wl->input.dpy); + + if (!vulkan_surface_create(&wl->vk, VULKAN_WSI_WAYLAND, + wl->input.dpy, wl->surface, + wl->width * wl->buffer_scale, wl->height * wl->buffer_scale, wl->swap_interval)) + goto error; + + if (fullscreen) + { + wl->cursor.visible = false; + gfx_ctx_wl_show_mouse(wl, false); + } + else + wl->cursor.visible = true; + + return true; + +error: + gfx_ctx_wl_destroy(data); + return false; +} + +bool input_wl_init(void *data, const char *joypad_name); + +static void gfx_ctx_wl_input_driver(void *data, + const char *joypad_name, + input_driver_t **input, void **input_data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + /* Input is heavily tied to the window stuff + * on Wayland, so just implement the input driver here. */ + if (!input_wl_init(&wl->input, joypad_name)) + { + *input = NULL; + *input_data = NULL; + } + else + { + *input = &input_wayland; + *input_data = &wl->input; + } +} + +static bool gfx_ctx_wl_has_focus(void *data) +{ + (void)data; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + return wl->input.keyboard_focus; +} + +static bool gfx_ctx_wl_suppress_screensaver(void *data, bool state) +{ + (void)data; + + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (!wl->idle_inhibit_manager) + return false; + if (state == (!!wl->idle_inhibitor)) + return true; + if (state) + { + RARCH_LOG("[Wayland]: Enabling idle inhibitor\n"); + struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager; + wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface); + } + else + { + RARCH_LOG("[Wayland]: Disabling the idle inhibitor\n"); + zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor); + wl->idle_inhibitor = NULL; + } + return true; +} + +static enum gfx_ctx_api gfx_ctx_wl_get_api(void *data) +{ + return wl_api; +} + +static bool gfx_ctx_wl_bind_api(void *video_driver, + enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + wl_api = api; + + if (api == GFX_CTX_VULKAN_API) + return true; + return false; +} + +static void *gfx_ctx_wl_get_context_data(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + return &wl->vk.context; +} + +static void gfx_ctx_wl_swap_buffers(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + vulkan_present(&wl->vk, wl->vk.context.current_swapchain_index); + vulkan_acquire_next_image(&wl->vk); + flush_wayland_fd(&wl->input); +} + +static gfx_ctx_proc_t gfx_ctx_wl_get_proc_address(const char *symbol) +{ + return NULL; +} + +static void gfx_ctx_wl_bind_hw_render(void *data, bool enable) { } + +static uint32_t gfx_ctx_wl_get_flags(void *data) +{ + uint32_t flags = 0; + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (wl->core_hw_context_enable) + BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT); + + switch (wl_api) + { + case GFX_CTX_OPENGL_API: + case GFX_CTX_OPENGL_ES_API: + if (string_is_equal(video_driver_get_ident(), "glcore")) + { +#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) + BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); +#endif + } + else if (string_is_equal(video_driver_get_ident(), "gl")) + { +#ifdef HAVE_GLSL + BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL); +#endif + } + break; + case GFX_CTX_VULKAN_API: +#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) + BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); +#endif + break; + case GFX_CTX_NONE: + default: + break; + } + + return flags; +} + +static void gfx_ctx_wl_set_flags(void *data, uint32_t flags) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT)) + wl->core_hw_context_enable = true; +} + +void gfx_ctx_wl_show_mouse(void *data, bool state) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + if (!wl->wl_pointer) + return; + + if (state) + { + struct wl_cursor_image *image = wl->cursor.default_cursor->images[0]; + wl_pointer_set_cursor(wl->wl_pointer, wl->cursor.serial, wl->cursor.surface, image->hotspot_x, image->hotspot_y); + wl_surface_attach(wl->cursor.surface, wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_damage(wl->cursor.surface, 0, 0, image->width, image->height); + wl_surface_commit(wl->cursor.surface); + } + else + wl_pointer_set_cursor(wl->wl_pointer, wl->cursor.serial, NULL, 0, 0); + + wl->cursor.visible = state; +} + +static float gfx_ctx_wl_get_refresh_rate(void *data) +{ + gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; + + if (!wl || !wl->current_output) + return false; + + return (float) wl->current_output->refresh_rate / 1000.0f; +} + +const gfx_ctx_driver_t gfx_ctx_vk_wayland = { + gfx_ctx_wl_init, + gfx_ctx_wl_destroy, + gfx_ctx_wl_get_api, + gfx_ctx_wl_bind_api, + gfx_ctx_wl_set_swap_interval, + gfx_ctx_wl_set_video_mode, + gfx_ctx_wl_get_video_size, + gfx_ctx_wl_get_refresh_rate, + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + gfx_ctx_wl_get_metrics, + NULL, + gfx_ctx_wl_update_title, + gfx_ctx_wl_check_window, + gfx_ctx_wl_set_resize, + gfx_ctx_wl_has_focus, + gfx_ctx_wl_suppress_screensaver, + true, /* has_windowed */ + gfx_ctx_wl_swap_buffers, + gfx_ctx_wl_input_driver, + gfx_ctx_wl_get_proc_address, + NULL, + NULL, + gfx_ctx_wl_show_mouse, + "wayland", + gfx_ctx_wl_get_flags, + gfx_ctx_wl_set_flags, + gfx_ctx_wl_bind_hw_render, + gfx_ctx_wl_get_context_data, + NULL, +}; diff --git a/retroarch.c b/retroarch.c index c0074a134d..e334f1e542 100644 --- a/retroarch.c +++ b/retroarch.c @@ -583,6 +583,9 @@ static const gfx_ctx_driver_t *gfx_ctx_vk_drivers[] = { #if defined(ANDROID) &gfx_ctx_vk_android, #endif +#if defined(HAVE_WAYLAND) + &gfx_ctx_vk_wayland, +#endif #if defined(HAVE_VULKAN_DISPLAY) &gfx_ctx_khr_display, #endif diff --git a/retroarch.h b/retroarch.h index ec09d8ffe1..276a92e882 100644 --- a/retroarch.h +++ b/retroarch.h @@ -1885,6 +1885,7 @@ extern const gfx_ctx_driver_t gfx_ctx_osmesa; extern const gfx_ctx_driver_t gfx_ctx_sdl_gl; extern const gfx_ctx_driver_t gfx_ctx_x_egl; extern const gfx_ctx_driver_t gfx_ctx_uwp; +extern const gfx_ctx_driver_t gfx_ctx_vk_wayland; extern const gfx_ctx_driver_t gfx_ctx_wayland; extern const gfx_ctx_driver_t gfx_ctx_x; extern const gfx_ctx_driver_t gfx_ctx_drm;