summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/detect.py4
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp72
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h15
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp10
-rw-r--r--platform/linuxbsd/os_linuxbsd.h4
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp35
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h3
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp116
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h4
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp4
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
11 files changed, 235 insertions, 33 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/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index b8a10ea6b9..c957dea32d 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -275,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.
@@ -863,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) {
@@ -1139,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();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index 58c5dab586..e42967eb7b 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -117,7 +117,7 @@ class DisplayServerWayland : public DisplayServer {
Context context;
- bool frame = false;
+ bool suspended = false;
bool emulate_vsync = false;
String rendering_driver;
@@ -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/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/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index b838e4b870..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;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 7c094d6a41..a5cbe34d26 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -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;