summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/detect.py9
-rw-r--r--platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml2
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp5
-rw-r--r--platform/linuxbsd/export/export_plugin.h2
-rw-r--r--platform/linuxbsd/export/run_icon.svg2
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp350
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h30
-rw-r--r--platform/linuxbsd/joypad_linux.cpp81
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp55
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp145
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h8
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h8
13 files changed, 578 insertions, 121 deletions
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 9faa73d6d2..a723bb5d58 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -235,11 +235,15 @@ def configure(env: "Environment"):
env.ParseConfig("pkg-config libenet --cflags --libs")
if not env["builtin_squish"]:
- env.ParseConfig("pkg-config libsquish --cflags --libs")
+ # libsquish doesn't reliably install its .pc file, so some distros lack it.
+ env.Append(LIBS=["libsquish"])
if not env["builtin_zstd"]:
env.ParseConfig("pkg-config libzstd --cflags --libs")
+ if env["brotli"] and not env["builtin_brotli"]:
+ env.ParseConfig("pkg-config libbrotlicommon libbrotlidec --cflags --libs")
+
# Sound and video libraries
# Keep the order as it triggers chained dependencies (ogg needed by others, etc.)
@@ -287,6 +291,9 @@ def configure(env: "Environment"):
# No pkgconfig file so far, hardcode expected lib name.
env.Append(LIBS=["embree3"])
+ if not env["builtin_openxr"]:
+ env.ParseConfig("pkg-config openxr --cflags --libs")
+
if env["fontconfig"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists fontconfig") == 0: # 0 means found
diff --git a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
index 77a6ece756..07378566c3 100644
--- a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
+++ b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="EditorExportPlatformLinuxBSD" inherits="EditorExportPlatformPC" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="EditorExportPlatformLinuxBSD" inherits="EditorExportPlatformPC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Exporter for Linux/BSD.
</brief_description>
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index f74bdf3516..40151b1a02 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -521,11 +521,10 @@ EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
set_logo(ImageTexture::create_from_image(img));
- img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#endif
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index cef714e86e..21bd81ed2f 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -34,7 +34,7 @@
#include "core/io/file_access.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
GDCLASS(EditorExportPlatformLinuxBSD, EditorExportPlatformPC);
diff --git a/platform/linuxbsd/export/run_icon.svg b/platform/linuxbsd/export/run_icon.svg
index 56465a0df3..ad58bcd5c7 100644
--- a/platform/linuxbsd/export/run_icon.svg
+++ b/platform/linuxbsd/export/run_icon.svg
@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444l-.058.001-.058-.001z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696a.11.11 0 0 1 .07-.028c.228-.003.456.826.897.827.44 0 .67-1.01.923-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888-.001.058-.004.114-.007.17a1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914Z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444zM8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696.253-.211.483.798.945.799.462 0 .692-1.01.945-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888a4.773 4.773 0 0 0-.007.17 1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914z" fill="#333" style="fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index c3cb0f411d..91c14e0e91 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -32,6 +32,7 @@
#ifdef DBUS_ENABLED
+#include "core/crypto/crypto_core.h"
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
@@ -43,12 +44,15 @@
#include <dbus/dbus.h>
#endif
+#include <unistd.h>
+
#define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
#define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
#define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
+#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
-static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
+bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
DBusMessageIter iter[3];
dbus_message_iter_init(p_reply_message, &iter[0]);
@@ -80,11 +84,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- unsupported = true;
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error opening D-Bus connection: " + error.message);
+ ERR_PRINT(vformat("Error opening D-Bus connection: %s", error.message));
}
+ dbus_error_free(&error);
+ unsupported = true;
return false;
}
@@ -100,11 +104,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- dbus_connection_unref(bus);
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error on D-Bus communication: " + error.message);
+ ERR_PRINT(vformat("Error on D-Bus communication: %s", error.message));
}
+ dbus_error_free(&error);
+ dbus_connection_unref(bus);
return false;
}
@@ -126,6 +130,317 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
return value;
}
+static const char *cs_empty = "";
+
+void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) {
+ CharString cs = p_string.utf8();
+ const char *cs_ptr = cs.ptr();
+ if (cs_ptr) {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr);
+ } else {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty);
+ }
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ DBusMessageIter arr_iter;
+ const char *filters_key = "filters";
+
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() == 2) {
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter array_struct_iter;
+ dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
+ append_dbus_string(&struct_iter, tokens[0]);
+
+ dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
+ {
+ const unsigned nil = 0;
+ dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
+ }
+ append_dbus_string(&array_struct_iter, tokens[1]);
+ dbus_message_iter_close_container(&array_iter, &array_struct_iter);
+ dbus_message_iter_close_container(&struct_iter, &array_iter);
+ dbus_message_iter_close_container(&arr_iter, &struct_iter);
+ }
+ }
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ if (p_as_byte_array) {
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter);
+ CharString cs = p_value.utf8();
+ const char *cs_ptr = cs.get_data();
+ do {
+ dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr);
+ } while (*cs_ptr++);
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ } else {
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter);
+ append_dbus_string(&var_iter, p_value);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter);
+ {
+ int val = p_value;
+ dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) {
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
+
+ dbus_uint32_t resp_code;
+ dbus_message_iter_get_basic(p_iter, &resp_code);
+ if (resp_code != 0) {
+ r_cancel = true;
+ } else {
+ r_cancel = false;
+ ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
+
+ DBusMessageIter dict_iter;
+ dbus_message_iter_recurse(p_iter, &dict_iter);
+ while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter iter;
+ dbus_message_iter_recurse(&dict_iter, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+ const char *key;
+ dbus_message_iter_get_basic(&iter, &key);
+ dbus_message_iter_next(&iter);
+
+ DBusMessageIter var_iter;
+ dbus_message_iter_recurse(&iter, &var_iter);
+ if (strcmp(key, "uris") == 0) {
+ if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter uri_iter;
+ dbus_message_iter_recurse(&var_iter, &uri_iter);
+ while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) {
+ const char *value;
+ dbus_message_iter_get_basic(&uri_iter, &value);
+ r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_decode());
+ if (!dbus_message_iter_next(&uri_iter)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!dbus_message_iter_next(&dict_iter)) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ if (unsupported) {
+ return FAILED;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ // Open connection and add signal handler.
+ FileDialogData fd;
+ fd.callback = p_callback;
+
+ CryptoCore::RandomGenerator rng;
+ ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
+ uint8_t uuid[64];
+ Error rng_err = rng.get_random_bytes(uuid, 64);
+ ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token.");
+
+ fd.connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to open DBus connection: %s", err.message));
+ dbus_error_free(&err);
+ unsupported = true;
+ return FAILED;
+ }
+
+ String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(fd.connection));
+ String token = String::hex_encode_buffer(uuid, 64);
+ String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token);
+
+ fd.path = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Generate FileChooser message.
+ const char *method = nullptr;
+ switch (p_mode) {
+ case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: {
+ method = "SaveFile";
+ } break;
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
+ method = "OpenFile";
+ } break;
+ }
+
+ DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
+ {
+ DBusMessageIter iter;
+ dbus_message_iter_init_append(message, &iter);
+
+ append_dbus_string(&iter, p_xid);
+ append_dbus_string(&iter, p_title);
+
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
+
+ append_dbus_dict_string(&arr_iter, "handle_token", token);
+ append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
+ append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
+ append_dbus_dict_filters(&arr_iter, p_filters);
+ append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
+ if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
+ append_dbus_dict_string(&arr_iter, "current_name", p_filename);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr_iter);
+ }
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(fd.connection, message, DBUS_TIMEOUT_INFINITE, &err);
+ dbus_message_unref(message);
+
+ if (!reply || dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to send DBus message: %s", err.message));
+ dbus_error_free(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Update signal path.
+ {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(reply, &iter)) {
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
+ const char *new_path = nullptr;
+ dbus_message_iter_get_basic(&iter, &new_path);
+ if (String::utf8(new_path) != path) {
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to remove DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ fd.path = String::utf8(new_path);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ dbus_message_unref(reply);
+
+ MutexLock lock(file_dialog_mutex);
+ file_dialogs.push_back(fd);
+
+ return OK;
+}
+
+void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
+ FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
+
+ while (!portal->file_dialog_thread_abort.is_set()) {
+ {
+ MutexLock lock(portal->file_dialog_mutex);
+ for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) {
+ bool remove = false;
+ {
+ FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
+ if (fd.connection) {
+ while (true) {
+ DBusMessage *msg = dbus_connection_pop_message(fd.connection);
+ if (!msg) {
+ break;
+ } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(msg, &iter)) {
+ bool cancel = false;
+ Vector<String> uris;
+ file_chooser_parse_response(&iter, cancel, uris);
+
+ if (fd.callback.is_valid()) {
+ Variant v_status = !cancel;
+ Variant v_files = uris;
+ Variant *v_args[2] = { &v_status, &v_files };
+ fd.callback.call_deferredp((const Variant **)&v_args, 2);
+ }
+ }
+ dbus_message_unref(msg);
+
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ remove = true;
+ break;
+ }
+ dbus_message_unref(msg);
+ }
+ dbus_connection_read_write(fd.connection, 0);
+ }
+ }
+ if (remove) {
+ portal->file_dialogs.remove_at(i);
+ }
+ }
+ }
+ usleep(50000);
+ }
+}
+
FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
@@ -153,6 +468,27 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
print_verbose("PortalDesktop: Unsupported DBus library version!");
unsupported = true;
}
+
+ if (!unsupported) {
+ file_dialog_thread_abort.clear();
+ file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this);
+ }
+}
+
+FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
+ file_dialog_thread_abort.set();
+ if (file_dialog_thread.is_started()) {
+ file_dialog_thread.wait_to_finish();
+ }
+ for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
+ if (fd.connection) {
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ }
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index 1520ab3a1f..a9b83b3844 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -33,20 +33,48 @@
#ifdef DBUS_ENABLED
-#include <stdint.h>
+#include "core/os/thread.h"
+#include "servers/display_server.h"
+
+struct DBusMessage;
+struct DBusConnection;
+struct DBusMessageIter;
class FreeDesktopPortalDesktop {
private:
bool unsupported = false;
+ static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value);
// Read a setting from org.freekdesktop.portal.Settings
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
+ static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
+ static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters);
+ static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
+ static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
+ static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls);
+
+ struct FileDialogData {
+ DBusConnection *connection = nullptr;
+ Callable callback;
+ String path;
+ };
+
+ Mutex file_dialog_mutex;
+ Vector<FileDialogData> file_dialogs;
+ Thread file_dialog_thread;
+ SafeFlag file_dialog_thread_abort;
+
+ static void _thread_file_dialog_monitor(void *p_ud);
+
public:
FreeDesktopPortalDesktop();
+ ~FreeDesktopPortalDesktop();
bool is_supported() { return !unsupported; }
+ Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
+
// Retrieve the system's preferred color scheme.
// 0: No preference or unknown.
// 1: Prefer dark appearance.
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index ab79885fb4..827c567785 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -56,6 +56,14 @@
static const char *ignore_str = "/dev/input/js";
#endif
+// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is
+// the Steam Input gamepad index
+#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad "
+// IDs used by Steam Input virtual controllers.
+// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
+#define VALVE_GAMEPAD_VID 0x28DE
+#define VALVE_GAMEPAD_PID 0x11FF
+
JoypadLinux::Joypad::~Joypad() {
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
@@ -74,43 +82,22 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
-#ifdef UDEV_ENABLED
-// This function is derived from SDL:
-// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
-static bool detect_sandbox() {
- if (access("/.flatpak-info", F_OK) == 0) {
- return true;
- }
-
- // For Snap, we check multiple variables because they might be set for
- // unrelated reasons. This is the same thing WebKitGTK does.
- if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
- return true;
- }
-
- if (access("/run/host/container-manager", F_OK) == 0) {
- return true;
- }
-
- return false;
-}
-#endif // UDEV_ENABLED
-
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
-#ifdef SOWRAP_ENABLED
-#ifdef DEBUG_ENABLED
- int dylibloader_verbose = 1;
-#else
- int dylibloader_verbose = 0;
-#endif
- if (detect_sandbox()) {
+ if (OS::get_singleton()->is_sandboxed()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
use_udev = false;
print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
- } else {
+ }
+#ifdef SOWRAP_ENABLED
+ else {
+#ifdef DEBUG_ENABLED
+ int dylibloader_verbose = 1;
+#else
+ int dylibloader_verbose = 0;
+#endif
use_udev = initialize_libudev(dylibloader_verbose) == 0;
if (use_udev) {
if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
@@ -124,10 +111,11 @@ JoypadLinux::JoypadLinux(Input *in) {
print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
}
}
-#endif
+#endif // SOWRAP_ENABLED
#else
print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
-#endif
+#endif // UDEV_ENABLED
+
input = in;
monitor_joypads_thread.start(monitor_joypads_thread_func, this);
joypad_events_thread.start(joypad_events_thread_func, this);
@@ -391,6 +379,16 @@ void JoypadLinux::open_joypad(const char *p_path) {
return;
}
+ uint16_t vendor = BSWAP16(inpid.vendor);
+ uint16_t product = BSWAP16(inpid.product);
+ uint16_t version = BSWAP16(inpid.version);
+
+ if (input->should_ignore_device(vendor, product)) {
+ // This can be true in cases where Steam is passing information into the game to ignore
+ // original gamepads when using virtual rebindings (See SteamInput).
+ return;
+ }
+
MutexLock lock(joypads_mutex[joy_num]);
Joypad &joypad = joypads[joy_num];
joypad.reset();
@@ -399,12 +397,23 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joypad);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
- uint16_t vendor = BSWAP16(inpid.vendor);
- uint16_t product = BSWAP16(inpid.product);
- uint16_t version = BSWAP16(inpid.version);
+ Dictionary joypad_info;
+ joypad_info["vendor_id"] = inpid.vendor;
+ joypad_info["product_id"] = inpid.product;
+ joypad_info["raw_name"] = name;
sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
- input->joy_connection_changed(joy_num, true, name, uid);
+
+ if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) {
+ if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) {
+ String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX));
+ if (idx_str.is_valid_int()) {
+ joypad_info["steam_input_index"] = idx_str.to_int();
+ }
+ }
+ }
+
+ input->joy_connection_changed(joy_num, true, name, uid, joypad_info);
} else {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 310778388b..d22d398a67 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -164,6 +164,27 @@ String OS_LinuxBSD::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string."));
}
+bool OS_LinuxBSD::is_sandboxed() const {
+ // This function is derived from SDL:
+ // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (has_environment("SNAP") && has_environment("SNAP_NAME") && has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
void OS_LinuxBSD::finalize() {
if (main_loop) {
memdelete(main_loop);
@@ -954,45 +975,33 @@ static String get_mountpoint(const String &p_path) {
}
Error OS_LinuxBSD::move_to_trash(const String &p_path) {
- String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory
+ // We try multiple methods, until we find one that works.
+ // So we only return on success until we exhausted possibilities.
+ String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory.
int err_code;
List<String> args;
args.push_back(path);
- args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
+
+ args.push_front("trash"); // The command is `gio trash <file_name>` so we add it before the path.
Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) { // Success.
+ return OK;
}
args.pop_front();
args.push_front("move");
args.push_back("trash:/"); // The command is `kioclient5 move <file_name> trash:/`.
result = execute("kioclient5", args, nullptr, &err_code); // For KDE based machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) {
+ return OK;
}
args.pop_front();
args.pop_back();
result = execute("gvfs-trash", args, nullptr, &err_code); // For older Linux machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) {
+ return OK;
}
// If the commands `kioclient5`, `gio` or `gvfs-trash` don't work on the system we do it manually.
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 007b90b82b..6917ea5ae7 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -123,6 +123,8 @@ public:
virtual String get_unique_id() const override;
virtual String get_processor_name() const override;
+ virtual bool is_sandboxed() const override;
+
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 8724bc871a..f38a9dd278 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -40,7 +40,7 @@
#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "main/main.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/atlas_texture.h"
#if defined(VULKAN_ENABLED)
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
@@ -122,6 +122,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_HIDPI:
case FEATURE_ICON:
+#ifdef DBUS_ENABLED
+ case FEATURE_NATIVE_DIALOG:
+#endif
//case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
@@ -360,6 +363,17 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
+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 = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
+ return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
+}
+
#endif
void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
@@ -379,7 +393,11 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
if (show_cursor && !previously_shown) {
WindowID window_id = get_window_at_screen_position(mouse_get_position());
- if (window_id != INVALID_WINDOW_ID) {
+ if (window_id != INVALID_WINDOW_ID && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
}
@@ -744,7 +762,7 @@ int DisplayServerX11::get_screen_count() const {
// Using Xinerama Extension
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
XFree(xsi);
} else {
@@ -756,7 +774,7 @@ int DisplayServerX11::get_screen_count() const {
int DisplayServerX11::get_primary_screen() const {
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
return 0;
} else {
return XDefaultScreen(x11_display);
@@ -809,7 +827,7 @@ Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
// Using Xinerama Extension.
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
@@ -1244,7 +1262,7 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
XImage *image = nullptr;
int event_base, error_base;
- if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) {
int xin_count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count);
if (p_screen < xin_count) {
@@ -1449,6 +1467,17 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+ if (window_mouseover_id == p_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
+ _send_window_event(windows[p_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+
+ window_set_rect_changed_callback(Callable(), p_id);
+ window_set_window_event_callback(Callable(), p_id);
+ window_set_input_event_callback(Callable(), p_id);
+ window_set_input_text_callback(Callable(), p_id);
+ window_set_drop_files_callback(Callable(), p_id);
+
while (wd.transient_children.size()) {
window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);
}
@@ -1580,6 +1609,7 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_COND(!xshaped_ext_ok);
const Vector<Vector2> region_path = windows[p_window].mpath;
@@ -2096,9 +2126,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
const WindowData &wd = windows[p_window];
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
- if (property == None) {
+ // Using EWMH instead of ICCCM, might work better for Wayland users.
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", True);
+ Atom hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", True);
+ if (property == None || hidden == None) {
return false;
}
@@ -2106,7 +2137,7 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = nullptr;
+ Atom *atoms = nullptr;
int result = XGetWindowProperty(
x11_display,
@@ -2115,20 +2146,21 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
0,
32,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
- &data);
+ (unsigned char **)&atoms);
- if (result == Success && data) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState) {
- XFree(data);
- return true;
+ if (result == Success && atoms) {
+ for (unsigned int i = 0; i < len; i++) {
+ if (atoms[i] == hidden) {
+ XFree(atoms);
+ return true;
+ }
}
- XFree(data);
+ XFree(atoms);
}
return false;
@@ -2980,6 +3012,30 @@ Key DisplayServerX11::keyboard_get_keycode_from_physical(Key p_keycode) const {
return (Key)(key | modifiers);
}
+Key DisplayServerX11::keyboard_get_label_from_physical(Key p_keycode) const {
+ Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
+ Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
+ unsigned int xkeycode = KeyMappingX11::get_xlibcode(keycode_no_mod);
+ KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, keyboard_get_current_layout(), 0);
+ if (is_ascii_lower_case(xkeysym)) {
+ xkeysym -= ('a' - 'A');
+ }
+
+ Key key = KeyMappingX11::get_keycode(xkeysym);
+#ifdef XKB_ENABLED
+ if (xkb_loaded_v08p) {
+ String keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(xkeysym)));
+ key = fix_key_label(keysym[0], KeyMappingX11::get_keycode(xkeysym));
+ }
+#endif
+
+ // If not found, fallback to QWERTY.
+ // This should match the behavior of the event pump
+ if (key == Key::NONE) {
+ return p_keycode;
+ }
+ return (Key)(key | modifiers);
+}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
Atom actual_type = None;
int actual_format = 0;
@@ -3927,7 +3983,7 @@ bool DisplayServerX11::mouse_process_popups() {
// Find top popup to close.
while (E) {
// Popup window area.
- Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get()));
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect(E->get());
if (win_rect.has_point(pos)) {
@@ -4261,7 +4317,8 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id == window_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
@@ -4273,7 +4330,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (!mouse_mode_grab) {
+ if (!mouse_mode_grab && window_mouseover_id != window_id) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+ window_mouseover_id = window_id;
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
@@ -4880,6 +4941,8 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
if (p_icon.is_valid()) {
+ ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
+
Ref<Image> img = p_icon->duplicate();
img->convert(Image::FORMAT_RGBA8);
@@ -5429,13 +5492,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
if (initialize_xinerama(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xinerama dynamically.");
+ xinerama_ext_ok = false;
}
if (initialize_xrandr(dylibloader_verbose) != 0) {
- r_error = ERR_UNAVAILABLE;
- ERR_FAIL_MSG("Can't load Xrandr dynamically.");
+ xrandr_ext_ok = false;
}
if (initialize_xrender(dylibloader_verbose) != 0) {
@@ -5449,7 +5510,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
#else
#ifdef XKB_ENABLED
- xkb_loaded = true;
+ bool xkb_loaded = true;
+ xkb_loaded_v05p = true;
+ xkb_loaded_v08p = true;
#endif
#endif
@@ -5476,6 +5539,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = OK;
+#ifdef SOWRAP_ENABLED
{
if (!XcursorImageCreate || !XcursorImageLoadCursor || !XcursorImageDestroy || !XcursorGetDefaultSize || !XcursorGetTheme || !XcursorLibraryLoadImage) {
// There's no API to check version, check if functions are available instead.
@@ -5484,6 +5548,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
}
+#endif
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;
@@ -5501,42 +5566,36 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
- {
+ if (xshaped_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XShapeQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xshape %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xshape library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xshaped_ext_ok = false;
+ print_verbose("Unsupported Xshape library version.");
}
}
- {
+ if (xinerama_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XineramaQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xinerama %d.%d detected.", version_major, version_minor));
if (rc != 1 || version_major < 1) {
- ERR_PRINT("Unsupported Xinerama library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xinerama_ext_ok = false;
+ print_verbose("Unsupported Xinerama library version.");
}
}
- {
+ if (xrandr_ext_ok) {
int version_major = 0;
int version_minor = 0;
int rc = XRRQueryVersion(x11_display, &version_major, &version_minor);
print_verbose(vformat("Xrandr %d.%d detected.", version_major, version_minor));
if (rc != 1 || (version_major == 1 && version_minor < 3) || (version_major < 1)) {
- ERR_PRINT("Unsupported Xrandr library version.");
- r_error = ERR_UNAVAILABLE;
- XCloseDisplay(x11_display);
- return;
+ xrandr_ext_ok = false;
+ print_verbose("Unsupported Xrandr library version.");
}
}
@@ -5602,7 +5661,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
if (!xrandr_handle) {
fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
}
- } else {
+ }
+
+ if (xrandr_handle) {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 180362923b..71beddce76 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -225,6 +225,7 @@ class DisplayServerX11 : public DisplayServer {
List<WindowID> popup_list;
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
@@ -322,7 +323,9 @@ class DisplayServerX11 : public DisplayServer {
xrr_get_monitors_t xrr_get_monitors = nullptr;
xrr_free_monitors_t xrr_free_monitors = nullptr;
void *xrandr_handle = nullptr;
- Bool xrandr_ext_ok;
+ bool xrandr_ext_ok = true;
+ bool xinerama_ext_ok = true;
+ bool xshaped_ext_ok = true;
struct Property {
unsigned char *data;
@@ -390,6 +393,8 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const 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;
#endif
virtual void mouse_set_mode(MouseMode p_mode) override;
@@ -502,6 +507,7 @@ public:
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
+ virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
virtual void process_events() override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 59e20fec45..d3a25506a8 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -74,17 +74,17 @@ private:
};
struct GLDisplay {
- GLDisplay() { context = nullptr; }
+ GLDisplay() {}
~GLDisplay();
GLManager_X11_Private *context = nullptr;
- ::Display *x11_display;
- XVisualInfo x_vi;
+ ::Display *x11_display = nullptr;
+ XVisualInfo x_vi = {};
};
// just for convenience, window and display struct
struct XWinDisp {
::Window x11_window;
- ::Display *x11_display;
+ ::Display *x11_display = nullptr;
} _x_windisp;
LocalVector<GLWindow> _windows;