summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/SCsub2
-rw-r--r--platform/linuxbsd/context_gl_x11.cpp14
-rw-r--r--platform/linuxbsd/context_gl_x11.h5
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp21
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.h5
-rw-r--r--platform/linuxbsd/detect.py38
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp13
-rw-r--r--platform/linuxbsd/detect_prime_x11.h4
-rw-r--r--platform/linuxbsd/display_server_x11.cpp1827
-rw-r--r--platform/linuxbsd/display_server_x11.h85
-rw-r--r--platform/linuxbsd/export/export.cpp7
-rw-r--r--platform/linuxbsd/export/export.h4
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp11
-rw-r--r--platform/linuxbsd/joypad_linux.cpp91
-rw-r--r--platform/linuxbsd/joypad_linux.h17
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp65
-rw-r--r--platform/linuxbsd/key_mapping_x11.h6
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp252
-rw-r--r--platform/linuxbsd/os_linuxbsd.h43
-rw-r--r--platform/linuxbsd/platform_config.h18
-rw-r--r--platform/linuxbsd/vulkan_context_x11.cpp5
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h5
22 files changed, 1542 insertions, 996 deletions
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index ae75a75830..6e43ffcedb 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -18,5 +18,5 @@ common_x11 = [
prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_x11)
-if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
+if env["debug_symbols"] == "yes" and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd))
diff --git a/platform/linuxbsd/context_gl_x11.cpp b/platform/linuxbsd/context_gl_x11.cpp
index 308d68521a..1f92370ab7 100644
--- a/platform/linuxbsd/context_gl_x11.cpp
+++ b/platform/linuxbsd/context_gl_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -46,22 +46,18 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
struct ContextGL_X11_Private {
-
::GLXContext glx_context;
};
void ContextGL_X11::release_current() {
-
glXMakeCurrent(x11_display, None, nullptr);
}
void ContextGL_X11::make_current() {
-
glXMakeCurrent(x11_display, x11_window, p->glx_context);
}
void ContextGL_X11::swap_buffers() {
-
glXSwapBuffers(x11_display, x11_window);
}
@@ -85,7 +81,6 @@ static void set_class_hint(Display *p_display, Window p_window) {
}
Error ContextGL_X11::initialize() {
-
//const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
@@ -167,7 +162,6 @@ Error ContextGL_X11::initialize() {
switch (context_type) {
case GLES_2_0_COMPATIBLE: {
-
p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, 0, true);
ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
} break;
@@ -192,7 +186,6 @@ Error ContextGL_X11::initialize() {
}
int ContextGL_X11::get_window_width() {
-
XWindowAttributes xwa;
XGetWindowAttributes(x11_display, x11_window, &xwa);
@@ -234,14 +227,13 @@ void ContextGL_X11::set_use_vsync(bool p_use) {
return;
use_vsync = p_use;
}
-bool ContextGL_X11::is_using_vsync() const {
+bool ContextGL_X11::is_using_vsync() const {
return use_vsync;
}
ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) :
x11_window(p_x11_window) {
-
default_video_mode = p_default_video_mode;
x11_display = p_x11_display;
diff --git a/platform/linuxbsd/context_gl_x11.h b/platform/linuxbsd/context_gl_x11.h
index 2c0643c95a..d089886f4d 100644
--- a/platform/linuxbsd/context_gl_x11.h
+++ b/platform/linuxbsd/context_gl_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,7 +42,6 @@
struct ContextGL_X11_Private;
class ContextGL_X11 {
-
public:
enum ContextType {
GLES_2_0_COMPATIBLE,
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index dbdb15918e..ea0222cb19 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,8 @@
#include "crash_handler_linuxbsd.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "main/main.h"
#ifdef DEBUG_ENABLED
@@ -63,10 +63,11 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
- if (OS::get_singleton()->get_main_loop())
+ if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+ }
- fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
for (size_t i = 1; i < size; i++) {
@@ -85,8 +86,9 @@ static void handle_crash(int sig) {
snprintf(fname, 1024, "%s", demangled);
}
- if (demangled)
+ if (demangled) {
free(demangled);
+ }
}
}
@@ -102,12 +104,12 @@ static void handle_crash(int sig) {
// Try to get the file/line number using addr2line
int ret;
- Error err = OS::get_singleton()->execute(String("addr2line"), args, true, nullptr, &output, &ret);
+ Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
if (err == OK) {
output.erase(output.length() - 1, 1);
}
- fprintf(stderr, "[%ld] %s (%ls)\n", (long int)i, fname, output.c_str());
+ fprintf(stderr, "[%ld] %s (%s)\n", (long int)i, fname, output.utf8().get_data());
}
free(strings);
@@ -128,8 +130,9 @@ CrashHandler::~CrashHandler() {
}
void CrashHandler::disable() {
- if (disabled)
+ if (disabled) {
return;
+ }
#ifdef CRASH_HANDLER_ENABLED
signal(SIGSEGV, nullptr);
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.h b/platform/linuxbsd/crash_handler_linuxbsd.h
index 94b4649690..a3dae0cc22 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.h
+++ b/platform/linuxbsd/crash_handler_linuxbsd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,6 @@
#define CRASH_HANDLER_X11_H
class CrashHandler {
-
bool disabled;
public:
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 07fa06bc06..a819731328 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -12,7 +12,6 @@ def get_name():
def can_build():
-
if os.name != "posix" or sys.platform == "darwin":
return False
@@ -35,6 +34,11 @@ def can_build():
print("xinerama not found.. x11 disabled.")
return False
+ x11_error = os.system("pkg-config xext --modversion > /dev/null ")
+ if x11_error:
+ print("xext not found.. x11 disabled.")
+ return False
+
x11_error = os.system("pkg-config xrandr --modversion > /dev/null ")
if x11_error:
print("xrandr not found.. x11 disabled.")
@@ -68,7 +72,7 @@ def get_opts():
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", False),
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("touch", "Enable touch events", True),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
@@ -76,12 +80,10 @@ def get_opts():
def get_flags():
-
return []
def configure(env):
-
## Build type
if env["target"] == "release":
@@ -91,8 +93,6 @@ def configure(env):
env.Prepend(CCFLAGS=["-Os"])
if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "release_debug":
@@ -103,13 +103,11 @@ def configure(env):
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["-rdynamic"])
## Architecture
@@ -128,8 +126,6 @@ def configure(env):
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
- env["LINK"] = "clang++"
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
if env["use_lld"]:
@@ -194,6 +190,7 @@ def configure(env):
env.ParseConfig("pkg-config x11 --cflags --libs")
env.ParseConfig("pkg-config xcursor --cflags --libs")
env.ParseConfig("pkg-config xinerama --cflags --libs")
+ env.ParseConfig("pkg-config xext --cflags --libs")
env.ParseConfig("pkg-config xrandr --cflags --libs")
env.ParseConfig("pkg-config xrender --cflags --libs")
env.ParseConfig("pkg-config xi --cflags --libs")
@@ -205,14 +202,31 @@ def configure(env):
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
- if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
+ if (
+ env["builtin_freetype"]
+ or env["builtin_libpng"]
+ or env["builtin_zlib"]
+ or env["builtin_graphite"]
+ or env["builtin_harfbuzz"]
+ ):
env["builtin_freetype"] = True
env["builtin_libpng"] = True
env["builtin_zlib"] = True
+ env["builtin_graphite"] = True
+ env["builtin_harfbuzz"] = True
if not env["builtin_freetype"]:
env.ParseConfig("pkg-config freetype2 --cflags --libs")
+ if not env["builtin_graphite"]:
+ env.ParseConfig("pkg-config graphite2 --cflags --libs")
+
+ if not env["builtin_icu"]:
+ env.ParseConfig("pkg-config icu-uc --cflags --libs")
+
+ if not env["builtin_harfbuzz"]:
+ env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
+
if not env["builtin_libpng"]:
env.ParseConfig("pkg-config libpng16 --cflags --libs")
diff --git a/platform/linuxbsd/detect_prime_x11.cpp b/platform/linuxbsd/detect_prime_x11.cpp
index 1bec65ff04..0f8d108dff 100644
--- a/platform/linuxbsd/detect_prime_x11.cpp
+++ b/platform/linuxbsd/detect_prime_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,8 +33,8 @@
#include "detect_prime.h"
-#include "core/print_string.h"
-#include "core/ustring.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
#include <stdlib.h>
@@ -56,7 +56,7 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX
struct vendor {
const char *glxvendor;
- int priority;
+ int priority = 0;
};
vendor vendormap[] = {
@@ -178,7 +178,8 @@ int detect_prime() {
close(fdset[0]);
- if (i) setenv("DRI_PRIME", "1", 1);
+ if (i)
+ setenv("DRI_PRIME", "1", 1);
create_context();
const char *vendor = (const char *)glGetString(GL_VENDOR);
diff --git a/platform/linuxbsd/detect_prime_x11.h b/platform/linuxbsd/detect_prime_x11.h
index 039bdee76b..0b548b849e 100644
--- a/platform/linuxbsd/detect_prime_x11.h
+++ b/platform/linuxbsd/detect_prime_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index dd9298d667..00b90923de 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,35 +32,27 @@
#ifdef X11_ENABLED
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
#include "detect_prime_x11.h"
-
-#include "core/os/dir_access.h"
-#include "core/print_string.h"
-#include "errno.h"
#include "key_mapping_x11.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
-#endif
-
+#include "main/main.h"
#include "scene/resources/texture.h"
-#ifdef HAVE_MNTENT
-#include <mntent.h>
+#if defined(VULKAN_ENABLED)
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "X11/Xutil.h"
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/shape.h>
-#include "X11/Xatom.h"
-#include "X11/extensions/Xinerama.h"
// ICCCM
#define WM_NormalState 1L // window normal state
#define WM_IconicState 3L // window minimized
@@ -69,8 +61,6 @@
#define _NET_WM_STATE_ADD 1L // add/set property
#define _NET_WM_STATE_TOGGLE 2L // toggle property
-#include "main/main.h"
-
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -82,14 +72,9 @@
#undef KEY_TAB
#endif
-#include <X11/Xatom.h>
-
#undef CursorShape
-
#include <X11/XKBlib.h>
-#include "core/project_settings.h"
-
// 2.2 is the first release with multitouch
#define XINPUT_CLIENT_VERSION_MAJOR 2
#define XINPUT_CLIENT_VERSION_MINOR 2
@@ -100,9 +85,25 @@
#define VALUATOR_TILTX 3
#define VALUATOR_TILTY 4
+//#define DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#define DEBUG_LOG_X11(...) printf(__VA_ARGS__)
+#else
+#define DEBUG_LOG_X11(...)
+#endif
+
static const double abs_resolution_mult = 10000.0;
static const double abs_resolution_range_mult = 10.0;
+// Hints for X11 fullscreen
+struct Hints {
+ unsigned long flags = 0;
+ unsigned long functions = 0;
+ unsigned long decorations = 0;
+ long inputMode = 0;
+ unsigned long status = 0;
+};
+
bool DisplayServerX11::has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SUBWINDOWS:
@@ -127,6 +128,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
return false;
}
+
String DisplayServerX11::get_name() const {
return "X11";
}
@@ -148,8 +150,9 @@ void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
}
}
- if (program.length())
+ if (program.length()) {
break;
+ }
}
List<String> args;
@@ -188,7 +191,7 @@ void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
}
if (program.length()) {
- OS::get_singleton()->execute(program, args, true);
+ OS::get_singleton()->execute(program, args);
} else {
print_line(p_alert);
}
@@ -204,7 +207,6 @@ void DisplayServerX11::_update_real_mouse_position(const WindowData &wd) {
if (xquerypointer_result) {
if (win_x > 0 && win_y > 0 && win_x <= wd.size.width && win_y <= wd.size.height) {
-
last_mouse_pos.x = win_x;
last_mouse_pos.y = win_y;
last_mouse_pos_valid = true;
@@ -245,10 +247,12 @@ bool DisplayServerX11::_refresh_device_info() {
for (int i = 0; i < dev_count; i++) {
XIDeviceInfo *dev = &info[i];
- if (!dev->enabled)
+ if (!dev->enabled) {
continue;
- if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave))
+ }
+ if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave)) {
continue;
+ }
bool direct_touch = false;
bool absolute_mode = false;
@@ -328,23 +332,19 @@ bool DisplayServerX11::_refresh_device_info() {
}
void DisplayServerX11::_flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
-
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
-
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
+ // Block events polling while flushing motion events.
+ MutexLock mutex_lock(events_mutex);
+
+ for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) {
+ XEvent &event = polled_events[event_index];
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+ if (event_data->evtype == XI_RawMotion) {
+ XFreeEventData(x11_display, &event.xcookie);
+ polled_events.remove(event_index--);
+ continue;
}
- } else {
+ XFreeEventData(x11_display, &event.xcookie);
break;
}
}
@@ -354,20 +354,20 @@ void DisplayServerX11::_flush_mouse_motion() {
}
void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
-
_THREAD_SAFE_METHOD_
- if (p_mode == mouse_mode)
+ if (p_mode == mouse_mode) {
return;
+ }
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED)
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
XUngrabPointer(x11_display, CurrentTime);
+ }
// The only modes that show a cursor are VISIBLE and CONFINED
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
-
if (showCursor) {
XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor
} else {
@@ -377,7 +377,6 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
mouse_mode = p_mode;
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
-
//flush pending motion events
_flush_mouse_motion();
WindowData &main_window = windows[MAIN_WINDOW_ID];
@@ -404,30 +403,35 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
XFlush(x11_display);
}
+
DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const {
return mouse_mode;
}
void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
-
_THREAD_SAFE_METHOD_
if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
last_mouse_pos = p_to;
} else {
-
- /*XWindowAttributes xwa;
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- printf("%d %d\n", xwa.x, xwa.y); needed? */
-
XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
}
}
Point2i DisplayServerX11::mouse_get_position() const {
- return last_mouse_pos;
+ int root_x, root_y;
+ int win_x, win_y;
+ unsigned int mask_return;
+ Window window_returned;
+
+ Bool result = XQueryPointer(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), &window_returned,
+ &window_returned, &root_x, &root_y, &win_x, &win_y,
+ &mask_return);
+ if (result == True) {
+ return Point2i(root_x, root_y);
+ }
+ return Point2i();
}
Point2i DisplayServerX11::mouse_get_absolute_position() const {
@@ -451,64 +455,158 @@ int DisplayServerX11::mouse_get_button_state() const {
}
void DisplayServerX11::clipboard_set(const String &p_text) {
-
_THREAD_SAFE_METHOD_
- internal_clipboard = p_text;
+ {
+ // The clipboard content can be accessed while polling for events.
+ MutexLock mutex_lock(events_mutex);
+ internal_clipboard = p_text;
+ }
+
XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
-static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
+ return True;
+ } else {
+ return False;
+ }
+}
- String ret;
+Bool DisplayServerX11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) {
+ return True;
+ } else {
+ return False;
+ }
+}
- Atom type;
- Atom selection = XA_PRIMARY;
- int format, result;
- unsigned long len, bytes_left, dummy;
- unsigned char *data;
- Window Sown = XGetSelectionOwner(x11_display, p_source);
+String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const {
+ String ret;
- if (Sown == x11_window) {
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+ if (selection_owner == x11_window) {
+ return internal_clipboard;
+ }
- return p_internal_clipboard;
- };
+ if (selection_owner != None) {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
- if (Sown != None) {
+ Atom selection = XA_PRIMARY;
XConvertSelection(x11_display, p_source, target, selection,
x11_window, CurrentTime);
+
XFlush(x11_display);
- while (true) {
- XEvent event;
- XNextEvent(x11_display, &event);
- if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
- break;
- };
- };
- //
- // Do not get any data, see how much data is there
- //
+ // Blocking wait for predicate to be True and remove the event from the queue.
+ XEvent event;
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+
+ // Do not get any data, see how much data is there.
+ Atom type;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ unsigned char *data;
XGetWindowProperty(x11_display, x11_window,
selection, // Tricky..
0, 0, // offset - len
0, // Delete 0==FALSE
- AnyPropertyType, //flag
+ AnyPropertyType, // flag
&type, // return type
&format, // return format
- &len, &bytes_left, //that
+ &len, &bytes_left, // data length
&data);
- // DATA is There
- if (bytes_left > 0) {
+
+ if (data) {
+ XFree(data);
+ }
+
+ if (type == XInternAtom(x11_display, "INCR", 0)) {
+ // Data is going to be received incrementally.
+ DEBUG_LOG_X11("INCR selection started.\n");
+
+ LocalVector<uint8_t> incr_data;
+ uint32_t data_size = 0;
+ bool success = false;
+
+ // Delete INCR property to notify the owner.
+ XDeleteProperty(x11_display, x11_window, type);
+
+ // Process events from the queue.
+ bool done = false;
+ while (!done) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, nullptr)) {
+ result = XGetWindowProperty(x11_display, x11_window,
+ selection, // selection type
+ 0, LONG_MAX, // offset - len
+ True, // delete property to notify the owner
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format);
+
+ if (result == Success) {
+ if (data && (len > 0)) {
+ uint32_t prev_size = incr_data.size();
+ if (prev_size == 0) {
+ // First property contains initial data size.
+ unsigned long initial_size = *(unsigned long *)data;
+ incr_data.resize(initial_size);
+ } else {
+ // New chunk, resize to be safe and append data.
+ incr_data.resize(MAX(data_size + len, prev_size));
+ memcpy(incr_data.ptr() + data_size, data, len);
+ data_size += len;
+ }
+ } else {
+ // Last chunk, process finished.
+ done = true;
+ success = true;
+ }
+ } else {
+ printf("Failed to get selection data chunk.\n");
+ done = true;
+ }
+
+ if (data) {
+ XFree(data);
+ }
+
+ if (done) {
+ break;
+ }
+ }
+ }
+
+ if (success && (data_size > 0)) {
+ ret.parse_utf8((const char *)incr_data.ptr(), data_size);
+ }
+ } else if (bytes_left > 0) {
+ // Data is ready and can be processed all at once.
result = XGetWindowProperty(x11_display, x11_window,
selection, 0, bytes_left, 0,
AnyPropertyType, &type, &format,
&len, &dummy, &data);
+
if (result == Success) {
ret.parse_utf8((const char *)data);
- } else
- printf("FAIL\n");
+ } else {
+ printf("Failed to get selection data.\n");
+ }
+
if (data) {
XFree(data);
}
@@ -518,48 +616,102 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
return ret;
}
-static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const {
String ret;
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
if (utf8_atom != None) {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
+ ret = _clipboard_get_impl(p_source, x11_window, utf8_atom);
}
- if (ret == "") {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ if (ret.is_empty()) {
+ ret = _clipboard_get_impl(p_source, x11_window, XA_STRING);
}
return ret;
}
String DisplayServerX11::clipboard_get() const {
-
_THREAD_SAFE_METHOD_
String ret;
- ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window);
- if (ret == "") {
- ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- };
+ if (ret.is_empty()) {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
+ }
return ret;
}
-int DisplayServerX11::get_screen_count() const {
+Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
+ if (event->xany.window == *(Window *)arg) {
+ return (event->type == SelectionRequest) ||
+ (event->type == SelectionNotify);
+ } else {
+ return False;
+ }
+}
+void DisplayServerX11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+
+ if (selection_owner != x11_window) {
+ return;
+ }
+
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False);
+ Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False);
+ XConvertSelection(x11_display, clipboard_manager, save_targets, None,
+ x11_window, CurrentTime);
+
+ // Process events from the queue.
+ while (true) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) {
+ switch (ev.type) {
+ case SelectionRequest:
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ break;
+
+ case SelectionNotify: {
+ if (ev.xselection.target == save_targets) {
+ // Once SelectionNotify is received, we're done whether it succeeded or not.
+ return;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+int DisplayServerX11::get_screen_count() const {
_THREAD_SAFE_METHOD_
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) return 0;
+ if (!ext_okay) {
+ return 0;
+ }
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
XFree(xsi);
return count;
}
-Point2i DisplayServerX11::screen_get_position(int p_screen) const {
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
_THREAD_SAFE_METHOD_
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
@@ -600,11 +752,15 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) return Rect2i(0, 0, 0, 0);
+ if (!ext_okay) {
+ return Rect2i(0, 0, 0, 0);
+ }
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) return Rect2i(0, 0, 0, 0);
+ if (p_screen >= count) {
+ return Rect2i(0, 0, 0, 0);
+ }
Rect2i rect = Rect2i(xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height);
XFree(xsi);
@@ -612,7 +768,6 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
}
int DisplayServerX11::screen_get_dpi(int p_screen) const {
-
_THREAD_SAFE_METHOD_
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
@@ -649,14 +804,15 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
int height_mm = DisplayHeightMM(x11_display, p_screen);
double xdpi = (width_mm ? sc.width / (double)width_mm * 25.4 : 0);
double ydpi = (height_mm ? sc.height / (double)height_mm * 25.4 : 0);
- if (xdpi || ydpi)
+ if (xdpi || ydpi) {
return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);
+ }
//could not get dpi
return 96;
}
-bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
+bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
_THREAD_SAFE_METHOD_
#ifndef _MSC_VER
@@ -677,7 +833,6 @@ Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
}
DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
-
_THREAD_SAFE_METHOD_
WindowID id = _create_window(p_mode, p_flags, p_rect);
@@ -690,8 +845,15 @@ DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, u
return id;
}
-void DisplayServerX11::delete_sub_window(WindowID p_id) {
+void DisplayServerX11::show_window(WindowID p_id) {
+ _THREAD_SAFE_METHOD_
+
+ WindowData &wd = windows[p_id];
+
+ XMapWindow(x11_display, wd.x11_window);
+}
+void DisplayServerX11::delete_sub_window(WindowID p_id) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_id));
@@ -699,6 +861,8 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
WindowData &wd = windows[p_id];
+ DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+
while (wd.transient_children.size()) {
window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
}
@@ -716,13 +880,13 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
windows.erase(p_id);
}
void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
-
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
@@ -730,19 +894,40 @@ void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p
}
ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) const {
-
ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
const WindowData &wd = windows[p_window];
return wd.instance_id;
}
DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const {
+ WindowID found_window = INVALID_WINDOW_ID;
+ WindowID parent_window = INVALID_WINDOW_ID;
+ unsigned int focus_order = 0;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd = E->get();
- return INVALID_WINDOW_ID;
+ // Discard windows with no focus.
+ if (wd.focus_order == 0) {
+ continue;
+ }
+
+ // Find topmost window which contains the given position.
+ WindowID window_id = E->key();
+ Rect2i win_rect = Rect2i(window_get_position(window_id), window_get_size(window_id));
+ if (win_rect.has_point(p_position)) {
+ // For siblings, pick the window which was focused last.
+ if ((parent_window != wd.transient_parent) || (wd.focus_order > focus_order)) {
+ found_window = window_id;
+ parent_window = wd.transient_parent;
+ focus_order = wd.focus_order;
+ }
+ }
+ }
+
+ return found_window;
}
void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -752,11 +937,44 @@ void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window
Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
- XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ if (_net_wm_name != None && utf8_string != None) {
+ XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ }
}
-void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!windows.has(p_window));
+ const WindowData &wd = windows[p_window];
+
+ int event_base, error_base;
+ const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base);
+ if (ext_okay) {
+ Region region;
+ if (p_region.size() == 0) {
+ region = XCreateRegion();
+ XRectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = window_get_real_size(p_window).x;
+ rect.height = window_get_real_size(p_window).y;
+ XUnionRectWithRegion(&rect, region, region);
+ } else {
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size());
+ for (int i = 0; i < p_region.size(); i++) {
+ points[i].x = p_region[i].x;
+ points[i].y = p_region[i].y;
+ }
+ region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ memfree(points);
+ }
+ XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ }
+}
+
+void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -765,7 +983,6 @@ void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callab
}
void DisplayServerX11::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -774,15 +991,14 @@ void DisplayServerX11::window_set_window_event_callback(const Callable &p_callab
}
void DisplayServerX11::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
wd.input_event_callback = p_callable;
}
-void DisplayServerX11::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+void DisplayServerX11::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -791,7 +1007,6 @@ void DisplayServerX11::window_set_input_text_callback(const Callable &p_callable
}
void DisplayServerX11::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -800,7 +1015,6 @@ void DisplayServerX11::window_set_drop_files_callback(const Callable &p_callable
}
int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), -1);
@@ -814,20 +1028,23 @@ int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
for (int i = 0; i < count; i++) {
Point2i pos = screen_get_position(i);
Size2i size = screen_get_size(i);
- if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height))
+ if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height)) {
return i;
+ }
}
return 0;
}
-void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
+void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
int count = get_screen_count();
- if (p_screen >= count) return;
+ if (p_screen >= count) {
+ return;
+ }
if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
Point2i position = screen_get_position(p_screen);
@@ -843,7 +1060,6 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
}
void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(p_window == p_parent);
@@ -851,24 +1067,34 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd_window = windows[p_window];
- ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+ WindowID prev_parent = wd_window.transient_parent;
+ ERR_FAIL_COND(prev_parent == p_parent);
ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
if (p_parent == INVALID_WINDOW_ID) {
//remove transient
- ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
- ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+ ERR_FAIL_COND(prev_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(prev_parent));
- WindowData &wd_parent = windows[wd_window.transient_parent];
+ WindowData &wd_parent = windows[prev_parent];
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
XSetTransientForHint(x11_display, wd_window.x11_window, None);
+
+ // Set focus to parent sub window to avoid losing all focus with nested menus.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd_window.menu_type && !wd_window.no_focus) {
+ if (!wd_parent.no_focus) {
+ XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ }
} else {
ERR_FAIL_COND(!windows.has(p_parent));
- ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ ERR_FAIL_COND_MSG(prev_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
WindowData &wd_parent = windows[p_parent];
wd_window.transient_parent = p_parent;
@@ -878,8 +1104,47 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
}
}
-Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
+// Helper method. Assumes that the window id has already been checked and exists.
+void DisplayServerX11::_update_size_hints(WindowID p_window) {
+ WindowData &wd = windows[p_window];
+ WindowMode window_mode = window_get_mode(p_window);
+ XSizeHints *xsh = XAllocSizeHints();
+
+ // Always set the position and size hints - they should be synchronized with the actual values after the window is mapped anyway
+ xsh->flags |= PPosition | PSize;
+ xsh->x = wd.position.x;
+ xsh->y = wd.position.y;
+ xsh->width = wd.size.width;
+ xsh->height = wd.size.height;
+
+ if (window_mode == WINDOW_MODE_FULLSCREEN) {
+ // Do not set any other hints to prevent the window manager from ignoring the fullscreen flags
+ } else if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ // If resizing is disabled, use the forced size
+ xsh->flags |= PMinSize | PMaxSize;
+ xsh->min_width = wd.size.x;
+ xsh->max_width = wd.size.x;
+ xsh->min_height = wd.size.y;
+ xsh->max_height = wd.size.y;
+ } else {
+ // Otherwise, just respect min_size and max_size
+ if (wd.min_size != Size2i()) {
+ xsh->flags |= PMinSize;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
+ }
+ if (wd.max_size != Size2i()) {
+ xsh->flags |= PMaxSize;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
+ }
+ }
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
+}
+
+Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
@@ -889,7 +1154,6 @@ Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
}
void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -922,7 +1186,6 @@ void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p
}
void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -934,28 +1197,11 @@ void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_windo
}
wd.max_size = p_size;
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
- XFlush(x11_display);
- }
+ _update_size_hints(p_window);
+ XFlush(x11_display);
}
-Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
+Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
@@ -965,7 +1211,6 @@ Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
}
void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -977,28 +1222,11 @@ void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_windo
}
wd.min_size = p_size;
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
- XFlush(x11_display);
- }
+ _update_size_hints(p_window);
+ XFlush(x11_display);
}
-Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
+Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
@@ -1008,7 +1236,6 @@ Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
}
void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1019,8 +1246,9 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
WindowData &wd = windows[p_window];
- if (wd.size.width == size.width && wd.size.height == size.height)
+ if (wd.size.width == size.width && wd.size.height == size.height) {
return;
+ }
XWindowAttributes xwa;
XSync(x11_display, False);
@@ -1028,57 +1256,36 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
int old_w = xwa.width;
int old_h = xwa.height;
- // If window resizable is disabled we need to update the attributes first
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ // Update our videomode width and height
+ wd.size = size;
+
+ // Update the size hints first to make sure the window size can be set
+ _update_size_hints(p_window);
// Resize the window
XResizeWindow(x11_display, wd.x11_window, size.x, size.y);
- // Update our videomode width and height
- wd.size = size;
-
for (int timeout = 0; timeout < 50; ++timeout) {
XSync(x11_display, False);
XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
- if (old_w != xwa.width || old_h != xwa.height)
+ if (old_w != xwa.width || old_h != xwa.height) {
break;
+ }
usleep(10000);
}
}
-Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
+Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
const WindowData &wd = windows[p_window];
return wd.size;
}
-Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
+Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
@@ -1108,19 +1315,23 @@ Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
return Size2i(w, h);
}
-bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
-
- _THREAD_SAFE_METHOD_
-
+// Just a helper to reduce code duplication in `window_is_maximize_allowed`
+// and `_set_wm_maximized`.
+bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_atom_name) const {
ERR_FAIL_COND_V(!windows.has(p_window), false);
const WindowData &wd = windows[p_window];
- Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
+ Atom property = XInternAtom(x11_display, p_atom_name, False);
Atom type;
int format;
unsigned long len;
unsigned long remaining;
unsigned char *data = nullptr;
+ bool retval = false;
+
+ if (property == None) {
+ return false;
+ }
int result = XGetWindowProperty(
x11_display,
@@ -1144,22 +1355,31 @@ bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
bool found_wm_act_max_vert = false;
for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_act_max_horz)
+ if (atoms[i] == wm_act_max_horz) {
found_wm_act_max_horz = true;
- if (atoms[i] == wm_act_max_vert)
+ }
+ if (atoms[i] == wm_act_max_vert) {
found_wm_act_max_vert = true;
+ }
- if (found_wm_act_max_horz || found_wm_act_max_vert)
- return true;
+ if (found_wm_act_max_horz || found_wm_act_max_vert) {
+ retval = true;
+ break;
+ }
}
- XFree(atoms);
+
+ XFree(data);
}
- return false;
+ return retval;
}
-void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
+bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+ return _window_maximize_check(p_window, "_NET_WM_ALLOWED_ACTIONS");
+}
+void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
@@ -1191,7 +1411,6 @@ void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
}
void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
-
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
@@ -1202,17 +1421,14 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
- if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ if (p_enabled) {
// Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
- XSizeHints *xsh;
-
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ _update_size_hints(p_window);
}
// Using EWMH -- Extended Window Manager Hints
@@ -1234,36 +1450,15 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
// set bypass compositor hint
Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
unsigned long compositing_disable_on = p_enabled ? 1 : 0;
- XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ if (bypass_compositor != None) {
+ XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ }
XFlush(x11_display);
if (!p_enabled) {
// Reset the non-resizable flags if we un-set these before.
- Size2i size = window_get_size(p_window);
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ _update_size_hints(p_window);
// put back or remove decorations according to the last set borderless state
Hints hints;
@@ -1271,12 +1466,13 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
}
void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1322,13 +1518,13 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_FULLSCREEN: {
//Remove full-screen
+ wd.fullscreen = false;
+
_set_wm_fullscreen(p_window, false);
//un-maximize required for always on top
bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window);
- wd.fullscreen = false;
-
window_set_position(wd.last_position_before_fs, p_window);
if (on_top) {
@@ -1337,7 +1533,6 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_MAXIMIZED: {
-
_set_wm_maximized(p_window, false);
} break;
}
@@ -1375,22 +1570,21 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_FULLSCREEN: {
wd.last_position_before_fs = wd.position;
+
if (window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window)) {
_set_wm_maximized(p_window, true);
}
- _set_wm_fullscreen(p_window, true);
+
wd.fullscreen = true;
+ _set_wm_fullscreen(p_window, true);
} break;
case WINDOW_MODE_MAXIMIZED: {
-
_set_wm_maximized(p_window, true);
-
} break;
}
}
DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) const {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
@@ -1399,60 +1593,20 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
if (wd.fullscreen) { //if fullscreen, it's not in another mode
return WINDOW_MODE_FULLSCREEN;
}
- { //test maximized
- // Using EWMH -- Extended Window Manager Hints
- Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = nullptr;
- bool retval = false;
- int result = XGetWindowProperty(
- x11_display,
- wd.x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
-
- if (result == Success && data) {
- Atom *atoms = (Atom *)data;
- Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
- Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
- bool found_wm_max_horz = false;
- bool found_wm_max_vert = false;
-
- for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_max_horz)
- found_wm_max_horz = true;
- if (atoms[i] == wm_max_vert)
- found_wm_max_vert = true;
-
- if (found_wm_max_horz && found_wm_max_vert) {
- retval = true;
- break;
- }
- }
-
- XFree(data);
- }
-
- if (retval) {
- return WINDOW_MODE_MAXIMIZED;
- }
+ // Test maximized.
+ // Using EWMH -- Extended Window Manager Hints
+ if (_window_maximize_check(p_window, "_NET_WM_STATE")) {
+ return WINDOW_MODE_MAXIMIZED;
}
- { // test minimzed
+ { // Test minimized.
// Using ICCCM -- Inter-Client Communication Conventions Manual
Atom property = XInternAtom(x11_display, "WM_STATE", True);
+ if (property == None) {
+ return WINDOW_MODE_WINDOWED;
+ }
+
Atom type;
int format;
unsigned long len;
@@ -1483,13 +1637,12 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
}
}
- // all other discarded, return windowed.
+ // All other discarded, return windowed.
return WINDOW_MODE_WINDOWED;
}
void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1497,46 +1650,21 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
switch (p_flag) {
case WINDOW_FLAG_RESIZE_DISABLED: {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (p_enabled) {
- Size2i size = window_get_size(p_window);
-
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
-
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
wd.resize_disabled = p_enabled;
- XFlush(x11_display);
+ _update_size_hints(p_window);
+ XFlush(x11_display);
} break;
case WINDOW_FLAG_BORDERLESS: {
-
Hints hints;
Atom property;
hints.flags = 2;
hints.decorations = p_enabled ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
// Preserve window size
window_set_size(window_get_size(p_window), p_window);
@@ -1544,7 +1672,6 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
wd.borderless = p_enabled;
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
-
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
if (p_enabled && wd.fullscreen) {
_set_wm_maximized(p_window, true);
@@ -1577,8 +1704,8 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
}
}
}
-bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), false);
@@ -1586,15 +1713,12 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
switch (p_flag) {
case WINDOW_FLAG_RESIZE_DISABLED: {
-
return wd.resize_disabled;
} break;
case WINDOW_FLAG_BORDERLESS: {
-
bool borderless = wd.borderless;
Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
if (prop != None) {
-
Atom type;
int format;
unsigned long len;
@@ -1612,7 +1736,6 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
return borderless;
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
-
return wd.on_top;
} break;
case WINDOW_FLAG_TRANSPARENT: {
@@ -1626,7 +1749,6 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
}
void DisplayServerX11::window_request_attention(WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1653,7 +1775,6 @@ void DisplayServerX11::window_request_attention(WindowID p_window) {
}
void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1675,12 +1796,11 @@ void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
}
bool DisplayServerX11::window_can_draw(WindowID p_window) const {
-
//this seems to be all that is provided by X11
return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
}
-bool DisplayServerX11::can_any_window_draw() const {
+bool DisplayServerX11::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -1693,7 +1813,6 @@ bool DisplayServerX11::can_any_window_draw() const {
}
void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_window) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1701,18 +1820,25 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
wd.im_active = p_active;
- if (!wd.xic)
+ if (!wd.xic) {
return;
+ }
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
if (p_active) {
- XSetICFocus(wd.xic);
+ {
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
window_set_ime_position(wd.im_position, p_window);
} else {
+ MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
}
}
-void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -1720,19 +1846,26 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
wd.im_position = p_pos;
- if (!wd.xic)
+ if (!wd.xic) {
return;
+ }
::XPoint spot;
spot.x = short(p_pos.x);
spot.y = short(p_pos.y);
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, nullptr);
- XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+
+ {
+ // Block events polling during this call
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+ }
+
XFree(preedit_attr);
}
void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
-
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
@@ -1755,15 +1888,15 @@ void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
current_cursor = p_shape;
}
+
DisplayServerX11::CursorShape DisplayServerX11::cursor_get_shape() const {
return current_cursor;
}
-void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
if (p_cursor.is_valid()) {
-
Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
if (cursor_c) {
@@ -1868,62 +2001,132 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
-DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- XkbDescRec *xkbdesc = XkbAllocKeyboard();
- ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_layout_count() const {
+ int _group_count = 0;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return _group_count;
+}
- XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
- ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
- ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_current_layout() const {
+ XkbStateRec state;
+ XkbGetState(x11_display, XkbUseCoreKbd, &state);
+ return state.group;
+}
- char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
- ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+void DisplayServerX11::keyboard_set_current_layout(int p_index) {
+ ERR_FAIL_INDEX(p_index, keyboard_get_layout_count());
+ XkbLockGroup(x11_display, XkbUseCoreKbd, p_index);
+}
- Vector<String> info = String(layout).split("+");
- ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+String DisplayServerX11::keyboard_get_layout_language(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
- if (info[1].find("colemak") != -1) {
- return LATIN_KEYBOARD_COLEMAK;
- } else if (info[1].find("qwertz") != -1) {
- return LATIN_KEYBOARD_QWERTZ;
- } else if (info[1].find("azerty") != -1) {
- return LATIN_KEYBOARD_AZERTY;
- } else if (info[1].find("qzerty") != -1) {
- return LATIN_KEYBOARD_QZERTY;
- } else if (info[1].find("dvorak") != -1) {
- return LATIN_KEYBOARD_DVORAK;
- } else if (info[1].find("neo") != -1) {
- return LATIN_KEYBOARD_NEO;
+ Atom names = kbd->names->symbols;
+ if (names != None) {
+ char *name = XGetAtomName(x11_display, names);
+ Vector<String> info = String(name).split("+");
+ if (p_index >= 0 && p_index < _group_count) {
+ if (p_index + 1 < info.size()) {
+ ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols.
+ } else {
+ ret = "en"; // No symbol for layout fallback to "en".
+ }
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XFree(name);
+ }
+ XkbFreeKeyboard(kbd, 0, true);
}
+ return ret.substr(0, 2);
+}
- return LATIN_KEYBOARD_QWERTY;
+String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
+
+ if (p_index >= 0 && p_index < _group_count) {
+ char *full_name = XGetAtomName(x11_display, groups[p_index]);
+ ret.parse_utf8(full_name);
+ XFree(full_name);
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return ret;
}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
-
- Atom actual_type;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
+ Atom actual_type = None;
+ int actual_format = 0;
+ unsigned long nitems = 0;
+ unsigned long bytes_after = 0;
unsigned char *ret = nullptr;
int read_bytes = 1024;
- //Keep trying to read the property until there are no
- //bytes unread.
- do {
- if (ret != nullptr)
- XFree(ret);
+ // Keep trying to read the property until there are no bytes unread.
+ if (p_property != None) {
+ do {
+ if (ret != nullptr) {
+ XFree(ret);
+ }
- XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
- &actual_type, &actual_format, &nitems, &bytes_after,
- &ret);
+ XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ &ret);
- read_bytes *= 2;
+ read_bytes *= 2;
- } while (bytes_after != 0);
+ } while (bytes_after != 0);
+ }
Property p = { ret, actual_format, (int)nitems, actual_type };
@@ -1931,36 +2134,36 @@ DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display,
}
static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
-
static const char *target_type = "text/uri-list";
for (int i = 0; i < p_count; i++) {
-
Atom atom = p_list[i];
- if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
+ if (atom != None && String(XGetAtomName(p_display, atom)) == target_type) {
return atom;
+ }
}
return None;
}
static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
-
static const char *target_type = "text/uri-list";
- if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
+ if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type) {
return p_t1;
+ }
- if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
+ if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type) {
return p_t2;
+ }
- if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
+ if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type) {
return p_t3;
+ }
return None;
}
void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
-
state->set_shift((p_x11_state & ShiftMask));
state->set_control((p_x11_state & ControlMask));
state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
@@ -1968,7 +2171,6 @@ void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<Inp
}
unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
-
unsigned int mask = 1 << (p_x11_button - 1);
if (p_x11_type == ButtonPress) {
@@ -1980,8 +2182,7 @@ unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button
return last_button_state;
}
-void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
-
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -2025,7 +2226,6 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
}
if (xkeyevent->type == KeyPress && wd.xic) {
-
Status status;
#ifdef X_HAVE_UTF8_STRING
int utf8len = 8;
@@ -2045,8 +2245,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
- if (keycode >= 'a' && keycode <= 'z')
+ if (keycode >= 'a' && keycode <= 'z') {
keycode -= 'a' - 'A';
+ }
String tmp;
tmp.parse_utf8(utf8string, utf8bytes);
@@ -2089,7 +2290,6 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
memfree(utf8string);
#else
do {
-
int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status);
xmbstring[mnbytes] = '\0';
@@ -2119,7 +2319,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
/* Phase 4, determine if event must be filtered */
// This seems to be a side-effect of using XIM.
- // XEventFilter looks like a core X11 function,
+ // XFilterEvent looks like a core X11 function,
// but it's actually just used to see if we must
// ignore a deadkey, or events XIM determines
// must not reach the actual gui.
@@ -2153,18 +2353,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// Echo characters in X11 are a keyrelease and a keypress
// one after the other with the (almot) same timestamp.
- // To detect them, i use XPeekEvent and check that their
- // difference in time is below a threshold.
+ // To detect them, i compare to the next event in list and
+ // check that their difference in time is below a threshold.
if (xkeyevent->type != KeyPress) {
-
p_echo = false;
// make sure there are events pending,
// so this call won't block.
- if (XPending(x11_display) > 0) {
- XEvent peek_event;
- XPeekEvent(x11_display, &peek_event);
+ if (p_event_index + 1 < p_events.size()) {
+ XEvent &peek_event = p_events[p_event_index + 1];
// I'm using a threshold of 5 msecs,
// since sometimes there seems to be a little
@@ -2179,9 +2377,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
KeySym rk;
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr);
if (rk == keysym_keycode) {
- XEvent event;
- XNextEvent(x11_display, &event); //erase next event
- _handle_key_event(p_window, (XKeyEvent *)&event, true);
+ // Consume to next event.
+ ++p_event_index;
+ _handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, true);
return; //ignore current, echo next
}
}
@@ -2196,8 +2394,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_pressed(keypress);
- if (keycode >= 'a' && keycode <= 'z')
+ if (keycode >= 'a' && keycode <= 'z') {
keycode -= 'a' - 'A';
+ }
k->set_keycode(keycode);
k->set_physical_keycode(physical_keycode);
@@ -2214,14 +2413,15 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
//don't set mod state if modifier keys are released by themselves
//else event.is_action() will not work correctly here
if (!k->is_pressed()) {
- if (k->get_keycode() == KEY_SHIFT)
+ if (k->get_keycode() == KEY_SHIFT) {
k->set_shift(false);
- else if (k->get_keycode() == KEY_CONTROL)
+ } else if (k->get_keycode() == KEY_CONTROL) {
k->set_control(false);
- else if (k->get_keycode() == KEY_ALT)
+ } else if (k->get_keycode() == KEY_ALT) {
k->set_alt(false);
- else if (k->get_keycode() == KEY_META)
+ } else if (k->get_keycode() == KEY_META) {
k->set_metakey(false);
+ }
}
bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_keycode());
@@ -2234,9 +2434,120 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->accumulate_input_event(k);
}
+Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
+ if (p_target == XInternAtom(x11_display, "TARGETS", 0)) {
+ // Request to list all supported targets.
+ Atom data[9];
+ data[0] = XInternAtom(x11_display, "TARGETS", 0);
+ data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0);
+ data[2] = XInternAtom(x11_display, "MULTIPLE", 0);
+ data[3] = XInternAtom(x11_display, "UTF8_STRING", 0);
+ data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
+ data[5] = XInternAtom(x11_display, "TEXT", 0);
+ data[6] = XA_STRING;
+ data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
+ data[8] = XInternAtom(x11_display, "text/plain", 0);
+
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (unsigned char *)&data,
+ sizeof(data) / sizeof(data[0]));
+ 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.
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ XInternAtom(x11_display, "NULL", False),
+ 32,
+ PropModeReplace,
+ nullptr,
+ 0);
+ return p_property;
+ } else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
+ p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
+ p_target == XInternAtom(x11_display, "TEXT", 0) ||
+ p_target == XA_STRING ||
+ p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
+ p_target == XInternAtom(x11_display, "text/plain", 0)) {
+ // Directly using internal clipboard because we know our window
+ // is the owner during a selection request.
+ CharString clip = internal_clipboard.utf8();
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ p_target,
+ 8,
+ PropModeReplace,
+ (unsigned char *)clip.get_data(),
+ clip.length());
+ return p_property;
+ } else {
+ char *target_name = XGetAtomName(x11_display, p_target);
+ printf("Target '%s' not supported.\n", target_name);
+ if (target_name) {
+ XFree(target_name);
+ }
+ return None;
+ }
+}
+
+void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const {
+ XEvent respond;
+ if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) {
+ // Request for multiple target conversions at once.
+ Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False);
+ respond.xselection.property = None;
+
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) {
+ if ((len >= 2) && data) {
+ Atom *targets = (Atom *)data;
+ for (uint64_t i = 0; i < len; i += 2) {
+ Atom target = targets[i];
+ Atom &property = targets[i + 1];
+ property = _process_selection_request_target(target, p_event->requestor, property);
+ }
+
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ atom_pair,
+ 32,
+ PropModeReplace,
+ (unsigned char *)targets,
+ len);
+
+ respond.xselection.property = p_event->property;
+ }
+ XFree(data);
+ }
+ } else {
+ // Request for target conversion.
+ respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property);
+ }
+
+ respond.xselection.type = SelectionNotify;
+ respond.xselection.display = p_event->display;
+ respond.xselection.requestor = p_event->requestor;
+ respond.xselection.selection = p_event->selection;
+ respond.xselection.target = p_event->target;
+ respond.xselection.time = p_event->time;
+
+ XSendEvent(x11_display, p_event->requestor, True, NoEventMask, &respond);
+ XFlush(x11_display);
+}
+
void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
-
WARN_PRINT("Input method stopped");
DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
ds->xim = nullptr;
@@ -2247,7 +2558,6 @@ void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
}
void DisplayServerX11::_window_changed(XEvent *event) {
-
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
@@ -2312,7 +2622,6 @@ void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
}
void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
-
Variant ev = p_event;
Variant *evp = &ev;
Variant ret;
@@ -2348,10 +2657,111 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
}
}
-void DisplayServerX11::process_events() {
+void DisplayServerX11::_poll_events_thread(void *ud) {
+ DisplayServerX11 *display_server = (DisplayServerX11 *)ud;
+ display_server->_poll_events();
+}
+
+Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) {
+ // Just accept all events.
+ return True;
+}
+
+bool DisplayServerX11::_wait_for_events() const {
+ int x11_fd = ConnectionNumber(x11_display);
+ fd_set in_fds;
+
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+
+ // Wait for next event or timeout.
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+
+ if (num_ready_fds > 0) {
+ // Event received.
+ return true;
+ } else {
+ // Error or timeout.
+ if (num_ready_fds < 0) {
+ ERR_PRINT("_wait_for_events: select error: " + itos(errno));
+ }
+ return false;
+ }
+}
+
+void DisplayServerX11::_poll_events() {
+ while (!events_thread_done) {
+ _wait_for_events();
+
+ // Process events from the queue.
+ {
+ MutexLock mutex_lock(events_mutex);
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
+ // Check if the input manager wants to process the event.
+ if (XFilterEvent(&ev, None)) {
+ // Event has been filtered by the Input Manager,
+ // it has to be ignored and a new one will be received.
+ continue;
+ }
+
+ // Handle selection request events directly in the event thread, because
+ // communication through the x server takes several events sent back and forth
+ // and we don't want to block other programs while processing only one each frame.
+ if (ev.type == SelectionRequest) {
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ continue;
+ }
+
+ polled_events.push_back(ev);
+ }
+ }
+ }
+}
+
+void DisplayServerX11::process_events() {
_THREAD_SAFE_METHOD_
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+ static int frame = 0;
+ ++frame;
+#endif
+
+ if (app_focused) {
+ //verify that one of the windows has focus, else send focus out notification
+ bool focus_found = false;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().focused) {
+ focus_found = true;
+ break;
+ }
+ }
+
+ if (!focus_found) {
+ uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_no_focus;
+
+ if (delta > 250) {
+ //X11 can go between windows and have no focus for a while, when creating them or something else. Use this as safety to avoid unnecessary focus in/outs.
+ if (OS::get_singleton()->get_main_loop()) {
+ DEBUG_LOG_X11("All focus lost, triggering NOTIFICATION_APPLICATION_FOCUS_OUT\n");
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+ app_focused = false;
+ }
+ } else {
+ time_since_no_focus = OS::get_singleton()->get_ticks_msec();
+ }
+ }
+
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
@@ -2361,9 +2771,16 @@ void DisplayServerX11::process_events() {
xi.tilt = Vector2();
xi.pressure_supported = false;
- while (XPending(x11_display) > 0) {
- XEvent event;
- XNextEvent(x11_display, &event);
+ LocalVector<XEvent> events;
+ {
+ // Block events polling while flushing events.
+ MutexLock mutex_lock(events_mutex);
+ events = polled_events;
+ polled_events.clear();
+ }
+
+ for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
+ XEvent &event = events[event_index];
WindowID window_id = MAIN_WINDOW_ID;
@@ -2375,14 +2792,8 @@ void DisplayServerX11::process_events() {
}
}
- if (XFilterEvent(&event, None)) {
- continue;
- }
-
if (XGetEventData(x11_display, &event.xcookie)) {
-
if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
-
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
int index = event_data->detail;
Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
@@ -2489,7 +2900,6 @@ void DisplayServerX11::process_events() {
//XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
case XI_TouchEnd: {
-
bool is_begin = event_data->evtype == XI_TouchBegin;
Ref<InputEventScreenTouch> st;
@@ -2500,8 +2910,9 @@ void DisplayServerX11::process_events() {
st->set_pressed(is_begin);
if (is_begin) {
- if (xi.state.has(index)) // Defensive
+ if (xi.state.has(index)) { // Defensive
break;
+ }
xi.state[index] = pos;
if (xi.state.size() == 1) {
// X11 may send a motion event when a touch gesture begins, that would result
@@ -2510,22 +2921,21 @@ void DisplayServerX11::process_events() {
}
Input::get_singleton()->accumulate_input_event(st);
} else {
- if (!xi.state.has(index)) // Defensive
+ if (!xi.state.has(index)) { // Defensive
break;
+ }
xi.state.erase(index);
Input::get_singleton()->accumulate_input_event(st);
}
} break;
case XI_TouchUpdate: {
-
Map<int, Vector2>::Element *curr_pos_elem = xi.state.find(index);
if (!curr_pos_elem) { // Defensive
break;
}
if (curr_pos_elem->value() != pos) {
-
Ref<InputEventScreenDrag> sd;
sd.instance();
sd->set_window_id(window_id);
@@ -2544,44 +2954,84 @@ void DisplayServerX11::process_events() {
XFreeEventData(x11_display, &event.xcookie);
switch (event.type) {
- case Expose:
+ case MapNotify: {
+ DEBUG_LOG_X11("[%u] MapNotify window=%lu (%u) \n", frame, event.xmap.window, window_id);
+
+ const WindowData &wd = windows[window_id];
+
+ // 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.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ } break;
+
+ case Expose: {
+ DEBUG_LOG_X11("[%u] Expose window=%lu (%u), count='%u' \n", frame, event.xexpose.window, window_id, event.xexpose.count);
+
Main::force_redraw();
- break;
+ } break;
- case NoExpose:
- minimized = true;
- break;
+ case NoExpose: {
+ DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id);
+
+ windows[window_id].minimized = true;
+ } break;
case VisibilityNotify: {
+ DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state);
+
XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
- minimized = (visibility->state == VisibilityFullyObscured);
+ windows[window_id].minimized = (visibility->state == VisibilityFullyObscured);
} break;
+
case LeaveNotify: {
+ DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
} break;
+
case EnterNotify: {
+ DEBUG_LOG_X11("[%u] EnterNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
- case FocusIn:
- minimized = false;
- window_has_focus = true;
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
- window_focused = true;
+
+ case FocusIn: {
+ DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = true;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
+
+ // Keep track of focus order for overlapping windows.
+ static unsigned int focus_order = 0;
+ wd.focus_order = ++focus_order;
+
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_IN);
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
-
- if (mouse_mode == MOUSE_MODE_CONFINED)
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
XUndefineCursor(x11_display, E->get().x11_window);
- else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED) { // or re-hide it in captured mode
XDefineCursor(x11_display, E->get().x11_window, null_cursor);
+ }
XGrabPointer(
x11_display, E->get().x11_window, True,
@@ -2595,20 +3045,34 @@ void DisplayServerX11::process_events() {
XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
- if (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
+
+ if (!app_focused) {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+ app_focused = true;
+ }
+ } break;
+
+ case FocusOut: {
+ DEBUG_LOG_X11("[%u] FocusOut window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = false;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XUnsetICFocus(wd.xic);
}
- break;
- case FocusOut:
- window_has_focus = false;
Input::get_singleton()->release_pressed_events();
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
- window_focused = false;
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
-
//dear X11, I try, I really try, but you never work, you do whathever you want.
if (mouse_mode == MOUSE_MODE_CAPTURED) {
// Show the cursor if we're in captured mode so it doesn't look weird.
@@ -2625,7 +3089,6 @@ void DisplayServerX11::process_events() {
// Release every pointer to avoid sticky points
for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
-
Ref<InputEventScreenTouch> st;
st.instance();
st->set_index(E->key());
@@ -2635,17 +3098,25 @@ void DisplayServerX11::process_events() {
}
xi.state.clear();
#endif
- if (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
+ } break;
+
+ case ConfigureNotify: {
+ DEBUG_LOG_X11("[%u] ConfigureNotify window=%lu (%u), event=%lu, above=%lu, override_redirect=%u \n", frame, event.xconfigure.window, window_id, event.xconfigure.event, event.xconfigure.above, event.xconfigure.override_redirect);
+
+ const WindowData &wd = windows[window_id];
+
+ // Set focus when menu window is re-used.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
}
- break;
- case ConfigureNotify:
_window_changed(&event);
- break;
+ } break;
+
case ButtonPress:
case ButtonRelease: {
-
/* exit in case of a mouse button press */
last_timestamp = event.xbutton.time;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2659,24 +3130,33 @@ void DisplayServerX11::process_events() {
mb->set_window_id(window_id);
_get_key_modifier_state(event.xbutton.state, mb);
mb->set_button_index(event.xbutton.button);
- if (mb->get_button_index() == 2)
+ if (mb->get_button_index() == 2) {
mb->set_button_index(3);
- else if (mb->get_button_index() == 3)
+ } else if (mb->get_button_index() == 3) {
mb->set_button_index(2);
+ }
mb->set_button_mask(_get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
mb->set_global_position(mb->get_position());
mb->set_pressed((event.type == ButtonPress));
+ const WindowData &wd = windows[window_id];
+
if (event.type == ButtonPress) {
+ DEBUG_LOG_X11("[%u] ButtonPress window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ // Ensure window focus on click.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (!wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;
if (mb->get_button_index() == last_click_button_index) {
-
if (diff < 400 && Vector2(last_click_pos).distance_to(Vector2(event.xbutton.x, event.xbutton.y)) < 5) {
-
last_click_ms = 0;
last_click_pos = Point2i(-100, -100);
last_click_button_index = -1;
@@ -2691,13 +3171,39 @@ void DisplayServerX11::process_events() {
last_click_ms += diff;
last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
+ } else {
+ DEBUG_LOG_X11("[%u] ButtonRelease window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ if (!wd.focused) {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ WindowID window_id_other = E->key();
+ if (wd_other.focused) {
+ if (window_id_other != window_id) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child);
+
+ mb->set_window_id(window_id_other);
+ mb->set_position(Vector2(x, y));
+ mb->set_global_position(mb->get_position());
+ Input::get_singleton()->accumulate_input_event(mb);
+ }
+ break;
+ }
+ }
+ }
}
Input::get_singleton()->accumulate_input_event(mb);
} break;
case MotionNotify: {
-
// The X11 API requires filtering one-by-one through the motion
// notify events, in order to figure out which event is the one
// generated by warping the mouse pointer.
@@ -2709,11 +3215,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (XPending(x11_display) > 0) {
- XEvent tevent;
- XPeekEvent(x11_display, &tevent);
- if (tevent.type == MotionNotify) {
- XNextEvent(x11_display, &event);
+ if (event_index + 1 < events.size()) {
+ const XEvent &next_event = events[event_index + 1];
+ if (next_event.type == MotionNotify) {
+ ++event_index;
+ event = next_event;
} else {
break;
}
@@ -2741,6 +3247,9 @@ void DisplayServerX11::process_events() {
break;
}
+ const WindowData &wd = windows[window_id];
+ bool focused = wd.focused;
+
if (mouse_mode == MOUSE_MODE_CAPTURED) {
if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {
break;
@@ -2749,11 +3258,10 @@ void DisplayServerX11::process_events() {
Point2i new_center = pos;
pos = last_mouse_pos + xi.relative_motion;
center = new_center;
- do_mouse_warp = window_has_focus; // warp the cursor if we're focused in
+ do_mouse_warp = focused; // warp the cursor if we're focused in
}
if (!last_mouse_pos_valid) {
-
last_mouse_pos = pos;
last_mouse_pos_valid = true;
}
@@ -2792,14 +3300,11 @@ void DisplayServerX11::process_events() {
}
mm->set_tilt(xi.tilt);
- // Make the absolute position integral so it doesn't look _too_ weird :)
- Point2i posi(pos);
-
_get_key_modifier_state(event.xmotion.state, mm);
mm->set_button_mask(mouse_get_button_state());
- mm->set_position(posi);
- mm->set_global_position(posi);
- Input::get_singleton()->set_mouse_position(posi);
+ mm->set_position(pos);
+ mm->set_global_position(pos);
+ Input::get_singleton()->set_mouse_position(pos);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2810,85 +3315,47 @@ void DisplayServerX11::process_events() {
// Don't propagate the motion event unless we have focus
// this is so that the relative motion doesn't get messed up
// after we regain focus.
- if (window_has_focus || !mouse_mode_grab)
+ if (focused) {
Input::get_singleton()->accumulate_input_event(mm);
+ } else {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ if (wd_other.focused) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xmotion.x, event.xmotion.y, &x, &y, &child);
+
+ Point2i pos_focused(x, y);
+
+ mm->set_window_id(E->key());
+ mm->set_position(pos_focused);
+ mm->set_global_position(pos_focused);
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+ Input::get_singleton()->accumulate_input_event(mm);
+
+ break;
+ }
+ }
+ }
} break;
case KeyPress:
case KeyRelease: {
-
last_timestamp = event.xkey.time;
// key event is a little complex, so
// it will be handled in its own function.
- _handle_key_event(window_id, (XKeyEvent *)&event);
- } break;
- case SelectionRequest: {
-
- XSelectionRequestEvent *req;
- XEvent e, respond;
- e = event;
-
- req = &(e.xselectionrequest);
- if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- req->target == XInternAtom(x11_display, "TEXT", 0) ||
- req->target == XA_STRING ||
- req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- req->target == XInternAtom(x11_display, "text/plain", 0)) {
- CharString clip = clipboard_get().utf8();
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- req->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = req->property;
- } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) {
-
- Atom data[7];
- data[0] = XInternAtom(x11_display, "TARGETS", 0);
- data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
- data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
- data[3] = XInternAtom(x11_display, "TEXT", 0);
- data[4] = XA_STRING;
- data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
- data[6] = XInternAtom(x11_display, "text/plain", 0);
-
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char *)&data,
- sizeof(data) / sizeof(data[0]));
- respond.xselection.property = req->property;
-
- } else {
- char *targetname = XGetAtomName(x11_display, req->target);
- printf("No Target '%s'\n", targetname);
- if (targetname)
- XFree(targetname);
- respond.xselection.property = None;
- }
-
- respond.xselection.type = SelectionNotify;
- respond.xselection.display = req->display;
- respond.xselection.requestor = req->requestor;
- respond.xselection.selection = req->selection;
- respond.xselection.target = req->target;
- respond.xselection.time = req->time;
- XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond);
- XFlush(x11_display);
+ _handle_key_event(window_id, (XKeyEvent *)&event, events, event_index);
} break;
case SelectionNotify:
if (event.xselection.target == requested) {
-
Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0));
Vector<String> files = String((char *)p.data).split("\n", false);
@@ -2927,7 +3394,6 @@ void DisplayServerX11::process_events() {
}
else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) {
-
//File(s) have been dragged over the window, check for supported target (text/uri-list)
xdnd_version = (event.xclient.data.l[1] >> 24);
Window source = event.xclient.data.l[0];
@@ -2935,10 +3401,10 @@ void DisplayServerX11::process_events() {
if (more_than_3) {
Property p = _read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems);
- } else
+ } else {
requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
+ }
} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_position) {
-
//xdnd position event, reply with an XDND status message
//just depending on type of data for now
XClientMessageEvent m;
@@ -2957,13 +3423,13 @@ void DisplayServerX11::process_events() {
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
XFlush(x11_display);
} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) {
-
if (requested != None) {
xdnd_source_window = event.xclient.data.l[0];
- if (xdnd_version >= 1)
+ if (xdnd_version >= 1) {
XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, event.xclient.data.l[2]);
- else
+ } else {
XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, CurrentTime);
+ }
} else {
//Reply that we're not interested.
XClientMessageEvent m;
@@ -2988,7 +3454,6 @@ void DisplayServerX11::process_events() {
XFlush(x11_display);
if (do_mouse_warp) {
-
XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
0, 0, 0, 0, (int)windows[MAIN_WINDOW_ID].size.width / 2, (int)windows[MAIN_WINDOW_ID].size.height / 2);
@@ -3020,7 +3485,6 @@ void DisplayServerX11::_update_context(WindowData &wd) {
XClassHint *classHint = XAllocClassHint();
if (classHint) {
-
CharString name_str;
switch (context) {
case CONTEXT_EDITOR:
@@ -3053,8 +3517,8 @@ void DisplayServerX11::_update_context(WindowData &wd) {
XFree(classHint);
}
}
-void DisplayServerX11::set_context(Context p_context) {
+void DisplayServerX11::set_context(Context p_context) {
_THREAD_SAFE_METHOD_
context = p_context;
@@ -3063,6 +3527,7 @@ void DisplayServerX11::set_context(Context p_context) {
_update_context(E->get());
}
}
+
void DisplayServerX11::set_native_icon(const String &p_filename) {
WARN_PRINT("Native icon not supported by this display server.");
}
@@ -3136,10 +3601,13 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
pr += 4;
}
- XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ if (net_wm_icon != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ }
- if (!g_set_icon_error)
+ if (!g_set_icon_error) {
break;
+ }
}
} else {
XDeleteProperty(x11_display, wd.x11_window, net_wm_icon);
@@ -3163,12 +3631,16 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
}
DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
-
- return memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ if (r_error != OK) {
+ ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n"
+ "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
+ "Unable to initialize Video driver");
+ }
+ return ds;
}
DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
-
//Create window
long visualMask = VisualScreenMask;
@@ -3187,19 +3659,46 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- WindowID id;
+ WindowID id = window_id_counter++;
+ WindowData &wd = windows[id];
+
+ if ((id != MAIN_WINDOW_ID) && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
+ wd.menu_type = true;
+ }
+
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ wd.menu_type = true;
+ wd.no_focus = true;
+ }
+
+ // Setup for menu subwindows:
+ // - override_redirect forces the WM not to interfere with the window, to avoid delays due to
+ // handling decorations and placement.
+ // On the other hand, focus changes need to be handled manually when this is set.
+ // - save_under is a hint for the WM to keep the content of windows behind to avoid repaint.
+ if (wd.menu_type) {
+ windowAttributes.override_redirect = True;
+ windowAttributes.save_under = True;
+ valuemask |= CWOverrideRedirect | CWSaveUnder;
+ }
+
{
- WindowData wd;
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
- XMapWindow(x11_display, wd.x11_window);
+ // Enable receiving notification when the window is initialized (MapNotify)
+ // so the focus can be set at the right time.
+ if (wd.menu_type && !wd.no_focus) {
+ XSelectInput(x11_display, wd.x11_window, StructureNotifyMask);
+ }
//associate PID
// make PID known to X11
{
const long pid = OS::get_singleton()->get_process_id();
Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
- XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ if (net_wm_pid != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ }
}
long im_event_mask = 0;
@@ -3247,9 +3746,14 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
/* set the titlebar name */
XStoreName(x11_display, wd.x11_window, "Godot");
XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1);
- XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ if (xdnd_aware != None) {
+ XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ }
if (xim && xim_style) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)nullptr);
if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) {
@@ -3263,96 +3767,40 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
WARN_PRINT("XCreateIC couldn't create wd.xic");
}
} else {
-
wd.xic = nullptr;
WARN_PRINT("XCreateIC couldn't create wd.xic");
}
_update_context(wd);
- id = window_id_counter++;
-
- windows[id] = wd;
-
- {
-
- if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
-
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
-
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = p_rect.size.width;
- xsh->max_width = p_rect.size.width;
- xsh->min_height = p_rect.size.height;
- xsh->max_height = p_rect.size.height;
-
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
- }
-
- bool make_utility = false;
-
- if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = 0;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (property != None) {
XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
-
- make_utility = true;
}
- if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
- make_utility = true;
- }
-
- if (make_utility) {
- //this one seems to disable the fade animations for regular windows
- //but has the drawback that will not get focus by default, so
- //we need fo force it, unless no focus requested
-
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
+ }
+ if (wd.menu_type) {
+ // Set Utility type to disable fade animations.
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
+ if (wt_atom != None && type_atom != None) {
XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
+ } else {
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
- //but as utility appears unfocused, it needs to be forcefuly focused, unless no focus requested
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = wd.x11_window;
- xev.xclient.message_type = net_active_window;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = 1;
- xev.xclient.data.l[1] = CurrentTime;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- } else {
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
-
+ if (wt_atom != None && type_atom != None) {
XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
}
}
- if (id != MAIN_WINDOW_ID) {
-
- XSizeHints my_hints = XSizeHints();
-
- my_hints.flags = PPosition | PSize; /* I want to specify position and size */
- my_hints.x = p_rect.position.x; /* The origin and size coords I want */
- my_hints.y = p_rect.position.y;
- my_hints.width = p_rect.size.width;
- my_hints.height = p_rect.size.height;
-
- XSetNormalHints(x11_display, wd.x11_window, &my_hints);
- XMoveWindow(x11_display, wd.x11_window, p_rect.position.x, p_rect.position.y);
- }
+ _update_size_hints(id);
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
@@ -3370,8 +3818,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XFree(visualInfo);
}
- WindowData &wd = windows[id];
-
window_set_mode(p_mode, id);
//sync size
@@ -3391,14 +3837,13 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
//set cursor
if (cursors[current_cursor] != None) {
-
XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]);
}
+
return id;
}
DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
-
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -3407,7 +3852,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
mouse_mode = MOUSE_MODE_VISIBLE;
for (int i = 0; i < CURSOR_MAX; i++) {
-
cursors[i] = None;
img[i] = nullptr;
}
@@ -3442,7 +3886,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
// Try to support IME if detectable auto-repeat is supported
if (xkb_dar == True) {
-
#ifdef X_HAVE_UTF8_STRING
// Xutf8LookupString will be used later instead of XmbLookupString before
// the multibyte sequences can be converted to unicode string.
@@ -3468,7 +3911,12 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
if (!xrandr_handle) {
err = dlerror();
- fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ // For some arcane reason, NetBSD now ships libXrandr.so.3 while the rest of the world has libXrandr.so.2...
+ // In case this happens for other X11 platforms in the future, let's give it a try too before failing.
+ xrandr_handle = dlopen("libXrandr.so.3", RTLD_LAZY);
+ if (!xrandr_handle) {
+ fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ }
} else {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
@@ -3519,10 +3967,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
if (xim_styles) {
xim_style = 0L;
for (int i = 0; i < xim_styles->count_styles; i++) {
-
if (xim_styles->supported_styles[i] ==
(XIMPreeditNothing | XIMStatusNothing)) {
-
xim_style = xim_styles->supported_styles[i];
break;
}
@@ -3557,7 +4003,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
-
context_vulkan = memnew(VulkanContextX11);
if (context_vulkan->initialize() != OK) {
memdelete(context_vulkan);
@@ -3579,7 +4024,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
getenv("PRIMUS_libGL") ||
getenv("PRIMUS_LOAD_GLOBAL") ||
getenv("BUMBLEBEE_SOCKET")) {
-
print_verbose("Optirun/primusrun detected. Skipping GPU detection");
use_prime = 0;
}
@@ -3591,7 +4035,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
for (int i = 0; i < libraries.size(); ++i) {
if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
FileAccess::exists(libraries[i] + "/libGL.so")) {
-
print_verbose("Custom libGL override detected. Skipping GPU detection");
use_prime = 0;
}
@@ -3636,26 +4079,30 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
WindowID main_window = _create_window(p_mode, p_flags, Rect2i(window_position, p_resolution));
+ if (main_window == INVALID_WINDOW_ID) {
+ r_error = ERR_CANT_CREATE;
+ return;
+ }
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, main_window);
}
}
+ show_window(main_window);
//create RenderingDevice if used
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
-
//temporary
rendering_device_vulkan = memnew(RenderingDeviceVulkan);
rendering_device_vulkan->initialize(context_vulkan);
- RasterizerRD::make_current();
+ RendererCompositorRD::make_current();
}
#endif
/*
- rendering_server = memnew(RenderingServerRaster);
+ rendering_server = memnew(RenderingServerDefault);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
rendering_server = memnew(RenderingServerWrapMT(rendering_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
@@ -3691,7 +4138,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
for (int i = 0; i < CURSOR_MAX; i++) {
-
static const char *cursor_file[] = {
"left_ptr",
"xterm",
@@ -3808,8 +4254,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
requested = None;
- window_has_focus = true; // Set focus to true at init
-
/*if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}*/
@@ -3822,11 +4266,23 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
+ events_thread = Thread::create(_poll_events_thread, this);
+
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
r_error = OK;
}
+
DisplayServerX11::~DisplayServerX11() {
+ // Send owned clipboard data to clipboard manager before exit.
+ Window x11_main_window = windows[MAIN_WINDOW_ID].x11_window;
+ _clipboard_transfer_ownership(XA_PRIMARY, x11_main_window);
+ _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window);
+
+ events_thread_done = true;
+ Thread::wait_to_finish(events_thread);
+ memdelete(events_thread);
+ events_thread = nullptr;
//destroy all windows
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -3836,35 +4292,40 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
- if (E->get().xic) {
- XDestroyIC(E->get().xic);
+ WindowData &wd = E->get();
+ if (wd.xic) {
+ XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
- XUnmapWindow(x11_display, E->get().x11_window);
- XDestroyWindow(x11_display, E->get().x11_window);
+ XUnmapWindow(x11_display, wd.x11_window);
+ XDestroyWindow(x11_display, wd.x11_window);
}
//destroy drivers
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
-
if (rendering_device_vulkan) {
rendering_device_vulkan->finalize();
memdelete(rendering_device_vulkan);
}
- if (context_vulkan)
+ if (context_vulkan) {
memdelete(context_vulkan);
+ }
}
#endif
- if (xrandr_handle)
+ if (xrandr_handle) {
dlclose(xrandr_handle);
+ }
for (int i = 0; i < CURSOR_MAX; i++) {
- if (cursors[i] != None)
+ if (cursors[i] != None) {
XFreeCursor(x11_display, cursors[i]);
- if (img[i] != nullptr)
+ }
+ if (img[i] != nullptr) {
XcursorImageDestroy(img[i]);
+ }
};
if (xim) {
@@ -3872,12 +4333,12 @@ DisplayServerX11::~DisplayServerX11() {
}
XCloseDisplay(x11_display);
- if (xmbstring)
+ if (xmbstring) {
memfree(xmbstring);
+ }
}
void DisplayServerX11::register_x11_driver() {
-
register_create_function("x11", create_func, get_rendering_drivers_func);
}
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index b5ea71f72a..7784ba82b5 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,14 +36,14 @@
#include "servers/display_server.h"
#include "core/input/input.h"
-
+#include "core/templates/local_vector.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"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#if defined(OPENGL_ENABLED)
@@ -61,27 +61,18 @@
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
-// Hints for X11 fullscreen
-typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long status;
-} Hints;
-
typedef struct _xrr_monitor_info {
Atom name;
- Bool primary;
- Bool automatic;
- int noutput;
- int x;
- int y;
- int width;
- int height;
- int mwidth;
- int mheight;
- RROutput *outputs;
+ Bool primary = false;
+ Bool automatic = false;
+ int noutput = 0;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ int mwidth = 0;
+ int mheight = 0;
+ RROutput *outputs = nullptr;
} xrr_monitor_info;
#undef CursorShape
@@ -133,6 +124,9 @@ class DisplayServerX11 : public DisplayServer {
ObjectID instance_id;
+ bool menu_type = false;
+ bool no_focus = false;
+
//better to guess on the fly, given WM can change it
//WindowMode mode;
bool fullscreen = false; //OS can't exit from this mode
@@ -140,6 +134,10 @@ class DisplayServerX11 : public DisplayServer {
bool borderless = false;
bool resize_disabled = false;
Vector2i last_position_before_fs;
+ bool focused = false;
+ bool minimized = false;
+
+ unsigned int focus_order = 0;
};
Map<WindowID, WindowData> windows;
@@ -165,6 +163,8 @@ class DisplayServerX11 : public DisplayServer {
uint64_t last_click_ms;
int last_click_button_index;
uint32_t last_button_state;
+ bool app_focused = false;
+ uint64_t time_since_no_focus = 0;
struct {
int opcode;
@@ -194,10 +194,17 @@ class DisplayServerX11 : public DisplayServer {
MouseMode mouse_mode;
Point2i center;
- void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
+
+ Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const;
+ void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
- bool minimized;
- bool window_has_focus;
+ String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
+ String _clipboard_get(Atom p_source, Window x11_window) const;
+ void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
+
+ //bool minimized;
+ //bool window_has_focus;
bool do_mouse_warp;
const char *cursor_theme;
@@ -211,7 +218,7 @@ class DisplayServerX11 : public DisplayServer {
bool layered_window;
String rendering_driver;
- bool window_focused;
+ //bool window_focused;
//void set_wm_border(bool p_enabled);
void set_wm_fullscreen(bool p_enabled);
void set_wm_above(bool p_enabled);
@@ -231,6 +238,8 @@ class DisplayServerX11 : public DisplayServer {
static Property _read_property(Display *p_display, Window p_window, Atom p_property);
void _update_real_mouse_position(const WindowData &wd);
+ bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const;
+ void _update_size_hints(WindowID p_window);
void _set_wm_fullscreen(WindowID p_window, bool p_enabled);
void _set_wm_maximized(WindowID p_window, bool p_enabled);
@@ -242,6 +251,19 @@ class DisplayServerX11 : public DisplayServer {
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ mutable Mutex events_mutex;
+ Thread *events_thread = nullptr;
+ bool events_thread_done = false;
+ LocalVector<XEvent> polled_events;
+ static void _poll_events_thread(void *ud);
+ bool _wait_for_events() const;
+ void _poll_events();
+
+ static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg);
+
protected:
void _window_changed(XEvent *event);
@@ -272,6 +294,7 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const;
virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
@@ -280,6 +303,8 @@ public:
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);
+
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
@@ -327,7 +352,11 @@ public:
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
virtual void process_events();
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 53e3ce8f85..cb95068314 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,7 +38,6 @@
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
void register_linuxbsd_exporter() {
-
Ref<EditorExportPlatformPC> platform;
platform.instance();
@@ -62,7 +61,6 @@ void register_linuxbsd_exporter() {
}
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
-
// Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data
FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
@@ -139,7 +137,6 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,
bool found = false;
for (int i = 0; i < num_sections; ++i) {
-
int64_t section_header_pos = section_table_pos + i * section_header_size;
f->seek(section_header_pos);
diff --git a/platform/linuxbsd/export/export.h b/platform/linuxbsd/export/export.h
index 5ee81f485e..61e96aa2f6 100644
--- a/platform/linuxbsd/export/export.h
+++ b/platform/linuxbsd/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 710ba3ca40..6f5c46b59c 100644
--- a/platform/linuxbsd/godot_linuxbsd.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,11 +37,13 @@
#include "os_linuxbsd.h"
int main(int argc, char *argv[]) {
-
OS_LinuxBSD os;
setlocale(LC_CTYPE, "");
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
+
char *cwd = (char *)malloc(PATH_MAX);
ERR_FAIL_COND_V(!cwd, ERR_OUT_OF_MEMORY);
char *ret = getcwd(cwd, PATH_MAX);
@@ -52,8 +54,9 @@ int main(int argc, char *argv[]) {
return 255;
}
- if (Main::start())
+ if (Main::start()) {
os.run(); // it is actually the OS that decides how to run
+ }
Main::cleanup();
if (ret) { // Previous getcwd was successful
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5ceea788e0..291ca49585 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -49,17 +49,7 @@
static const char *ignore_str = "/dev/input/js";
#endif
-JoypadLinux::Joypad::Joypad() {
- fd = -1;
- dpad = 0;
- devpath = "";
- for (int i = 0; i < MAX_ABS; i++) {
- abs_info[i] = nullptr;
- }
-}
-
JoypadLinux::Joypad::~Joypad() {
-
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
memdelete(abs_info[i]);
@@ -94,7 +84,6 @@ JoypadLinux::~JoypadLinux() {
}
void JoypadLinux::joy_thread_func(void *p_user) {
-
if (p_user) {
JoypadLinux *joy = (JoypadLinux *)p_user;
joy->run_joypad_thread();
@@ -115,7 +104,6 @@ void JoypadLinux::run_joypad_thread() {
#ifdef UDEV_ENABLED
void JoypadLinux::enumerate_joypads(udev *p_udev) {
-
udev_enumerate *enumerate;
udev_list_entry *devices, *dev_list_entry;
udev_device *dev;
@@ -126,13 +114,11 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) {
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
-
const char *path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(p_udev, path);
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
-
String devnode_str = devnode;
if (devnode_str.find(ignore_str) == -1) {
MutexLock lock(joy_mutex);
@@ -145,7 +131,6 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) {
}
void JoypadLinux::monitor_joypads(udev *p_udev) {
-
udev_device *dev = nullptr;
udev_monitor *mon = udev_monitor_new_from_netlink(p_udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "input", nullptr);
@@ -153,7 +138,6 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
int fd = udev_monitor_get_fd(mon);
while (!exit_udev) {
-
fd_set fds;
struct timeval tv;
int ret;
@@ -172,15 +156,12 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
dev = udev_monitor_receive_device(mon);
if (dev && udev_device_get_devnode(dev) != 0) {
-
MutexLock lock(joy_mutex);
String action = udev_device_get_action(dev);
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
-
String devnode_str = devnode;
if (devnode_str.find(ignore_str) == -1) {
-
if (action == "add")
open_joypad(devnode);
else if (String(action) == "remove")
@@ -198,7 +179,6 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
#endif
void JoypadLinux::monitor_joypads() {
-
while (!exit_udev) {
{
MutexLock lock(joy_mutex);
@@ -216,9 +196,7 @@ void JoypadLinux::monitor_joypads() {
}
int JoypadLinux::get_joy_from_path(String p_path) const {
-
for (int i = 0; i < JOYPADS_MAX; i++) {
-
if (joypads[i].devpath == p_path) {
return i;
}
@@ -229,17 +207,16 @@ int JoypadLinux::get_joy_from_path(String p_path) const {
void JoypadLinux::close_joypad(int p_id) {
if (p_id == -1) {
for (int i = 0; i < JOYPADS_MAX; i++) {
-
close_joypad(i);
};
return;
- } else if (p_id < 0)
+ } else if (p_id < 0) {
return;
+ }
Joypad &joy = joypads[p_id];
if (joy.fd != -1) {
-
close(joy.fd);
joy.fd = -1;
attached_devices.remove(attached_devices.find(joy.devpath));
@@ -248,7 +225,6 @@ void JoypadLinux::close_joypad(int p_id) {
}
static String _hex_str(uint8_t p_byte) {
-
static const char *dict = "0123456789abcdef";
char ret[3];
ret[2] = 0;
@@ -260,7 +236,6 @@ static String _hex_str(uint8_t p_byte) {
}
void JoypadLinux::setup_joypad_properties(int p_id) {
-
Joypad *joy = &joypads[p_id];
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
@@ -274,16 +249,12 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
return;
}
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
-
if (test_bit(i, keybit)) {
-
joy->key_map[i] = num_buttons++;
}
}
for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
-
if (test_bit(i, keybit)) {
-
joy->key_map[i] = num_buttons++;
}
}
@@ -294,7 +265,6 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
continue;
}
if (test_bit(i, absbit)) {
-
joy->abs_map[i] = num_axes++;
joy->abs_info[i] = memnew(input_absinfo);
if (ioctl(joy->fd, EVIOCGABS(i), joy->abs_info[i]) < 0) {
@@ -315,11 +285,9 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
}
void JoypadLinux::open_joypad(const char *p_path) {
-
int joy_num = input->get_unused_joy_id();
int fd = open(p_path, O_RDWR | O_NONBLOCK);
if (fd != -1 && joy_num != -1) {
-
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
@@ -334,16 +302,9 @@ void JoypadLinux::open_joypad(const char *p_path) {
return;
}
- //check if the device supports basic gamepad events, prevents certain keyboards from
- //being detected as joypads
+ // Check if the device supports basic gamepad events
if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
- (test_bit(ABS_X, absbit) || test_bit(ABS_Y, absbit) || test_bit(ABS_HAT0X, absbit) ||
- test_bit(ABS_GAS, absbit) || test_bit(ABS_RUDDER, absbit)) &&
- (test_bit(BTN_A, keybit) || test_bit(BTN_THUMBL, keybit) ||
- test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_1, keybit))) &&
- !(test_bit(EV_ABS, evbit) &&
- test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) &&
- test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit))) {
+ test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
close(fd);
return;
}
@@ -369,7 +330,6 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joy_num);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
-
uint16_t vendor = BSWAP16(inpid.vendor);
uint16_t product = BSWAP16(inpid.product);
uint16_t version = BSWAP16(inpid.version);
@@ -380,7 +340,6 @@ void JoypadLinux::open_joypad(const char *p_path) {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
for (int i = 0; i < uidlen; i++) {
-
uidname = uidname + _hex_str(name[i]);
}
uidname += "00";
@@ -437,7 +396,6 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
}
Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
-
int min = p_abs->minimum;
int max = p_abs->maximum;
Input::JoyAxis jx;
@@ -457,13 +415,13 @@ Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value
}
void JoypadLinux::process_joypads() {
-
if (joy_mutex.try_lock() != OK) {
return;
}
for (int i = 0; i < JOYPADS_MAX; i++) {
-
- if (joypads[i].fd == -1) continue;
+ if (joypads[i].fd == -1) {
+ continue;
+ }
input_event events[32];
Joypad *joy = &joypads[i];
@@ -473,13 +431,13 @@ void JoypadLinux::process_joypads() {
while ((len = read(joy->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
for (int j = 0; j < len; j++) {
-
input_event &ev = events[j];
// ev may be tainted and out of MAX_KEY range, which will cause
// joy->key_map[ev.code] to crash
- if (ev.code >= MAX_KEY)
+ if (ev.code >= MAX_KEY) {
return;
+ }
switch (ev.type) {
case EV_KEY:
@@ -491,31 +449,36 @@ void JoypadLinux::process_joypads() {
switch (ev.code) {
case ABS_HAT0X:
if (ev.value != 0) {
- if (ev.value < 0)
- joy->dpad |= Input::HAT_MASK_LEFT;
- else
- joy->dpad |= Input::HAT_MASK_RIGHT;
- } else
+ if (ev.value < 0) {
+ joy->dpad = (joy->dpad | Input::HAT_MASK_LEFT) & ~Input::HAT_MASK_RIGHT;
+ } else {
+ joy->dpad = (joy->dpad | Input::HAT_MASK_RIGHT) & ~Input::HAT_MASK_LEFT;
+ }
+ } else {
joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT);
+ }
input->joy_hat(i, joy->dpad);
break;
case ABS_HAT0Y:
if (ev.value != 0) {
- if (ev.value < 0)
- joy->dpad |= Input::HAT_MASK_UP;
- else
- joy->dpad |= Input::HAT_MASK_DOWN;
- } else
+ if (ev.value < 0) {
+ joy->dpad = (joy->dpad | Input::HAT_MASK_UP) & ~Input::HAT_MASK_DOWN;
+ } else {
+ joy->dpad = (joy->dpad | Input::HAT_MASK_DOWN) & ~Input::HAT_MASK_UP;
+ }
+ } else {
joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN);
+ }
input->joy_hat(i, joy->dpad);
break;
default:
- if (ev.code >= MAX_ABS)
+ if (ev.code >= MAX_ABS) {
return;
+ }
if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
Input::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
joy->curr_axis[joy->abs_map[ev.code]] = value;
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index 0d175193a5..20d30b510c 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -56,17 +56,16 @@ private:
Input::JoyAxis curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
- int dpad;
- int fd;
+ int dpad = 0;
+ int fd = -1;
String devpath;
- input_absinfo *abs_info[MAX_ABS];
+ input_absinfo *abs_info[MAX_ABS] = {};
- bool force_feedback;
- int ff_effect_id;
- uint64_t ff_effect_timestamp;
+ bool force_feedback = false;
+ int ff_effect_id = 0;
+ uint64_t ff_effect_timestamp = 0;
- Joypad();
~Joypad();
void reset();
};
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 78bd2b71a0..f9f612fa74 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,6 @@
/***** SCAN CODE CONVERSION ******/
struct _XTranslatePair {
-
KeySym keysym;
unsigned int keycode;
};
@@ -181,13 +180,11 @@ static _XTranslatePair _xkeysym_to_keycode[] = {
};
struct _TranslatePair {
-
unsigned int keysym;
unsigned int keycode;
};
static _TranslatePair _scancode_to_keycode[] = {
-
{ KEY_ESCAPE, 0x09 },
{ KEY_1, 0x0A },
{ KEY_2, 0x0B },
@@ -301,10 +298,8 @@ static _TranslatePair _scancode_to_keycode[] = {
};
unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
-
unsigned int keycode = KEY_UNKNOWN;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
if (_scancode_to_keycode[i].keycode == p_code) {
keycode = _scancode_to_keycode[i].keysym;
break;
@@ -315,34 +310,34 @@ unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
}
unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) {
-
// kinda bruteforce.. could optimize.
- if (p_keysym < 0x100) // Latin 1, maps 1-1
+ if (p_keysym < 0x100) { // Latin 1, maps 1-1
return p_keysym;
+ }
// look for special key
for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) {
-
- if (_xkeysym_to_keycode[idx].keysym == p_keysym)
+ if (_xkeysym_to_keycode[idx].keysym == p_keysym) {
return _xkeysym_to_keycode[idx].keycode;
+ }
}
return 0;
}
KeySym KeyMappingX11::get_keysym(unsigned int p_code) {
-
// kinda bruteforce.. could optimize.
- if (p_code < 0x100) // Latin 1, maps 1-1
+ if (p_code < 0x100) { // Latin 1, maps 1-1
return p_code;
+ }
// look for special key
for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) {
-
- if (_xkeysym_to_keycode[idx].keycode == p_code)
+ if (_xkeysym_to_keycode[idx].keycode == p_code) {
return _xkeysym_to_keycode[idx].keysym;
+ }
}
return 0;
@@ -353,13 +348,11 @@ KeySym KeyMappingX11::get_keysym(unsigned int p_code) {
// Tables taken from FOX toolkit
struct _XTranslateUnicodePair {
-
KeySym keysym;
unsigned int unicode;
};
enum {
-
_KEYSYM_MAX = 759
};
@@ -1125,43 +1118,46 @@ static _XTranslateUnicodePair _xkeysym_to_unicode[_KEYSYM_MAX] = {
};
unsigned int KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) {
-
/* Latin-1 */
- if (p_keysym >= 0x20 && p_keysym <= 0x7e)
+ if (p_keysym >= 0x20 && p_keysym <= 0x7e) {
return p_keysym;
- if (p_keysym >= 0xa0 && p_keysym <= 0xff)
+ }
+ if (p_keysym >= 0xa0 && p_keysym <= 0xff) {
return p_keysym;
+ }
// keypad to latin1 is easy
- if (p_keysym >= 0xffaa && p_keysym <= 0xffb9)
+ if (p_keysym >= 0xffaa && p_keysym <= 0xffb9) {
return p_keysym - 0xff80;
+ }
/* Unicode (may be present)*/
- if ((p_keysym & 0xff000000) == 0x01000000)
+ if ((p_keysym & 0xff000000) == 0x01000000) {
return p_keysym & 0x00ffffff;
+ }
int middle, low = 0, high = _KEYSYM_MAX - 1;
do {
middle = (high + low) / 2;
- if (_xkeysym_to_unicode[middle].keysym == p_keysym)
+ if (_xkeysym_to_unicode[middle].keysym == p_keysym) {
return _xkeysym_to_unicode[middle].unicode;
- if (_xkeysym_to_unicode[middle].keysym <= p_keysym)
+ }
+ if (_xkeysym_to_unicode[middle].keysym <= p_keysym) {
low = middle + 1;
- else
+ } else {
high = middle - 1;
+ }
} while (high >= low);
return 0;
}
struct _XTranslateUnicodePairReverse {
-
unsigned int unicode;
KeySym keysym;
};
enum {
-
_UNICODE_MAX = 750
};
@@ -1919,24 +1915,27 @@ static _XTranslateUnicodePairReverse _unicode_to_xkeysym[_UNICODE_MAX] = {
};
KeySym KeyMappingX11::get_keysym_from_unicode(unsigned int p_unicode) {
-
/* Latin 1 */
- if (p_unicode >= 0x20 && p_unicode <= 0x7e)
+ if (p_unicode >= 0x20 && p_unicode <= 0x7e) {
return p_unicode;
+ }
- if (p_unicode >= 0xa0 && p_unicode <= 0xff)
+ if (p_unicode >= 0xa0 && p_unicode <= 0xff) {
return p_unicode;
+ }
int middle, low = 0, high = _UNICODE_MAX - 1;
do {
middle = (high + low) / 2;
- if (_unicode_to_xkeysym[middle].keysym == p_unicode)
+ if (_unicode_to_xkeysym[middle].keysym == p_unicode) {
return _unicode_to_xkeysym[middle].keysym;
- if (_unicode_to_xkeysym[middle].keysym <= p_unicode)
+ }
+ if (_unicode_to_xkeysym[middle].keysym <= p_unicode) {
low = middle + 1;
- else
+ } else {
high = middle - 1;
+ }
} while (high >= low);
// if not found, let's hope X understands it as unicode
diff --git a/platform/linuxbsd/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 10db43bcc4..163a8e21db 100644
--- a/platform/linuxbsd/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,7 +41,7 @@
#include "core/os/keyboard.h"
class KeyMappingX11 {
- KeyMappingX11(){};
+ KeyMappingX11() {}
public:
static unsigned int get_keycode(KeySym p_keysym);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 7b76f7394b..44b3930d6c 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,8 +31,11 @@
#include "os_linuxbsd.h"
#include "core/os/dir_access.h"
-#include "core/print_string.h"
-#include "errno.h"
+#include "main/main.h"
+
+#ifdef X11_ENABLED
+#include "display_server_x11.h"
+#endif
#ifdef HAVE_MNTENT
#include <mntent.h>
@@ -48,32 +51,23 @@
#include <sys/types.h>
#include <unistd.h>
-#include "main/main.h"
-
-#ifdef X11_ENABLED
-#include "display_server_x11.h"
-#endif
-
void OS_LinuxBSD::initialize() {
-
crash_handler.initialize();
OS_Unix::initialize_core();
}
void OS_LinuxBSD::initialize_joypads() {
-
#ifdef JOYDEV_ENABLED
joypad = memnew(JoypadLinux(Input::get_singleton()));
#endif
}
String OS_LinuxBSD::get_unique_id() const {
-
static String machine_id;
- if (machine_id.empty()) {
+ if (machine_id.is_empty()) {
if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
- while (machine_id.empty() && !f->eof_reached()) {
+ while (machine_id.is_empty() && !f->eof_reached()) {
machine_id = f->get_line().strip_edges();
}
f->close();
@@ -84,9 +78,9 @@ String OS_LinuxBSD::get_unique_id() const {
}
void OS_LinuxBSD::finalize() {
-
- if (main_loop)
+ if (main_loop) {
memdelete(main_loop);
+ }
main_loop = nullptr;
#ifdef ALSAMIDI_ENABLED
@@ -94,29 +88,28 @@ void OS_LinuxBSD::finalize() {
#endif
#ifdef JOYDEV_ENABLED
- memdelete(joypad);
+ if (joypad) {
+ memdelete(joypad);
+ }
#endif
}
MainLoop *OS_LinuxBSD::get_main_loop() const {
-
return main_loop;
}
void OS_LinuxBSD::delete_main_loop() {
-
- if (main_loop)
+ if (main_loop) {
memdelete(main_loop);
+ }
main_loop = nullptr;
}
void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
-
main_loop = p_main_loop;
}
String OS_LinuxBSD::get_name() const {
-
#ifdef __linux__
return "Linux";
#elif defined(__FreeBSD__)
@@ -129,27 +122,47 @@ String OS_LinuxBSD::get_name() const {
}
Error OS_LinuxBSD::shell_open(String p_uri) {
-
Error ok;
+ int err_code;
List<String> args;
args.push_back(p_uri);
- ok = execute("xdg-open", args, false);
- if (ok == OK)
+
+ // Agnostic
+ ok = execute("xdg-open", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
return OK;
- ok = execute("gnome-open", args, false);
- if (ok == OK)
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+ // GNOME
+ args.push_front("open"); // The command is `gio open`, so we need to add it to args
+ ok = execute("gio", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
return OK;
- ok = execute("kde-open", args, false);
- return ok;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+ args.pop_front();
+ ok = execute("gvfs-open", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+ // KDE
+ ok = execute("kde-open5", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
+ return OK;
+ }
+ ok = execute("kde-open", args, nullptr, &err_code);
+ return !err_code ? ok : FAILED;
}
bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
-
return p_feature == "pc";
}
String OS_LinuxBSD::get_config_path() const {
-
if (has_environment("XDG_CONFIG_HOME")) {
return get_environment("XDG_CONFIG_HOME");
} else if (has_environment("HOME")) {
@@ -160,7 +173,6 @@ String OS_LinuxBSD::get_config_path() const {
}
String OS_LinuxBSD::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else if (has_environment("HOME")) {
@@ -171,7 +183,6 @@ String OS_LinuxBSD::get_data_path() const {
}
String OS_LinuxBSD::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("HOME")) {
@@ -182,46 +193,37 @@ String OS_LinuxBSD::get_cache_path() const {
}
String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
-
String xdgparam;
switch (p_dir) {
case SYSTEM_DIR_DESKTOP: {
-
xdgparam = "DESKTOP";
} break;
case SYSTEM_DIR_DCIM: {
-
xdgparam = "PICTURES";
} break;
case SYSTEM_DIR_DOCUMENTS: {
-
xdgparam = "DOCUMENTS";
} break;
case SYSTEM_DIR_DOWNLOADS: {
-
xdgparam = "DOWNLOAD";
} break;
case SYSTEM_DIR_MOVIES: {
-
xdgparam = "VIDEOS";
} break;
case SYSTEM_DIR_MUSIC: {
-
xdgparam = "MUSIC";
} break;
case SYSTEM_DIR_PICTURES: {
-
xdgparam = "PICTURES";
} break;
case SYSTEM_DIR_RINGTONES: {
-
xdgparam = "MUSIC";
} break;
@@ -230,20 +232,21 @@ String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
String pipe;
List<String> arg;
arg.push_back(xdgparam);
- Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe);
- if (err != OK)
+ Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, &pipe);
+ if (err != OK) {
return ".";
+ }
return pipe.strip_edges();
}
void OS_LinuxBSD::run() {
-
force_quit = false;
- if (!main_loop)
+ if (!main_loop) {
return;
+ }
- main_loop->init();
+ main_loop->initialize();
//uint64_t last_ticks=get_ticks_usec();
@@ -251,16 +254,16 @@ void OS_LinuxBSD::run() {
//uint64_t frame=0;
while (!force_quit) {
-
DisplayServer::get_singleton()->process_events(); // get rid of pending events
#ifdef JOYDEV_ENABLED
joypad->process_joypads();
#endif
- if (Main::iteration())
+ if (Main::iteration()) {
break;
+ }
};
- main_loop->finish();
+ main_loop->finalize();
}
void OS_LinuxBSD::disable_crash_handler() {
@@ -300,70 +303,151 @@ static String get_mountpoint(const String &p_path) {
}
Error OS_LinuxBSD::move_to_trash(const String &p_path) {
- String trash_can = "";
+ int err_code;
+ List<String> args;
+ args.push_back(p_path);
+ args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
+ Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ args.pop_front();
+ args.push_front("move");
+ args.push_back("trash:/"); // The command is `kioclient5 move <file_name> trash:/`.
+ result = execute("kioclient5", args, nullptr, &err_code); // For KDE based machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ args.pop_front();
+ args.pop_back();
+ result = execute("gvfs-trash", args, nullptr, &err_code); // For older Linux machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ // If the commands `kioclient5`, `gio` or `gvfs-trash` don't exist on the system we do it manually.
+ String trash_path = "";
String mnt = get_mountpoint(p_path);
- // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
+ // If there is a directory "[Mountpoint]/.Trash-[UID], use it as the trash can.
if (mnt != "") {
- String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
+ String path(mnt + "/.Trash-" + itos(getuid()));
struct stat s;
if (!stat(path.utf8().get_data(), &s)) {
- trash_can = path;
+ trash_path = path;
}
}
- // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
- if (trash_can == "") {
+ // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash" as the trash can.
+ if (trash_path == "") {
char *dhome = getenv("XDG_DATA_HOME");
if (dhome) {
- trash_can = String(dhome) + "/Trash/files";
+ trash_path = String(dhome) + "/Trash";
}
}
- // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
- if (trash_can == "") {
+ // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash" as the trash can.
+ if (trash_path == "") {
char *home = getenv("HOME");
if (home) {
- trash_can = String(home) + "/.local/share/Trash/files";
+ trash_path = String(home) + "/.local/share/Trash";
}
}
// Issue an error if none of the previous locations is appropriate for the trash can.
- if (trash_can == "") {
- ERR_PRINT("move_to_trash: Could not determine the trash can location");
- return FAILED;
- }
+ ERR_FAIL_COND_V_MSG(trash_path == "", FAILED, "Could not determine the trash can location");
// Create needed directories for decided trash can location.
- DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- Error err = dir_access->make_dir_recursive(trash_can);
- memdelete(dir_access);
+ {
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Error err = dir_access->make_dir_recursive(trash_path);
+
+ // Issue an error if trash can is not created proprely.
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"");
+ err = dir_access->make_dir_recursive(trash_path + "/files");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/files");
+ err = dir_access->make_dir_recursive(trash_path + "/info");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/info");
+ memdelete(dir_access);
+ }
- // Issue an error if trash can is not created proprely.
- if (err != OK) {
- ERR_PRINT("move_to_trash: Could not create the trash can \"" + trash_can + "\"");
- return err;
+ // The trash can is successfully created, now we check that we don't exceed our file name length limit.
+ // If the file name is too long trim it so we can add the identifying number and ".trashinfo".
+ // Assumes that the file name length limit is 255 characters.
+ String file_name = basename(p_path.utf8().get_data());
+ if (file_name.length() > 240) {
+ file_name = file_name.substr(0, file_name.length() - 15);
+ }
+
+ String dest_path = trash_path + "/files/" + file_name;
+ struct stat buff;
+ int id_number = 0;
+ String fn = file_name;
+
+ // Checks if a resource with the same name already exist in the trash can,
+ // if there is, add an identifying number to our resource's name.
+ while (stat(dest_path.utf8().get_data(), &buff) == 0) {
+ id_number++;
+
+ // Added a limit to check for identically named files already on the trash can
+ // if there are too many it could make the editor unresponsive.
+ ERR_FAIL_COND_V_MSG(id_number > 99, FAILED, "Too many identically named resources already in the trash can.");
+ fn = file_name + "." + itos(id_number);
+ dest_path = trash_path + "/files/" + fn;
+ }
+ file_name = fn;
+
+ // Generates the .trashinfo file
+ OS::Date date = OS::get_singleton()->get_date(false);
+ OS::Time time = OS::get_singleton()->get_time(false);
+ String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, date.month, date.day, time.hour, time.min);
+ timestamp = vformat("%s%02d", timestamp, time.sec); // vformat only supports up to 6 arguments.
+ String trash_info = "[Trash Info]\nPath=" + p_path.http_escape() + "\nDeletionDate=" + timestamp + "\n";
+ {
+ Error err;
+ FileAccess *file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file:" + trash_path + "/info/" + file_name + ".trashinfo");
+ file->store_string(trash_info);
+ file->close();
+
+ // Rename our resource before moving it to the trash can.
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ err = dir_access->rename(p_path, p_path.get_base_dir() + "/" + file_name);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + p_path + "\"");
+ memdelete(dir_access);
}
- // The trash can is successfully created, now move the given resource to it.
+ // Move the given resource to the trash can.
// Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
List<String> mv_args;
- mv_args.push_back(p_path);
- mv_args.push_back(trash_can);
- int retval;
- err = execute("mv", mv_args, true, nullptr, nullptr, &retval);
-
- // Issue an error if "mv" failed to move the given resource to the trash can.
- if (err != OK || retval != 0) {
- ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\"");
- return FAILED;
+ mv_args.push_back(p_path.get_base_dir() + "/" + file_name);
+ mv_args.push_back(trash_path + "/files");
+ {
+ int retval;
+ Error err = execute("mv", mv_args, nullptr, &retval);
+
+ // Issue an error if "mv" failed to move the given resource to the trash can.
+ if (err != OK || retval != 0) {
+ ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_path + "/files\"");
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ err = dir_access->rename(p_path.get_base_dir() + "/" + file_name, p_path);
+ memdelete(dir_access);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename " + p_path.get_base_dir() + "/" + file_name + " back to its original name:" + p_path);
+ return FAILED;
+ }
}
-
return OK;
}
OS_LinuxBSD::OS_LinuxBSD() {
-
main_loop = nullptr;
force_quit = false;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 391f29e8a3..b6cf93c551 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,17 +39,16 @@
#include "drivers/unix/os_unix.h"
#include "joypad_linux.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
class OS_LinuxBSD : public OS_Unix {
-
- virtual void delete_main_loop();
+ virtual void delete_main_loop() override;
bool force_quit;
#ifdef JOYDEV_ENABLED
- JoypadLinux *joypad;
+ JoypadLinux *joypad = nullptr;
#endif
#ifdef ALSA_ENABLED
@@ -69,36 +68,36 @@ class OS_LinuxBSD : public OS_Unix {
MainLoop *main_loop;
protected:
- virtual void initialize();
- virtual void finalize();
+ virtual void initialize() override;
+ virtual void finalize() override;
- virtual void initialize_joypads();
+ virtual void initialize_joypads() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
public:
- virtual String get_name() const;
+ virtual String get_name() const override;
- virtual MainLoop *get_main_loop() const;
+ virtual MainLoop *get_main_loop() const override;
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
+ virtual String get_config_path() const override;
+ virtual String get_data_path() const override;
+ virtual String get_cache_path() const override;
- virtual String get_system_dir(SystemDir p_dir) const;
+ virtual String get_system_dir(SystemDir p_dir) const override;
- virtual Error shell_open(String p_uri);
+ virtual Error shell_open(String p_uri) override;
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const override;
- virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
+ virtual void disable_crash_handler() override;
+ virtual bool is_disable_crash_handler() const override;
- virtual Error move_to_trash(const String &p_path);
+ virtual Error move_to_trash(const String &p_path) override;
OS_LinuxBSD();
};
diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h
index ac30519132..3195d08935 100644
--- a/platform/linuxbsd/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,9 +31,15 @@
#ifdef __linux__
#include <alloca.h>
#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#include <stdlib.h> // alloca
+// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms,
+// include NetBSD, use pthread_setname_np. NetBSD's version however requires
+// a different format, we handle this directly in thread_posix.
+#ifdef __NetBSD__
+#define PTHREAD_NETBSD_SET_NAME
+#else
#define PTHREAD_BSD_SET_NAME
#endif
-
-#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
+#endif
diff --git a/platform/linuxbsd/vulkan_context_x11.cpp b/platform/linuxbsd/vulkan_context_x11.cpp
index 1798a7026e..021db630e0 100644
--- a/platform/linuxbsd/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,7 +36,6 @@ const char *VulkanContextX11::_get_platform_surface_extension() const {
}
Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
-
VkXlibSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
diff --git a/platform/linuxbsd/vulkan_context_x11.h b/platform/linuxbsd/vulkan_context_x11.h
index 6e144ab2d9..26472444ad 100644
--- a/platform/linuxbsd/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,7 +35,6 @@
#include <X11/Xlib.h>
class VulkanContextX11 : public VulkanContext {
-
virtual const char *_get_platform_surface_extension() const;
public: