feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -14,7 +14,7 @@ used by this platform.
## Artwork license
[`logo.png`](logo.png) is derived from the [Linux logo](https://isc.tamu.edu/~lewing/linux/):
[`logo.svg`](export/logo.svg) is derived from the [Linux logo](https://isc.tamu.edu/~lewing/linux/):
> Permission to use and/or modify this image is granted provided you acknowledge me
> <lewing@isc.tamu.edu> and [The GIMP](https://isc.tamu.edu/~lewing/gimp/)

View file

@ -11,6 +11,7 @@ common_linuxbsd = [
"joypad_linux.cpp",
"freedesktop_portal_desktop.cpp",
"freedesktop_screensaver.cpp",
"freedesktop_at_spi_monitor.cpp",
]
if env["use_sowrap"]:

View file

@ -81,10 +81,10 @@ static void handle_crash(int sig) {
print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, sig));
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
if (String(VERSION_HASH).is_empty()) {
print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
if (String(GODOT_VERSION_HASH).is_empty()) {
print_error(vformat("Engine version: %s", GODOT_VERSION_FULL_NAME));
} else {
print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH));
print_error(vformat("Engine version: %s (%s)", GODOT_VERSION_FULL_NAME, GODOT_VERSION_HASH));
}
print_error(vformat("Dumping the backtrace. %s", msg));
char **strings = backtrace_symbols(bt_buffer, size);
@ -137,7 +137,8 @@ static void handle_crash(int sig) {
}
}
print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i] : ""));
// Simplify printed file paths to remove redundant `/./` sections (e.g. `/opt/godot/./core` -> `/opt/godot/core`).
print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i].replace("/./", "/") : ""));
}
free(strings);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CRASH_HANDLER_LINUXBSD_H
#define CRASH_HANDLER_LINUXBSD_H
#pragma once
class CrashHandler {
bool disabled;
@ -43,5 +42,3 @@ public:
CrashHandler();
~CrashHandler();
};
#endif // CRASH_HANDLER_LINUXBSD_H

View file

@ -293,7 +293,7 @@ def configure(env: "SConsEnvironment"):
if not env["builtin_recastnavigation"]:
# No pkgconfig file so far, hardcode default paths.
env.Prepend(CPPPATH=["/usr/include/recastnavigation"])
env.Prepend(CPPEXTPATH=["/usr/include/recastnavigation"])
env.Append(LIBS=["Recast"])
if not env["builtin_embree"] and env["arch"] in ["x86_64", "arm64"]:
@ -394,7 +394,7 @@ def configure(env: "SConsEnvironment"):
env.Prepend(CPPPATH=["#platform/linuxbsd"])
if env["use_sowrap"]:
env.Prepend(CPPPATH=["#thirdparty/linuxbsd_headers"])
env.Prepend(CPPEXTPATH=["#thirdparty/linuxbsd_headers"])
env.Append(
CPPDEFINES=[
@ -456,9 +456,9 @@ def configure(env: "SConsEnvironment"):
sys.exit(255)
env.ParseConfig("pkg-config wayland-egl --cflags --libs")
else:
env.Prepend(CPPPATH=["#thirdparty/linuxbsd_headers/wayland/"])
env.Prepend(CPPEXTPATH=["#thirdparty/linuxbsd_headers/wayland/"])
if env["libdecor"]:
env.Prepend(CPPPATH=["#thirdparty/linuxbsd_headers/libdecor-0/"])
env.Prepend(CPPEXTPATH=["#thirdparty/linuxbsd_headers/libdecor-0/"])
if env["libdecor"]:
env.Append(CPPDEFINES=["LIBDECOR_ENABLED"])
@ -466,6 +466,24 @@ def configure(env: "SConsEnvironment"):
env.Append(CPPDEFINES=["WAYLAND_ENABLED"])
env.Append(LIBS=["rt"]) # Needed by glibc, used by _allocate_shm_file
if env["accesskit"]:
if env["accesskit_sdk_path"] != "":
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
if env["arch"] == "arm64":
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/linux/arm64/static/"])
elif env["arch"] == "arm32":
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/linux/arm32/static/"])
elif env["arch"] == "rv64":
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/linux/riscv64gc/static/"])
elif env["arch"] == "x86_64":
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/linux/x86_64/static/"])
elif env["arch"] == "x86_32":
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/linux/x86/static/"])
env.Append(LIBS=["accesskit"])
else:
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:

View file

@ -28,10 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef LINUXBSD_EXPORT_H
#define LINUXBSD_EXPORT_H
#pragma once
void register_linuxbsd_exporter_types();
void register_linuxbsd_exporter();
#endif // LINUXBSD_EXPORT_H

View file

@ -50,7 +50,7 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
}
f->store_line("#!/bin/sh");
f->store_line("echo -ne '\\033c\\033]0;" + p_app_name + "\\a'");
f->store_line("printf '\\033c\\033]0;%s\\a' " + p_app_name);
f->store_line("base_path=\"$(dirname \"$(realpath \"$0\")\")\"");
f->store_line("\"$base_path/" + p_pkg_name + "\" \"$@\"");
@ -67,16 +67,21 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
if (!template_path.is_empty()) {
String exe_arch = _get_exe_arch(template_path);
if (arch != exe_arch) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture, found \"%s\", expected \"%s\"."), exe_arch, arch));
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch));
return ERR_CANT_CREATE;
}
}
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da->file_exists(template_path.get_base_dir().path_join("libaccesskit." + arch + ".so"))) {
da->copy(template_path.get_base_dir().path_join("libaccesskit." + arch + ".so"), p_path.get_base_dir().path_join("libaccesskit." + arch + ".so"), get_chmod_flags());
}
bool export_as_zip = p_path.ends_with("zip");
String pkg_name;
if (String(GLOBAL_GET("application/config/name")) != "") {
pkg_name = String(GLOBAL_GET("application/config/name"));
if (String(get_project_setting(p_preset, "application/config/name")) != "") {
pkg_name = String(get_project_setting(p_preset, "application/config/name"));
} else {
pkg_name = "Unnamed";
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef LINUXBSD_EXPORT_PLUGIN_H
#define LINUXBSD_EXPORT_PLUGIN_H
#pragma once
#include "core/io/file_access.h"
#include "editor/editor_settings.h"
@ -92,5 +91,3 @@ public:
EditorExportPlatformLinuxBSD();
};
#endif // LINUXBSD_EXPORT_PLUGIN_H

View file

@ -1 +1 @@
<svg width="32" height="32"><path fill="#fff" d="M13 31h6s3 0 6-6c2.864-5.727-6-17-6-17h-6S3.775 18.55 7 25c3 6 6 6 6 6z"/><path fill="#333" d="M15.876 28.636c-.05.322-.116.637-.204.941-.142.496-.35.993-.659 1.416.32.02.649.023.985.007a9.1 9.1 0 0 0 .985-.007c-.309-.423-.516-.92-.659-1.416a7.666 7.666 0 0 1-.203-.94l-.123.003c-.04 0-.081-.003-.122-.004z"/><path fill="#f4bb37" d="M21.693 21.916c-.629.01-.934.633-1.497.7-.694.08-1.128-.722-2.11-.123-.98.6-1.826 7.473.45 8.409 2.274.935 6.506-4.545 6.23-5.662-.275-1.116-1.146-.853-1.582-1.399-.436-.545.003-1.41-.995-1.82a1.246 1.246 0 0 0-.496-.105zm-11.461 0a1.315 1.315 0 0 0-.421.105c-.998.41-.56 1.275-.995 1.82-.436.546-1.31.283-1.586 1.4-.275 1.116 3.956 6.596 6.232 5.66 2.275-.935 1.429-7.808.448-8.408-.981-.6-1.415.204-2.11.122-.584-.068-.888-.739-1.568-.7z"/><path fill="#333" d="M15.998.99c-2.934 0-4.657 1.79-4.982 4.204-.324 2.414.198 2.856-.614 5.328-.813 2.472-4.456 6.71-4.37 10.62.026 1.217.166 2.27.41 3.192.3-.496.743-.846 1.066-.995.253-.117.375-.173.432-.194.008-.062.04-.205.098-.485.08-.386.387-.99.91-1.386-.005-.12-.01-.239-.013-.363-.06-3.033 3.073-6.318 3.65-8.236.577-1.917.326-2.114.421-2.59.096-.477.463-1.032.992-1.475a.23.23 0 0 1 .15-.06c.482-.005.965 1.75 1.898 1.752.933 0 1.419-2.141 1.956-1.692.529.443.896.998.992 1.474.095.477-.156.674.42 2.591.578 1.918 3.708 5.203 3.648 8.236-.003.123-.008.24-.014.36.526.396.834 1.002.914 1.389.058.28.09.423.098.485.057.021.18.08.432.197.323.15.764.499 1.063.995.244-.922.387-1.976.414-3.195.085-3.91-3.562-8.148-4.374-10.62-.813-2.472-.287-2.914-.611-5.328C20.659 2.78 18.933.99 15.998.99z"/></svg>
<svg width="32" height="32"><path fill="#fff" d="M13 31h6s3 0 6-6c2.864-5.727-6-17-6-17h-6S3.775 18.55 7 25c3 6 6 6 6 6z"/><path fill="#333" d="M15.876 28.636c-.05.322-.116.637-.204.941-.142.496-.35.993-.659 1.416.32.02.649.023.985.007a9.1 9.1 0 0 0 .985-.007c-.309-.423-.516-.92-.659-1.416a7.666 7.666 0 0 1-.203-.94l-.123.003c-.04 0-.081-.003-.122-.004z"/><path fill="#f4bb37" d="M21.693 21.916c-.629.01-.934.633-1.497.7-.694.08-1.128-.722-2.11-.123-.98.6-1.826 7.473.45 8.409 2.274.935 6.506-4.545 6.23-5.662-.275-1.116-1.146-.853-1.582-1.399-.436-.545.003-1.41-.995-1.82a1.246 1.246 0 0 0-.496-.105zm-11.461 0a1.315 1.315 0 0 0-.421.105c-.998.41-.56 1.275-.995 1.82-.436.546-1.31.283-1.586 1.4-.275 1.116 3.956 6.596 6.232 5.66 2.275-.935 1.429-7.808.448-8.408-.981-.6-1.415.204-2.11.122-.584-.068-.888-.739-1.568-.7z"/><path fill="#333" d="M15.998.99c-2.934 0-4.657 1.79-4.982 4.204-.324 2.414.198 2.856-.614 5.328-.813 2.472-4.456 6.71-4.37 10.62.026 1.217.166 2.27.41 3.192.3-.496.743-.846 1.066-.995.253-.117.375-.173.432-.194.008-.062.04-.205.098-.485.08-.386.387-.99.91-1.386-.005-.12-.01-.239-.013-.363-.06-3.033 3.073-6.318 3.65-8.236.577-1.917.326-2.114.421-2.59.096-.477.463-1.032.992-1.475a.23.23 0 0 1 .15-.06c.482-.005.965 1.75 1.898 1.752.933 0 1.419-2.141 1.956-1.692.529.443.896.998.992 1.474.095.477-.156.674.42 2.591.578 1.918 3.708 5.203 3.648 8.236-.003.123-.008.24-.014.36.526.396.834 1.002.914 1.389.058.28.09.423.098.485.057.021.18.08.432.197.323.15.764.499 1.063.995.244-.922.387-1.976.414-3.195.085-3.91-3.562-8.148-4.374-10.62-.813-2.472-.287-2.914-.611-5.328C20.659 2.78 18.933.99 15.998.99z"/></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" 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"/><path fill="#e0e0e0" 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"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" 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"/><path fill="#e0e0e0" 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"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

@ -0,0 +1,157 @@
/**************************************************************************/
/* freedesktop_at_spi_monitor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "freedesktop_at_spi_monitor.h"
#ifdef DBUS_ENABLED
#include "core/os/os.h"
#ifdef SOWRAP_ENABLED
#include "dbus-so_wrap.h"
#else
#include <dbus/dbus.h>
#endif
#include <unistd.h>
#define BUS_OBJECT_NAME "org.a11y.Bus"
#define BUS_OBJECT_PATH "/org/a11y/bus"
#define BUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
void FreeDesktopAtSPIMonitor::monitor_thread_func(void *p_userdata) {
FreeDesktopAtSPIMonitor *mon = (FreeDesktopAtSPIMonitor *)p_userdata;
DBusError error;
dbus_error_init(&error);
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
dbus_error_free(&error);
mon->supported.clear();
return;
}
static const char *iface = "org.a11y.Status";
static const char *member = "IsEnabled";
while (!mon->exit_thread.is_set()) {
DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_PROPERTIES, "Get");
dbus_message_append_args(
message,
DBUS_TYPE_STRING, &iface,
DBUS_TYPE_STRING, &member,
DBUS_TYPE_INVALID);
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (!dbus_error_is_set(&error)) {
DBusMessageIter iter, iter_variant, iter_struct;
dbus_bool_t result;
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &iter_variant);
switch (dbus_message_iter_get_arg_type(&iter_variant)) {
case DBUS_TYPE_STRUCT: {
dbus_message_iter_recurse(&iter_variant, &iter_struct);
if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
dbus_message_iter_get_basic(&iter_struct, &result);
if (result) {
mon->sr_enabled.set();
} else {
mon->sr_enabled.clear();
}
}
} break;
case DBUS_TYPE_BOOLEAN: {
dbus_message_iter_get_basic(&iter_variant, &result);
if (result) {
mon->sr_enabled.set();
} else {
mon->sr_enabled.clear();
}
} break;
default:
break;
}
dbus_message_unref(reply);
} else {
dbus_error_free(&error);
}
usleep(50000);
}
dbus_connection_unref(bus);
}
FreeDesktopAtSPIMonitor::FreeDesktopAtSPIMonitor() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
int dylibloader_verbose = 1;
#else
int dylibloader_verbose = 0;
#endif
if (initialize_dbus(dylibloader_verbose) != 0) {
print_verbose("AT-SPI2: Failed to load DBus library!");
supported.clear();
return;
}
#endif
bool ver_ok = false;
int version_major = 0;
int version_minor = 0;
int version_rev = 0;
dbus_get_version(&version_major, &version_minor, &version_rev);
ver_ok = (version_major == 1 && version_minor >= 10) || (version_major > 1); // 1.10.0
print_verbose(vformat("AT-SPI2: DBus %d.%d.%d detected.", version_major, version_minor, version_rev));
if (!ver_ok) {
print_verbose("AT-SPI2: Unsupported DBus library version!");
supported.clear();
return;
}
supported.set();
sr_enabled.clear();
exit_thread.clear();
thread.start(FreeDesktopAtSPIMonitor::monitor_thread_func, this);
}
FreeDesktopAtSPIMonitor::~FreeDesktopAtSPIMonitor() {
exit_thread.set();
if (thread.is_started()) {
thread.wait_to_finish();
}
}
#endif // DBUS_ENABLED

View file

@ -0,0 +1,56 @@
/**************************************************************************/
/* freedesktop_at_spi_monitor.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#ifdef DBUS_ENABLED
#include "core/os/thread.h"
#include "core/os/thread_safe.h"
class FreeDesktopAtSPIMonitor {
private:
Thread thread;
SafeFlag exit_thread;
SafeFlag sr_enabled;
SafeFlag supported;
static void monitor_thread_func(void *p_userdata);
public:
FreeDesktopAtSPIMonitor();
~FreeDesktopAtSPIMonitor();
bool is_supported() { return supported.is_set(); }
bool is_active() { return sr_enabled.is_set(); }
};
#endif // DBUS_ENABLED

View file

@ -52,8 +52,9 @@
#define BUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
#define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
#define BUS_INTERFACE_SCREENSHOT "org.freedesktop.portal.Screenshot"
bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, ReadVariantType p_type, void *r_value) {
DBusMessageIter iter[3];
dbus_message_iter_init(p_reply_message, &iter[0]);
@ -67,15 +68,49 @@ bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, i
}
dbus_message_iter_recurse(&iter[1], &iter[2]);
if (dbus_message_iter_get_arg_type(&iter[2]) != p_type) {
return false;
if (p_type == VAR_TYPE_COLOR) {
if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_STRUCT) {
return false;
}
DBusMessageIter struct_iter;
dbus_message_iter_recurse(&iter[2], &struct_iter);
int idx = 0;
while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_DOUBLE) {
double value = 0.0;
dbus_message_iter_get_basic(&struct_iter, &value);
if (value < 0.0 || value > 1.0) {
return false;
}
if (idx == 0) {
static_cast<Color *>(r_value)->r = value;
} else if (idx == 1) {
static_cast<Color *>(r_value)->g = value;
} else if (idx == 2) {
static_cast<Color *>(r_value)->b = value;
}
idx++;
if (!dbus_message_iter_next(&struct_iter)) {
break;
}
}
if (idx != 3) {
return false;
}
} else if (p_type == VAR_TYPE_UINT32) {
if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_UINT32) {
return false;
}
dbus_message_iter_get_basic(&iter[2], r_value);
} else if (p_type == VAR_TYPE_BOOL) {
if (dbus_message_iter_get_arg_type(&iter[2]) != DBUS_TYPE_BOOLEAN) {
return false;
}
dbus_message_iter_get_basic(&iter[2], r_value);
}
dbus_message_iter_get_basic(&iter[2], r_value);
return true;
}
bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value) {
bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char *p_key, ReadVariantType p_type, void *r_value) {
if (unsupported) {
return false;
}
@ -127,8 +162,36 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
}
uint32_t value = 0;
read_setting("org.freedesktop.appearance", "color-scheme", DBUS_TYPE_UINT32, &value);
return value;
if (read_setting("org.freedesktop.appearance", "color-scheme", VAR_TYPE_UINT32, &value)) {
return value;
} else {
return 0;
}
}
Color FreeDesktopPortalDesktop::get_appearance_accent_color() {
if (unsupported) {
return Color(0, 0, 0, 0);
}
Color value;
if (read_setting("org.freedesktop.appearance", "accent-color", VAR_TYPE_COLOR, &value)) {
return value;
} else {
return Color(0, 0, 0, 0);
}
}
uint32_t FreeDesktopPortalDesktop::get_high_contrast() {
if (unsupported) {
return -1;
}
dbus_bool_t value = false;
if (read_setting("org.gnome.desktop.a11y.interface", "high-contrast", VAR_TYPE_BOOL, &value)) {
return value;
}
return -1;
}
static const char *cs_empty = "";
@ -226,7 +289,7 @@ void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter,
int filter_slice_count = flt.get_slice_count(",");
for (int j = 0; j < filter_slice_count; j++) {
dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
String str = (flt.get_slice(",", j).strip_edges());
String str = (flt.get_slicec(',', j).strip_edges());
{
const unsigned flt_type = 0;
dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &flt_type);
@ -295,6 +358,61 @@ void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, co
dbus_message_iter_close_container(p_iter, &dict_iter);
}
bool FreeDesktopPortalDesktop::color_picker_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Color &r_color) {
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, "color") == 0) { // (ddd)
if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) {
DBusMessageIter struct_iter;
dbus_message_iter_recurse(&var_iter, &struct_iter);
int idx = 0;
while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_DOUBLE) {
double value = 0.0;
dbus_message_iter_get_basic(&struct_iter, &value);
if (idx == 0) {
r_color.r = value;
} else if (idx == 1) {
r_color.g = value;
} else if (idx == 2) {
r_color.b = value;
}
idx++;
if (!dbus_message_iter_next(&struct_iter)) {
break;
}
}
}
}
}
if (!dbus_message_iter_next(&dict_iter)) {
break;
}
}
}
return true;
}
bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, const HashMap<String, String> &p_ids, bool &r_cancel, Vector<String> &r_urls, int &r_index, Dictionary &r_options) {
ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
@ -372,7 +490,7 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it
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());
r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_file_decode());
if (!dbus_message_iter_next(&uri_iter)) {
break;
}
@ -388,6 +506,92 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it
return true;
}
bool FreeDesktopPortalDesktop::color_picker(const String &p_xid, const Callable &p_callback) {
if (unsupported) {
return false;
}
DBusError err;
dbus_error_init(&err);
// Open connection and add signal handler.
ColorPickerData cd;
cd.callback = p_callback;
CryptoCore::RandomGenerator rng;
ERR_FAIL_COND_V_MSG(rng.init(), false, "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, false, "Failed to generate unique token.");
String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(monitor_connection));
String token = String::hex_encode_buffer(uuid, 64);
String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace_char('.', '_').remove_char(':'), token);
cd.path = path;
cd.filter = 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(monitor_connection, cd.filter.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);
return false;
}
DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_SCREENSHOT, "PickColor");
{
DBusMessageIter iter;
dbus_message_iter_init_append(message, &iter);
append_dbus_string(&iter, p_xid);
DBusMessageIter arr_iter;
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
append_dbus_dict_string(&arr_iter, "handle_token", token);
dbus_message_iter_close_container(&iter, &arr_iter);
}
DBusMessage *reply = dbus_connection_send_with_reply_and_block(monitor_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(monitor_connection, cd.filter.utf8().get_data(), &err);
return false;
}
// 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(monitor_connection, cd.filter.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);
return false;
}
cd.filter = String::utf8(new_path);
dbus_bus_add_match(monitor_connection, cd.filter.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);
return false;
}
}
}
}
}
dbus_message_unref(reply);
MutexLock lock(color_picker_mutex);
color_pickers.push_back(cd);
return true;
}
bool FreeDesktopPortalDesktop::_is_interface_supported(const char *p_iface) {
bool supported = false;
DBusError err;
@ -443,6 +647,14 @@ bool FreeDesktopPortalDesktop::is_settings_supported() {
return supported;
}
bool FreeDesktopPortalDesktop::is_screenshot_supported() {
static int supported = -1;
if (supported == -1) {
supported = _is_interface_supported(BUS_INTERFACE_SCREENSHOT);
}
return supported;
}
Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
if (unsupported) {
return FAILED;
@ -505,7 +717,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(monitor_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);
String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace_char('.', '_').remove_char(':'), token);
fd.path = path;
fd.filter = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name);
@ -592,29 +804,47 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
return OK;
}
void FreeDesktopPortalDesktop::process_file_dialog_callbacks() {
MutexLock lock(file_dialog_mutex);
while (!pending_cbs.is_empty()) {
FileDialogCallback cb = pending_cbs.front()->get();
pending_cbs.pop_front();
void FreeDesktopPortalDesktop::process_callbacks() {
{
MutexLock lock(file_dialog_mutex);
while (!pending_file_cbs.is_empty()) {
FileDialogCallback cb = pending_file_cbs.front()->get();
pending_file_cbs.pop_front();
if (cb.opt_in_cb) {
Variant ret;
Callable::CallError ce;
const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };
if (cb.opt_in_cb) {
Variant ret;
Callable::CallError ce;
const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };
cb.callback.callp(args, 4, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));
cb.callback.callp(args, 4, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));
}
} else {
Variant ret;
Callable::CallError ce;
const Variant *args[3] = { &cb.status, &cb.files, &cb.index };
cb.callback.callp(args, 3, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));
}
}
} else {
}
}
{
MutexLock lock(color_picker_mutex);
while (!pending_color_cbs.is_empty()) {
ColorPickerCallback cb = pending_color_cbs.front()->get();
pending_color_cbs.pop_front();
Variant ret;
Callable::CallError ce;
const Variant *args[3] = { &cb.status, &cb.files, &cb.index };
const Variant *args[2] = { &cb.status, &cb.color };
cb.callback.callp(args, 3, ret, ce);
cb.callback.callp(args, 2, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));
ERR_PRINT(vformat("Failed to execute color picker callback: %s.", Variant::get_callable_error_text(cb.callback, args, 2, ce)));
}
}
}
@ -639,46 +869,78 @@ void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) {
dbus_message_iter_get_basic(&iter, &value);
String key = String::utf8(value);
if (name_space == "org.freedesktop.appearance" && key == "color-scheme") {
if (name_space == "org.freedesktop.appearance" && (key == "color-scheme" || key == "accent-color")) {
callable_mp(portal, &FreeDesktopPortalDesktop::_system_theme_changed_callback).call_deferred();
}
}
} else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
String path = String::utf8(dbus_message_get_path(msg));
MutexLock lock(portal->file_dialog_mutex);
for (int i = 0; i < portal->file_dialogs.size(); i++) {
FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
if (fd.path == path) {
DBusMessageIter iter;
if (dbus_message_iter_init(msg, &iter)) {
bool cancel = false;
Vector<String> uris;
Dictionary options;
int index = 0;
file_chooser_parse_response(&iter, fd.filter_names, fd.option_ids, cancel, uris, index, options);
{
MutexLock lock(portal->file_dialog_mutex);
for (int i = 0; i < portal->file_dialogs.size(); i++) {
FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
if (fd.path == path) {
DBusMessageIter iter;
if (dbus_message_iter_init(msg, &iter)) {
bool cancel = false;
Vector<String> uris;
Dictionary options;
int index = 0;
file_chooser_parse_response(&iter, fd.filter_names, fd.option_ids, cancel, uris, index, options);
if (fd.callback.is_valid()) {
FileDialogCallback cb;
cb.callback = fd.callback;
cb.status = !cancel;
cb.files = uris;
cb.index = index;
cb.options = options;
cb.opt_in_cb = fd.opt_in_cb;
portal->pending_cbs.push_back(cb);
}
if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);
if (fd.callback.is_valid()) {
FileDialogCallback cb;
cb.callback = fd.callback;
cb.status = !cancel;
cb.files = uris;
cb.index = index;
cb.options = options;
cb.opt_in_cb = fd.opt_in_cb;
portal->pending_file_cbs.push_back(cb);
}
if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);
}
}
DBusError err;
dbus_error_init(&err);
dbus_bus_remove_match(portal->monitor_connection, fd.filter.utf8().get_data(), &err);
dbus_error_free(&err);
portal->file_dialogs.remove_at(i);
break;
}
}
}
{
MutexLock lock(portal->color_picker_mutex);
for (int i = 0; i < portal->color_pickers.size(); i++) {
FreeDesktopPortalDesktop::ColorPickerData &cd = portal->color_pickers.write[i];
if (cd.path == path) {
DBusMessageIter iter;
if (dbus_message_iter_init(msg, &iter)) {
bool cancel = false;
Color c;
color_picker_parse_response(&iter, cancel, c);
DBusError err;
dbus_error_init(&err);
dbus_bus_remove_match(portal->monitor_connection, fd.filter.utf8().get_data(), &err);
dbus_error_free(&err);
if (cd.callback.is_valid()) {
ColorPickerCallback cb;
cb.callback = cd.callback;
cb.color = c;
cb.status = !cancel;
portal->pending_color_cbs.push_back(cb);
}
}
portal->file_dialogs.remove_at(i);
break;
DBusError err;
dbus_error_init(&err);
dbus_bus_remove_match(portal->monitor_connection, cd.filter.utf8().get_data(), &err);
dbus_error_free(&err);
portal->color_pickers.remove_at(i);
break;
}
}
}
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FREEDESKTOP_PORTAL_DESKTOP_H
#define FREEDESKTOP_PORTAL_DESKTOP_H
#pragma once
#ifdef DBUS_ENABLED
@ -45,9 +44,15 @@ class FreeDesktopPortalDesktop : public Object {
private:
bool unsupported = false;
static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value);
enum ReadVariantType {
VAR_TYPE_UINT32, // u
VAR_TYPE_BOOL, // b
VAR_TYPE_COLOR, // (ddd)
};
static bool try_parse_variant(DBusMessage *p_reply_message, ReadVariantType 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);
bool read_setting(const char *p_namespace, const char *p_key, ReadVariantType p_type, void *r_value);
static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
static void append_dbus_dict_options(DBusMessageIter *p_iter, const TypedArray<Dictionary> &p_options, HashMap<String, String> &r_ids);
@ -55,6 +60,23 @@ private:
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, const Vector<String> &p_names, const HashMap<String, String> &p_ids, bool &r_cancel, Vector<String> &r_urls, int &r_index, Dictionary &r_options);
static bool color_picker_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Color &r_color);
struct ColorPickerData {
Callable callback;
String filter;
String path;
};
struct ColorPickerCallback {
Callable callback;
Variant status;
Variant color;
};
List<ColorPickerCallback> pending_color_cbs;
Mutex color_picker_mutex;
Vector<ColorPickerData> color_pickers;
struct FileDialogData {
Vector<String> filter_names;
@ -74,7 +96,7 @@ private:
Variant options;
bool opt_in_cb = false;
};
List<FileDialogCallback> pending_cbs;
List<FileDialogCallback> pending_file_cbs;
Mutex file_dialog_mutex;
Vector<FileDialogData> file_dialogs;
@ -96,11 +118,11 @@ public:
bool is_supported() { return !unsupported; }
bool is_file_chooser_supported();
bool is_settings_supported();
bool is_screenshot_supported();
// org.freedesktop.portal.FileChooser methods.
Error file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb);
void process_file_dialog_callbacks();
void process_callbacks();
// org.freedesktop.portal.Settings methods.
@ -109,11 +131,19 @@ public:
// 1: Prefer dark appearance.
// 2: Prefer light appearance.
uint32_t get_appearance_color_scheme();
Color get_appearance_accent_color();
void set_system_theme_change_callback(const Callable &p_system_theme_changed) {
system_theme_changed = p_system_theme_changed;
}
// Retrieve high-contrast setting.
// -1: Unknown.
// 0: Disabled.
// 1: Enabled.
uint32_t get_high_contrast();
// org.freedesktop.portal.Screenshot methods.
bool color_picker(const String &p_xid, const Callable &p_callback);
};
#endif // DBUS_ENABLED
#endif // FREEDESKTOP_PORTAL_DESKTOP_H

View file

@ -79,7 +79,7 @@ void FreeDesktopScreenSaver::inhibit() {
if (dbus_error_is_set(&error)) {
dbus_error_free(&error);
dbus_connection_unref(bus);
unsupported = false;
unsupported = true;
return;
}
@ -116,6 +116,7 @@ void FreeDesktopScreenSaver::uninhibit() {
DBUS_TYPE_INVALID);
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);
@ -125,7 +126,6 @@ void FreeDesktopScreenSaver::uninhibit() {
print_verbose("FreeDesktopScreenSaver: Released screensaver inhibition cookie: " + uitos(cookie));
dbus_message_unref(message);
dbus_message_unref(reply);
dbus_connection_unref(bus);
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FREEDESKTOP_SCREENSAVER_H
#define FREEDESKTOP_SCREENSAVER_H
#pragma once
#ifdef DBUS_ENABLED
@ -47,5 +46,3 @@ public:
};
#endif // DBUS_ENABLED
#endif // FREEDESKTOP_SCREENSAVER_H

View file

@ -73,7 +73,7 @@ JoypadLinux::Joypad::~Joypad() {
}
void JoypadLinux::Joypad::reset() {
dpad = 0;
dpad.clear();
fd = -1;
for (int i = 0; i < MAX_ABS; i++) {
abs_map[i] = -1;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOYPAD_LINUX_H
#define JOYPAD_LINUX_H
#pragma once
#ifdef JOYDEV_ENABLED
@ -63,7 +62,7 @@ private:
float curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
BitField<HatMask> dpad;
BitField<HatMask> dpad = HatMask::CENTER;
int fd = -1;
String devpath;
@ -135,5 +134,3 @@ private:
};
#endif // JOYDEV_ENABLED
#endif // JOYPAD_LINUX_H

View file

@ -0,0 +1,11 @@
# Tuples with the name of the arch
def get_platforms():
return [("x64", "x86_64")]
def get_configurations():
return ["editor", "template_debug", "template_release"]
def get_build_prefix(env):
return []

View file

@ -37,10 +37,12 @@
#include "servers/rendering_server.h"
#ifdef X11_ENABLED
#include "x11/detect_prime_x11.h"
#include "x11/display_server_x11.h"
#endif
#ifdef WAYLAND_ENABLED
#include "wayland/detect_prime_egl.h"
#include "wayland/display_server_wayland.h"
#endif
@ -49,6 +51,22 @@
#include "modules/regex/regex.h"
#endif
#if defined(RD_ENABLED)
#include "servers/rendering/rendering_device.h"
#endif
#if defined(VULKAN_ENABLED)
#ifdef X11_ENABLED
#include "x11/rendering_context_driver_vulkan_x11.h"
#endif
#ifdef WAYLAND_ENABLED
#include "wayland/rendering_context_driver_vulkan_wayland.h"
#endif
#endif
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
#endif
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
@ -74,7 +92,7 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
String program;
for (int i = 0; i < path_elems.size(); i++) {
for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
for (uint64_t k = 0; k < std::size(message_programs); k++) {
String tested_path = path_elems[i].path_join(message_programs[k]);
if (FileAccess::exists(tested_path)) {
@ -155,7 +173,7 @@ String OS_LinuxBSD::get_unique_id() const {
memset(buf, 0, sizeof(buf));
size_t len = sizeof(buf) - 1;
if (sysctl(mib, 2, buf, &len, 0x0, 0) != -1) {
machine_id = String::utf8(buf).replace("-", "");
machine_id = String::utf8(buf).remove_char('-');
}
#else
Ref<FileAccess> f = FileAccess::open("/etc/machine-id", FileAccess::READ);
@ -751,7 +769,7 @@ Vector<String> OS_LinuxBSD::get_system_font_path_for_text(const String &p_font_n
Vector<String> ret;
static const char *allowed_formats[] = { "TrueType", "CFF" };
for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) {
for (size_t i = 0; i < std::size(allowed_formats); i++) {
FcPattern *pattern = FcPatternCreate();
if (pattern) {
FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
@ -1180,6 +1198,73 @@ String OS_LinuxBSD::get_system_ca_certificates() {
return f->get_as_text();
}
bool OS_LinuxBSD::_test_create_rendering_device(const String &p_display_driver) const {
// Tests Rendering Device creation.
bool ok = false;
#if defined(RD_ENABLED)
Error err;
RenderingContextDriver *rcd = nullptr;
#if defined(VULKAN_ENABLED)
#ifdef X11_ENABLED
if (p_display_driver == "x11" || p_display_driver.is_empty()) {
rcd = memnew(RenderingContextDriverVulkanX11);
}
#endif
#ifdef WAYLAND_ENABLED
if (p_display_driver == "wayland") {
rcd = memnew(RenderingContextDriverVulkanWayland);
}
#endif
#endif
if (rcd != nullptr) {
err = rcd->initialize();
if (err == OK) {
RenderingDevice *rd = memnew(RenderingDevice);
err = rd->initialize(rcd);
memdelete(rd);
rd = nullptr;
if (err == OK) {
ok = true;
}
}
memdelete(rcd);
rcd = nullptr;
}
#endif
return ok;
}
bool OS_LinuxBSD::_test_create_rendering_device_and_gl(const String &p_display_driver) const {
// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some drivers.
#ifdef GLES3_ENABLED
#ifdef X11_ENABLED
if (p_display_driver == "x11" || p_display_driver.is_empty()) {
#ifdef SOWRAP_ENABLED
if (initialize_xlib(0) != 0) {
return false;
}
#endif
DetectPrimeX11::create_context();
}
#endif
#ifdef WAYLAND_ENABLED
if (p_display_driver == "wayland") {
#ifdef SOWRAP_ENABLED
if (initialize_wayland_egl(0) != 0) {
return false;
}
#endif
DetectPrimeEGL::create_context(EGL_PLATFORM_WAYLAND_KHR);
}
#endif
RasterizerGLES3::make_current(true);
#endif
return _test_create_rendering_device(p_display_driver);
}
OS_LinuxBSD::OS_LinuxBSD() {
main_loop = nullptr;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OS_LINUXBSD_H
#define OS_LINUXBSD_H
#pragma once
#include "crash_handler_linuxbsd.h"
#include "joypad_linux.h"
@ -138,8 +137,9 @@ public:
virtual String get_system_ca_certificates() override;
virtual bool _test_create_rendering_device_and_gl(const String &p_display_driver) const override;
virtual bool _test_create_rendering_device(const String &p_display_driver) const override;
OS_LinuxBSD();
~OS_LinuxBSD();
};
#endif // OS_LINUXBSD_H

View file

@ -28,6 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#ifdef __linux__
#include <alloca.h>
#endif

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef PLATFORM_GL_H
#define PLATFORM_GL_H
#pragma once
#ifndef GL_API_ENABLED
#define GL_API_ENABLED // Allow using desktop GL.
@ -41,5 +40,3 @@
#include "thirdparty/glad/glad/egl.h"
#include "thirdparty/glad/glad/gl.h"
#endif // PLATFORM_GL_H

View file

@ -63,7 +63,7 @@ void TTS_Linux::speech_init_thread_func(void *p_userdata) {
} else {
class_str = config_name.utf8();
}
tts->synth = spd_open(class_str, "Godot_Engine_Speech_API", "Godot_Engine", SPD_MODE_THREADED);
tts->synth = spd_open(class_str.get_data(), "Godot_Engine_Speech_API", "Godot_Engine", SPD_MODE_THREADED);
if (tts->synth) {
tts->synth->callback_end = &speech_event_callback;
tts->synth->callback_cancel = &speech_event_callback;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TTS_LINUX_H
#define TTS_LINUX_H
#pragma once
#include "core/os/thread.h"
#include "core/os/thread_safe.h"
@ -90,5 +89,3 @@ public:
TTS_Linux();
~TTS_Linux();
};
#endif // TTS_LINUX_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DETECT_PRIME_EGL_H
#define DETECT_PRIME_EGL_H
#pragma once
#ifdef GLES3_ENABLED
#ifdef EGL_ENABLED
@ -77,13 +76,11 @@ private:
{ nullptr, 0 }
};
public:
static void create_context(EGLenum p_platform_enum);
public:
static int detect_prime(EGLenum p_platform_enum);
};
#endif // GLES3_ENABLED
#endif // EGL_ENABLED
#endif // DETECT_PRIME_EGL_H

File diff suppressed because it is too large Load diff

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISPLAY_SERVER_WAYLAND_H
#define DISPLAY_SERVER_WAYLAND_H
#pragma once
#ifdef WAYLAND_ENABLED
@ -53,6 +52,7 @@
#endif
#ifdef DBUS_ENABLED
#include "freedesktop_at_spi_monitor.h"
#include "freedesktop_portal_desktop.h"
#include "freedesktop_screensaver.h"
#endif
@ -67,9 +67,18 @@
#undef CursorShape
class DisplayServerWayland : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
GDSOFTCLASS(DisplayServerWayland, DisplayServer);
struct WindowData {
WindowID id;
WindowID id = INVALID_WINDOW_ID;
WindowID parent_id = INVALID_WINDOW_ID;
// For popups.
WindowID root_id = INVALID_WINDOW_ID;
// For toplevels.
List<WindowID> popup_stack;
Rect2i rect;
Size2i max_size;
@ -77,6 +86,8 @@ class DisplayServerWayland : public DisplayServer {
Rect2i safe_rect;
bool emulate_vsync = false;
#ifdef GLES3_ENABLED
struct wl_egl_window *wl_egl_window = nullptr;
#endif
@ -120,16 +131,25 @@ class DisplayServerWayland : public DisplayServer {
HashMap<CursorShape, CustomCursor> custom_cursors;
WindowData main_window;
HashMap<WindowID, WindowData> windows;
WindowID window_id_counter = MAIN_WINDOW_ID;
WaylandThread wayland_thread;
Context context;
bool swap_cancel_ok = false;
// NOTE: These are the based on WINDOW_FLAG_POPUP, which does NOT imply what it
// seems. It's particularly confusing for our usecase, but just know that these
// are the "take all input thx" windows while the `popup_stack` variable keeps
// track of all the generic floating window concept.
List<WindowID> popup_menu_list;
BitField<MouseButtonMask> last_mouse_monitor_mask = MouseButtonMask::NONE;
String ime_text;
Vector2i ime_selection;
SuspendState suspend_state = SuspendState::NONE;
bool emulate_vsync = false;
String rendering_driver;
@ -149,23 +169,24 @@ class DisplayServerWayland : public DisplayServer {
#if DBUS_ENABLED
FreeDesktopPortalDesktop *portal_desktop = nullptr;
FreeDesktopAtSPIMonitor *atspi_monitor = nullptr;
FreeDesktopScreenSaver *screensaver = nullptr;
bool screensaver_inhibited = false;
#endif
static String _get_app_id_from_context(Context p_context);
void _send_window_event(WindowEvent p_event);
void _send_window_event(WindowEvent p_event, WindowID p_window_id = MAIN_WINDOW_ID);
static void dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
void _resize_window(const Size2i &p_size);
virtual void _show_window();
void _update_window_rect(const Rect2i &p_rect, WindowID p_window_id = MAIN_WINDOW_ID);
void try_suspend();
void initialize_tts() const;
public:
virtual bool has_feature(Feature p_feature) const override;
@ -185,10 +206,11 @@ public:
#ifdef DBUS_ENABLED
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() 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;
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, WindowID p_window_id) 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, WindowID p_window_id) override;
#endif
virtual void beep() const override;
@ -224,6 +246,14 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) override;
virtual void show_window(WindowID p_id) override;
virtual void delete_sub_window(WindowID p_id) override;
virtual WindowID window_get_active_popup() const override;
virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override;
virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override;
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
@ -280,6 +310,9 @@ public:
virtual void window_set_ime_active(const bool p_active, WindowID p_window_id = MAIN_WINDOW_ID) override;
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window_id = MAIN_WINDOW_ID) override;
virtual int accessibility_should_increase_contrast() const override;
virtual int accessibility_screen_reader_active() const override;
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
@ -293,6 +326,8 @@ public:
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override;
virtual bool get_swap_cancel_ok() override;
virtual int keyboard_get_layout_count() const override;
virtual int keyboard_get_current_layout() const override;
virtual void keyboard_set_current_layout(int p_index) override;
@ -300,6 +335,8 @@ public:
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual bool color_picker(const Callable &p_callback) override;
virtual void process_events() override;
virtual void release_rendering_thread() override;
@ -319,5 +356,3 @@ public:
};
#endif // WAYLAND_ENABLED
#endif // DISPLAY_SERVER_WAYLAND_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EGL_MANAGER_WAYLAND_H
#define EGL_MANAGER_WAYLAND_H
#pragma once
#ifdef WAYLAND_ENABLED
#ifdef EGL_ENABLED
@ -49,5 +48,3 @@ public:
#endif // GLES3_ENABLED
#endif // EGL_ENABLED
#endif // WAYLAND_ENABLED
#endif // EGL_MANAGER_WAYLAND_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EGL_MANAGER_WAYLAND_GLES_H
#define EGL_MANAGER_WAYLAND_GLES_H
#pragma once
#ifdef WAYLAND_ENABLED
#ifdef EGL_ENABLED
@ -49,5 +48,3 @@ public:
#endif // GLES3_ENABLED
#endif // EGL_ENABLED
#endif // WAYLAND_ENABLED
#endif // EGL_MANAGER_WAYLAND_GLES_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef KEY_MAPPING_XKB_H
#define KEY_MAPPING_XKB_H
#pragma once
#include "core/os/keyboard.h"
#include "core/templates/hash_map.h"
@ -61,5 +60,3 @@ public:
static Key get_scancode(unsigned int p_code);
static KeyLocation get_location(unsigned int p_code);
};
#endif // KEY_MAPPING_XKB_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H
#define RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H
#pragma once
#ifdef VULKAN_ENABLED
@ -53,5 +52,3 @@ public:
};
#endif // VULKAN_ENABLED
#endif // RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H

File diff suppressed because it is too large Load diff

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef WAYLAND_THREAD_H
#define WAYLAND_THREAD_H
#pragma once
#ifdef WAYLAND_ENABLED
@ -89,41 +88,62 @@ class WaylandThread {
public:
// Messages used for exchanging information between Godot's and Wayland's thread.
class Message : public RefCounted {
GDSOFTCLASS(Message, RefCounted);
public:
Message() {}
virtual ~Message() = default;
};
class WindowMessage : public Message {
GDSOFTCLASS(WindowMessage, Message);
public:
DisplayServer::WindowID id = DisplayServer::INVALID_WINDOW_ID;
};
// Message data for window rect changes.
class WindowRectMessage : public Message {
class WindowRectMessage : public WindowMessage {
GDSOFTCLASS(WindowRectMessage, WindowMessage);
public:
// NOTE: This is in "scaled" terms. For example, if there's a 1920x1080 rect
// with a scale factor of 2, the actual value of `rect` will be 3840x2160.
Rect2i rect;
};
class WindowEventMessage : public Message {
class WindowEventMessage : public WindowMessage {
GDSOFTCLASS(WindowEventMessage, WindowMessage);
public:
DisplayServer::WindowEvent event;
};
class InputEventMessage : public Message {
GDSOFTCLASS(InputEventMessage, Message);
public:
Ref<InputEvent> event;
};
class DropFilesEventMessage : public Message {
class DropFilesEventMessage : public WindowMessage {
GDSOFTCLASS(DropFilesEventMessage, WindowMessage);
public:
Vector<String> files;
};
class IMEUpdateEventMessage : public Message {
class IMEUpdateEventMessage : public WindowMessage {
GDSOFTCLASS(IMEUpdateEventMessage, WindowMessage);
public:
String text;
Vector2i selection;
};
class IMECommitEventMessage : public Message {
class IMECommitEventMessage : public WindowMessage {
GDSOFTCLASS(IMECommitEventMessage, WindowMessage);
public:
String text;
};
@ -202,7 +222,8 @@ public:
// TODO: Make private?
struct WindowState {
DisplayServer::WindowID id;
DisplayServer::WindowID id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID parent_id = DisplayServer::INVALID_WINDOW_ID;
Rect2i rect;
DisplayServer::WindowMode mode = DisplayServer::WINDOW_MODE_WINDOWED;
@ -224,6 +245,7 @@ public:
// be called even after being destroyed, pointing to probably invalid window
// data by then and segfaulting hard.
struct wl_callback *frame_callback = nullptr;
uint64_t last_frame_time = 0;
struct wl_surface *wl_surface = nullptr;
struct xdg_surface *xdg_surface = nullptr;
@ -237,6 +259,8 @@ public:
struct zxdg_exported_v2 *xdg_exported_v2 = nullptr;
struct xdg_popup *xdg_popup = nullptr;
String exported_handle;
// Currently applied buffer scale.
@ -317,11 +341,14 @@ public:
Vector2 relative_motion;
uint32_t relative_motion_time = 0;
BitField<MouseButtonMask> pressed_button_mask;
BitField<MouseButtonMask> pressed_button_mask = MouseButtonMask::NONE;
MouseButton last_button_pressed = MouseButton::NONE;
Point2 last_pressed_position;
DisplayServer::WindowID pointed_id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID last_pointed_id = DisplayServer::INVALID_WINDOW_ID;
// This is needed to check for a new double click every time.
bool double_click_begun = false;
@ -344,27 +371,24 @@ public:
Vector2 tilt;
uint32_t pressure = 0;
BitField<MouseButtonMask> pressed_button_mask;
BitField<MouseButtonMask> pressed_button_mask = MouseButtonMask::NONE;
MouseButton last_button_pressed = MouseButton::NONE;
Point2 last_pressed_position;
bool double_click_begun = false;
// Note: the protocol doesn't have it (I guess that this isn't really meant to
// be used as a mouse...), but we'll hack one in with the current ticks.
uint64_t button_time = 0;
uint64_t motion_time = 0;
DisplayServer::WindowID proximal_id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID last_proximal_id = DisplayServer::INVALID_WINDOW_ID;
uint32_t proximity_serial = 0;
struct wl_surface *proximal_surface = nullptr;
};
struct TabletToolState {
struct wl_seat *wl_seat = nullptr;
struct wl_surface *last_surface = nullptr;
bool is_eraser = false;
TabletToolData data_pending;
@ -388,9 +412,6 @@ public:
uint32_t pointer_enter_serial = 0;
struct wl_surface *pointed_surface = nullptr;
struct wl_surface *last_pointed_surface = nullptr;
struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
@ -421,6 +442,9 @@ public:
// Keyboard.
struct wl_keyboard *wl_keyboard = nullptr;
// For key events.
DisplayServer::WindowID focused_id = DisplayServer::INVALID_WINDOW_ID;
struct xkb_context *xkb_context = nullptr;
struct xkb_keymap *xkb_keymap = nullptr;
struct xkb_state *xkb_state = nullptr;
@ -449,6 +473,7 @@ public:
struct wl_data_device *wl_data_device = nullptr;
// Drag and drop.
DisplayServer::WindowID dnd_id = DisplayServer::INVALID_WINDOW_ID;
struct wl_data_offer *wl_data_offer_dnd = nullptr;
uint32_t dnd_enter_serial = 0;
@ -473,6 +498,7 @@ public:
// IME.
struct zwp_text_input_v3 *wp_text_input = nullptr;
DisplayServer::WindowID ime_window_id = DisplayServer::INVALID_WINDOW_ID;
bool ime_enabled = false;
bool ime_active = false;
String ime_text;
@ -503,7 +529,7 @@ private:
Thread events_thread;
ThreadData thread_data;
WindowState main_window;
HashMap<DisplayServer::WindowID, WindowState> windows;
List<Ref<Message>> messages;
@ -615,6 +641,10 @@ private:
static void _xdg_toplevel_on_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height);
static void _xdg_toplevel_on_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities);
static void _xdg_popup_on_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height);
static void _xdg_popup_on_popup_done(void *data, struct xdg_popup *xdg_popup);
static void _xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token);
// wayland-protocols event handlers.
static void _wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale);
@ -770,6 +800,12 @@ private:
.wm_capabilities = _xdg_toplevel_on_wm_capabilities,
};
static constexpr struct xdg_popup_listener xdg_popup_listener = {
.configure = _xdg_popup_on_configure,
.popup_done = _xdg_popup_on_popup_done,
.repositioned = _xdg_popup_on_repositioned,
};
// wayland-protocols event listeners.
static constexpr struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
.preferred_scale = _wp_fractional_scale_on_preferred_scale,
@ -955,8 +991,13 @@ public:
void beep() const;
void window_create(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void window_create_popup(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id, Rect2i p_rect);
void window_destroy(DisplayServer::WindowID p_window_Id);
void window_set_parent(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id);
struct wl_surface *window_get_wl_surface(DisplayServer::WindowID p_window_id) const;
WindowState *window_get_state(DisplayServer::WindowID p_window_id);
void window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window);
@ -989,6 +1030,7 @@ public:
void pointer_set_hint(const Point2i &p_hint);
PointerConstraint pointer_get_constraint() const;
DisplayServer::WindowID pointer_get_pointed_window_id() const;
DisplayServer::WindowID pointer_get_last_pointed_window_id() const;
BitField<MouseButtonMask> pointer_get_button_mask() const;
void cursor_set_visible(bool p_visible);
@ -1027,6 +1069,8 @@ public:
bool get_reset_frame();
bool wait_frame_suspend_ms(int p_timeout);
uint64_t window_get_last_frame_time(DisplayServer::WindowID p_window_id) const;
bool window_is_suspended(DisplayServer::WindowID p_window_id) const;
bool is_suspended() const;
Error init();
@ -1034,5 +1078,3 @@ public:
};
#endif // WAYLAND_ENABLED
#endif // WAYLAND_THREAD_H

View file

@ -60,25 +60,23 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX
// To prevent shadowing warnings
#undef glGetString
struct vendor {
const char *glxvendor = nullptr;
int priority = 0;
};
int silent_error_handler(Display *display, XErrorEvent *error) {
static char message[1024];
XGetErrorText(display, error->error_code, message, sizeof(message));
print_verbose(vformat("XServer error: %s"
"\n Major opcode of failed request: %d"
"\n Serial number of failed request: %d"
"\n Current serial number in output stream: %d",
String::utf8(message), (uint64_t)error->request_code, (uint64_t)error->minor_code, (uint64_t)error->serial));
vendor vendormap[] = {
{ "Advanced Micro Devices, Inc.", 30 },
{ "AMD", 30 },
{ "NVIDIA Corporation", 30 },
{ "X.Org", 30 },
{ "Intel Open Source Technology Center", 20 },
{ "Intel", 20 },
{ "nouveau", 10 },
{ "Mesa Project", 0 },
{ nullptr, 0 }
};
quick_exit(1);
return 0;
}
// Runs inside a child. Exiting will not quit the engine.
void create_context() {
void DetectPrimeX11::create_context() {
XSetErrorHandler(&silent_error_handler);
Display *x11_display = XOpenDisplay(nullptr);
Window x11_window;
GLXContext glx_context;
@ -137,20 +135,7 @@ void create_context() {
XFree(vi);
}
int silent_error_handler(Display *display, XErrorEvent *error) {
static char message[1024];
XGetErrorText(display, error->error_code, message, sizeof(message));
print_verbose(vformat("XServer error: %s"
"\n Major opcode of failed request: %d"
"\n Serial number of failed request: %d"
"\n Current serial number in output stream: %d",
String::utf8(message), (uint64_t)error->request_code, (uint64_t)error->minor_code, (uint64_t)error->serial));
quick_exit(1);
return 0;
}
int detect_prime() {
int DetectPrimeX11::detect_prime() {
pid_t p;
int priorities[2] = {};
String vendors[2];
@ -202,7 +187,6 @@ int detect_prime() {
// cleaning up these processes, and fork() makes a copy
// of all globals.
CoreGlobals::leak_reporting_enabled = false;
XSetErrorHandler(&silent_error_handler);
char string[201];
@ -253,7 +237,7 @@ int detect_prime() {
}
for (int i = 1; i >= 0; --i) {
vendor *v = vendormap;
const Vendor *v = vendor_map;
while (v->glxvendor) {
if (v->glxvendor == vendors[i]) {
priorities[i] = v->priority;

View file

@ -28,13 +28,33 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DETECT_PRIME_X11_H
#define DETECT_PRIME_X11_H
#pragma once
#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
int detect_prime();
class DetectPrimeX11 {
private:
struct Vendor {
const char *glxvendor = nullptr;
int priority = 0;
};
static constexpr Vendor vendor_map[] = {
{ "Advanced Micro Devices, Inc.", 30 },
{ "AMD", 30 },
{ "NVIDIA Corporation", 30 },
{ "X.Org", 30 },
{ "Intel Open Source Technology Center", 20 },
{ "Intel", 20 },
{ "nouveau", 10 },
{ "Mesa Project", 0 },
{ nullptr, 0 }
};
public:
static void create_context();
static int detect_prime();
};
#endif // X11_ENABLED && GLES3_ENABLED
#endif // DETECT_PRIME_X11_H

View file

@ -39,9 +39,12 @@
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "core/version.h"
#include "drivers/png/png_driver_common.h"
#include "main/main.h"
#include "servers/rendering/dummy/rasterizer_dummy.h"
#if defined(VULKAN_ENABLED)
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
@ -50,6 +53,10 @@
#include "drivers/gles3/rasterizer_gles3.h"
#endif
#ifdef ACCESSKIT_ENABLED
#include "drivers/accesskit/accessibility_driver_accesskit.h"
#endif
#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
@ -111,8 +118,7 @@ struct Hints {
static String get_atom_name(Display *p_disp, Atom p_atom) {
char *name = XGetAtomName(p_disp, p_atom);
ERR_FAIL_NULL_V_MSG(name, String(), "Atom is invalid.");
String ret;
ret.parse_utf8(name);
String ret = String::utf8(name);
XFree(name);
return ret;
}
@ -156,6 +162,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_NATIVE_DIALOG_FILE_MIME: {
return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_file_chooser_supported());
} break;
case FEATURE_NATIVE_COLOR_PICKER: {
return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_screenshot_supported());
} break;
#endif
case FEATURE_SCREEN_CAPTURE: {
return !xwayland;
@ -167,6 +176,12 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
} break;
#endif
#ifdef ACCESSKIT_ENABLED
case FEATURE_ACCESSIBILITY_SCREEN_READER: {
return (accessibility_driver != nullptr);
} break;
#endif
default: {
return false;
}
@ -338,38 +353,63 @@ void DisplayServerX11::_flush_mouse_motion() {
#ifdef SPEECHD_ENABLED
void DisplayServerX11::initialize_tts() const {
const_cast<DisplayServerX11 *>(this)->tts = memnew(TTS_Linux);
}
bool DisplayServerX11::tts_is_speaking() const {
ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, false);
return tts->is_speaking();
}
bool DisplayServerX11::tts_is_paused() const {
ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, false);
return tts->is_paused();
}
TypedArray<Dictionary> DisplayServerX11::tts_get_voices() const {
ERR_FAIL_NULL_V_MSG(tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
return tts->get_voices();
}
void DisplayServerX11::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
}
void DisplayServerX11::tts_pause() {
ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
tts->pause();
}
void DisplayServerX11::tts_resume() {
ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
tts->resume();
}
void DisplayServerX11::tts_stop() {
ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
tts->stop();
}
@ -398,30 +438,34 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
Color DisplayServerX11::get_accent_color() const {
return portal_desktop->get_appearance_accent_color();
}
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;
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 p_window_id) {
WindowID window_id = p_window_id;
if (!windows.has(window_id)) {
if (!windows.has(window_id) || windows[window_id].is_popup) {
window_id = MAIN_WINDOW_ID;
}
String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
return portal_desktop->file_dialog_show(last_focused_window, xid, p_title, p_current_directory, String(), p_filename, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
return portal_desktop->file_dialog_show(p_window_id, xid, p_title, p_current_directory, String(), p_filename, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
}
Error DisplayServerX11::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) {
WindowID window_id = last_focused_window;
Error DisplayServerX11::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, WindowID p_window_id) {
WindowID window_id = p_window_id;
if (!windows.has(window_id)) {
if (!windows.has(window_id) || windows[window_id].is_popup) {
window_id = MAIN_WINDOW_ID;
}
String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
return portal_desktop->file_dialog_show(last_focused_window, xid, p_title, p_current_directory, p_root, p_filename, p_mode, p_filters, p_options, p_callback, true);
return portal_desktop->file_dialog_show(p_window_id, xid, p_title, p_current_directory, p_root, p_filename, p_mode, p_filters, p_options, p_callback, true);
}
#endif
@ -578,7 +622,7 @@ BitField<MouseButtonMask> DisplayServerX11::mouse_get_button_state() const {
int root_x, root_y, win_x, win_y;
unsigned int mask;
if (XQueryPointer(x11_display, XRootWindow(x11_display, i), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask)) {
BitField<MouseButtonMask> last_button_state = 0;
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
if (mask & Button1Mask) {
last_button_state.set_flag(MouseButtonMask::LEFT);
@ -599,7 +643,7 @@ BitField<MouseButtonMask> DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
}
return 0;
return MouseButtonMask::NONE;
}
void DisplayServerX11::clipboard_set(const String &p_text) {
@ -759,7 +803,7 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A
}
if (success && (data_size > 0)) {
ret.parse_utf8((const char *)incr_data.ptr(), data_size);
ret.append_utf8((const char *)incr_data.ptr(), data_size);
}
} else if (bytes_left > 0) {
// Data is ready and can be processed all at once.
@ -769,7 +813,7 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A
&len, &dummy, &data);
if (result == Success) {
ret.parse_utf8((const char *)data);
ret.append_utf8((const char *)data);
} else {
print_verbose("Failed to get selection data.");
}
@ -1912,6 +1956,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
}
#endif
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver) {
accessibility_driver->window_destroy(p_id);
}
#endif
if (wd.xic) {
XDestroyIC(wd.xic);
wd.xic = nullptr;
@ -2061,7 +2111,7 @@ void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
Region region = XCreateRegion();
XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
} else if (region_path.size() == 0) {
} else if (region_path.is_empty()) {
XShapeCombineMask(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, None, ShapeSet);
} else {
XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * region_path.size());
@ -2284,6 +2334,40 @@ void DisplayServerX11::_update_size_hints(WindowID p_window) {
XFree(xsh);
}
void DisplayServerX11::_update_actions_hints(WindowID p_window) {
WindowData &wd = windows[p_window];
Atom prop = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
if (prop != None) {
Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
Atom wm_act_min = XInternAtom(x11_display, "_NET_WM_ACTION_MINIMIZE", False);
Atom type;
int format;
unsigned long len;
unsigned long remaining;
unsigned char *data = nullptr;
if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 1024, False, XA_ATOM, &type, &format, &len, &remaining, &data) == Success) {
Atom *atoms = (Atom *)data;
Vector<Atom> new_atoms;
for (uint64_t i = 0; i < len; i++) {
if (atoms[i] != wm_act_max_horz && atoms[i] != wm_act_max_vert && atoms[i] != wm_act_min) {
new_atoms.push_back(atoms[i]);
}
}
if (!wd.no_max_btn) {
new_atoms.push_back(wm_act_max_horz);
new_atoms.push_back(wm_act_max_vert);
}
if (!wd.no_min_btn) {
new_atoms.push_back(wm_act_min);
}
XChangeProperty(x11_display, wd.x11_window, prop, XA_ATOM, 32, PropModeReplace, (unsigned char *)new_atoms.ptrw(), new_atoms.size());
XFree(data);
}
}
}
Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
_THREAD_SAFE_METHOD_
@ -2338,6 +2422,7 @@ void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p
return;
}
wd.position = p_position;
int x = 0;
int y = 0;
if (!window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
@ -2563,7 +2648,8 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
Atom *atoms = (Atom *)data;
Atom wm_act_max_horz;
Atom wm_act_max_vert;
if (strcmp(p_atom_name, "_NET_WM_STATE") == 0) {
bool checking_state = strcmp(p_atom_name, "_NET_WM_STATE") == 0;
if (checking_state) {
wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
} else {
@ -2581,9 +2667,16 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
found_wm_act_max_vert = true;
}
if (found_wm_act_max_horz || found_wm_act_max_vert) {
retval = true;
break;
if (checking_state) {
if (found_wm_act_max_horz && found_wm_act_max_vert) {
retval = true;
break;
}
} else {
if (found_wm_act_max_horz || found_wm_act_max_vert) {
retval = true;
break;
}
}
}
@ -2961,6 +3054,18 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
WindowData &wd = windows[p_window];
switch (p_flag) {
case WINDOW_FLAG_MAXIMIZE_DISABLED: {
wd.no_max_btn = p_enabled;
_update_actions_hints(p_window);
XFlush(x11_display);
} break;
case WINDOW_FLAG_MINIMIZE_DISABLED: {
wd.no_min_btn = p_enabled;
_update_actions_hints(p_window);
XFlush(x11_display);
} break;
case WINDOW_FLAG_RESIZE_DISABLED: {
if (p_enabled && wd.embed_parent) {
print_line("Embedded window resize can't be disabled.");
@ -2969,6 +3074,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
wd.resize_disabled = p_enabled;
_update_size_hints(p_window);
_update_actions_hints(p_window);
XFlush(x11_display);
} break;
@ -3055,6 +3161,12 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
const WindowData &wd = windows[p_window];
switch (p_flag) {
case WINDOW_FLAG_MAXIMIZE_DISABLED: {
return wd.no_max_btn;
} break;
case WINDOW_FLAG_MINIMIZE_DISABLED: {
return wd.no_min_btn;
} break;
case WINDOW_FLAG_RESIZE_DISABLED: {
return wd.resize_disabled;
} break;
@ -3244,6 +3356,22 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
}
}
int DisplayServerX11::accessibility_should_increase_contrast() const {
#ifdef DBUS_ENABLED
return portal_desktop->get_high_contrast();
#endif
return -1;
}
int DisplayServerX11::accessibility_screen_reader_active() const {
#ifdef DBUS_ENABLED
if (atspi_monitor->is_supported()) {
return atspi_monitor->is_active();
}
#endif
return -1;
}
Point2i DisplayServerX11::ime_get_selection() const {
return im_selection;
}
@ -3355,6 +3483,10 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
}
}
bool DisplayServerX11::get_swap_cancel_ok() {
return swap_cancel_ok;
}
int DisplayServerX11::keyboard_get_layout_count() const {
int _group_count = 0;
XkbDescRec *kbd = XkbAllocKeyboard();
@ -3496,6 +3628,17 @@ Key DisplayServerX11::keyboard_get_label_from_physical(Key p_keycode) const {
return (Key)(key | modifiers);
}
bool DisplayServerX11::color_picker(const Callable &p_callback) {
WindowID window_id = last_focused_window;
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->color_picker(xid, p_callback);
}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
Atom actual_type = None;
int actual_format = 0;
@ -3657,8 +3800,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
keycode -= 'a' - 'A';
}
String tmp;
tmp.parse_utf8(utf8string, utf8bytes);
String tmp = String::utf8(utf8string, utf8bytes);
for (int i = 0; i < tmp.length(); i++) {
Ref<InputEventKey> k;
k.instantiate();
@ -3738,8 +3880,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
char str_xkb[256] = {};
int str_xkb_size = xkb_compose_state_get_utf8(wd.xkb_state, str_xkb, 255);
String tmp;
tmp.parse_utf8(str_xkb, str_xkb_size);
String tmp = String::utf8(str_xkb, str_xkb_size);
for (int i = 0; i < tmp.length(); i++) {
Ref<InputEventKey> k;
k.instantiate();
@ -3961,7 +4102,7 @@ Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p
32,
PropModeReplace,
(unsigned char *)&data,
sizeof(data) / sizeof(data[0]));
std::size(data));
return p_property;
} else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) {
// Request to check if SAVE_TARGETS is supported, nothing special to do.
@ -4094,7 +4235,7 @@ void DisplayServerX11::_xim_preedit_draw_callback(::XIM xim, ::XPointer client_d
if (xim_text->encoding_is_wchar) {
changed_text = String(xim_text->string.wide_char);
} else {
changed_text.parse_utf8(xim_text->string.multi_byte);
changed_text.append_utf8(xim_text->string.multi_byte);
}
if (call_data->chg_length < 0) {
@ -4789,6 +4930,9 @@ void DisplayServerX11::process_events() {
XSync(x11_display, False);
XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
_update_actions_hints(window_id);
XFlush(x11_display);
// Set focus when menu window is started.
// RevertToPointerRoot is used to make sure we don't lose all focus in case
// a subwindow and its parent are both destroyed.
@ -4878,6 +5022,11 @@ void DisplayServerX11::process_events() {
static unsigned int focus_order = 0;
wd.focus_order = ++focus_order;
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver) {
accessibility_driver->accessibility_set_window_focused(window_id, true);
}
#endif
_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);
if (mouse_mode_grab) {
@ -4929,6 +5078,11 @@ void DisplayServerX11::process_events() {
wd.focused = false;
Input::get_singleton()->release_pressed_events();
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver) {
accessibility_driver->accessibility_set_window_focused(window_id, false);
}
#endif
_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
@ -5164,7 +5318,7 @@ void DisplayServerX11::process_events() {
pos = Point2i(windows[focused_window_id].size.width / 2, windows[focused_window_id].size.height / 2);
}
BitField<MouseButtonMask> last_button_state = 0;
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
if (event.xmotion.state & Button1Mask) {
last_button_state.set_flag(MouseButtonMask::LEFT);
}
@ -5268,7 +5422,7 @@ void DisplayServerX11::process_events() {
Vector<String> files = String((char *)p.data).split("\r\n", false);
XFree(p.data);
for (int i = 0; i < files.size(); i++) {
files.write[i] = files[i].replace("file://", "").uri_decode();
files.write[i] = files[i].replace("file://", "").uri_file_decode();
}
if (windows[window_id].drop_files_callback.is_valid()) {
@ -5385,7 +5539,7 @@ void DisplayServerX11::process_events() {
#ifdef DBUS_ENABLED
if (portal_desktop) {
portal_desktop->process_file_dialog_callbacks();
portal_desktop->process_callbacks();
}
#endif
@ -6030,6 +6184,7 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
drivers.push_back("opengl3");
drivers.push_back("opengl3_es");
#endif
drivers.push_back("dummy");
return drivers;
}
@ -6178,6 +6333,15 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
if (dead_tbl && xkb_loaded_v05p) {
wd.xkb_state = xkb_compose_state_new(dead_tbl, XKB_COMPOSE_STATE_NO_FLAGS);
}
#endif
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver && !accessibility_driver->window_create(id, nullptr)) {
if (OS::get_singleton()->is_stdout_verbose()) {
ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");
}
memdelete(accessibility_driver);
accessibility_driver = nullptr;
}
#endif
// Enable receiving notification when the window is initialized (MapNotify)
// so the focus can be set at the right time.
@ -6466,8 +6630,12 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt
DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
KeyMappingX11::initialize();
String current_desk = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower();
String session_desk = OS::get_singleton()->get_environment("XDG_SESSION_DESKTOP").to_lower();
swap_cancel_ok = (current_desk.contains("kde") || session_desk.contains("kde") || current_desk.contains("lxqt") || session_desk.contains("lxqt"));
xwayland = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").to_lower() == "wayland";
kde5_embed_workaround = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower() == "kde" && OS::get_singleton()->get_environment("KDE_SESSION_VERSION") == "5";
kde5_embed_workaround = current_desk == "kde" && OS::get_singleton()->get_environment("KDE_SESSION_VERSION") == "5";
native_menu = memnew(NativeMenu);
context = p_context;
@ -6757,7 +6925,17 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
// Init TTS
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
if (tts_enabled) {
tts = memnew(TTS_Linux);
initialize_tts();
}
#endif
#ifdef ACCESSKIT_ENABLED
if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {
accessibility_driver = memnew(AccessibilityDriverAccessKit);
if (accessibility_driver->init() != OK) {
memdelete(accessibility_driver);
accessibility_driver = nullptr;
}
}
#endif
@ -6770,6 +6948,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
// Initialize context and rendering device.
if (rendering_driver == "dummy") {
RasterizerDummy::make_current();
driver_found = true;
}
#if defined(RD_ENABLED)
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
@ -6844,7 +7027,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
if (use_prime == -1) {
print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
use_prime = detect_prime();
use_prime = DetectPrimeX11::detect_prime();
}
if (use_prime) {
@ -6931,7 +7114,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
window_set_flag(WindowFlags(i), true, main_window);
}
}
show_window(main_window);
#if defined(RD_ENABLED)
if (rendering_context) {
@ -7101,8 +7283,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
portal_desktop = memnew(FreeDesktopPortalDesktop);
atspi_monitor = memnew(FreeDesktopAtSPIMonitor);
#endif // DBUS_ENABLED
XSetErrorHandler(&default_window_error_handler);
r_error = OK;
@ -7142,6 +7324,12 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver) {
accessibility_driver->window_destroy(E.key);
}
#endif
WindowData &wd = E.value;
if (wd.xic) {
XDestroyIC(wd.xic);
@ -7216,7 +7404,11 @@ DisplayServerX11::~DisplayServerX11() {
if (xmbstring) {
memfree(xmbstring);
}
#ifdef ACCESSKIT_ENABLED
if (accessibility_driver) {
memdelete(accessibility_driver);
}
#endif
#ifdef SPEECHD_ENABLED
if (tts) {
memdelete(tts);
@ -7226,6 +7418,7 @@ DisplayServerX11::~DisplayServerX11() {
#ifdef DBUS_ENABLED
memdelete(screensaver);
memdelete(portal_desktop);
memdelete(atspi_monitor);
#endif
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISPLAY_SERVER_X11_H
#define DISPLAY_SERVER_X11_H
#pragma once
#ifdef X11_ENABLED
@ -66,6 +65,7 @@
#endif
#if defined(DBUS_ENABLED)
#include "freedesktop_at_spi_monitor.h"
#include "freedesktop_portal_desktop.h"
#include "freedesktop_screensaver.h"
#endif
@ -123,7 +123,7 @@ typedef struct _xrr_monitor_info {
#undef CursorShape
class DisplayServerX11 : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
GDSOFTCLASS(DisplayServerX11, DisplayServer);
_THREAD_SAFE_CLASS_
@ -160,6 +160,7 @@ class DisplayServerX11 : public DisplayServer {
#if defined(DBUS_ENABLED)
FreeDesktopPortalDesktop *portal_desktop = nullptr;
FreeDesktopAtSPIMonitor *atspi_monitor = nullptr;
#endif
struct WindowData {
@ -200,6 +201,8 @@ class DisplayServerX11 : public DisplayServer {
bool on_top = false;
bool borderless = false;
bool resize_disabled = false;
bool no_min_btn = false;
bool no_max_btn = false;
Vector2i last_position_before_fs;
bool focused = true;
bool minimized = false;
@ -353,6 +356,7 @@ class DisplayServerX11 : public DisplayServer {
bool _window_minimize_check(WindowID p_window) const;
void _validate_mode_on_map(WindowID p_window);
void _update_size_hints(WindowID p_window);
void _update_actions_hints(WindowID p_window);
void _set_wm_fullscreen(WindowID p_window, bool p_enabled, bool p_exclusive);
void _set_wm_maximized(WindowID p_window, bool p_enabled);
void _set_wm_minimized(WindowID p_window, bool p_enabled);
@ -360,6 +364,7 @@ class DisplayServerX11 : public DisplayServer {
void _update_context(WindowData &wd);
Context context = CONTEXT_ENGINE;
bool swap_cancel_ok = false;
WindowID _get_focused_window_or_popup() const;
bool _window_focus_check();
@ -395,6 +400,8 @@ class DisplayServerX11 : public DisplayServer {
void _set_window_taskbar_pager_enabled(Window p_window, bool p_enabled);
Rect2i _screens_get_full_rect() const;
void initialize_tts() const;
protected:
void _window_changed(XEvent *event);
@ -420,10 +427,11 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() 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;
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, WindowID p_window_id) 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, WindowID p_window_id) override;
#endif
virtual void beep() const override;
@ -530,6 +538,9 @@ public:
virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override;
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) override;
virtual int accessibility_should_increase_contrast() const override;
virtual int accessibility_screen_reader_active() const override;
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
@ -548,6 +559,8 @@ public:
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override;
virtual bool get_swap_cancel_ok() override;
virtual int keyboard_get_layout_count() const override;
virtual int keyboard_get_current_layout() const override;
virtual void keyboard_set_current_layout(int p_index) override;
@ -556,6 +569,8 @@ public:
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 bool color_picker(const Callable &p_callback) override;
virtual void process_events() override;
virtual void release_rendering_thread() override;
@ -578,5 +593,3 @@ public:
};
#endif // X11_ENABLED
#endif // DISPLAY_SERVER_X11_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GL_MANAGER_X11_H
#define GL_MANAGER_X11_H
#pragma once
#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
@ -134,5 +133,3 @@ public:
};
#endif // X11_ENABLED && GLES3_ENABLED
#endif // GL_MANAGER_X11_H

View file

@ -28,12 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef GL_MANAGER_X11_EGL_H
#define GL_MANAGER_X11_EGL_H
#pragma once
#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
#include "core/error/error_list.h"
#include "core/os/os.h"
#include "core/templates/local_vector.h"
#include "drivers/egl/egl_manager.h"
@ -57,5 +55,3 @@ public:
};
#endif // X11_ENABLED && GLES3_ENABLED
#endif // GL_MANAGER_X11_EGL_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef KEY_MAPPING_X11_H
#define KEY_MAPPING_X11_H
#pragma once
#include "core/os/keyboard.h"
#include "core/templates/hash_map.h"
@ -68,5 +67,3 @@ public:
static char32_t get_unicode_from_keysym(KeySym p_keysym);
static KeyLocation get_location(unsigned int p_code);
};
#endif // KEY_MAPPING_X11_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_X11_H
#define RENDERING_CONTEXT_DRIVER_VULKAN_X11_H
#pragma once
#ifdef VULKAN_ENABLED
@ -55,5 +54,3 @@ public:
};
#endif // VULKAN_ENABLED
#endif // RENDERING_CONTEXT_DRIVER_VULKAN_X11_H