summaryrefslogtreecommitdiff
path: root/platform/linuxbsd/display_server_x11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd/display_server_x11.cpp')
-rw-r--r--platform/linuxbsd/display_server_x11.cpp1567
1 files changed, 1109 insertions, 458 deletions
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 8b0d08d1cb..2a62780410 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,17 +32,23 @@
#ifdef X11_ENABLED
-#include "core/print_string.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
#include "detect_prime_x11.h"
#include "key_mapping_x11.h"
#include "main/main.h"
#include "scene/resources/texture.h"
#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
+#if defined(GLES3_ENABLED)
+#include "drivers/gles3/rasterizer_gles3.h"
+#endif
+
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -94,6 +100,15 @@
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:
@@ -111,6 +126,10 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
+#ifdef DBUS_ENABLED
+ case FEATURE_KEEP_SCREEN_ON:
+#endif
+ case FEATURE_CLIPBOARD_PRIMARY:
return true;
default: {
}
@@ -123,70 +142,6 @@ String DisplayServerX11::get_name() const {
return "X11";
}
-void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
- const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
-
- String path = OS::get_singleton()->get_environment("PATH");
- Vector<String> path_elems = path.split(":", false);
- String program;
-
- for (int i = 0; i < path_elems.size(); i++) {
- for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
- String tested_path = path_elems[i].plus_file(message_programs[k]);
-
- if (FileAccess::exists(tested_path)) {
- program = tested_path;
- break;
- }
- }
-
- if (program.length()) {
- break;
- }
- }
-
- List<String> args;
-
- if (program.ends_with("zenity")) {
- args.push_back("--error");
- args.push_back("--width");
- args.push_back("500");
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--text");
- args.push_back(p_alert);
- }
-
- if (program.ends_with("kdialog")) {
- args.push_back("--error");
- args.push_back(p_alert);
- args.push_back("--title");
- args.push_back(p_title);
- }
-
- if (program.ends_with("Xdialog")) {
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--msgbox");
- args.push_back(p_alert);
- args.push_back("0");
- args.push_back("0");
- }
-
- if (program.ends_with("xmessage")) {
- args.push_back("-center");
- args.push_back("-title");
- args.push_back(p_title);
- args.push_back(p_alert);
- }
-
- if (program.length()) {
- OS::get_singleton()->execute(program, args, true);
- } else {
- print_line(p_alert);
- }
-}
-
void DisplayServerX11::_update_real_mouse_position(const WindowData &wd) {
Window root_return, child_return;
int root_x, root_y, win_x, win_y;
@@ -350,23 +305,23 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
return;
}
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
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()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
if (showCursor) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor
+ XDefineCursor(x11_display, E.value.x11_window, cursors[current_cursor]); // show cursor
} else {
- XDefineCursor(x11_display, E->get().x11_window, null_cursor); // hide cursor
+ XDefineCursor(x11_display, E.value.x11_window, null_cursor); // hide cursor
}
}
mouse_mode = p_mode;
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
//flush pending motion events
_flush_mouse_motion();
WindowData &main_window = windows[MAIN_WINDOW_ID];
@@ -440,7 +395,7 @@ Point2i DisplayServerX11::mouse_get_absolute_position() const {
return Vector2i();
}
-int DisplayServerX11::mouse_get_button_state() const {
+MouseButton DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
@@ -457,6 +412,20 @@ void DisplayServerX11::clipboard_set(const String &p_text) {
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
+void DisplayServerX11::clipboard_set_primary(const String &p_text) {
+ _THREAD_SAFE_METHOD_
+ if (!p_text.is_empty()) {
+ {
+ // The clipboard content can be accessed while polling for events.
+ MutexLock mutex_lock(events_mutex);
+ internal_clipboard_primary = p_text;
+ }
+
+ XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
+ XSetSelectionOwner(x11_display, XInternAtom(x11_display, "PRIMARY", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
+ }
+}
+
Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
return True;
@@ -465,59 +434,143 @@ Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *
}
}
+Bool DisplayServerX11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) {
+ return True;
+ } else {
+ return False;
+ }
+}
+
String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const {
String ret;
- Atom type;
- Atom selection = XA_PRIMARY;
- int format, result;
- unsigned long len, bytes_left, dummy;
- unsigned char *data;
Window selection_owner = XGetSelectionOwner(x11_display, p_source);
-
if (selection_owner == x11_window) {
- return internal_clipboard;
+ static const char *target_type = "PRIMARY";
+ if (p_source != None && String(XGetAtomName(x11_display, p_source)) == target_type) {
+ return internal_clipboard_primary;
+ } else {
+ return internal_clipboard;
+ }
}
if (selection_owner != None) {
- {
- // Block events polling while processing selection events.
- MutexLock mutex_lock(events_mutex);
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
- XConvertSelection(x11_display, p_source, target, selection,
- x11_window, CurrentTime);
+ Atom selection = XA_PRIMARY;
+ XConvertSelection(x11_display, p_source, target, selection,
+ x11_window, CurrentTime);
- XFlush(x11_display);
+ XFlush(x11_display);
- // 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);
- }
+ // 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
- //
+ // 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");
+ printf("Failed to get selection data.\n");
}
+
if (data) {
XFree(data);
}
@@ -533,7 +586,7 @@ String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const
if (utf8_atom != None) {
ret = _clipboard_get_impl(p_source, x11_window, utf8_atom);
}
- if (ret.empty()) {
+ if (ret.is_empty()) {
ret = _clipboard_get_impl(p_source, x11_window, XA_STRING);
}
return ret;
@@ -545,13 +598,80 @@ String DisplayServerX11::clipboard_get() const {
String ret;
ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window);
- if (ret.empty()) {
+ if (ret.is_empty()) {
ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
}
return ret;
}
+String DisplayServerX11::clipboard_get_primary() const {
+ _THREAD_SAFE_METHOD_
+
+ String ret;
+ ret = _clipboard_get(XInternAtom(x11_display, "PRIMARY", 0), windows[MAIN_WINDOW_ID].x11_window);
+
+ if (ret.is_empty()) {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
+ }
+
+ return ret;
+}
+
+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_
@@ -568,35 +688,59 @@ int DisplayServerX11::get_screen_count() const {
return count;
}
-Point2i DisplayServerX11::screen_get_position(int p_screen) const {
- _THREAD_SAFE_METHOD_
+Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
+ Rect2i rect(0, 0, 0, 0);
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
p_screen = window_get_current_screen();
}
- // Using Xinerama Extension
+ ERR_FAIL_COND_V(p_screen < 0, rect);
+
+ // Using Xinerama Extension.
int event_base, error_base;
- const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) {
- return Point2i(0, 0);
- }
+ if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+ int count;
+ XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
+
+ // Check if screen is valid.
+ if (p_screen < count) {
+ rect.position.x = xsi[p_screen].x_org;
+ rect.position.y = xsi[p_screen].y_org;
+ rect.size.width = xsi[p_screen].width;
+ rect.size.height = xsi[p_screen].height;
+ } else {
+ ERR_PRINT("Invalid screen index: " + itos(p_screen) + "(count: " + itos(count) + ").");
+ }
- int count;
- XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) {
- return Point2i(0, 0);
+ if (xsi) {
+ XFree(xsi);
+ }
}
- Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org);
+ return rect;
+}
- XFree(xsi);
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
+ _THREAD_SAFE_METHOD_
- return position;
+ return _screen_get_rect(p_screen).position;
}
Size2i DisplayServerX11::screen_get_size(int p_screen) const {
- return screen_get_usable_rect(p_screen).size;
+ _THREAD_SAFE_METHOD_
+
+ return _screen_get_rect(p_screen).size;
+}
+
+bool g_bad_window = false;
+int bad_window_error_handler(Display *display, XErrorEvent *error) {
+ if (error->error_code == BadWindow) {
+ g_bad_window = true;
+ } else {
+ ERR_PRINT("Unhandled XServer error code: " + itos(error->error_code));
+ }
+ return 0;
}
Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
@@ -606,21 +750,276 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
p_screen = window_get_current_screen();
}
- // 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);
+ int screen_count = get_screen_count();
+
+ // Check if screen is valid.
+ ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i(0, 0, 0, 0));
+
+ bool is_multiscreen = screen_count > 1;
+
+ // Use full monitor size as fallback.
+ Rect2i rect = _screen_get_rect(p_screen);
+
+ // There's generally only one screen reported by xlib even in multi-screen setup,
+ // in this case it's just one virtual screen composed of all physical monitors.
+ int x11_screen_count = ScreenCount(x11_display);
+ Window x11_window = RootWindow(x11_display, p_screen < x11_screen_count ? p_screen : 0);
+
+ Atom type;
+ int format = 0;
+ unsigned long remaining = 0;
+
+ // Find active desktop for the root window.
+ unsigned int desktop_index = 0;
+ Atom desktop_prop = XInternAtom(x11_display, "_NET_CURRENT_DESKTOP", True);
+ if (desktop_prop != None) {
+ unsigned long desktop_len = 0;
+ unsigned char *desktop_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, desktop_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &desktop_len, &remaining, &desktop_data) == Success) {
+ if ((format == 32) && (desktop_len > 0) && desktop_data) {
+ desktop_index = (unsigned int)desktop_data[0];
+ }
+ if (desktop_data) {
+ XFree(desktop_data);
+ }
+ }
}
- int count;
- XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) {
- return Rect2i(0, 0, 0, 0);
+ bool use_simple_method = true;
+
+ // First check for GTK work area, which is more accurate for multi-screen setup.
+ if (is_multiscreen) {
+ // Use already calculated work area when available.
+ Atom gtk_workareas_prop = XInternAtom(x11_display, "_GTK_WORKAREAS", False);
+ if (gtk_workareas_prop != None) {
+ char gtk_workarea_prop_name[32];
+ snprintf(gtk_workarea_prop_name, 32, "_GTK_WORKAREAS_D%d", desktop_index);
+ Atom gtk_workarea_prop = XInternAtom(x11_display, gtk_workarea_prop_name, True);
+ if (gtk_workarea_prop != None) {
+ unsigned long workarea_len = 0;
+ unsigned char *workarea_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, gtk_workarea_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &workarea_len, &remaining, &workarea_data) == Success) {
+ if ((format == 32) && (workarea_len % 4 == 0) && workarea_data) {
+ long *rect_data = (long *)workarea_data;
+ for (uint32_t data_offset = 0; data_offset < workarea_len; data_offset += 4) {
+ Rect2i workarea_rect;
+ workarea_rect.position.x = rect_data[data_offset];
+ workarea_rect.position.y = rect_data[data_offset + 1];
+ workarea_rect.size.x = rect_data[data_offset + 2];
+ workarea_rect.size.y = rect_data[data_offset + 3];
+
+ // Intersect with actual monitor size to find the correct area,
+ // because areas are not in the same order as screens from Xinerama.
+ if (rect.grow(-1).intersects(workarea_rect)) {
+ rect = rect.intersection(workarea_rect);
+ XFree(workarea_data);
+ return rect;
+ }
+ }
+ }
+ }
+ if (workarea_data) {
+ XFree(workarea_data);
+ }
+ }
+ }
+
+ // Fallback to calculating work area by hand from struts.
+ Atom client_list_prop = XInternAtom(x11_display, "_NET_CLIENT_LIST", True);
+ if (client_list_prop != None) {
+ unsigned long clients_len = 0;
+ unsigned char *clients_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, client_list_prop, 0, LONG_MAX, False, XA_WINDOW, &type, &format, &clients_len, &remaining, &clients_data) == Success) {
+ if ((format == 32) && (clients_len > 0) && clients_data) {
+ Window *windows_data = (Window *)clients_data;
+
+ Rect2i desktop_rect;
+ bool desktop_valid = false;
+
+ // Get full desktop size.
+ {
+ Atom desktop_geometry_prop = XInternAtom(x11_display, "_NET_DESKTOP_GEOMETRY", True);
+ if (desktop_geometry_prop != None) {
+ unsigned long geom_len = 0;
+ unsigned char *geom_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, desktop_geometry_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &geom_len, &remaining, &geom_data) == Success) {
+ if ((format == 32) && (geom_len >= 2) && geom_data) {
+ desktop_valid = true;
+ long *size_data = (long *)geom_data;
+ desktop_rect.size.x = size_data[0];
+ desktop_rect.size.y = size_data[1];
+ }
+ }
+ if (geom_data) {
+ XFree(geom_data);
+ }
+ }
+ }
+
+ // Get full desktop position.
+ if (desktop_valid) {
+ Atom desktop_viewport_prop = XInternAtom(x11_display, "_NET_DESKTOP_VIEWPORT", True);
+ if (desktop_viewport_prop != None) {
+ unsigned long viewport_len = 0;
+ unsigned char *viewport_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, desktop_viewport_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &viewport_len, &remaining, &viewport_data) == Success) {
+ if ((format == 32) && (viewport_len >= 2) && viewport_data) {
+ desktop_valid = true;
+ long *pos_data = (long *)viewport_data;
+ desktop_rect.position.x = pos_data[0];
+ desktop_rect.position.y = pos_data[1];
+ }
+ }
+ if (viewport_data) {
+ XFree(viewport_data);
+ }
+ }
+ }
+
+ if (desktop_valid) {
+ use_simple_method = false;
+
+ // Handle bad window errors silently because there's no other way to check
+ // that one of the windows has been destroyed in the meantime.
+ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&bad_window_error_handler);
+
+ for (unsigned long win_index = 0; win_index < clients_len; ++win_index) {
+ g_bad_window = false;
+
+ // Remove strut size from desktop size to get a more accurate result.
+ bool strut_found = false;
+ unsigned long strut_len = 0;
+ unsigned char *strut_data = nullptr;
+ Atom strut_partial_prop = XInternAtom(x11_display, "_NET_WM_STRUT_PARTIAL", True);
+ if (strut_partial_prop != None) {
+ if (XGetWindowProperty(x11_display, windows_data[win_index], strut_partial_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &strut_len, &remaining, &strut_data) == Success) {
+ strut_found = true;
+ }
+ }
+ // Fallback to older strut property.
+ if (!g_bad_window && !strut_found) {
+ Atom strut_prop = XInternAtom(x11_display, "_NET_WM_STRUT", True);
+ if (strut_prop != None) {
+ if (XGetWindowProperty(x11_display, windows_data[win_index], strut_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &strut_len, &remaining, &strut_data) == Success) {
+ strut_found = true;
+ }
+ }
+ }
+ if (!g_bad_window && strut_found && (format == 32) && (strut_len >= 4) && strut_data) {
+ long *struts = (long *)strut_data;
+
+ long left = struts[0];
+ long right = struts[1];
+ long top = struts[2];
+ long bottom = struts[3];
+
+ long left_start_y, left_end_y, right_start_y, right_end_y;
+ long top_start_x, top_end_x, bottom_start_x, bottom_end_x;
+
+ if (strut_len >= 12) {
+ left_start_y = struts[4];
+ left_end_y = struts[5];
+ right_start_y = struts[6];
+ right_end_y = struts[7];
+ top_start_x = struts[8];
+ top_end_x = struts[9];
+ bottom_start_x = struts[10];
+ bottom_end_x = struts[11];
+ } else {
+ left_start_y = 0;
+ left_end_y = desktop_rect.size.y;
+ right_start_y = 0;
+ right_end_y = desktop_rect.size.y;
+ top_start_x = 0;
+ top_end_x = desktop_rect.size.x;
+ bottom_start_x = 0;
+ bottom_end_x = desktop_rect.size.x;
+ }
+
+ const Point2i &pos = desktop_rect.position;
+ const Size2i &size = desktop_rect.size;
+
+ Rect2i left_rect(pos.x, pos.y + left_start_y, left, left_end_y - left_start_y);
+ if (left_rect.size.x > 0) {
+ Rect2i intersection = rect.intersection(left_rect);
+ if (!intersection.has_no_area() && intersection.size.x < rect.size.x) {
+ rect.position.x = left_rect.size.x;
+ rect.size.x = rect.size.x - intersection.size.x;
+ }
+ }
+
+ Rect2i right_rect(pos.x + size.x - right, pos.y + right_start_y, right, right_end_y - right_start_y);
+ if (right_rect.size.x > 0) {
+ Rect2i intersection = rect.intersection(right_rect);
+ if (!intersection.has_no_area() && right_rect.size.x < rect.size.x) {
+ rect.size.x = intersection.position.x - rect.position.x;
+ }
+ }
+
+ Rect2i top_rect(pos.x + top_start_x, pos.y, top_end_x - top_start_x, top);
+ if (top_rect.size.y > 0) {
+ Rect2i intersection = rect.intersection(top_rect);
+ if (!intersection.has_no_area() && intersection.size.y < rect.size.y) {
+ rect.position.y = top_rect.size.y;
+ rect.size.y = rect.size.y - intersection.size.y;
+ }
+ }
+
+ Rect2i bottom_rect(pos.x + bottom_start_x, pos.y + size.y - bottom, bottom_end_x - bottom_start_x, bottom);
+ if (bottom_rect.size.y > 0) {
+ Rect2i intersection = rect.intersection(bottom_rect);
+ if (!intersection.has_no_area() && right_rect.size.y < rect.size.y) {
+ rect.size.y = intersection.position.y - rect.position.y;
+ }
+ }
+ }
+ if (strut_data) {
+ XFree(strut_data);
+ }
+ }
+
+ // Restore default error handler.
+ XSetErrorHandler(oldHandler);
+ }
+ }
+ }
+ if (clients_data) {
+ XFree(clients_data);
+ }
+ }
+ }
+
+ // Single screen or fallback for multi screen.
+ if (use_simple_method) {
+ // Get desktop available size from the global work area.
+ Atom workarea_prop = XInternAtom(x11_display, "_NET_WORKAREA", True);
+ if (workarea_prop != None) {
+ unsigned long workarea_len = 0;
+ unsigned char *workarea_data = nullptr;
+ if (XGetWindowProperty(x11_display, x11_window, workarea_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &workarea_len, &remaining, &workarea_data) == Success) {
+ if ((format == 32) && (workarea_len >= ((desktop_index + 1) * 4)) && workarea_data) {
+ long *rect_data = (long *)workarea_data;
+ int data_offset = desktop_index * 4;
+ Rect2i workarea_rect;
+ workarea_rect.position.x = rect_data[data_offset];
+ workarea_rect.position.y = rect_data[data_offset + 1];
+ workarea_rect.size.x = rect_data[data_offset + 2];
+ workarea_rect.size.y = rect_data[data_offset + 3];
+
+ // Intersect with actual monitor size to get a proper approximation in multi-screen setup.
+ if (!is_multiscreen) {
+ rect = workarea_rect;
+ } else if (rect.intersects(workarea_rect)) {
+ rect = rect.intersection(workarea_rect);
+ }
+ }
+ }
+ if (workarea_data) {
+ XFree(workarea_data);
+ }
+ }
}
- Rect2i rect = Rect2i(xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height);
- XFree(xsi);
return rect;
}
@@ -679,20 +1078,40 @@ bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
return DisplayServer::screen_is_touchscreen(p_screen);
}
+#ifdef DBUS_ENABLED
+void DisplayServerX11::screen_set_keep_on(bool p_enable) {
+ if (screen_is_kept_on() == p_enable) {
+ return;
+ }
+
+ if (p_enable) {
+ screensaver->inhibit();
+ } else {
+ screensaver->uninhibit();
+ }
+
+ keep_screen_on = p_enable;
+}
+
+bool DisplayServerX11::screen_is_kept_on() const {
+ return keep_screen_on;
+}
+#endif
+
Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
_THREAD_SAFE_METHOD_
Vector<int> ret;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ ret.push_back(E.key);
}
return ret;
}
-DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
_THREAD_SAFE_METHOD_
- WindowID id = _create_window(p_mode, p_flags, p_rect);
+ WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
@@ -705,7 +1124,9 @@ DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, u
void DisplayServerX11::show_window(WindowID p_id) {
_THREAD_SAFE_METHOD_
- WindowData &wd = windows[p_id];
+ const WindowData &wd = windows[p_id];
+
+ DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id);
XMapWindow(x11_display, wd.x11_window);
}
@@ -733,10 +1154,17 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
context_vulkan->window_destroy(p_id);
}
#endif
+#ifdef GLES3_ENABLED
+ if (gl_manager) {
+ gl_manager->window_destroy(p_id);
+ }
+#endif
+
XUnmapWindow(x11_display, wd.x11_window);
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
windows.erase(p_id);
@@ -759,8 +1187,8 @@ DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_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();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd = E.value;
// Discard windows with no focus.
if (wd.focus_order == 0) {
@@ -768,7 +1196,7 @@ DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const
}
// Find topmost window which contains the given position.
- WindowID window_id = E->key();
+ 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.
@@ -793,7 +1221,9 @@ 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_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
@@ -871,22 +1301,38 @@ 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);
+ int count = get_screen_count();
+ if (count < 2) {
+ // Early exit with single monitor.
+ return 0;
+ }
+
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
const WindowData &wd = windows[p_window];
- int x, y;
- Window child;
- XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+ const Rect2i window_rect(wd.position, wd.size);
- int count = get_screen_count();
+ // Find which monitor has the largest overlap with the given window.
+ int screen_index = 0;
+ int max_area = 0;
for (int i = 0; i < count; i++) {
- 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)) {
- return i;
+ Rect2i screen_rect = _screen_get_rect(i);
+ Rect2i intersection = screen_rect.intersection(window_rect);
+ int area = intersection.get_area();
+ if (area > max_area) {
+ max_area = area;
+ screen_index = i;
}
}
- return 0;
+
+ return screen_index;
+}
+
+void DisplayServerX11::gl_window_make_current(DisplayServer::WindowID p_window_id) {
+#if defined(GLES3_ENABLED)
+ if (gl_manager)
+ gl_manager->window_make_current(p_window_id);
+#endif
}
void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
@@ -895,11 +1341,13 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
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 == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
+ // Check if screen is valid
+ ERR_FAIL_INDEX(p_screen, get_screen_count());
+
if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
Point2i position = screen_get_position(p_screen);
Size2i size = screen_get_size(p_screen);
@@ -924,6 +1372,8 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
WindowID prev_parent = wd_window.transient_parent;
ERR_FAIL_COND(prev_parent == p_parent);
+ DEBUG_LOG_X11("window_set_transient: %lu (%u), prev_parent=%u, parent=%u\n", wd_window.x11_window, p_window, 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
@@ -938,10 +1388,10 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
XSetTransientForHint(x11_display, wd_window.x11_window, None);
- // Set focus to parent sub window to avoid losing all focus with nested menus.
+ // Set focus to parent sub window to avoid losing all focus when closing a nested sub-menu.
// 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_window.menu_type && !wd_window.no_focus && wd_window.focused) {
if (!wd_parent.no_focus) {
XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime);
}
@@ -1183,6 +1633,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
unsigned char *data = nullptr;
bool retval = false;
+ if (property == None) {
+ return false;
+ }
+
int result = XGetWindowProperty(
x11_display,
wd.x11_window,
@@ -1271,7 +1725,9 @@ 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) {
@@ -1298,7 +1754,9 @@ 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);
@@ -1312,7 +1770,9 @@ 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);
+ }
}
}
@@ -1447,6 +1907,10 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
{ // 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;
@@ -1502,7 +1966,9 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
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);
@@ -1641,8 +2107,8 @@ bool DisplayServerX11::window_can_draw(WindowID p_window) const {
bool DisplayServerX11::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) {
return true;
}
}
@@ -1714,12 +2180,12 @@ void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
if (cursors[p_shape] != None) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[p_shape]);
}
} else if (cursors[CURSOR_ARROW] != None) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[CURSOR_ARROW]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[CURSOR_ARROW]);
}
}
}
@@ -1753,7 +2219,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
Rect2i atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -1776,7 +2242,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -1817,8 +2283,8 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
if (p_shape == current_cursor) {
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[p_shape]);
}
}
}
@@ -1848,7 +2314,7 @@ int DisplayServerX11::keyboard_get_layout_count() const {
XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -1882,7 +2348,7 @@ String DisplayServerX11::keyboard_get_layout_language(int p_index) const {
int _group_count = 0;
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -1921,7 +2387,7 @@ String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
int _group_count = 0;
const Atom *groups = kbd->names->groups;
- if (kbd->ctrls != NULL) {
+ if (kbd->ctrls != nullptr) {
_group_count = kbd->ctrls->num_groups;
} else {
while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
@@ -1941,29 +2407,48 @@ String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
return ret;
}
+Key DisplayServerX11::keyboard_get_keycode_from_physical(Key p_keycode) const {
+ Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
+ Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
+ unsigned int xkeycode = KeyMappingX11::get_xlibcode(keycode_no_mod);
+ KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, 0, 0);
+ if (xkeysym >= 'a' && xkeysym <= 'z') {
+ xkeysym -= ('a' - 'A');
+ }
+
+ Key key = KeyMappingX11::get_keycode(xkeysym);
+ // If not found, fallback to QWERTY.
+ // This should match the behavior of the event pump
+ if (key == Key::NONE) {
+ return p_keycode;
+ }
+ return (Key)(key | modifiers);
+}
+
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
- Atom actual_type;
- 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 };
@@ -2001,14 +2486,14 @@ static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p
}
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
- state->set_metakey((p_x11_state & Mod4Mask));
+ state->set_shift_pressed((p_x11_state & ShiftMask));
+ state->set_ctrl_pressed((p_x11_state & ControlMask));
+ state->set_alt_pressed((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
+ state->set_meta_pressed((p_x11_state & Mod4Mask));
}
-unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
- unsigned int mask = 1 << (p_x11_button - 1);
+MouseButton DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
+ MouseButton mask = mouse_button_to_mask(p_x11_button);
if (p_x11_type == ButtonPress) {
last_button_state |= mask;
@@ -2039,7 +2524,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// still works in half the cases. (won't handle deadkeys)
// For more complex input methods (deadkeys and more advanced)
// you have to use XmbLookupString (??).
- // So.. then you have to chosse which of both results
+ // So then you have to choose which of both results
// you want to keep.
// This is a real bizarreness and cpu waster.
@@ -2079,10 +2564,10 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (status == XLookupChars) {
bool keypress = xkeyevent->type == KeyPress;
- unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
- unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
+ Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
+ Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
- if (keycode >= 'a' && keycode <= 'z') {
+ if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
keycode -= 'a' - 'A';
}
@@ -2090,13 +2575,13 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
tmp.parse_utf8(utf8string, utf8bytes);
for (int i = 0; i < tmp.length(); i++) {
Ref<InputEventKey> k;
- k.instance();
- if (physical_keycode == 0 && keycode == 0 && tmp[i] == 0) {
+ k.instantiate();
+ if (physical_keycode == Key::NONE && keycode == Key::NONE && tmp[i] == 0) {
continue;
}
- if (keycode == 0) {
- keycode = physical_keycode;
+ if (keycode == Key::NONE) {
+ keycode = (Key)physical_keycode;
}
_get_key_modifier_state(xkeyevent->state, k);
@@ -2108,18 +2593,18 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_keycode(keycode);
- k->set_physical_keycode(physical_keycode);
+ k->set_physical_keycode((Key)physical_keycode);
k->set_echo(false);
- if (k->get_keycode() == KEY_BACKTAB) {
+ if (k->get_keycode() == Key::BACKTAB) {
//make it consistent across platforms.
- k->set_keycode(KEY_TAB);
- k->set_physical_keycode(KEY_TAB);
- k->set_shift(true);
+ k->set_keycode(Key::TAB);
+ k->set_physical_keycode(Key::TAB);
+ k->set_shift_pressed(true);
}
- Input::get_singleton()->accumulate_input_event(k);
+ Input::get_singleton()->parse_input_event(k);
}
memfree(utf8string);
return;
@@ -2143,8 +2628,8 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// KeyMappingX11 just translated the X11 keysym to a PIGUI
// keysym, so it works in all platforms the same.
- unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
- unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
+ Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
+ Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
/* Phase 3, obtain a unicode character from the keysym */
@@ -2164,12 +2649,12 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
bool keypress = xkeyevent->type == KeyPress;
- if (physical_keycode == 0 && keycode == 0 && unicode == 0) {
+ if (physical_keycode == Key::NONE && keycode == Key::NONE && unicode == 0) {
return;
}
- if (keycode == 0) {
- keycode = physical_keycode;
+ if (keycode == Key::NONE) {
+ keycode = (Key)physical_keycode;
}
/* Phase 5, determine modifier mask */
@@ -2181,7 +2666,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
//print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask));
Ref<InputEventKey> k;
- k.instance();
+ k.instantiate();
k->set_window_id(p_window);
_get_key_modifier_state(xkeyevent->state, k);
@@ -2231,33 +2716,33 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_pressed(keypress);
- if (keycode >= 'a' && keycode <= 'z') {
- keycode -= 'a' - 'A';
+ if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
+ keycode -= int('a' - 'A');
}
k->set_keycode(keycode);
- k->set_physical_keycode(physical_keycode);
+ k->set_physical_keycode((Key)physical_keycode);
k->set_unicode(unicode);
k->set_echo(p_echo);
- if (k->get_keycode() == KEY_BACKTAB) {
+ if (k->get_keycode() == Key::BACKTAB) {
//make it consistent across platforms.
- k->set_keycode(KEY_TAB);
- k->set_physical_keycode(KEY_TAB);
- k->set_shift(true);
+ k->set_keycode(Key::TAB);
+ k->set_physical_keycode(Key::TAB);
+ k->set_shift_pressed(true);
}
//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) {
- k->set_shift(false);
- } else if (k->get_keycode() == KEY_CONTROL) {
- k->set_control(false);
- } else if (k->get_keycode() == KEY_ALT) {
- k->set_alt(false);
- } else if (k->get_keycode() == KEY_META) {
- k->set_metakey(false);
+ if (k->get_keycode() == Key::SHIFT) {
+ k->set_shift_pressed(false);
+ } else if (k->get_keycode() == Key::CTRL) {
+ k->set_ctrl_pressed(false);
+ } else if (k->get_keycode() == Key::ALT) {
+ k->set_alt_pressed(false);
+ } else if (k->get_keycode() == Key::META) {
+ k->set_meta_pressed(false);
}
}
@@ -2268,56 +2753,114 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
}
}
- Input::get_singleton()->accumulate_input_event(k);
+ Input::get_singleton()->parse_input_event(k);
}
-void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) {
- XEvent respond;
- if (p_event->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- p_event->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- p_event->target == XInternAtom(x11_display, "TEXT", 0) ||
- p_event->target == XA_STRING ||
- p_event->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- p_event->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_event->requestor,
- p_event->property,
- p_event->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = p_event->property;
- } else if (p_event->target == XInternAtom(x11_display, "TARGETS", 0)) {
- Atom data[7];
+Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) 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, "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);
+ 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_event->requestor,
- p_event->property,
+ p_requestor,
+ p_property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char *)&data,
sizeof(data) / sizeof(data[0]));
- respond.xselection.property = p_event->property;
-
+ 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;
+ static const char *target_type = "PRIMARY";
+ if (p_selection != None && String(XGetAtomName(x11_display, p_selection)) == target_type) {
+ clip = internal_clipboard_primary.utf8();
+ } else {
+ 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 *targetname = XGetAtomName(x11_display, p_event->target);
- printf("No Target '%s'\n", targetname);
- if (targetname) {
- XFree(targetname);
+ 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, p_event->selection);
+ }
+
+ 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, p_event->selection);
}
respond.xselection.type = SelectionNotify;
@@ -2337,8 +2880,8 @@ void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
ds->xim = nullptr;
- for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) {
- E->get().xic = nullptr;
+ for (KeyValue<WindowID, WindowData> &E : ds->windows) {
+ E.value.xic = nullptr;
}
}
@@ -2346,9 +2889,9 @@ void DisplayServerX11::_window_changed(XEvent *event) {
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (event->xany.window == E->get().x11_window) {
- window_id = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (event->xany.window == E.value.x11_window) {
+ window_id = E.key;
break;
}
}
@@ -2388,8 +2931,12 @@ void DisplayServerX11::_window_changed(XEvent *event) {
context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
}
#endif
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
- print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
if (!wd.rect_changed_callback.is_null()) {
Rect2i r = new_rect;
@@ -2423,8 +2970,8 @@ void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
callable.call((const Variant **)&evp, 1, ret, ce);
} else {
//send to all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- Callable callable = E->get().input_event_callback;
+ for (KeyValue<WindowID, WindowData> &E : windows) {
+ Callable callable = E.value.input_event_callback;
if (callable.is_null()) {
continue;
}
@@ -2453,52 +3000,70 @@ Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XP
return True;
}
-void DisplayServerX11::_poll_events() {
+bool DisplayServerX11::_wait_for_events() const {
int x11_fd = ConnectionNumber(x11_display);
fd_set in_fds;
- while (!events_thread_done) {
- XFlush(x11_display);
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
- FD_ZERO(&in_fds);
- FD_SET(x11_fd, &in_fds);
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
- 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, nullptr, nullptr, &tv);
- // 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("_poll_events: select error: " + itos(errno));
+ ERR_PRINT("_wait_for_events: select error: " + itos(errno));
}
+ return false;
+ }
+}
+
+void DisplayServerX11::_poll_events() {
+ while (!events_thread_done.is_set()) {
+ _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;
- }
+ _check_pending_events(polled_events);
+ }
+ }
+}
- // 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;
- }
+void DisplayServerX11::_check_pending_events(LocalVector<XEvent> &r_events) {
+ // Flush to make sure to gather all pending events.
+ XFlush(x11_display);
- polled_events.push_back(ev);
- }
+ // 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;
}
+
+ r_events.push_back(ev);
}
}
@@ -2513,8 +3078,8 @@ void DisplayServerX11::process_events() {
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) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (E.value.focused) {
focus_found = true;
break;
}
@@ -2539,7 +3104,7 @@ void DisplayServerX11::process_events() {
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
- bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED;
+ bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN;
xi.pressure = 0;
xi.tilt = Vector2();
@@ -2551,6 +3116,9 @@ void DisplayServerX11::process_events() {
MutexLock mutex_lock(events_mutex);
events = polled_events;
polled_events.clear();
+
+ // Check for more pending events to avoid an extra frame delay.
+ _check_pending_events(events);
}
for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
@@ -2559,9 +3127,9 @@ void DisplayServerX11::process_events() {
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (event.xany.window == E->get().x11_window) {
- window_id = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (event.xany.window == E.value.x11_window) {
+ window_id = E.key;
break;
}
}
@@ -2610,7 +3178,7 @@ void DisplayServerX11::process_events() {
if (pen_pressure_range != Vector2()) {
xi.pressure_supported = true;
xi.pressure = (*values - pen_pressure_range[0]) /
- (pen_pressure_range[1] - pen_pressure_range[0]);
+ (pen_pressure_range[1] - pen_pressure_range[0]);
}
}
@@ -2669,15 +3237,12 @@ void DisplayServerX11::process_events() {
xi.last_relative_time = raw_event->time;
} break;
#ifdef TOUCH_ENABLED
- case XI_TouchBegin: // Fall-through
- // Disabled hand-in-hand with the grabbing
- //XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
-
+ case XI_TouchBegin:
case XI_TouchEnd: {
bool is_begin = event_data->evtype == XI_TouchBegin;
Ref<InputEventScreenTouch> st;
- st.instance();
+ st.instantiate();
st->set_window_id(window_id);
st->set_index(index);
st->set_position(pos);
@@ -2693,13 +3258,13 @@ void DisplayServerX11::process_events() {
// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
xi.mouse_pos_to_filter = pos;
}
- Input::get_singleton()->accumulate_input_event(st);
+ Input::get_singleton()->parse_input_event(st);
} else {
if (!xi.state.has(index)) { // Defensive
break;
}
xi.state.erase(index);
- Input::get_singleton()->accumulate_input_event(st);
+ Input::get_singleton()->parse_input_event(st);
}
} break;
@@ -2711,12 +3276,12 @@ void DisplayServerX11::process_events() {
if (curr_pos_elem->value() != pos) {
Ref<InputEventScreenDrag> sd;
- sd.instance();
+ sd.instantiate();
sd->set_window_id(window_id);
sd->set_index(index);
sd->set_position(pos);
sd->set_relative(pos - curr_pos_elem->value());
- Input::get_singleton()->accumulate_input_event(sd);
+ Input::get_singleton()->parse_input_event(sd);
curr_pos_elem->value() = pos;
}
@@ -2784,6 +3349,13 @@ void DisplayServerX11::process_events() {
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;
@@ -2793,17 +3365,17 @@ void DisplayServerX11::process_events() {
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()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
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
- XDefineCursor(x11_display, E->get().x11_window, null_cursor);
+ XUndefineCursor(x11_display, E.value.x11_window);
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { // Or re-hide it.
+ XDefineCursor(x11_display, E.value.x11_window, null_cursor);
}
XGrabPointer(
- x11_display, E->get().x11_window, True,
+ x11_display, E.value.x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, E->get().x11_window, None, CurrentTime);
+ GrabModeAsync, GrabModeAsync, E.value.x11_window, None, CurrentTime);
}
}
#ifdef TOUCH_ENABLED
@@ -2812,12 +3384,6 @@ void DisplayServerX11::process_events() {
XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
- 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);
- }
if (!app_focused) {
if (OS::get_singleton()->get_main_loop()) {
@@ -2834,15 +3400,22 @@ void DisplayServerX11::process_events() {
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);
+ }
+
Input::get_singleton()->release_pressed_events();
_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
//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.
- XUndefineCursor(x11_display, E->get().x11_window);
+ XUndefineCursor(x11_display, E.value.x11_window);
}
}
XUngrabPointer(x11_display, CurrentTime);
@@ -2854,22 +3427,16 @@ void DisplayServerX11::process_events() {
}*/
// Release every pointer to avoid sticky points
- for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
+ for (const KeyValue<int, Vector2> &E : xi.state) {
Ref<InputEventScreenTouch> st;
- st.instance();
- st->set_index(E->key());
+ st.instantiate();
+ st->set_index(E.key);
st->set_window_id(window_id);
- st->set_position(E->get());
- Input::get_singleton()->accumulate_input_event(st);
+ st->set_position(E.value);
+ Input::get_singleton()->parse_input_event(st);
}
xi.state.clear();
#endif
- 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 ConfigureNotify: {
@@ -2897,15 +3464,15 @@ void DisplayServerX11::process_events() {
}
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
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) {
- mb->set_button_index(3);
- } else if (mb->get_button_index() == 3) {
- mb->set_button_index(2);
+ mb->set_button_index((MouseButton)event.xbutton.button);
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ mb->set_button_index(MouseButton::MIDDLE);
+ } else if (mb->get_button_index() == MouseButton::MIDDLE) {
+ mb->set_button_index(MouseButton::RIGHT);
}
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));
@@ -2931,15 +3498,15 @@ void DisplayServerX11::process_events() {
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;
- mb->set_doubleclick(true);
+ last_click_button_index = MouseButton::NONE;
+ mb->set_double_click(true);
}
- } else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) {
+ } else if (mb->get_button_index() < MouseButton::WHEEL_UP || mb->get_button_index() > MouseButton::WHEEL_RIGHT) {
last_click_button_index = mb->get_button_index();
}
- if (!mb->is_doubleclick()) {
+ if (!mb->is_double_click()) {
last_click_ms += diff;
last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
@@ -2952,9 +3519,9 @@ void DisplayServerX11::process_events() {
// 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();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd_other = E.value;
+ WindowID window_id_other = E.key;
if (wd_other.focused) {
if (window_id_other != window_id) {
int x, y;
@@ -2964,7 +3531,7 @@ void DisplayServerX11::process_events() {
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);
+ Input::get_singleton()->parse_input_event(mb);
}
break;
}
@@ -2972,7 +3539,7 @@ void DisplayServerX11::process_events() {
}
}
- Input::get_singleton()->accumulate_input_event(mb);
+ Input::get_singleton()->parse_input_event(mb);
} break;
case MotionNotify: {
@@ -3062,18 +3629,18 @@ void DisplayServerX11::process_events() {
}
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
if (xi.pressure_supported) {
mm->set_pressure(xi.pressure);
} else {
- mm->set_pressure((mouse_get_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f);
+ mm->set_pressure(bool(mouse_get_button_state() & MouseButton::MASK_LEFT) ? 1.0f : 0.0f);
}
mm->set_tilt(xi.tilt);
_get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask(mouse_get_button_state());
+ mm->set_button_mask((MouseButton)mouse_get_button_state());
mm->set_position(pos);
mm->set_global_position(pos);
Input::get_singleton()->set_mouse_position(pos);
@@ -3088,15 +3655,15 @@ void DisplayServerX11::process_events() {
// this is so that the relative motion doesn't get messed up
// after we regain focus.
if (focused) {
- Input::get_singleton()->accumulate_input_event(mm);
+ Input::get_singleton()->parse_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();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd_other = E.value;
if (wd_other.focused) {
int x, y;
Window child;
@@ -3104,11 +3671,11 @@ void DisplayServerX11::process_events() {
Point2i pos_focused(x, y);
- mm->set_window_id(E->key());
+ 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);
+ Input::get_singleton()->parse_input_event(mm);
break;
}
@@ -3118,11 +3685,18 @@ void DisplayServerX11::process_events() {
} break;
case KeyPress:
case KeyRelease: {
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+ if (event.type == KeyPress) {
+ DEBUG_LOG_X11("[%u] KeyPress window=%lu (%u), keycode=%u, time=%lu \n", frame, event.xkey.window, window_id, event.xkey.keycode, event.xkey.time);
+ } else {
+ DEBUG_LOG_X11("[%u] KeyRelease window=%lu (%u), keycode=%u, time=%lu \n", frame, event.xkey.window, window_id, event.xkey.keycode, event.xkey.time);
+ }
+#endif
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, events, event_index);
+ _handle_key_event(window_id, &event.xkey, events, event_index);
} break;
case SelectionNotify:
@@ -3132,7 +3706,7 @@ void DisplayServerX11::process_events() {
Vector<String> files = String((char *)p.data).split("\n", false);
for (int i = 0; i < files.size(); i++) {
- files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges();
+ files.write[i] = files[i].replace("file://", "").uri_decode().strip_edges();
}
if (!windows[window_id].drop_files_callback.is_null()) {
@@ -3241,16 +3815,27 @@ void DisplayServerX11::process_events() {
*/
}
- Input::get_singleton()->flush_accumulated_events();
+ Input::get_singleton()->flush_buffered_events();
}
void DisplayServerX11::release_rendering_thread() {
+#if defined(GLES3_ENABLED)
+// gl_manager->release_current();
+#endif
}
void DisplayServerX11::make_rendering_thread() {
+#if defined(GLES3_ENABLED)
+// gl_manager->make_current();
+#endif
}
void DisplayServerX11::swap_buffers() {
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->swap_buffers();
+ }
+#endif
}
void DisplayServerX11::_update_context(WindowData &wd) {
@@ -3295,8 +3880,8 @@ void DisplayServerX11::set_context(Context p_context) {
context = p_context;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- _update_context(E->get());
+ for (KeyValue<WindowID, WindowData> &E : windows) {
+ _update_context(E.value);
}
}
@@ -3373,7 +3958,9 @@ 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) {
break;
@@ -3387,30 +3974,61 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
XSetErrorHandler(oldHandler);
}
+void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+ }
+#endif
+
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->set_use_vsync(p_vsync_mode == DisplayServer::VSYNC_ENABLED);
+ }
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ return context_vulkan->get_vsync_mode(p_window);
+ }
+#endif
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ return gl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
+ }
+#endif
+ return DisplayServer::VSYNC_ENABLED;
+}
+
Vector<String> DisplayServerX11::get_rendering_drivers_func() {
Vector<String> drivers;
#ifdef VULKAN_ENABLED
drivers.push_back("vulkan");
#endif
-#ifdef OPENGL_ENABLED
- drivers.push_back("opengl");
+#ifdef GLES3_ENABLED
+ drivers.push_back("opengl3");
#endif
return drivers;
}
-DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_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.",
+ OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n"
+ "Please update your drivers or if you have a very old or integrated GPU, upgrade it.\n"
+ "If you have updated your graphics drivers recently, try rebooting.",
"Unable to initialize Video driver");
}
return ds;
}
-DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
//Create window
long visualMask = VisualScreenMask;
@@ -3466,7 +4084,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
{
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;
@@ -3476,18 +4096,18 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XSetWindowAttributes new_attr;
new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
- ButtonReleaseMask | EnterWindowMask |
- LeaveWindowMask | PointerMotionMask |
- Button1MotionMask |
- Button2MotionMask | Button3MotionMask |
- Button4MotionMask | Button5MotionMask |
- ButtonMotionMask | KeymapStateMask |
- ExposureMask | VisibilityChangeMask |
- StructureNotifyMask |
- SubstructureNotifyMask | SubstructureRedirectMask |
- FocusChangeMask | PropertyChangeMask |
- ColormapChangeMask | OwnerGrabButtonMask |
- im_event_mask;
+ ButtonReleaseMask | EnterWindowMask |
+ LeaveWindowMask | PointerMotionMask |
+ Button1MotionMask |
+ Button2MotionMask | Button3MotionMask |
+ Button4MotionMask | Button5MotionMask |
+ ButtonMotionMask | KeymapStateMask |
+ ExposureMask | VisibilityChangeMask |
+ StructureNotifyMask |
+ SubstructureNotifyMask | SubstructureRedirectMask |
+ FocusChangeMask | PropertyChangeMask |
+ ColormapChangeMask | OwnerGrabButtonMask |
+ im_event_mask;
XChangeWindowAttributes(x11_display, wd.x11_window, CWEventMask, &new_attr);
@@ -3514,7 +4134,9 @@ 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
@@ -3545,30 +4167,41 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
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 (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);
-
- XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ 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);
- XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ if (wt_atom != None && type_atom != None) {
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
}
_update_size_hints(id);
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
- Error err = context_vulkan->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window");
}
#endif
+#ifdef GLES3_ENABLED
+ if (gl_manager) {
+ Error err = gl_manager->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window");
+ }
+#endif
//set_class_hint(x11_display, wd.x11_window);
XFlush(x11_display);
@@ -3592,8 +4225,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
wd.position.y = xwa.y;
wd.size.width = xwa.width;
wd.size.height = xwa.height;
-
- print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
//set cursor
@@ -3604,7 +4235,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
return id;
}
-DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_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;
@@ -3617,12 +4248,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
img[i] = nullptr;
}
- last_button_state = 0;
-
xmbstring = nullptr;
last_click_ms = 0;
- last_click_button_index = -1;
+ last_click_button_index = MouseButton::NONE;
last_click_pos = Point2i(-100, -100);
last_timestamp = 0;
@@ -3697,8 +4326,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
if (!_refresh_device_info()) {
- alert("Your system does not support XInput 2.\n"
- "Please upgrade your distribution.",
+ OS::get_singleton()->alert("Your system does not support XInput 2.\n"
+ "Please upgrade your distribution.",
"Unable to initialize XInput");
r_error = ERR_UNAVAILABLE;
return;
@@ -3754,14 +4383,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
//!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ //TODO - do Vulkan and OpenGL support checks, driver selection and fallback
rendering_driver = p_rendering_driver;
-#ifndef _MSC_VER
-#warning Forcing vulkan rendering driver because OpenGL not implemented yet
-#endif
- rendering_driver = "vulkan";
-
+ bool driver_found = false;
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
context_vulkan = memnew(VulkanContextX11);
@@ -3771,11 +4396,12 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = ERR_CANT_CREATE;
ERR_FAIL_MSG("Could not initialize Vulkan");
}
+ driver_found = true;
}
#endif
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (rendering_driver == "opengl_es") {
+ // Initialize context and rendering device.
+#if defined(GLES3_ENABLED)
+ if (rendering_driver == "opengl3") {
if (getenv("DRI_PRIME") == nullptr) {
int use_prime = -1;
@@ -3789,7 +4415,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
use_prime = 0;
}
- if (getenv("LD_LIBRARY_PATH")) {
+ // Some tools use fake libGL libraries and have them override the real one using
+ // LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its
+ // runtime and includes system `/lib` and `/lib64`... so ignore Steam.
+ if (use_prime == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {
String ld_library_path(getenv("LD_LIBRARY_PATH"));
Vector<String> libraries = ld_library_path.split(":");
@@ -3814,32 +4443,45 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
- ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
+ GLManager_X11::ContextType opengl_api_type = GLManager_X11::GLES_3_0_COMPATIBLE;
- context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
+ gl_manager = memnew(GLManager_X11(p_resolution, opengl_api_type));
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = nullptr;
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ if (gl_manager->initialize() != OK) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
}
+ driver_found = true;
- context_gles2->set_use_vsync(current_videomode.use_vsync);
+ // gl_manager->set_use_vsync(current_videomode.use_vsync);
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
+ if (true) {
+ // if (RasterizerGLES3::is_viable() == OK) {
+ // RasterizerGLES3::register_config();
+ RasterizerGLES3::make_current();
} else {
- memdelete(context_gles2);
- context_gles2 = nullptr;
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
}
}
#endif
+ if (!driver_found) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Video driver not found");
+ }
+
Point2i window_position(
(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));
+ WindowID main_window = _create_window(p_mode, p_vsync_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);
@@ -3847,24 +4489,16 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
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);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- rendering_server = memnew(RenderingServerWrapMT(rendering_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
- }
- */
-
{
//set all event master mask
XIEventMask all_master_event_mask;
@@ -3877,15 +4511,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
XISelectEvents(x11_display, DefaultRootWindow(x11_display), &all_master_event_mask, 1);
}
- // Disabled by now since grabbing also blocks mouse events
- // (they are received as extended events instead of standard events)
- /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
-
- // Grab touch devices to avoid OS gesture interference
- for (int i = 0; i < xi.touch_devices.size(); ++i) {
- XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
- }*/
-
cursor_size = XcursorGetDefaultSize(x11_display);
cursor_theme = XcursorGetTheme(x11_display);
@@ -4023,32 +4648,47 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
- events_thread = Thread::create(_poll_events_thread, this);
+ events_thread.start(_poll_events_thread, this);
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
+#ifdef DBUS_ENABLED
+ screensaver = memnew(FreeDesktopScreenSaver);
+ screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+#endif
+
r_error = OK;
}
DisplayServerX11::~DisplayServerX11() {
- events_thread_done = true;
- Thread::wait_to_finish(events_thread);
- memdelete(events_thread);
- events_thread = nullptr;
+ // 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.set();
+ events_thread.wait_to_finish();
//destroy all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (KeyValue<WindowID, WindowData> &E : windows) {
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
- context_vulkan->window_destroy(E->key());
+ context_vulkan->window_destroy(E.key);
+ }
+#endif
+#ifdef GLES3_ENABLED
+ if (rendering_driver == "opengl3") {
+ gl_manager->window_destroy(E.key);
}
#endif
- if (E->get().xic) {
- XDestroyIC(E->get().xic);
+ WindowData &wd = E.value;
+ 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
@@ -4065,6 +4705,13 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
+#ifdef GLES3_ENABLED
+ if (gl_manager) {
+ memdelete(gl_manager);
+ gl_manager = nullptr;
+ }
+#endif
+
if (xrandr_handle) {
dlclose(xrandr_handle);
}
@@ -4086,6 +4733,10 @@ DisplayServerX11::~DisplayServerX11() {
if (xmbstring) {
memfree(xmbstring);
}
+
+#ifdef DBUS_ENABLED
+ memdelete(screensaver);
+#endif
}
void DisplayServerX11::register_x11_driver() {