summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/export/export.cpp3
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp5
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp8
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp5
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp7
-rw-r--r--platform/linuxbsd/joypad_linux.cpp50
-rw-r--r--platform/linuxbsd/joypad_linux.h1
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp111
-rw-r--r--platform/linuxbsd/os_linuxbsd.h8
-rw-r--r--platform/linuxbsd/tts_linux.cpp57
-rw-r--r--platform/linuxbsd/tts_linux.h8
-rw-r--r--platform/linuxbsd/x11/detect_prime_x11.cpp17
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp181
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h39
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp13
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h10
-rw-r--r--platform/linuxbsd/x11/key_mapping_x11.h7
-rw-r--r--platform/linuxbsd/x11/vulkan_context_x11.h1
18 files changed, 385 insertions, 146 deletions
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 09f354246d..f72c079d1d 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -30,9 +30,10 @@
#include "export.h"
-#include "editor/export/editor_export.h"
#include "export_plugin.h"
+#include "editor/export/editor_export.h"
+
void register_linuxbsd_exporter_types() {
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformLinuxBSD);
}
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 9544cc761d..daa2664b09 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -30,13 +30,14 @@
#include "export_plugin.h"
+#include "../logo_svg.gen.h"
+#include "../run_icon_svg.gen.h"
+
#include "core/config/project_settings.h"
#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"
#include "modules/modules_enabled.gen.h" // For svg.
#ifdef MODULE_SVG_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index b45b7e676d..c3cb0f411d 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -35,6 +35,7 @@
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
+#include "core/variant/variant.h"
#ifdef SOWRAP_ENABLED
#include "dbus-so_wrap.h"
@@ -42,8 +43,6 @@
#include <dbus/dbus.h>
#endif
-#include "core/variant/variant.h"
-
#define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
#define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
@@ -138,6 +137,11 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
bool ver_ok = false;
int version_major = 0;
int version_minor = 0;
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
index 78f2337599..cf179b5735 100644
--- a/platform/linuxbsd/freedesktop_screensaver.cpp
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -141,6 +141,11 @@ FreeDesktopScreenSaver::FreeDesktopScreenSaver() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
bool ver_ok = false;
int version_major = 0;
int version_minor = 0;
diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 76d579dd53..d059d60b72 100644
--- a/platform/linuxbsd/godot_linuxbsd.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -28,6 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
+#include "os_linuxbsd.h"
+
+#include "main/main.h"
+
#include <limits.h>
#include <locale.h>
#include <stdlib.h>
@@ -37,9 +41,6 @@
#include <sys/resource.h>
#endif
-#include "main/main.h"
-#include "os_linuxbsd.h"
-
int main(int argc, char *argv[]) {
#if defined(SANITIZERS_ENABLED)
// Note: Set stack size to be at least 30 MB (vs 8 MB default) to avoid overflow, address sanitizer can increase stack usage up to 3 times.
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5c623b8ba2..ab79885fb4 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,28 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
+#ifdef UDEV_ENABLED
+// This function is derived from SDL:
+// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+static bool detect_sandbox() {
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+#endif // UDEV_ENABLED
+
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
#ifdef SOWRAP_ENABLED
@@ -80,17 +104,25 @@ JoypadLinux::JoypadLinux(Input *in) {
#else
int dylibloader_verbose = 0;
#endif
- use_udev = initialize_libudev(dylibloader_verbose) == 0;
- if (use_udev) {
- if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
- // There's no API to check version, check if functions are available instead.
- use_udev = false;
- print_verbose("JoypadLinux: Unsupported udev library version!");
+ 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 {
+ 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 and loaded successfully.");
+ print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. 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.");
}
#endif
#else
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index 6661abdb37..26a9908d4e 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -32,6 +32,7 @@
#define JOYPAD_LINUX_H
#ifdef JOYDEV_ENABLED
+
#include "core/input/input.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 11c81be4a9..ebdbd061d1 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -30,21 +30,18 @@
#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"
-#include "modules/modules_enabled.gen.h" // For regex.
-#ifdef MODULE_REGEX_ENABLED
-#include "modules/regex/regex.h"
-#endif
-
#ifdef X11_ENABLED
#include "x11/display_server_x11.h"
#endif
-#ifdef HAVE_MNTENT
-#include <mntent.h>
+#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
#endif
#include <dlfcn.h>
@@ -56,6 +53,10 @@
#include <sys/utsname.h>
#include <unistd.h>
+#ifdef HAVE_MNTENT
+#include <mntent.h>
+#endif
+
void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
@@ -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";
@@ -210,48 +215,54 @@ String OS_LinuxBSD::get_name() const {
}
String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const {
- static String info;
- if (info.is_empty()) {
- Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
- if (f.is_valid()) {
- while (!f->eof_reached()) {
- const String line = f->get_line();
- if (line.find(key) != -1) {
- return line.split("=")[1].strip_edges();
- }
+ Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
+ if (f.is_valid()) {
+ while (!f->eof_reached()) {
+ const String line = f->get_line();
+ if (line.find(key) != -1) {
+ String value = line.split("=")[1].strip_edges();
+ value = value.trim_prefix("\"");
+ return value.trim_suffix("\"");
}
}
}
- return info;
+ return "";
}
String OS_LinuxBSD::get_distribution_name() const {
- static String systemd_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
- if (!systemd_name.is_empty()) {
- return systemd_name;
+ static String distribution_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
+ if (!distribution_name.is_empty()) {
+ return distribution_name;
}
struct utsname uts; // returns a decent value for BSD family.
uname(&uts);
- return uts.sysname;
+ distribution_name = uts.sysname;
+ return distribution_name;
}
String OS_LinuxBSD::get_version() const {
- static String systemd_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string.
- if (!systemd_version.is_empty()) {
- return systemd_version;
+ static String release_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string.
+ if (!release_version.is_empty()) {
+ return release_version;
}
struct utsname uts; // returns a decent value for BSD family.
uname(&uts);
- return uts.version;
+ release_version = uts.version;
+ return release_version;
}
Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
- if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) {
+ if (RenderingServer::get_singleton() == nullptr) {
return Vector<String>();
}
- const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970`
- const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA`
+ static Vector<String> info;
+ if (!info.is_empty()) {
+ return info;
+ }
+
+ const String rendering_device_name = RenderingServer::get_singleton()->get_video_adapter_name(); // e.g. `NVIDIA GeForce GTX 970`
+ const String rendering_device_vendor = RenderingServer::get_singleton()->get_video_adapter_vendor(); // e.g. `NVIDIA`
const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970`
String vendor_device_id_mappings;
@@ -315,8 +326,8 @@ Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
Vector<String> class_display_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_display_device_candidates, kernel_lit, dummys);
Vector<String> class_3d_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_3d_device_candidates, kernel_lit, dummys);
- static String driver_name;
- static String driver_version;
+ String driver_name;
+ String driver_version;
// Use first valid value:
for (const String &driver : class_3d_device_drivers) {
@@ -336,7 +347,6 @@ Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
}
}
- Vector<String> info;
info.push_back(driver_name);
String modinfo;
@@ -490,6 +500,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 +1091,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;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 9423514944..007b90b82b 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -31,13 +31,14 @@
#ifndef OS_LINUXBSD_H
#define OS_LINUXBSD_H
-#include "core/input/input.h"
#include "crash_handler_linuxbsd.h"
+#include "joypad_linux.h"
+
+#include "core/input/input.h"
#include "drivers/alsa/audio_driver_alsa.h"
#include "drivers/alsamidi/midi_driver_alsamidi.h"
#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
#include "drivers/unix/os_unix.h"
-#include "joypad_linux.h"
#include "servers/audio_server.h"
#ifdef FONTCONFIG_ENABLED
@@ -96,6 +97,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 +134,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 a0cb4f5c6c..8bc83f5b9c 100644
--- a/platform/linuxbsd/tts_linux.cpp
+++ b/platform/linuxbsd/tts_linux.cpp
@@ -101,6 +101,24 @@ void TTS_Linux::speech_event_callback(size_t p_msg_id, size_t p_client_id, SPDNo
}
}
+void TTS_Linux::_load_voices() {
+ if (!voices_loaded) {
+ SPDVoice **spd_voices = spd_list_synthesis_voices(synth);
+ if (spd_voices != nullptr) {
+ SPDVoice **voices_ptr = spd_voices;
+ while (*voices_ptr != nullptr) {
+ VoiceInfo vi;
+ vi.language = String::utf8((*voices_ptr)->language);
+ vi.variant = String::utf8((*voices_ptr)->variant);
+ voices[String::utf8((*voices_ptr)->name)] = vi;
+ voices_ptr++;
+ }
+ free_spd_voices(spd_voices);
+ }
+ voices_loaded = true;
+ }
+}
+
void TTS_Linux::_speech_event(size_t p_msg_id, size_t p_client_id, int p_type) {
_THREAD_SAFE_METHOD_
@@ -123,18 +141,13 @@ void TTS_Linux::_speech_event(size_t p_msg_id, size_t p_client_id, int p_type) {
// Inject index mark after each word.
String text;
String language;
- SPDVoice **voices = spd_list_synthesis_voices(synth);
- if (voices != nullptr) {
- SPDVoice **voices_ptr = voices;
- while (*voices_ptr != nullptr) {
- if (String::utf8((*voices_ptr)->name) == message.voice) {
- language = String::utf8((*voices_ptr)->language);
- break;
- }
- voices_ptr++;
- }
- free_spd_voices(voices);
+
+ _load_voices();
+ const VoiceInfo *voice = voices.getptr(message.voice);
+ if (voice) {
+ language = voice->language;
}
+
PackedInt32Array breaks = TS->string_get_word_breaks(message.text, language);
for (int i = 0; i < breaks.size(); i += 2) {
const int start = breaks[i];
@@ -174,21 +187,17 @@ Array TTS_Linux::get_voices() const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!synth, Array());
+ const_cast<TTS_Linux *>(this)->_load_voices();
+
Array list;
- SPDVoice **voices = spd_list_synthesis_voices(synth);
- if (voices != nullptr) {
- SPDVoice **voices_ptr = voices;
- while (*voices_ptr != nullptr) {
- Dictionary voice_d;
- voice_d["name"] = String::utf8((*voices_ptr)->name);
- voice_d["id"] = String::utf8((*voices_ptr)->name);
- voice_d["language"] = String::utf8((*voices_ptr)->language) + "_" + String::utf8((*voices_ptr)->variant);
- list.push_back(voice_d);
-
- voices_ptr++;
- }
- free_spd_voices(voices);
+ for (const KeyValue<String, VoiceInfo> &E : voices) {
+ Dictionary voice_d;
+ voice_d["name"] = E.key;
+ voice_d["id"] = E.key;
+ voice_d["language"] = E.value.language + "_" + E.value.variant;
+ list.push_back(voice_d);
}
+
return list;
}
diff --git a/platform/linuxbsd/tts_linux.h b/platform/linuxbsd/tts_linux.h
index 651a64d9d6..ec68219689 100644
--- a/platform/linuxbsd/tts_linux.h
+++ b/platform/linuxbsd/tts_linux.h
@@ -55,6 +55,13 @@ class TTS_Linux : public Object {
int last_msg_id = -1;
HashMap<int, int> ids;
+ struct VoiceInfo {
+ String language;
+ String variant;
+ };
+ bool voices_loaded = false;
+ HashMap<String, VoiceInfo> voices;
+
Thread init_thread;
static void speech_init_thread_func(void *p_userdata);
@@ -64,6 +71,7 @@ class TTS_Linux : public Object {
static TTS_Linux *singleton;
protected:
+ void _load_voices();
void _speech_event(size_t p_msg_id, size_t p_client_id, int p_type);
void _speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_type, const String &p_index_mark);
diff --git a/platform/linuxbsd/x11/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp
index 3d07be1c76..2b5776ce54 100644
--- a/platform/linuxbsd/x11/detect_prime_x11.cpp
+++ b/platform/linuxbsd/x11/detect_prime_x11.cpp
@@ -28,29 +28,26 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifdef X11_ENABLED
-#if defined(GLES3_ENABLED)
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
#include "detect_prime_x11.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
-#include <stdlib.h>
-
#include "thirdparty/glad/glad/gl.h"
#include "thirdparty/glad/glad/glx.h"
#ifdef SOWRAP_ENABLED
-#include "dynwrappers/xlib-so_wrap.h"
+#include "x11/dynwrappers/xlib-so_wrap.h"
#else
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
-#include <cstring>
-
+#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -60,6 +57,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;
@@ -262,5 +262,4 @@ int detect_prime() {
return preferred;
}
-#endif
-#endif
+#endif // X11_ENABLED && GLES3_ENABLED
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index ac4fbe2068..83b6fb7628 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -32,12 +32,13 @@
#ifdef X11_ENABLED
+#include "x11/detect_prime_x11.h"
+#include "x11/key_mapping_x11.h"
+
#include "core/config/project_settings.h"
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
-#include "detect_prime_x11.h"
-#include "key_mapping_x11.h"
#include "main/main.h"
#include "scene/resources/texture.h"
@@ -58,6 +59,9 @@
#include <sys/types.h>
#include <unistd.h>
+#undef CursorShape
+#include <X11/XKBlib.h>
+
// ICCCM
#define WM_NormalState 1L // window normal state
#define WM_IconicState 3L // window minimized
@@ -65,9 +69,6 @@
#define _NET_WM_STATE_REMOVE 0L // remove/unset property
#define _NET_WM_STATE_ADD 1L // add/set property
-#undef CursorShape
-#include <X11/XKBlib.h>
-
// 2.2 is the first release with multitouch
#define XINPUT_CLIENT_VERSION_MAJOR 2
#define XINPUT_CLIENT_VERSION_MINOR 2
@@ -186,6 +187,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 +304,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();
}
@@ -854,12 +856,30 @@ Size2i DisplayServerX11::screen_get_size(int p_screen) const {
return _screen_get_rect(p_screen).size;
}
+// A Handler to avoid crashing on non-fatal X errors by default.
+//
+// The original X11 error formatter `_XPrintDefaultError` is defined here:
+// https://gitlab.freedesktop.org/xorg/lib/libx11/-/blob/e45ca7b41dcd3ace7681d6897505f85d374640f2/src/XlibInt.c#L1322
+// It is not exposed through the API, accesses X11 internals,
+// and is much more complex, so this is a less complete simplified error X11 printer.
+int default_window_error_handler(Display *display, XErrorEvent *error) {
+ static char message[1024];
+ XGetErrorText(display, error->error_code, message, sizeof(message));
+
+ ERR_PRINT(vformat("Unhandled 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));
+ return 0;
+}
+
bool g_bad_window = false;
int bad_window_error_handler(Display *display, XErrorEvent *error) {
if (error->error_code == BadWindow) {
g_bad_window = true;
} else {
- ERR_PRINT("Unhandled XServer error code: " + itos(error->error_code));
+ return default_window_error_handler(display, error);
}
return 0;
}
@@ -1205,6 +1225,105 @@ Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const {
return Color();
}
+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: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
+ }
+
+ 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_
@@ -2724,8 +2843,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;
@@ -3497,8 +3616,8 @@ 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;
@@ -5359,7 +5478,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;
- img[i] = nullptr;
+ cursor_img[i] = nullptr;
}
XInitThreads(); //always use threads
@@ -5552,7 +5671,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
//!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -5716,8 +5838,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) {
@@ -5755,7 +5877,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";
@@ -5768,11 +5890,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]));
}
@@ -5827,6 +5949,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
portal_desktop = memnew(FreeDesktopPortalDesktop);
#endif
+ XSetErrorHandler(&default_window_error_handler);
r_error = OK;
}
@@ -5911,8 +6034,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]);
}
}
@@ -5926,7 +6049,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 e8e0680c14..176a1ffb9a 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -33,7 +33,7 @@
#ifdef X11_ENABLED
-#include "servers/display_server.h"
+#include "joypad_linux.h"
#include "core/input/input.h"
#include "core/os/mutex.h"
@@ -43,27 +43,28 @@
#include "drivers/alsamidi/midi_driver_alsamidi.h"
#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
#include "drivers/unix/os_unix.h"
-#include "joypad_linux.h"
#include "servers/audio_server.h"
+#include "servers/display_server.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#if defined(SPEECHD_ENABLED)
-#include "../tts_linux.h"
+#include "tts_linux.h"
#endif
#if defined(GLES3_ENABLED)
-#include "gl_manager_x11.h"
+#include "x11/gl_manager_x11.h"
#endif
#if defined(VULKAN_ENABLED)
+#include "x11/vulkan_context_x11.h"
+
#include "drivers/vulkan/rendering_device_vulkan.h"
-#include "vulkan_context_x11.h"
#endif
#if defined(DBUS_ENABLED)
-#include "../freedesktop_portal_desktop.h"
-#include "../freedesktop_screensaver.h"
+#include "freedesktop_portal_desktop.h"
+#include "freedesktop_screensaver.h"
#endif
#include <X11/Xatom.h>
@@ -72,16 +73,16 @@
#include <X11/keysym.h>
#ifdef SOWRAP_ENABLED
-#include "dynwrappers/xlib-so_wrap.h"
+#include "x11/dynwrappers/xlib-so_wrap.h"
-#include "dynwrappers/xcursor-so_wrap.h"
-#include "dynwrappers/xext-so_wrap.h"
-#include "dynwrappers/xinerama-so_wrap.h"
-#include "dynwrappers/xinput2-so_wrap.h"
-#include "dynwrappers/xrandr-so_wrap.h"
-#include "dynwrappers/xrender-so_wrap.h"
+#include "x11/dynwrappers/xcursor-so_wrap.h"
+#include "x11/dynwrappers/xext-so_wrap.h"
+#include "x11/dynwrappers/xinerama-so_wrap.h"
+#include "x11/dynwrappers/xinput2-so_wrap.h"
+#include "x11/dynwrappers/xrandr-so_wrap.h"
+#include "x11/dynwrappers/xrender-so_wrap.h"
-#include "../xkbcommon-so_wrap.h"
+#include "xkbcommon-so_wrap.h"
#else
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
@@ -119,8 +120,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_
@@ -307,7 +307,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;
@@ -413,6 +413,7 @@ public:
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;
@@ -521,6 +522,6 @@ public:
~DisplayServerX11();
};
-#endif // X11 enabled
+#endif // X11_ENABLED
#endif // DISPLAY_SERVER_X11_H
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index ee767dfa80..f24bac5e19 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -30,20 +30,22 @@
#include "gl_manager_x11.h"
-#ifdef X11_ENABLED
-#if defined(GLES3_ENABLED)
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
+
+#include "thirdparty/glad/glad/glx.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include "thirdparty/glad/glad/glx.h"
-
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+// To prevent shadowing warnings
+#undef glXCreateContextAttribsARB
+
struct GLManager_X11_Private {
::GLXContext glx_context;
};
@@ -400,5 +402,4 @@ GLManager_X11::~GLManager_X11() {
release_current();
}
-#endif
-#endif
+#endif // X11_ENABLED && GLES3_ENABLED
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 0203dff679..59e20fec45 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -31,12 +31,11 @@
#ifndef GL_MANAGER_X11_H
#define GL_MANAGER_X11_H
-#ifdef X11_ENABLED
-
-#ifdef GLES3_ENABLED
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
#include "core/os/os.h"
#include "core/templates/local_vector.h"
+#include "servers/display_server.h"
#ifdef SOWRAP_ENABLED
#include "dynwrappers/xlib-so_wrap.h"
@@ -53,8 +52,6 @@
#include <X11/extensions/shape.h>
#endif
-#include "servers/display_server.h"
-
struct GLManager_X11_Private;
class GLManager_X11 {
@@ -136,7 +133,6 @@ public:
~GLManager_X11();
};
-#endif // GLES3_ENABLED
-#endif // X11_ENABLED
+#endif // X11_ENABLED && GLES3_ENABLED
#endif // GL_MANAGER_X11_H
diff --git a/platform/linuxbsd/x11/key_mapping_x11.h b/platform/linuxbsd/x11/key_mapping_x11.h
index 48beefff4c..ae8fd67f27 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/x11/key_mapping_x11.h
@@ -31,16 +31,17 @@
#ifndef KEY_MAPPING_X11_H
#define KEY_MAPPING_X11_H
+#include "core/os/keyboard.h"
+#include "core/templates/hash_map.h"
+
#include <X11/XF86keysym.h>
#include <X11/Xlib.h>
+
#define XK_MISCELLANY
#define XK_LATIN1
#define XK_XKB_KEYS
#include <X11/keysymdef.h>
-#include "core/os/keyboard.h"
-#include "core/templates/hash_map.h"
-
class KeyMappingX11 {
struct HashMapHasherKeys {
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
diff --git a/platform/linuxbsd/x11/vulkan_context_x11.h b/platform/linuxbsd/x11/vulkan_context_x11.h
index d093bca853..294fdc710e 100644
--- a/platform/linuxbsd/x11/vulkan_context_x11.h
+++ b/platform/linuxbsd/x11/vulkan_context_x11.h
@@ -34,6 +34,7 @@
#ifdef VULKAN_ENABLED
#include "drivers/vulkan/vulkan_context.h"
+
#include <X11/Xlib.h>
class VulkanContextX11 : public VulkanContext {