summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/detect.py13
-rw-r--r--platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml73
-rw-r--r--platform/linuxbsd/export/export.cpp4
-rw-r--r--platform/linuxbsd/export/export.h1
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp42
-rw-r--r--platform/linuxbsd/export/export_plugin.h5
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp16
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp16
-rw-r--r--platform/linuxbsd/joypad_linux.cpp44
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp54
-rw-r--r--platform/linuxbsd/os_linuxbsd.h3
-rw-r--r--platform/linuxbsd/tts_linux.cpp5
-rw-r--r--platform/linuxbsd/tts_linux.h2
-rw-r--r--platform/linuxbsd/x11/detect_prime_x11.cpp3
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp366
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h11
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp3
17 files changed, 547 insertions, 114 deletions
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 54351757cd..dadc03685b 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -56,6 +56,16 @@ def get_opts():
]
+def get_doc_classes():
+ return [
+ "EditorExportPlatformLinuxBSD",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
+
+
def get_flags():
return [
("arch", detect_arch()),
@@ -453,6 +463,9 @@ def configure(env: "Environment"):
else:
env.Append(LINKFLAGS=["-T", "platform/linuxbsd/pck_embed.legacy.ld"])
+ if platform.system() == "FreeBSD":
+ env.Append(LINKFLAGS=["-lkvm"])
+
## Cross-compilation
# TODO: Support cross-compilation on architectures other than x86.
host_is_64_bit = sys.maxsize > 2**32
diff --git a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
new file mode 100644
index 0000000000..4ab2464929
--- /dev/null
+++ b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorExportPlatformLinuxBSD" inherits="EditorExportPlatformPC" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Exporter for Linux/BSD.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ <link title="Exporting for Linux">$DOCS_URL/tutorials/export/exporting_for_linux.html</link>
+ </tutorials>
+ <members>
+ <member name="binary_format/architecture" type="String" setter="" getter="">
+ Application executable architecture.
+ Supported architectures: [code]x86_32[/code], [code]x86_64[/code], [code]arm64[/code], [code]arm32[/code], [code]rv64[/code], [code]ppc64[/code], and [code]ppc32[/code].
+ Official export templates include [code]x86_32[/code] and [code]x86_64[/code] binaries only.
+ </member>
+ <member name="binary_format/embed_pck" type="bool" setter="" getter="">
+ If [code]true[/code], project resources are embedded into the executable.
+ </member>
+ <member name="custom_template/debug" type="String" setter="" getter="">
+ Path to the custom export template. If left empty, default template is used.
+ </member>
+ <member name="custom_template/release" type="String" setter="" getter="">
+ Path to the custom export template. If left empty, default template is used.
+ </member>
+ <member name="debug/export_console_script" type="int" setter="" getter="">
+ If [code]true[/code], a console wrapper script is exported alongside the main executable, which allows running the project with enabled console output.
+ </member>
+ <member name="ssh_remote_deploy/cleanup_script" type="String" setter="" getter="">
+ Script code to execute on the remote host when app is finished.
+ The following variables can be used in the script:
+ - [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
+ - [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
+ - [code]{exe_name}[/code] - Name of application executable.
+ - [code]{cmd_args}[/code] - Array of the command line argument for the application.
+ </member>
+ <member name="ssh_remote_deploy/enabled" type="bool" setter="" getter="">
+ Enables remote deploy using SSH/SCP.
+ </member>
+ <member name="ssh_remote_deploy/extra_args_scp" type="String" setter="" getter="">
+ Array of the additional command line arguments passed to the SCP.
+ </member>
+ <member name="ssh_remote_deploy/extra_args_ssh" type="String" setter="" getter="">
+ Array of the additional command line arguments passed to the SSH.
+ </member>
+ <member name="ssh_remote_deploy/host" type="String" setter="" getter="">
+ Remote host SSH user name and address, in [code]user@address[/code] format.
+ </member>
+ <member name="ssh_remote_deploy/port" type="String" setter="" getter="">
+ Remote host SSH port number.
+ </member>
+ <member name="ssh_remote_deploy/run_script" type="String" setter="" getter="">
+ Script code to execute on the remote host when running the app.
+ The following variables can be used in the script:
+ - [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
+ - [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
+ - [code]{exe_name}[/code] - Name of application executable.
+ - [code]{cmd_args}[/code] - Array of the command line argument for the application.
+ </member>
+ <member name="texture_format/bptc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the BPTC format.
+ </member>
+ <member name="texture_format/etc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC format.
+ </member>
+ <member name="texture_format/etc2" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC2 format.
+ </member>
+ <member name="texture_format/s3tc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the S3TC format.
+ </member>
+ </members>
+</class>
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 2c5a945b6c..09f354246d 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -33,6 +33,10 @@
#include "editor/export/editor_export.h"
#include "export_plugin.h"
+void register_linuxbsd_exporter_types() {
+ GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformLinuxBSD);
+}
+
void register_linuxbsd_exporter() {
Ref<EditorExportPlatformLinuxBSD> platform;
platform.instantiate();
diff --git a/platform/linuxbsd/export/export.h b/platform/linuxbsd/export/export.h
index a2d70a73e3..f493047815 100644
--- a/platform/linuxbsd/export/export.h
+++ b/platform/linuxbsd/export/export.h
@@ -31,6 +31,7 @@
#ifndef LINUXBSD_EXPORT_H
#define LINUXBSD_EXPORT_H
+void register_linuxbsd_exporter_types();
void register_linuxbsd_exporter();
#endif // LINUXBSD_EXPORT_H
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 2528bb2b99..9544cc761d 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
#include "platform/linuxbsd/logo_svg.gen.h"
#include "platform/linuxbsd/run_icon_svg.gen.h"
@@ -142,7 +143,18 @@ List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<Edito
return list;
}
-void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_options) {
+bool EditorExportPlatformLinuxBSD::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ if (p_preset) {
+ // Hide SSH options.
+ bool ssh = p_preset->get("ssh_remote_deploy/enabled");
+ if (!ssh && p_option != "ssh_remote_deploy/enabled" && p_option.begins_with("ssh_remote_deploy/")) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_options) const {
EditorExportPlatformPC::get_export_options(r_options);
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64,arm32,rv64,ppc64,ppc32"), "x86_64"));
@@ -156,7 +168,7 @@ void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_opti
"kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\n"
"rm -rf \"{temp_dir}\"";
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
@@ -503,22 +515,24 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset,
}
EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
+ if (EditorNode::get_singleton()) {
#ifdef MODULE_SVG_ENABLED
- Ref<Image> img = memnew(Image);
- const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
- set_logo(ImageTexture::create_from_image(img));
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
+ set_logo(ImageTexture::create_from_image(img));
- img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
- run_icon = ImageTexture::create_from_image(img);
+ img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
#endif
- Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
- if (theme.is_valid()) {
- stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
- } else {
- stop_icon.instantiate();
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
}
}
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index 4f860c3fd0..cef714e86e 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -37,6 +37,8 @@
#include "scene/resources/texture.h"
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
+ GDCLASS(EditorExportPlatformLinuxBSD, EditorExportPlatformPC);
+
HashMap<String, String> extensions;
struct SSHCleanupCommand {
@@ -69,8 +71,9 @@ class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
public:
- virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual void get_export_options(List<ExportOption> *r_options) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
+ virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index ec1fcf6698..6dfa8ed93c 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -138,6 +138,22 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
+ 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("PortalDesktop: DBus %d.%d.%d detected.", version_major, version_minor, version_rev));
+ if (!ver_ok) {
+ print_verbose("PortalDesktop: Unsupported DBus library version!");
+ unsupported = true;
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
index d07e781a5f..cf179b5735 100644
--- a/platform/linuxbsd/freedesktop_screensaver.cpp
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -141,6 +141,22 @@ FreeDesktopScreenSaver::FreeDesktopScreenSaver() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
+ 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("ScreenSaver: DBus %d.%d.%d detected.", version_major, version_minor, version_rev));
+ if (!ver_ok) {
+ print_verbose("ScreenSaver:: Unsupported DBus library version!");
+ unsupported = true;
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 0256af0a59..a9725fff2e 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -32,6 +32,8 @@
#include "joypad_linux.h"
+#include "core/os/os.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -72,6 +74,26 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
+// This function is derived from SDL:
+// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+static bool detect_sandbox() {
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
#ifdef SOWRAP_ENABLED
@@ -80,11 +102,25 @@ JoypadLinux::JoypadLinux(Input *in) {
#else
int dylibloader_verbose = 0;
#endif
- use_udev = initialize_libudev(dylibloader_verbose) == 0;
- if (use_udev) {
- print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+ if (detect_sandbox()) {
+ // Linux binaries in sandboxes / containers need special handling because
+ // libudev doesn't work there. So we need to fallback to manual parsing
+ // of /dev/input in such case.
+ use_udev = false;
+ print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
} else {
- print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
+ use_udev = initialize_libudev(dylibloader_verbose) == 0;
+ if (use_udev) {
+ if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
+ // There's no API to check version, check if functions are available instead.
+ use_udev = false;
+ print_verbose("JoypadLinux: Unsupported udev library version!");
+ } else {
+ print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+ }
+ } else {
+ print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
+ }
}
#endif
#else
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 88c3d2cc14..8d8c8ce27b 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -30,6 +30,7 @@
#include "os_linuxbsd.h"
+#include "core/io/certs_compressed.gen.h"
#include "core/io/dir_access.h"
#include "main/main.h"
#include "servers/display_server.h"
@@ -195,6 +196,10 @@ void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
+String OS_LinuxBSD::get_identifier() const {
+ return "linuxbsd";
+}
+
String OS_LinuxBSD::get_name() const {
#ifdef __linux__
return "Linux";
@@ -490,6 +495,11 @@ bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
return true;
}
+ // Match against the specific OS (linux, freebsd, etc).
+ if (p_feature == get_name().to_lower()) {
+ return true;
+ }
+
return false;
}
@@ -1076,6 +1086,40 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
return OK;
}
+String OS_LinuxBSD::get_system_ca_certificates() {
+ String certfile;
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ // Compile time preferred certificates path.
+ if (!String(_SYSTEM_CERTS_PATH).is_empty() && da->file_exists(_SYSTEM_CERTS_PATH)) {
+ certfile = _SYSTEM_CERTS_PATH;
+ } else if (da->file_exists("/etc/ssl/certs/ca-certificates.crt")) {
+ // Debian/Ubuntu
+ certfile = "/etc/ssl/certs/ca-certificates.crt";
+ } else if (da->file_exists("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")) {
+ // Fedora
+ certfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem";
+ } else if (da->file_exists("/etc/ca-certificates/extracted/tls-ca-bundle.pem")) {
+ // Arch Linux
+ certfile = "/etc/ca-certificates/extracted/tls-ca-bundle.pem";
+ } else if (da->file_exists("/var/lib/ca-certificates/ca-bundle.pem")) {
+ // openSUSE
+ certfile = "/var/lib/ca-certificates/ca-bundle.pem";
+ } else if (da->file_exists("/etc/ssl/cert.pem")) {
+ // FreeBSD/OpenBSD
+ certfile = "/etc/ssl/cert.pem";
+ }
+
+ if (certfile.is_empty()) {
+ return "";
+ }
+
+ Ref<FileAccess> f = FileAccess::open(certfile, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), "", vformat("Failed to open system CA certificates file: '%s'", certfile));
+
+ return f->get_as_text();
+}
+
OS_LinuxBSD::OS_LinuxBSD() {
main_loop = nullptr;
@@ -1103,6 +1147,16 @@ OS_LinuxBSD::OS_LinuxBSD() {
font_config_initialized = true;
#endif
if (font_config_initialized) {
+ bool ver_ok = false;
+ int version = FcGetVersion();
+ ver_ok = ((version / 100 / 100) == 2 && (version / 100 % 100) >= 11) || ((version / 100 / 100) > 2); // 2.11.0
+ print_verbose(vformat("FontConfig %d.%d.%d detected.", version / 100 / 100, version / 100 % 100, version % 100));
+ if (!ver_ok) {
+ font_config_initialized = false;
+ }
+ }
+
+ if (font_config_initialized) {
config = FcInitLoadConfigAndFonts();
if (!config) {
font_config_initialized = false;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 9423514944..c1e735b0d4 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -96,6 +96,7 @@ protected:
virtual void set_main_loop(MainLoop *p_main_loop) override;
public:
+ virtual String get_identifier() const override;
virtual String get_name() const override;
virtual String get_distribution_name() const override;
virtual String get_version() const override;
@@ -132,6 +133,8 @@ public:
virtual Error move_to_trash(const String &p_path) override;
+ virtual String get_system_ca_certificates() override;
+
OS_LinuxBSD();
~OS_LinuxBSD();
};
diff --git a/platform/linuxbsd/tts_linux.cpp b/platform/linuxbsd/tts_linux.cpp
index ce0199e87f..a0cb4f5c6c 100644
--- a/platform/linuxbsd/tts_linux.cpp
+++ b/platform/linuxbsd/tts_linux.cpp
@@ -48,6 +48,11 @@ void TTS_Linux::speech_init_thread_func(void *p_userdata) {
if (initialize_speechd(dylibloader_verbose) != 0) {
print_verbose("Text-to-Speech: Cannot load Speech Dispatcher library!");
} else {
+ if (!spd_open || !spd_set_notification_on || !spd_list_synthesis_voices || !free_spd_voices || !spd_set_synthesis_voice || !spd_set_volume || !spd_set_voice_pitch || !spd_set_voice_rate || !spd_set_data_mode || !spd_say || !spd_pause || !spd_resume || !spd_cancel) {
+ // There's no API to check version, check if functions are available instead.
+ print_verbose("Text-to-Speech: Unsupported Speech Dispatcher library version!");
+ return;
+ }
#else
{
#endif
diff --git a/platform/linuxbsd/tts_linux.h b/platform/linuxbsd/tts_linux.h
index 4134f8fa2f..651a64d9d6 100644
--- a/platform/linuxbsd/tts_linux.h
+++ b/platform/linuxbsd/tts_linux.h
@@ -34,8 +34,8 @@
#include "core/os/thread.h"
#include "core/os/thread_safe.h"
#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
#include "core/templates/list.h"
-#include "core/templates/rb_map.h"
#include "core/variant/array.h"
#include "servers/display_server.h"
diff --git a/platform/linuxbsd/x11/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp
index 3d07be1c76..78778a8b56 100644
--- a/platform/linuxbsd/x11/detect_prime_x11.cpp
+++ b/platform/linuxbsd/x11/detect_prime_x11.cpp
@@ -60,6 +60,9 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+// To prevent shadowing warnings
+#undef glGetString
+
struct vendor {
const char *glxvendor = nullptr;
int priority = 0;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 4c5d9552e6..507acfcf34 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -186,6 +186,7 @@ bool DisplayServerX11::_refresh_device_info() {
xi.absolute_devices.clear();
xi.touch_devices.clear();
xi.pen_inverted_devices.clear();
+ xi.last_relative_time = 0;
int dev_count;
XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
@@ -302,37 +303,37 @@ void DisplayServerX11::_flush_mouse_motion() {
#ifdef SPEECHD_ENABLED
bool DisplayServerX11::tts_is_speaking() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_speaking();
}
bool DisplayServerX11::tts_is_paused() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_paused();
}
TypedArray<Dictionary> DisplayServerX11::tts_get_voices() const {
- ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>());
+ ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
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_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
}
void DisplayServerX11::tts_pause() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->pause();
}
void DisplayServerX11::tts_resume() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->resume();
}
void DisplayServerX11::tts_stop() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->stop();
}
@@ -753,23 +754,56 @@ int DisplayServerX11::get_screen_count() const {
}
int DisplayServerX11::get_primary_screen() const {
- return XDefaultScreen(x11_display);
+ int event_base, error_base;
+ if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ return 0;
+ } else {
+ return XDefaultScreen(x11_display);
+ }
}
-Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
- Rect2i rect(0, 0, 0, 0);
+int DisplayServerX11::get_keyboard_focus_screen() const {
+ int count = get_screen_count();
+ if (count < 2) {
+ // Early exit with single monitor.
+ return 0;
+ }
- switch (p_screen) {
- case SCREEN_PRIMARY: {
- p_screen = get_primary_screen();
- } break;
- case SCREEN_OF_MAIN_WINDOW: {
- p_screen = window_get_current_screen(MAIN_WINDOW_ID);
- } break;
- default:
- break;
+ Window focus = 0;
+ int revert_to = 0;
+
+ XGetInputFocus(x11_display, &focus, &revert_to);
+ if (focus) {
+ Window focus_child = 0;
+ int x = 0, y = 0;
+ XTranslateCoordinates(x11_display, focus, DefaultRootWindow(x11_display), 0, 0, &x, &y, &focus_child);
+
+ XWindowAttributes xwa;
+ XGetWindowAttributes(x11_display, focus, &xwa);
+ Rect2i window_rect = Rect2i(x, y, xwa.width, xwa.height);
+
+ // Find which monitor has the largest overlap with the given window.
+ int screen_index = 0;
+ int max_area = 0;
+ for (int i = 0; i < count; i++) {
+ Rect2i screen_rect = _screen_get_rect(i);
+ Rect2i intersection = screen_rect.intersection(window_rect);
+ int area = intersection.get_area();
+ if (area > max_area) {
+ max_area = area;
+ screen_index = i;
+ }
+ }
+ return screen_index;
}
+ return get_primary_screen();
+}
+
+Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
+ Rect2i rect(0, 0, 0, 0);
+
+ p_screen = _get_screen_index(p_screen);
ERR_FAIL_COND_V(p_screen < 0, rect);
// Using Xinerama Extension.
@@ -834,17 +868,7 @@ int bad_window_error_handler(Display *display, XErrorEvent *error) {
Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
- switch (p_screen) {
- case SCREEN_PRIMARY: {
- p_screen = get_primary_screen();
- } break;
- case SCREEN_OF_MAIN_WINDOW: {
- p_screen = window_get_current_screen(MAIN_WINDOW_ID);
- } break;
- default:
- break;
- }
-
+ p_screen = _get_screen_index(p_screen);
int screen_count = get_screen_count();
// Check if screen is valid.
@@ -1121,18 +1145,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
int DisplayServerX11::screen_get_dpi(int p_screen) const {
_THREAD_SAFE_METHOD_
- switch (p_screen) {
- case SCREEN_PRIMARY: {
- p_screen = get_primary_screen();
- } break;
- case SCREEN_OF_MAIN_WINDOW: {
- p_screen = window_get_current_screen(MAIN_WINDOW_ID);
- } break;
- default:
- break;
- }
-
- //invalid screen?
+ p_screen = _get_screen_index(p_screen);
ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0);
//Get physical monitor Dimensions through XRandR and calculate dpi
@@ -1193,8 +1206,8 @@ Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const {
return Color();
}
-float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
- _THREAD_SAFE_METHOD_
+Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
+ ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>());
switch (p_screen) {
case SCREEN_PRIMARY: {
@@ -1207,7 +1220,95 @@ float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
break;
}
- //invalid screen?
+ ERR_FAIL_COND_V(p_screen < 0, Ref<Image>());
+
+ XImage *image = nullptr;
+
+ int event_base, error_base;
+ if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ int xin_count;
+ XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count);
+ if (p_screen < xin_count) {
+ int x_count = XScreenCount(x11_display);
+ for (int i = 0; i < x_count; i++) {
+ Window root = XRootWindow(x11_display, i);
+ XWindowAttributes root_attrs;
+ XGetWindowAttributes(x11_display, root, &root_attrs);
+ if ((xsi[p_screen].x_org >= root_attrs.x) && (xsi[p_screen].x_org <= root_attrs.x + root_attrs.width) && (xsi[p_screen].y_org >= root_attrs.y) && (xsi[p_screen].y_org <= root_attrs.y + root_attrs.height)) {
+ image = XGetImage(x11_display, root, xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height, AllPlanes, ZPixmap);
+ break;
+ }
+ }
+ } else {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Invalid screen index: " + itos(p_screen) + "(count: " + itos(xin_count) + ").");
+ }
+ } else {
+ int x_count = XScreenCount(x11_display);
+ if (p_screen < x_count) {
+ Window root = XRootWindow(x11_display, p_screen);
+
+ XWindowAttributes root_attrs;
+ XGetWindowAttributes(x11_display, root, &root_attrs);
+
+ image = XGetImage(x11_display, root, root_attrs.x, root_attrs.y, root_attrs.width, root_attrs.height, AllPlanes, ZPixmap);
+ } else {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Invalid screen index: " + itos(p_screen) + "(count: " + itos(x_count) + ").");
+ }
+ }
+
+ Ref<Image> img;
+ if (image) {
+ int width = image->width;
+ int height = image->height;
+
+ Vector<uint8_t> img_data;
+ img_data.resize(height * width * 4);
+
+ uint8_t *sr = (uint8_t *)image->data;
+ uint8_t *wr = (uint8_t *)img_data.ptrw();
+
+ if (image->bits_per_pixel == 24 && image->red_mask == 0xff0000 && image->green_mask == 0x00ff00 && image->blue_mask == 0x0000ff) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 3 + 2];
+ wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 3 + 1];
+ wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 3 + 0];
+ wr[(y * width + x) * 4 + 3] = 255;
+ }
+ }
+ } else if (image->bits_per_pixel == 24 && image->red_mask == 0x0000ff && image->green_mask == 0x00ff00 && image->blue_mask == 0xff0000) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 3 + 2];
+ wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 3 + 1];
+ wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 3 + 0];
+ wr[(y * width + x) * 4 + 3] = 255;
+ }
+ }
+ } else if (image->bits_per_pixel == 32 && image->red_mask == 0xff0000 && image->green_mask == 0x00ff00 && image->blue_mask == 0x0000ff) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 4 + 2];
+ wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 4 + 1];
+ wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 4 + 0];
+ wr[(y * width + x) * 4 + 3] = 255;
+ }
+ }
+ } else {
+ XFree(image);
+ ERR_FAIL_V_MSG(Ref<Image>(), vformat("XImage with RGB mask %x %x %x and depth %d is not supported.", (uint64_t)image->red_mask, (uint64_t)image->green_mask, (uint64_t)image->blue_mask, (int64_t)image->bits_per_pixel));
+ }
+ img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
+ XFree(image);
+ }
+
+ return img;
+}
+
+float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ p_screen = _get_screen_index(p_screen);
ERR_FAIL_INDEX_V(p_screen, get_screen_count(), SCREEN_REFRESH_RATE_FALLBACK);
//Use xrandr to get screen refresh rate.
@@ -1354,7 +1455,7 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
}
XDestroyWindow(x11_display, wd.x11_xim_window);
#ifdef XKB_ENABLED
- if (xkb_loaded) {
+ if (xkb_loaded_v05p) {
if (wd.xkb_state) {
xkb_compose_state_unref(wd.xkb_state);
wd.xkb_state = nullptr;
@@ -1570,18 +1671,7 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- switch (p_screen) {
- case SCREEN_PRIMARY: {
- p_screen = get_primary_screen();
- } break;
- case SCREEN_OF_MAIN_WINDOW: {
- p_screen = window_get_current_screen(MAIN_WINDOW_ID);
- } break;
- default:
- break;
- }
-
- // Check if screen is valid
+ p_screen = _get_screen_index(p_screen);
ERR_FAIL_INDEX(p_screen, get_screen_count());
if (window_get_current_screen(p_window) == p_screen) {
@@ -2654,16 +2744,12 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
}
Ref<Texture2D> texture = p_cursor;
+ ERR_FAIL_COND(!texture.is_valid());
Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
Size2i texture_size;
Rect2i atlas_rect;
- if (texture.is_valid()) {
- image = texture->get_image();
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
+ if (atlas_texture.is_valid()) {
texture = atlas_texture->get_atlas();
atlas_rect.size.width = texture->get_width();
@@ -2673,19 +2759,23 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
texture_size.width = atlas_texture->get_region().size.x;
texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
+ } else {
texture_size.width = texture->get_width();
texture_size.height = texture->get_height();
}
- ERR_FAIL_COND(!texture.is_valid());
ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_image();
+ Ref<Image> image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
+ if (image->is_compressed()) {
+ image = image->duplicate(true);
+ Error err = image->decompress();
+ ERR_FAIL_COND_MSG(err != OK, "Couldn't decompress VRAM-compressed custom mouse cursor image. Switch to a lossless compression mode in the Import dock.");
+ }
// Create the cursor structure
XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
@@ -2734,8 +2824,8 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
XcursorImageDestroy(cursor_image);
} else {
// Reset to default system cursor
- if (img[p_shape]) {
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
+ if (cursor_img[p_shape]) {
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_img[p_shape]);
}
CursorShape c = current_cursor;
@@ -2987,7 +3077,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
String keysym;
#ifdef XKB_ENABLED
- if (xkb_loaded) {
+ if (xkb_loaded_v08p) {
KeySym keysym_unicode_nm = 0; // keysym used to find unicode
XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_unicode_nm, nullptr);
keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(keysym_unicode_nm)));
@@ -3082,7 +3172,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
} while (status == XBufferOverflow);
#endif
#ifdef XKB_ENABLED
- } else if (xkeyevent->type == KeyPress && wd.xkb_state && xkb_loaded) {
+ } else if (xkeyevent->type == KeyPress && wd.xkb_state && xkb_loaded_v05p) {
xkb_compose_feed_result res = xkb_compose_state_feed(wd.xkb_state, keysym_unicode);
if (res == XKB_COMPOSE_FEED_ACCEPTED) {
if (xkb_compose_state_get_status(wd.xkb_state) == XKB_COMPOSE_COMPOSED) {
@@ -3507,8 +3597,17 @@ void DisplayServerX11::_window_changed(XEvent *event) {
// Query display server about a possible new window state.
wd.fullscreen = _window_fullscreen_check(window_id);
- wd.minimized = _window_minimize_check(window_id);
- wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE");
+ wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE") && !wd.fullscreen;
+ wd.minimized = _window_minimize_check(window_id) && !wd.fullscreen && !wd.maximized;
+
+ // Readjusting the window position if the window is being reparented by the window manager for decoration
+ Window root, parent, *children;
+ unsigned int nchildren;
+ if (XQueryTree(x11_display, wd.x11_window, &root, &parent, &children, &nchildren) && wd.parent != parent) {
+ wd.parent = parent;
+ window_set_position(wd.position, window_id);
+ }
+ XFree(children);
{
//the position in xconfigure is not useful here, obtain it manually
@@ -4994,12 +5093,13 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
{
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes);
+ wd.parent = RootWindow(x11_display, visualInfo.screen);
XSetWindowAttributes window_attributes_ime = {};
window_attributes_ime.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWEventMask, &window_attributes_ime);
#ifdef XKB_ENABLED
- if (dead_tbl && xkb_loaded) {
+ if (dead_tbl && xkb_loaded_v05p) {
wd.xkb_state = xkb_compose_state_new(dead_tbl, XKB_COMPOSE_STATE_NO_FLAGS);
}
#endif
@@ -5283,9 +5383,16 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
ERR_FAIL_MSG("Can't load XCursor dynamically.");
}
#ifdef XKB_ENABLED
- xkb_loaded = (initialize_xkbcommon(dylibloader_verbose) == 0);
- if (!xkb_context_new || !xkb_compose_table_new_from_locale || !xkb_compose_table_unref || !xkb_context_unref || !xkb_compose_state_feed || !xkb_compose_state_unref || !xkb_compose_state_new || !xkb_compose_state_get_status || !xkb_compose_state_get_utf8 || !xkb_keysym_to_utf32 || !xkb_keysym_to_upper) {
- xkb_loaded = false;
+ bool xkb_loaded = (initialize_xkbcommon(dylibloader_verbose) == 0);
+ xkb_loaded_v05p = xkb_loaded;
+ if (!xkb_context_new || !xkb_compose_table_new_from_locale || !xkb_compose_table_unref || !xkb_context_unref || !xkb_compose_state_feed || !xkb_compose_state_unref || !xkb_compose_state_new || !xkb_compose_state_get_status || !xkb_compose_state_get_utf8) {
+ xkb_loaded_v05p = false;
+ print_verbose("Detected XKBcommon library version older than 0.5, dead key composition and Unicode key labels disabled.");
+ }
+ xkb_loaded_v08p = xkb_loaded;
+ if (!xkb_keysym_to_utf32 || !xkb_keysym_to_upper) {
+ xkb_loaded_v08p = false;
+ print_verbose("Detected XKBcommon library version older than 0.8, Unicode key labels disabled.");
}
#endif
if (initialize_xext(dylibloader_verbose) != 0) {
@@ -5341,9 +5448,18 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = OK;
+ {
+ if (!XcursorImageCreate || !XcursorImageLoadCursor || !XcursorImageDestroy || !XcursorGetDefaultSize || !XcursorGetTheme || !XcursorLibraryLoadImage) {
+ // There's no API to check version, check if functions are available instead.
+ ERR_PRINT("Unsupported Xcursor library version.");
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;
- img[i] = nullptr;
+ cursor_img[i] = nullptr;
}
XInitThreads(); //always use threads
@@ -5357,6 +5473,71 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
+ {
+ int version_major = 0;
+ int version_minor = 0;
+ int rc = XShapeQueryVersion(x11_display, &version_major, &version_minor);
+ print_verbose(vformat("Xshape %d.%d detected.", version_major, version_minor));
+ if (rc != 1 || version_major < 1) {
+ ERR_PRINT("Unsupported Xshape library version.");
+ r_error = ERR_UNAVAILABLE;
+ XCloseDisplay(x11_display);
+ return;
+ }
+ }
+
+ {
+ int version_major = 0;
+ int version_minor = 0;
+ int rc = XineramaQueryVersion(x11_display, &version_major, &version_minor);
+ print_verbose(vformat("Xinerama %d.%d detected.", version_major, version_minor));
+ if (rc != 1 || version_major < 1) {
+ ERR_PRINT("Unsupported Xinerama library version.");
+ r_error = ERR_UNAVAILABLE;
+ XCloseDisplay(x11_display);
+ return;
+ }
+ }
+
+ {
+ int version_major = 0;
+ int version_minor = 0;
+ int rc = XRRQueryVersion(x11_display, &version_major, &version_minor);
+ print_verbose(vformat("Xrandr %d.%d detected.", version_major, version_minor));
+ if (rc != 1 || (version_major == 1 && version_minor < 3) || (version_major < 1)) {
+ ERR_PRINT("Unsupported Xrandr library version.");
+ r_error = ERR_UNAVAILABLE;
+ XCloseDisplay(x11_display);
+ return;
+ }
+ }
+
+ {
+ int version_major = 0;
+ int version_minor = 0;
+ int rc = XRenderQueryVersion(x11_display, &version_major, &version_minor);
+ print_verbose(vformat("Xrender %d.%d detected.", version_major, version_minor));
+ if (rc != 1 || (version_major == 0 && version_minor < 11)) {
+ ERR_PRINT("Unsupported Xrender library version.");
+ r_error = ERR_UNAVAILABLE;
+ XCloseDisplay(x11_display);
+ return;
+ }
+ }
+
+ {
+ int version_major = 2; // Report 2.2 as supported by engine, but should work with 2.1 or 2.0 library as well.
+ int version_minor = 2;
+ int rc = XIQueryVersion(x11_display, &version_major, &version_minor);
+ print_verbose(vformat("Xinput %d.%d detected.", version_major, version_minor));
+ if (rc != Success || (version_major < 2)) {
+ ERR_PRINT("Unsupported Xinput2 library version.");
+ r_error = ERR_UNAVAILABLE;
+ XCloseDisplay(x11_display);
+ return;
+ }
+ }
+
char *modifiers = nullptr;
Bool xkb_dar = False;
XAutoRepeatOn(x11_display);
@@ -5471,7 +5652,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
#ifdef SPEECHD_ENABLED
// Init TTS
- tts = memnew(TTS_Linux);
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ tts = memnew(TTS_Linux);
+ }
#endif
//!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -5635,8 +5819,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
"question_arrow"
};
- img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
- if (!img[i]) {
+ cursor_img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
+ if (!cursor_img[i]) {
const char *fallback = nullptr;
switch (i) {
@@ -5674,7 +5858,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
fallback = "bd_double_arrow";
break;
case CURSOR_MOVE:
- img[i] = img[CURSOR_DRAG];
+ cursor_img[i] = cursor_img[CURSOR_DRAG];
break;
case CURSOR_VSPLIT:
fallback = "sb_v_double_arrow";
@@ -5687,11 +5871,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
break;
}
if (fallback != nullptr) {
- img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
+ cursor_img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
}
}
- if (img[i]) {
- cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
+ if (cursor_img[i]) {
+ cursors[i] = XcursorImageLoadCursor(x11_display, cursor_img[i]);
} else {
print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
}
@@ -5779,7 +5963,7 @@ DisplayServerX11::~DisplayServerX11() {
}
XDestroyWindow(x11_display, wd.x11_xim_window);
#ifdef XKB_ENABLED
- if (xkb_loaded) {
+ if (xkb_loaded_v05p) {
if (wd.xkb_state) {
xkb_compose_state_unref(wd.xkb_state);
wd.xkb_state = nullptr;
@@ -5791,7 +5975,7 @@ DisplayServerX11::~DisplayServerX11() {
}
#ifdef XKB_ENABLED
- if (xkb_loaded) {
+ if (xkb_loaded_v05p) {
if (dead_tbl) {
xkb_compose_table_unref(dead_tbl);
}
@@ -5830,8 +6014,8 @@ DisplayServerX11::~DisplayServerX11() {
if (cursors[i] != None) {
XFreeCursor(x11_display, cursors[i]);
}
- if (img[i] != nullptr) {
- XcursorImageDestroy(img[i]);
+ if (cursor_img[i] != nullptr) {
+ XcursorImageDestroy(cursor_img[i]);
}
}
@@ -5845,7 +6029,9 @@ DisplayServerX11::~DisplayServerX11() {
}
#ifdef SPEECHD_ENABLED
- memdelete(tts);
+ if (tts) {
+ memdelete(tts);
+ }
#endif
#ifdef DBUS_ENABLED
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index c98409253e..fd3a5dccfa 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -119,8 +119,7 @@ typedef struct _xrr_monitor_info {
#undef CursorShape
class DisplayServerX11 : public DisplayServer {
- //No need to register, it's platform-specific and nothing is added
- //GDCLASS(DisplayServerX11, DisplayServer)
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
_THREAD_SAFE_CLASS_
@@ -160,6 +159,7 @@ class DisplayServerX11 : public DisplayServer {
struct WindowData {
Window x11_window;
Window x11_xim_window;
+ Window parent;
::XIC xic;
bool ime_active = false;
bool ime_in_progress = false;
@@ -211,7 +211,8 @@ class DisplayServerX11 : public DisplayServer {
String im_text;
#ifdef XKB_ENABLED
- bool xkb_loaded = false;
+ bool xkb_loaded_v05p = false;
+ bool xkb_loaded_v08p = false;
xkb_context *xkb_ctx = nullptr;
xkb_compose_table *dead_tbl = nullptr;
#endif
@@ -305,7 +306,7 @@ class DisplayServerX11 : public DisplayServer {
const char *cursor_theme = nullptr;
int cursor_size = 0;
- XcursorImage *img[CURSOR_MAX];
+ XcursorImage *cursor_img[CURSOR_MAX];
Cursor cursors[CURSOR_MAX];
Cursor null_cursor;
CursorShape current_cursor = CURSOR_ARROW;
@@ -404,12 +405,14 @@ public:
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
+ virtual int get_keyboard_focus_screen() const override;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Color screen_get_pixel(const Point2i &p_position) const override;
+ virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
#if defined(DBUS_ENABLED)
virtual void screen_set_keep_on(bool p_enable) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index ee767dfa80..1e579c9f01 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -44,6 +44,9 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+// To prevent shadowing warnings
+#undef glXCreateContextAttribsARB
+
struct GLManager_X11_Private {
::GLXContext glx_context;
};