diff options
Diffstat (limited to 'platform/linuxbsd')
17 files changed, 389 insertions, 141 deletions
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 94784f2da9..8fbd8b10b1 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -7,7 +7,7 @@ from platform_methods import detect_arch from typing import TYPE_CHECKING if TYPE_CHECKING: - from SCons import Environment + from SCons.Script.SConscript import SConsEnvironment def get_name(): @@ -70,7 +70,7 @@ def get_flags(): ] -def configure(env: "Environment"): +def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"] if env["arch"] not in supported_arches: diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index a3633e72b7..cdebed58b2 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -528,10 +528,10 @@ void FreeDesktopPortalDesktop::_file_dialog_callback(const Callable &p_callable, } } -void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) { +void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) { FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud; - while (!portal->file_dialog_thread_abort.is_set()) { + while (!portal->monitor_thread_abort.is_set()) { { MutexLock lock(portal->file_dialog_mutex); for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) { @@ -579,10 +579,44 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) { } } } + + if (portal->theme_connection) { + while (true) { + DBusMessage *msg = dbus_connection_pop_message(portal->theme_connection); + if (!msg) { + break; + } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Settings", "SettingChanged")) { + DBusMessageIter iter; + if (dbus_message_iter_init(msg, &iter)) { + const char *value; + dbus_message_iter_get_basic(&iter, &value); + String name_space = String::utf8(value); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &value); + String key = String::utf8(value); + + if (name_space == "org.freedesktop.appearance" && key == "color-scheme") { + callable_mp(portal, &FreeDesktopPortalDesktop::_system_theme_changed_callback).call_deferred(); + } + } + dbus_message_unref(msg); + break; + } + dbus_message_unref(msg); + } + dbus_connection_read_write(portal->theme_connection, 0); + } + usleep(50000); } } +void FreeDesktopPortalDesktop::_system_theme_changed_callback() { + if (system_theme_changed.is_valid()) { + system_theme_changed.call(); + } +} + FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() { #ifdef SOWRAP_ENABLED #ifdef DEBUG_ENABLED @@ -611,17 +645,34 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() { unsupported = true; } + DBusError err; + dbus_error_init(&err); + theme_connection = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + } else { + theme_path = "type='signal',sender='org.freedesktop.portal.Desktop',interface='org.freedesktop.portal.Settings',member='SettingChanged'"; + dbus_bus_add_match(theme_connection, theme_path.utf8().get_data(), &err); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + dbus_connection_unref(theme_connection); + theme_connection = nullptr; + } + dbus_connection_read_write(theme_connection, 0); + } + if (!unsupported) { - file_dialog_thread_abort.clear(); - file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this); + monitor_thread_abort.clear(); + monitor_thread.start(FreeDesktopPortalDesktop::_thread_monitor, this); } } FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() { - file_dialog_thread_abort.set(); - if (file_dialog_thread.is_started()) { - file_dialog_thread.wait_to_finish(); + monitor_thread_abort.set(); + if (monitor_thread.is_started()) { + monitor_thread.wait_to_finish(); } + for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) { if (fd.connection) { DBusError err; @@ -631,6 +682,13 @@ FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() { dbus_connection_unref(fd.connection); } } + if (theme_connection) { + DBusError err; + dbus_error_init(&err); + dbus_bus_remove_match(theme_connection, theme_path.utf8().get_data(), &err); + dbus_error_free(&err); + dbus_connection_unref(theme_connection); + } } #endif // DBUS_ENABLED diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h index c9da387241..75afe02a26 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.h +++ b/platform/linuxbsd/freedesktop_portal_desktop.h @@ -34,6 +34,7 @@ #ifdef DBUS_ENABLED #include "core/os/thread.h" +#include "core/os/thread_safe.h" #include "servers/display_server.h" struct DBusMessage; @@ -68,10 +69,15 @@ private: Mutex file_dialog_mutex; Vector<FileDialogData> file_dialogs; - Thread file_dialog_thread; - SafeFlag file_dialog_thread_abort; + Thread monitor_thread; + SafeFlag monitor_thread_abort; - static void _thread_file_dialog_monitor(void *p_ud); + DBusConnection *theme_connection = nullptr; + String theme_path; + Callable system_theme_changed; + void _system_theme_changed_callback(); + + static void _thread_monitor(void *p_ud); public: FreeDesktopPortalDesktop(); @@ -86,6 +92,9 @@ public: // 1: Prefer dark appearance. // 2: Prefer light appearance. uint32_t get_appearance_color_scheme(); + void set_system_theme_change_callback(const Callable &p_system_theme_changed) { + system_theme_changed = p_system_theme_changed; + } }; #endif // DBUS_ENABLED diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index f9e1aca742..85846335f7 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -127,14 +127,6 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) { } } -int OS_LinuxBSD::get_low_processor_usage_mode_sleep_usec() const { - if (DisplayServer::get_singleton() == nullptr || DisplayServer::get_singleton()->get_name() != "Wayland" || is_in_low_processor_usage_mode()) { - return OS::get_low_processor_usage_mode_sleep_usec(); - } - - return 500; // Roughly 2000 FPS, improves frame time when emulating VSync. -} - void OS_LinuxBSD::initialize() { crash_handler.initialize(); @@ -486,7 +478,7 @@ Vector<String> OS_LinuxBSD::lspci_get_device_value(Vector<String> vendor_device_ return values; } -Error OS_LinuxBSD::shell_open(String p_uri) { +Error OS_LinuxBSD::shell_open(const String &p_uri) { Error ok; int err_code; List<String> args; diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 6ea2fc8e94..9084061eb9 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -118,7 +118,7 @@ public: virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; - virtual Error shell_open(String p_uri) override; + virtual Error shell_open(const String &p_uri) override; virtual String get_unique_id() const override; virtual String get_processor_name() const override; @@ -127,8 +127,6 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; - virtual int get_low_processor_usage_mode_sleep_usec() const override; - virtual bool _check_internal_feature_support(const String &p_feature) override; void run(); diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub index 910ac29b5e..cab45b7672 100644 --- a/platform/linuxbsd/wayland/SCsub +++ b/platform/linuxbsd/wayland/SCsub @@ -195,7 +195,7 @@ if env["use_sowrap"]: if env["vulkan"]: - source_files.append("vulkan_context_wayland.cpp") + source_files.append("rendering_context_driver_vulkan_wayland.cpp") if env["opengl3"]: source_files.append("egl_manager_wayland.cpp") diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 02b715056a..c957dea32d 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -100,8 +100,8 @@ void DisplayServerWayland::_resize_window(const Size2i &p_size) { wd.rect.size = p_size; #ifdef RD_ENABLED - if (wd.visible && context_rd) { - context_rd->window_resize(MAIN_WINDOW_ID, wd.rect.size.width, wd.rect.size.height); + if (wd.visible && rendering_context) { + rendering_context->window_set_size(MAIN_WINDOW_ID, wd.rect.size.width, wd.rect.size.height); } #endif @@ -140,10 +140,10 @@ void DisplayServerWayland::_show_window() { // the only acceptable way of implementing window showing is to move the // graphics context window creation logic here. #ifdef RD_ENABLED - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextWayland::WindowPlatformData vulkan; + RenderingContextDriverVulkanWayland::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -152,14 +152,17 @@ void DisplayServerWayland::_show_window() { wpd.vulkan.display = wayland_thread.get_wl_display(); } #endif - Error err = context_rd->window_create(wd.id, wd.vsync_mode, wd.rect.size.width, wd.rect.size.height, &wpd); - ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", context_rd->get_api_name())); + Error err = rendering_context->window_create(wd.id, &wpd); + ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver)); - emulate_vsync = (context_rd->get_vsync_mode(wd.id) == DisplayServer::VSYNC_ENABLED); + rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height); + rendering_context->window_set_vsync_mode(wd.id, wd.vsync_mode); + + emulate_vsync = (rendering_context->window_get_vsync_mode(wd.id) == DisplayServer::VSYNC_ENABLED); if (emulate_vsync) { print_verbose("VSYNC: manually throttling frames using MAILBOX."); - context_rd->set_vsync_mode(wd.id, DisplayServer::VSYNC_MAILBOX); + rendering_context->window_set_vsync_mode(wd.id, DisplayServer::VSYNC_MAILBOX); } } #endif @@ -272,6 +275,10 @@ bool DisplayServerWayland::is_dark_mode() const { } } +void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) { + portal_desktop->set_system_theme_change_callback(p_callable); +} + Error DisplayServerWayland::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { WindowID window_id = MAIN_WINDOW_ID; // TODO: Use window IDs for multiwindow support. @@ -860,11 +867,11 @@ bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const { } bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const { - return frame; + return !suspended; } bool DisplayServerWayland::can_any_window_draw() const { - return frame; + return !suspended; } void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) { @@ -885,14 +892,14 @@ void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn MutexLock mutex_lock(wayland_thread.mutex); #ifdef RD_ENABLED - if (context_rd) { - context_rd->set_vsync_mode(p_window_id, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode); - emulate_vsync = (context_rd->get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED); + emulate_vsync = (rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED); if (emulate_vsync) { print_verbose("VSYNC: manually throttling frames using MAILBOX."); - context_rd->set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX); + rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX); } } #endif // VULKAN_ENABLED @@ -917,8 +924,8 @@ DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServ } #ifdef VULKAN_ENABLED - if (context_rd) { - return context_rd->get_vsync_mode(p_window_id); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window_id); } #endif // VULKAN_ENABLED @@ -1136,7 +1143,32 @@ void DisplayServerWayland::process_events() { wayland_thread.keyboard_echo_keys(); - frame = wayland_thread.get_reset_frame(); + if (!suspended) { + if (emulate_vsync) { + // Due to various reasons, we manually handle display synchronization by + // waiting for a frame event (request to draw) or, if available, the actual + // window's suspend status. When a window is suspended, we can avoid drawing + // altogether, either because the compositor told us that we don't need to or + // because the pace of the frame events became unreliable. + bool frame = wayland_thread.wait_frame_suspend_ms(1000); + if (!frame) { + suspended = true; + } + } else { + if (wayland_thread.is_suspended()) { + suspended = true; + } + } + + if (suspended) { + DEBUG_LOG_WAYLAND("Window suspended."); + } + } else { + if (wayland_thread.get_reset_frame()) { + // At last, a sign of life! We're no longer suspended. + suspended = false; + } + } wayland_thread.mutex.unlock(); @@ -1236,15 +1268,15 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win #ifdef RD_ENABLED #ifdef VULKAN_ENABLED if (p_rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextWayland); + rendering_context = memnew(RenderingContextDriverVulkanWayland); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Could not initialize %s", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Could not initialize %s", p_rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_CANT_CREATE; return; } @@ -1329,9 +1361,10 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win _show_window(); #ifdef RD_ENABLED - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -1351,8 +1384,12 @@ DisplayServerWayland::~DisplayServerWayland() { // TODO: Multiwindow support. if (main_window.visible) { #ifdef VULKAN_ENABLED - if (context_rd) { - context_rd->window_destroy(MAIN_WINDOW_ID); + if (rendering_device) { + rendering_device->screen_free(MAIN_WINDOW_ID); + } + + if (rendering_context) { + rendering_context->window_destroy(MAIN_WINDOW_ID); } #endif @@ -1374,12 +1411,11 @@ DisplayServerWayland::~DisplayServerWayland() { // Destroy all drivers. #ifdef RD_ENABLED if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); } - if (context_rd) { - memdelete(context_rd); + if (rendering_context) { + memdelete(rendering_context); } #endif diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index 3e7f3c4cb4..e42967eb7b 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -39,7 +39,7 @@ #include "servers/rendering/rendering_device.h" #ifdef VULKAN_ENABLED -#include "wayland/vulkan_context_wayland.h" +#include "wayland/rendering_context_driver_vulkan_wayland.h" #endif #endif //RD_ENABLED @@ -117,13 +117,13 @@ class DisplayServerWayland : public DisplayServer { Context context; - bool frame = false; + bool suspended = false; bool emulate_vsync = false; String rendering_driver; #ifdef RD_ENABLED - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif @@ -171,6 +171,7 @@ public: #ifdef DBUS_ENABLED virtual bool is_dark_mode_supported() const override; virtual bool is_dark_mode() const override; + virtual void set_system_theme_change_callback(const Callable &p_callable) override; virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override; virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override; diff --git a/platform/linuxbsd/wayland/vulkan_context_wayland.cpp b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp index b3f28a1678..c874c45a8a 100644 --- a/platform/linuxbsd/wayland/vulkan_context_wayland.cpp +++ b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_wayland.cpp */ +/* rendering_context_driver_vulkan_wayland.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,32 +28,43 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "vulkan_context_wayland.h" - #ifdef VULKAN_ENABLED +#include "rendering_context_driver_vulkan_wayland.h" + #ifdef USE_VOLK #include <volk.h> #else #include <vulkan/vulkan.h> #endif -const char *VulkanContextWayland::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanWayland::_get_platform_surface_extension() const { return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; } -Error VulkanContextWayland::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanWayland::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); + + VkWaylandSurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + create_info.display = wpd->display; + create_info.surface = wpd->surface; + + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateWaylandSurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); - VkWaylandSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - createInfo.display = wpd->display; - createInfo.surface = wpd->surface; + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); +} + +RenderingContextDriverVulkanWayland::RenderingContextDriverVulkanWayland() { + // Does nothing. +} - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateWaylandSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); +RenderingContextDriverVulkanWayland::~RenderingContextDriverVulkanWayland() { + // Does nothing. } #endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/wayland/vulkan_context_wayland.h b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.h index b0a7d1ff87..dfebca1890 100644 --- a/platform/linuxbsd/wayland/vulkan_context_wayland.h +++ b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_wayland.h */ +/* rendering_context_driver_vulkan_wayland.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,19 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_WAYLAND_H -#define VULKAN_CONTEXT_WAYLAND_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" -class VulkanContextWayland : public VulkanContext { - const char *_get_platform_surface_extension() const override final; +class RenderingContextDriverVulkanWayland : public RenderingContextDriverVulkan { +private: + virtual const char *_get_platform_surface_extension() const override final; + +protected: + SurfaceID surface_create(const void *p_platform_data) override final; public: struct WindowPlatformData { @@ -44,9 +48,10 @@ public: struct wl_surface *surface; }; - Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; + RenderingContextDriverVulkanWayland(); + ~RenderingContextDriverVulkanWayland(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_WAYLAND_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index ae1d96a3b1..ebb21722e2 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -469,7 +469,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re } if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(5, (int)version))); + registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version))); registry->xdg_wm_base_name = name; xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr); @@ -1063,9 +1063,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel * WindowState *ws = (WindowState *)data; ERR_FAIL_NULL(ws); - // Expect the window to be in windowed mode. The mode will get overridden if - // the compositor reports otherwise. + // Expect the window to be in a plain state. It will get properly set if the + // compositor reports otherwise below. ws->mode = DisplayServer::WINDOW_MODE_WINDOWED; + ws->suspended = false; uint32_t *state = nullptr; wl_array_for_each(state, states) { @@ -1078,6 +1079,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel * ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN; } break; + case XDG_TOPLEVEL_STATE_SUSPENDED: { + ws->suspended = true; + } break; + default: { // We don't care about the other states (for now). } break; @@ -1176,9 +1181,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st libdecor_window_state window_state = LIBDECOR_WINDOW_STATE_NONE; - // Expect the window to be in windowed mode. The mode will get overridden if - // the compositor reports otherwise. + // Expect the window to be in a plain state. It will get properly set if the + // compositor reports otherwise below. ws->mode = DisplayServer::WINDOW_MODE_WINDOWED; + ws->suspended = false; if (libdecor_configuration_get_window_state(configuration, &window_state)) { if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) { @@ -1188,6 +1194,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) { ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN; } + + if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) { + ws->suspended = true; + } } window_state_update_size(ws, width, height); @@ -3872,6 +3882,102 @@ bool WaylandThread::get_reset_frame() { return old_frame; } +// Dispatches events until a frame event is received, a window is reported as +// suspended or the timeout expires. +bool WaylandThread::wait_frame_suspend_ms(int p_timeout) { + if (main_window.suspended) { + // The window is suspended! The compositor is telling us _explicitly_ that we + // don't need to draw, without letting us guess through the frame event's + // timing and stuff like that. Our job here is done. + return false; + } + + if (frame) { + // We already have a frame! Probably it got there while the caller locked :D + frame = false; + return true; + } + + struct pollfd poll_fd; + poll_fd.fd = wl_display_get_fd(wl_display); + poll_fd.events = POLLIN | POLLHUP; + + int begin_ms = OS::get_singleton()->get_ticks_msec(); + int remaining_ms = p_timeout; + + while (remaining_ms > 0) { + // Empty the event queue while it's full. + while (wl_display_prepare_read(wl_display) != 0) { + if (wl_display_dispatch_pending(wl_display) == -1) { + // Oh no. We'll check and handle any display error below. + break; + } + + if (main_window.suspended) { + return false; + } + + if (frame) { + // We had a frame event in the queue :D + frame = false; + return true; + } + } + + int werror = wl_display_get_error(wl_display); + + if (werror) { + if (werror == EPROTO) { + struct wl_interface *wl_interface = nullptr; + uint32_t id = 0; + + int error_code = wl_display_get_protocol_error(wl_display, (const struct wl_interface **)&wl_interface, &id); + CRASH_NOW_MSG(vformat("Wayland protocol error %d on interface %s@%d.", error_code, wl_interface ? wl_interface->name : "unknown", id)); + } else { + CRASH_NOW_MSG(vformat("Wayland client error code %d.", werror)); + } + } + + wl_display_flush(wl_display); + + // Wait for the event file descriptor to have new data. + poll(&poll_fd, 1, remaining_ms); + + if (poll_fd.revents | POLLIN) { + // Load the queues with fresh new data. + wl_display_read_events(wl_display); + } else { + // Oh well... Stop signaling that we want to read. + wl_display_cancel_read(wl_display); + + // We've got no new events :( + // We won't even bother with checking the frame flag. + return false; + } + + // Let's try dispatching now... + wl_display_dispatch_pending(wl_display); + + if (main_window.suspended) { + return false; + } + + if (frame) { + frame = false; + return true; + } + + remaining_ms -= OS::get_singleton()->get_ticks_msec() - begin_ms; + } + + DEBUG_LOG_WAYLAND_THREAD("Frame timeout."); + return false; +} + +bool WaylandThread::is_suspended() const { + return main_window.suspended; +} + void WaylandThread::destroy() { if (!initialized) { return; diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index 86033c1a09..f3e3c3a2ac 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -177,6 +177,7 @@ public: Rect2i rect; DisplayServer::WindowMode mode = DisplayServer::WINDOW_MODE_WINDOWED; + bool suspended = false; // These are true by default as it isn't guaranteed that we'll find an // xdg-shell implementation with wm_capabilities available. If and once we @@ -939,6 +940,9 @@ public: void set_frame(); bool get_reset_frame(); + bool wait_frame_suspend_ms(int p_timeout); + + bool is_suspended() const; Error init(); void destroy(); diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub index bbfaaf10d1..75fe584ad5 100644 --- a/platform/linuxbsd/x11/SCsub +++ b/platform/linuxbsd/x11/SCsub @@ -21,7 +21,7 @@ if env["use_sowrap"]: ) if env["vulkan"]: - source_files.append("vulkan_context_x11.cpp") + source_files.append("rendering_context_driver_vulkan_x11.cpp") if env["opengl3"]: env.Append(CPPDEFINES=["GLAD_GLX_NO_X11"]) diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index c0937b6d4f..35bfe81827 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -364,6 +364,10 @@ bool DisplayServerX11::is_dark_mode() const { } } +void DisplayServerX11::set_system_theme_change_callback(const Callable &p_callable) { + portal_desktop->set_system_theme_change_callback(p_callable); +} + Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { WindowID window_id = last_focused_window; @@ -1670,7 +1674,11 @@ DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, V window_set_flag(WindowFlags(i), true, id); } } - +#ifdef RD_ENABLED + if (rendering_device) { + rendering_device->screen_create(id); + } +#endif return id; } @@ -1719,8 +1727,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { } #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_destroy(p_id); + if (rendering_device) { + rendering_device->screen_free(p_id); + } + + if (rendering_context) { + rendering_context->window_destroy(p_id); } #endif #ifdef GLES3_ENABLED @@ -2245,8 +2257,8 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { // Keep rendering context window size in sync #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(p_window, xwa.width, xwa.height); + if (rendering_context) { + rendering_context->window_set_size(p_window, xwa.width, xwa.height); } #endif #if defined(GLES3_ENABLED) @@ -3970,8 +3982,8 @@ void DisplayServerX11::_window_changed(XEvent *event) { wd.size = new_rect.size; #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(window_id, wd.size.width, wd.size.height); + if (rendering_context) { + rendering_context->window_set_size(window_id, wd.size.width, wd.size.height); } #endif #if defined(GLES3_ENABLED) @@ -5300,8 +5312,8 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) { void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif @@ -5318,8 +5330,8 @@ void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif #if defined(GLES3_ENABLED) @@ -5662,10 +5674,10 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V _update_size_hints(id); #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextX11::WindowPlatformData vulkan; + RenderingContextDriverVulkanX11::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -5674,8 +5686,11 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V wpd.vulkan.display = x11_display; } #endif - Error err = context_rd->window_create(id, p_vsync_mode, win_rect.size.width, win_rect.size.height, &wpd); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s window", context_rd->get_api_name())); + Error err = rendering_context->window_create(id, &wpd); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s window", rendering_driver)); + + rendering_context->window_set_size(id, win_rect.size.width, win_rect.size.height); + rendering_context->window_set_vsync_mode(id, p_vsync_mode); } #endif #ifdef GLES3_ENABLED @@ -6077,15 +6092,15 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode #if defined(RD_ENABLED) #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextX11); + rendering_context = memnew(RenderingContextDriverVulkanX11); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Could not initialize %s", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Could not initialize %s", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_CANT_CREATE; return; } @@ -6197,9 +6212,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode show_window(main_window); #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -6374,8 +6390,12 @@ DisplayServerX11::~DisplayServerX11() { //destroy all windows for (KeyValue<WindowID, WindowData> &E : windows) { #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_destroy(E.key); + if (rendering_device) { + rendering_device->screen_free(E.key); + } + + if (rendering_context) { + rendering_context->window_destroy(E.key); } #endif #ifdef GLES3_ENABLED @@ -6419,14 +6439,13 @@ DisplayServerX11::~DisplayServerX11() { //destroy drivers #if defined(RD_ENABLED) if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); rendering_device = nullptr; } - if (context_rd) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + memdelete(rendering_context); + rendering_context = nullptr; } #endif diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index da4085772a..a5cbe34d26 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -61,7 +61,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#include "x11/vulkan_context_x11.h" +#include "x11/rendering_context_driver_vulkan_x11.h" #endif #endif @@ -144,7 +144,7 @@ class DisplayServerX11 : public DisplayServer { GLManagerEGL_X11 *gl_manager_egl = nullptr; #endif #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif @@ -400,6 +400,7 @@ public: #if defined(DBUS_ENABLED) virtual bool is_dark_mode_supported() const override; virtual bool is_dark_mode() const override; + virtual void set_system_theme_change_callback(const Callable &p_callable) override; virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override; virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override; diff --git a/platform/linuxbsd/x11/vulkan_context_x11.cpp b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp index 3eee1706b0..bf44062266 100644 --- a/platform/linuxbsd/x11/vulkan_context_x11.cpp +++ b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_x11.cpp */ +/* rendering_context_driver_vulkan_x11.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,7 +30,7 @@ #ifdef VULKAN_ENABLED -#include "vulkan_context_x11.h" +#include "rendering_context_driver_vulkan_x11.h" #ifdef USE_VOLK #include <volk.h> @@ -38,28 +38,33 @@ #include <vulkan/vulkan.h> #endif -const char *VulkanContextX11::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanX11::_get_platform_surface_extension() const { return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; } -Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanX11::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkXlibSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = wpd->display; - createInfo.window = wpd->window; + VkXlibSurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info.dpy = wpd->display; + create_info.window = wpd->window; - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateXlibSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateXlibSurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); + + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } -VulkanContextX11::VulkanContextX11() { +RenderingContextDriverVulkanX11::RenderingContextDriverVulkanX11() { + // Does nothing. } -VulkanContextX11::~VulkanContextX11() { +RenderingContextDriverVulkanX11::~RenderingContextDriverVulkanX11() { + // Does nothing. } #endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/x11/vulkan_context_x11.h b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.h index 2390326b44..d525b69ec7 100644 --- a/platform/linuxbsd/x11/vulkan_context_x11.h +++ b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_x11.h */ +/* rendering_context_driver_vulkan_x11.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,29 +28,32 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_X11_H -#define VULKAN_CONTEXT_X11_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_X11_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_X11_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #include <X11/Xlib.h> -class VulkanContextX11 : public VulkanContext { +class RenderingContextDriverVulkanX11 : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { ::Window window; Display *display; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextX11(); - ~VulkanContextX11(); + RenderingContextDriverVulkanX11(); + ~RenderingContextDriverVulkanX11(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_X11_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_X11_H |