summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd/x11/display_server_x11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd/x11/display_server_x11.cpp')
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp805
1 files changed, 635 insertions, 170 deletions
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index b86bc10643..428cf3a145 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* display_server_x11.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 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 */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* display_server_x11.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "display_server_x11.h"
@@ -58,11 +58,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include <X11/Xatom.h>
-#include <X11/Xutil.h>
-#include <X11/extensions/Xinerama.h>
-#include <X11/extensions/shape.h>
-
// ICCCM
#define WM_NormalState 1L // window normal state
#define WM_IconicState 3L // window minimized
@@ -463,7 +458,7 @@ Point2i DisplayServerX11::mouse_get_position() const {
return Vector2i();
}
-MouseButton DisplayServerX11::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
@@ -756,11 +751,22 @@ int DisplayServerX11::get_screen_count() const {
return count;
}
+int DisplayServerX11::get_primary_screen() const {
+ return XDefaultScreen(x11_display);
+}
+
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();
+ switch (p_screen) {
+ case SCREEN_PRIMARY: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
}
ERR_FAIL_COND_V(p_screen < 0, rect);
@@ -827,8 +833,15 @@ int bad_window_error_handler(Display *display, XErrorEvent *error) {
Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
- if (p_screen == SCREEN_OF_MAIN_WINDOW) {
- p_screen = window_get_current_screen();
+ switch (p_screen) {
+ case SCREEN_PRIMARY: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
}
int screen_count = get_screen_count();
@@ -1107,8 +1120,15 @@ 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) {
- p_screen = window_get_current_screen();
+ switch (p_screen) {
+ case SCREEN_PRIMARY: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
}
//invalid screen?
@@ -1152,8 +1172,15 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
- if (p_screen == SCREEN_OF_MAIN_WINDOW) {
- p_screen = window_get_current_screen();
+ switch (p_screen) {
+ case SCREEN_PRIMARY: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
}
//invalid screen?
@@ -1262,6 +1289,8 @@ void DisplayServerX11::show_window(WindowID p_id) {
DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id);
XMapWindow(x11_display, wd.x11_window);
+ XSync(x11_display, False);
+ _validate_mode_on_map(p_id);
}
void DisplayServerX11::delete_sub_window(WindowID p_id) {
@@ -1295,6 +1324,8 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
}
#endif
+ XDestroyWindow(x11_display, wd.x11_xim_window);
+
XUnmapWindow(x11_display, wd.x11_window);
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
@@ -1391,31 +1422,35 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- const WindowData &wd = windows[p_window];
+ windows[p_window].mpath = p_region;
+ _update_window_mouse_passthrough(p_window);
+}
+
+void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+
+ const Vector<Vector2> region_path = windows[p_window].mpath;
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);
+ if (windows[p_window].mpass) {
+ Region region = XCreateRegion();
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ } else if (region_path.size() == 0) {
+ XShapeCombineMask(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, None, ShapeSet);
} 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;
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * region_path.size());
+ for (int i = 0; i < region_path.size(); i++) {
+ points[i].x = region_path[i].x;
+ points[i].y = region_path[i].y;
}
- region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ Region region = XPolygonRegion(points, region_path.size(), EvenOddRule);
memfree(points);
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
}
- XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
}
}
@@ -1503,23 +1538,39 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if (p_screen == SCREEN_OF_MAIN_WINDOW) {
- p_screen = window_get_current_screen();
+ switch (p_screen) {
+ case SCREEN_PRIMARY: {
+ p_screen = get_primary_screen();
+ } break;
+ case SCREEN_OF_MAIN_WINDOW: {
+ p_screen = window_get_current_screen(MAIN_WINDOW_ID);
+ } break;
+ default:
+ break;
}
// Check if screen is valid
ERR_FAIL_INDEX(p_screen, get_screen_count());
+ if (window_get_current_screen(p_window) == p_screen) {
+ return;
+ }
+
if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
Point2i position = screen_get_position(p_screen);
Size2i size = screen_get_size(p_screen);
XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y);
} else {
- if (p_screen != window_get_current_screen(p_window)) {
- Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
- window_set_position(ofs + screen_get_position(p_screen), p_window);
+ Rect2i srect = screen_get_usable_rect(p_screen);
+ Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ Size2i wsize = window_get_size(p_window);
+ wpos += srect.position;
+ if (srect != Rect2i()) {
+ wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3);
+ wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3);
}
+ window_set_position(wpos, p_window);
}
}
@@ -1559,7 +1610,7 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
// a subwindow and its parent are both destroyed.
if (!wd_window.no_focus && !wd_window.is_popup && wd_window.focused) {
if ((xwa.map_state == IsViewable) && !wd_parent.no_focus && !wd_window.is_popup) {
- XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime);
+ XSetInputFocus(x11_display, wd_parent.x11_window, RevertToParent, CurrentTime);
}
}
} else {
@@ -1587,7 +1638,7 @@ void DisplayServerX11::_update_size_hints(WindowID p_window) {
xsh->width = wd.size.width;
xsh->height = wd.size.height;
- if (window_mode == WINDOW_MODE_FULLSCREEN) {
+ if (window_mode == WINDOW_MODE_FULLSCREEN || window_mode == WINDOW_MODE_EXCLUSIVE_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
@@ -1623,6 +1674,40 @@ Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
return wd.position;
}
+Point2i DisplayServerX11::window_get_position_with_decorations(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) {
+ return wd.position;
+ }
+
+ XWindowAttributes xwa;
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
+ int x = wd.position.x;
+ int y = wd.position.y;
+ Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
+ if (prop != None) {
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (format == 32 && len == 4 && data) {
+ long *extents = (long *)data;
+ x -= extents[0]; // left
+ y -= extents[2]; // top
+ }
+ XFree(data);
+ }
+ }
+ return Size2i(x, y);
+}
+
void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -1767,12 +1852,16 @@ Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
return wd.size;
}
-Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
+Size2i DisplayServerX11::window_get_size_with_decorations(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
const WindowData &wd = windows[p_window];
+ if (wd.fullscreen) {
+ return wd.size;
+ }
+
XWindowAttributes xwa;
XSync(x11_display, False);
XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
@@ -1954,7 +2043,7 @@ void DisplayServerX11::_validate_mode_on_map(WindowID p_window) {
// Check if we applied any window modes that didn't take effect while unmapped
const WindowData &wd = windows[p_window];
if (wd.fullscreen && !_window_fullscreen_check(p_window)) {
- _set_wm_fullscreen(p_window, true);
+ _set_wm_fullscreen(p_window, true, wd.exclusive_fullscreen);
} else if (wd.maximized && !_window_maximize_check(p_window, "_NET_WM_STATE")) {
_set_wm_maximized(p_window, true);
} else if (wd.minimized && !_window_minimize_check(p_window)) {
@@ -2029,7 +2118,7 @@ void DisplayServerX11::_set_wm_minimized(WindowID p_window, bool p_enabled) {
wd.minimized = p_enabled;
}
-void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
+void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled, bool p_exclusive) {
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
@@ -2068,7 +2157,14 @@ 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;
+ unsigned long compositing_disable_on = 0; // Use default.
+ if (p_enabled) {
+ if (p_exclusive) {
+ compositing_disable_on = 1; // Force composition OFF to reduce overhead.
+ } else {
+ compositing_disable_on = 2; // Force composition ON to allow popup windows.
+ }
+ }
if (bypass_compositor != None) {
XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
}
@@ -2114,8 +2210,9 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
case WINDOW_MODE_FULLSCREEN: {
//Remove full-screen
wd.fullscreen = false;
+ wd.exclusive_fullscreen = false;
- _set_wm_fullscreen(p_window, false);
+ _set_wm_fullscreen(p_window, false, false);
//un-maximize required for always on top
bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window);
@@ -2148,7 +2245,13 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
}
wd.fullscreen = true;
- _set_wm_fullscreen(p_window, true);
+ if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ wd.exclusive_fullscreen = true;
+ _set_wm_fullscreen(p_window, true, true);
+ } else {
+ wd.exclusive_fullscreen = false;
+ _set_wm_fullscreen(p_window, true, false);
+ }
} break;
case WINDOW_MODE_MAXIMIZED: {
_set_wm_maximized(p_window, true);
@@ -2163,7 +2266,11 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
const WindowData &wd = windows[p_window];
if (wd.fullscreen) { //if fullscreen, it's not in another mode
- return WINDOW_MODE_FULLSCREEN;
+ if (wd.exclusive_fullscreen) {
+ return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;
+ } else {
+ return WINDOW_MODE_FULLSCREEN;
+ }
}
// Test maximized.
@@ -2211,6 +2318,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
window_set_size(window_get_size(p_window), p_window);
wd.borderless = p_enabled;
+ _update_window_mouse_passthrough(p_window);
} 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.");
@@ -2244,6 +2352,10 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ _update_window_mouse_passthrough(p_window);
+ } break;
case WINDOW_FLAG_POPUP: {
XWindowAttributes xwa;
XSync(x11_display, False);
@@ -2297,6 +2409,9 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -2377,23 +2492,40 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- wd.im_active = p_active;
-
if (!wd.xic) {
return;
}
+ if (!wd.focused) {
+ wd.ime_active = false;
+ im_text = String();
+ im_selection = Vector2i();
+ return;
+ }
// Block events polling while changing input focus
// because it triggers some event polling internally.
if (p_active) {
- {
- MutexLock mutex_lock(events_mutex);
- XSetICFocus(wd.xic);
+ MutexLock mutex_lock(events_mutex);
+
+ wd.ime_active = true;
+
+ XMapWindow(x11_display, wd.x11_xim_window);
+
+ XWindowAttributes xwa;
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_xim_window, &xwa);
+ if (xwa.map_state == IsViewable) {
+ XSetInputFocus(x11_display, wd.x11_xim_window, RevertToPointerRoot, CurrentTime);
}
- window_set_ime_position(wd.im_position, p_window);
+ XSetICFocus(wd.xic);
} else {
MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
+ XUnmapWindow(x11_display, wd.x11_xim_window);
+ wd.ime_active = false;
+
+ im_text = String();
+ im_selection = Vector2i();
}
}
@@ -2403,25 +2535,29 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- wd.im_position = p_pos;
-
if (!wd.xic) {
return;
}
+ if (!wd.focused) {
+ return;
+ }
- ::XPoint spot;
- spot.x = short(p_pos.x);
- spot.y = short(p_pos.y);
- XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, 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);
+ if (wd.ime_active) {
+ XWindowAttributes xwa;
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_xim_window, &xwa);
+ if (xwa.map_state == IsViewable) {
+ XMoveWindow(x11_display, wd.x11_xim_window, p_pos.x, p_pos.y);
+ }
}
+}
+
+Point2i DisplayServerX11::ime_get_selection() const {
+ return im_selection;
+}
- XFree(preedit_attr);
+String DisplayServerX11::ime_get_text() const {
+ return im_text;
}
void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
@@ -2742,13 +2878,13 @@ void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<Inp
state->set_meta_pressed((p_x11_state & Mod4Mask));
}
-MouseButton DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
- MouseButton mask = mouse_button_to_mask(p_x11_button);
+BitField<MouseButtonMask> DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
+ MouseButtonMask mask = mouse_button_to_mask(p_x11_button);
if (p_x11_type == ButtonPress) {
- last_button_state |= mask;
+ last_button_state.set_flag(mask);
} else {
- last_button_state &= ~mask;
+ last_button_state.clear_flag(mask);
}
return last_button_state;
@@ -2759,6 +2895,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
+ if (wd.ime_in_progress) {
+ return;
+ }
+ if (wd.ime_suppress_next_keyup) {
+ wd.ime_suppress_next_keyup = false;
+ if (xkeyevent->type != KeyPress) {
+ return;
+ }
+ }
+
// This code was pretty difficult to write.
// The docs stink and every toolkit seems to
// do it in a different way.
@@ -2782,13 +2928,20 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
KeySym keysym_unicode = 0; // keysym used to find unicode
// XLookupString returns keysyms usable as nice keycodes.
- char str[256 + 1];
+ char str[256] = {};
XKeyEvent xkeyevent_no_mod = *xkeyevent;
xkeyevent_no_mod.state &= ~ShiftMask;
xkeyevent_no_mod.state &= ~ControlMask;
- XLookupString(xkeyevent, str, 256, &keysym_unicode, nullptr);
+ XLookupString(xkeyevent, str, 255, &keysym_unicode, nullptr);
XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr);
+ String keysym;
+ if (xkb_keysym_to_utf32 && xkb_keysym_to_upper) {
+ KeySym keysym_unicode_nm = 0; // keysym used to find unicode
+ XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_unicode_nm, nullptr);
+ keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(keysym_unicode_nm)));
+ }
+
// Meanwhile, XLookupString returns keysyms useful for unicode.
if (!xmbstring) {
@@ -2837,13 +2990,18 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
_get_key_modifier_state(xkeyevent->state, k);
k->set_window_id(p_window);
- k->set_unicode(tmp[i]);
-
k->set_pressed(keypress);
k->set_keycode(keycode);
-
- k->set_physical_keycode((Key)physical_keycode);
+ k->set_physical_keycode(physical_keycode);
+ if (!keysym.is_empty()) {
+ k->set_key_label(fix_key_label(keysym[0], keycode));
+ } else {
+ k->set_key_label(keycode);
+ }
+ if (keypress) {
+ k->set_unicode(fix_unicode(tmp[i]));
+ }
k->set_echo(false);
@@ -2886,7 +3044,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// KeyMappingX11 also translates keysym to unicode.
// It does a binary search on a table to translate
// most properly.
- unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0;
+ char32_t unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0;
/* Phase 4, determine if event must be filtered */
@@ -2972,7 +3130,14 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_keycode(keycode);
k->set_physical_keycode((Key)physical_keycode);
- k->set_unicode(unicode);
+ if (!keysym.is_empty()) {
+ k->set_key_label(fix_key_label(keysym[0], keycode));
+ } else {
+ k->set_key_label(keycode);
+ }
+ if (keypress) {
+ k->set_unicode(fix_unicode(unicode));
+ }
k->set_echo(p_echo);
if (k->get_keycode() == Key::BACKTAB) {
@@ -3124,6 +3289,81 @@ void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p
XFlush(x11_display);
}
+int DisplayServerX11::_xim_preedit_start_callback(::XIM xim, ::XPointer client_data,
+ ::XPointer call_data) {
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ WindowID window_id = ds->_get_focused_window_or_popup();
+ WindowData &wd = ds->windows[window_id];
+ if (wd.ime_active) {
+ wd.ime_in_progress = true;
+ }
+
+ return -1; // Allow preedit strings of any length (no limit).
+}
+
+void DisplayServerX11::_xim_preedit_done_callback(::XIM xim, ::XPointer client_data,
+ ::XPointer call_data) {
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ WindowID window_id = ds->_get_focused_window_or_popup();
+ WindowData &wd = ds->windows[window_id];
+ if (wd.ime_active) {
+ wd.ime_in_progress = false;
+ wd.ime_suppress_next_keyup = true;
+ }
+}
+
+void DisplayServerX11::_xim_preedit_draw_callback(::XIM xim, ::XPointer client_data,
+ ::XIMPreeditDrawCallbackStruct *call_data) {
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ WindowID window_id = ds->_get_focused_window_or_popup();
+ WindowData &wd = ds->windows[window_id];
+
+ XIMText *xim_text = call_data->text;
+ if (wd.ime_active) {
+ if (xim_text != nullptr) {
+ String changed_text;
+ if (xim_text->encoding_is_wchar) {
+ changed_text = String(xim_text->string.wide_char);
+ } else {
+ changed_text.parse_utf8(xim_text->string.multi_byte);
+ }
+
+ if (call_data->chg_length < 0) {
+ ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text;
+ } else {
+ ds->im_text = ds->im_text.substr(0, call_data->chg_first) + changed_text + ds->im_text.substr(call_data->chg_length);
+ }
+
+ // Find the start and end of the selection.
+ int start = 0, count = 0;
+ for (int i = 0; i < xim_text->length; i++) {
+ if (xim_text->feedback[i] & XIMReverse) {
+ if (count == 0) {
+ start = i;
+ count = 1;
+ } else {
+ count++;
+ }
+ }
+ }
+ if (count > 0) {
+ ds->im_selection = Point2i(start + call_data->chg_first, count);
+ } else {
+ ds->im_selection = Point2i(call_data->caret, 0);
+ }
+ } else {
+ ds->im_text = String();
+ ds->im_selection = Point2i();
+ }
+
+ OS_Unix::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ }
+}
+
+void DisplayServerX11::_xim_preedit_caret_callback(::XIM xim, ::XPointer client_data,
+ ::XIMPreeditCaretCallbackStruct *call_data) {
+}
+
void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
WARN_PRINT("Input method stopped");
@@ -3173,10 +3413,6 @@ void DisplayServerX11::_window_changed(XEvent *event) {
if (new_rect == Rect2i(wd.position, wd.size)) {
return;
}
- if (wd.xic) {
- // Not portable.
- window_set_ime_position(Point2(0, 1));
- }
wd.position = new_rect.position;
wd.size = new_rect.size;
@@ -3524,6 +3760,7 @@ void DisplayServerX11::process_events() {
continue;
}
+ bool ime_window_event = false;
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
@@ -3532,6 +3769,11 @@ void DisplayServerX11::process_events() {
window_id = E.key;
break;
}
+ if (event.xany.window == E.value.x11_xim_window) {
+ window_id = E.key;
+ ime_window_event = true;
+ break;
+ }
}
if (XGetEventData(x11_display, &event.xcookie)) {
@@ -3546,6 +3788,9 @@ void DisplayServerX11::process_events() {
_refresh_device_info();
} break;
case XI_RawMotion: {
+ if (ime_window_event) {
+ break;
+ }
XIRawEvent *raw_event = (XIRawEvent *)event_data;
int device_id = raw_event->sourceid;
@@ -3648,6 +3893,9 @@ void DisplayServerX11::process_events() {
#ifdef TOUCH_ENABLED
case XI_TouchBegin:
case XI_TouchEnd: {
+ if (ime_window_event) {
+ break;
+ }
bool is_begin = event_data->evtype == XI_TouchBegin;
Ref<InputEventScreenTouch> st;
@@ -3678,6 +3926,9 @@ void DisplayServerX11::process_events() {
} break;
case XI_TouchUpdate: {
+ if (ime_window_event) {
+ break;
+ }
HashMap<int, Vector2>::Iterator curr_pos_elem = xi.state.find(index);
if (!curr_pos_elem) { // Defensive
break;
@@ -3704,6 +3955,9 @@ void DisplayServerX11::process_events() {
switch (event.type) {
case MapNotify: {
DEBUG_LOG_X11("[%u] MapNotify window=%lu (%u) \n", frame, event.xmap.window, window_id);
+ if (ime_window_event) {
+ break;
+ }
const WindowData &wd = windows[window_id];
@@ -3724,6 +3978,9 @@ void DisplayServerX11::process_events() {
case Expose: {
DEBUG_LOG_X11("[%u] Expose window=%lu (%u), count='%u' \n", frame, event.xexpose.window, window_id, event.xexpose.count);
+ if (ime_window_event) {
+ break;
+ }
windows[window_id].fullscreen = _window_fullscreen_check(window_id);
@@ -3732,18 +3989,27 @@ void DisplayServerX11::process_events() {
case NoExpose: {
DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id);
+ if (ime_window_event) {
+ break;
+ }
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);
+ if (ime_window_event) {
+ break;
+ }
windows[window_id].minimized = _window_minimize_check(window_id);
} break;
case LeaveNotify: {
DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+ if (ime_window_event) {
+ break;
+ }
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
@@ -3753,6 +4019,9 @@ void DisplayServerX11::process_events() {
case EnterNotify: {
DEBUG_LOG_X11("[%u] EnterNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+ if (ime_window_event) {
+ break;
+ }
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
@@ -3761,18 +4030,14 @@ void DisplayServerX11::process_events() {
case FocusIn: {
DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+ if (ime_window_event) {
+ break;
+ }
WindowData &wd = windows[window_id];
last_focused_window = 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;
@@ -3812,17 +4077,20 @@ void DisplayServerX11::process_events() {
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.
+ if (wd.ime_active && event.xfocus.detail == NotifyInferior) {
+ break;
+ }
+ if (wd.ime_active) {
MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
+ XUnmapWindow(x11_display, wd.x11_xim_window);
+ wd.ime_active = false;
+ im_text = String();
+ im_selection = Vector2i();
+ OS_Unix::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
}
+ wd.focused = false;
Input::get_singleton()->release_pressed_events();
_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
@@ -3858,6 +4126,9 @@ void DisplayServerX11::process_events() {
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);
+ if (event.xconfigure.window == windows[window_id].x11_xim_window) {
+ break;
+ }
const WindowData &wd = windows[window_id];
@@ -3877,6 +4148,9 @@ void DisplayServerX11::process_events() {
case ButtonPress:
case ButtonRelease: {
+ if (ime_window_event) {
+ break;
+ }
/* exit in case of a mouse button press */
last_timestamp = event.xbutton.time;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -3934,36 +4208,50 @@ void DisplayServerX11::process_events() {
} 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) {
+ WindowID window_id_other = INVALID_WINDOW_ID;
+ Window wd_other_x11_window;
+ if (wd.focused) {
+ // Handle cases where an unfocused popup is open that needs to receive button-up events.
+ WindowID popup_id = _get_focused_window_or_popup();
+ if (popup_id != INVALID_WINDOW_ID && popup_id != window_id) {
+ window_id_other = popup_id;
+ wd_other_x11_window = windows[popup_id].x11_window;
+ }
+ } 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 (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;
- 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()->parse_input_event(mb);
+ if (E.value.focused) {
+ if (E.key != window_id) {
+ window_id_other = E.key;
+ wd_other_x11_window = E.value.x11_window;
}
break;
}
}
}
+
+ if (window_id_other != INVALID_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()->parse_input_event(mb);
} break;
case MotionNotify: {
+ if (ime_window_event) {
+ break;
+ }
// 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.
@@ -4059,13 +4347,13 @@ void DisplayServerX11::process_events() {
if (xi.pressure_supported) {
mm->set_pressure(xi.pressure);
} else {
- mm->set_pressure(bool(mouse_get_button_state() & MouseButton::MASK_LEFT) ? 1.0f : 0.0f);
+ mm->set_pressure(bool(mouse_get_button_state().has_flag(MouseButtonMask::LEFT)) ? 1.0f : 0.0f);
}
mm->set_tilt(xi.tilt);
mm->set_pen_inverted(xi.pen_inverted);
_get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask((MouseButton)mouse_get_button_state());
+ mm->set_button_mask(mouse_get_button_state());
mm->set_position(pos);
mm->set_global_position(pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
@@ -4124,7 +4412,9 @@ void DisplayServerX11::process_events() {
} break;
case SelectionNotify:
-
+ if (ime_window_event) {
+ break;
+ }
if (event.xselection.target == requested) {
Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0));
@@ -4159,7 +4449,9 @@ void DisplayServerX11::process_events() {
break;
case ClientMessage:
-
+ if (ime_window_event) {
+ break;
+ }
if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete) {
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
}
@@ -4414,7 +4706,7 @@ void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo
#if defined(GLES3_ENABLED)
if (gl_manager) {
- gl_manager->set_use_vsync(p_vsync_mode == DisplayServer::VSYNC_ENABLED);
+ gl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
}
#endif
}
@@ -4447,23 +4739,25 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, 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_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error));
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n'./" +
- executable_name + " --rendering-driver opengl3'.\n "
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s --rendering-driver opengl3'\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
@@ -4545,8 +4839,32 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
valuemask |= CWOverrideRedirect | CWSaveUnder;
}
+ int rq_screen = get_screen_from_rect(p_rect);
+ if (rq_screen < 0) {
+ rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.
+ }
+
+ Rect2i win_rect = p_rect;
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
+
+ win_rect = screen_rect;
+ } else {
+ Rect2i srect = screen_get_usable_rect(rq_screen);
+ Point2i wpos = p_rect.position;
+ wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3);
+ wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3);
+
+ win_rect.position = wpos;
+ }
+
{
- 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);
+ wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes);
+
+ XSetWindowAttributes window_attributes_ime = {};
+ window_attributes_ime.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
+
+ wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWEventMask, &window_attributes_ime);
// Enable receiving notification when the window is initialized (MapNotify)
// so the focus can be set at the right time.
@@ -4618,7 +4936,50 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
// 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);
+ // Force on-the-spot for the over-the-spot style.
+ if ((xim_style & XIMPreeditPosition) != 0) {
+ xim_style &= ~XIMPreeditPosition;
+ xim_style |= XIMPreeditCallbacks;
+ }
+ if ((xim_style & XIMPreeditCallbacks) != 0) {
+ ::XIMCallback preedit_start_callback;
+ preedit_start_callback.client_data = (::XPointer)(this);
+ preedit_start_callback.callback = (::XIMProc)(void *)(_xim_preedit_start_callback);
+
+ ::XIMCallback preedit_done_callback;
+ preedit_done_callback.client_data = (::XPointer)(this);
+ preedit_done_callback.callback = (::XIMProc)(_xim_preedit_done_callback);
+
+ ::XIMCallback preedit_draw_callback;
+ preedit_draw_callback.client_data = (::XPointer)(this);
+ preedit_draw_callback.callback = (::XIMProc)(_xim_preedit_draw_callback);
+
+ ::XIMCallback preedit_caret_callback;
+ preedit_caret_callback.client_data = (::XPointer)(this);
+ preedit_caret_callback.callback = (::XIMProc)(_xim_preedit_caret_callback);
+
+ ::XVaNestedList preedit_attributes = XVaCreateNestedList(0,
+ XNPreeditStartCallback, &preedit_start_callback,
+ XNPreeditDoneCallback, &preedit_done_callback,
+ XNPreeditDrawCallback, &preedit_draw_callback,
+ XNPreeditCaretCallback, &preedit_caret_callback,
+ (char *)nullptr);
+
+ wd.xic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, wd.x11_xim_window,
+ XNFocusWindow, wd.x11_xim_window,
+ XNPreeditAttributes, preedit_attributes,
+ (char *)nullptr);
+ XFree(preedit_attributes);
+ } else {
+ wd.xic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, wd.x11_xim_window,
+ XNFocusWindow, wd.x11_xim_window,
+ (char *)nullptr);
+ }
+
if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) {
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
XDestroyIC(wd.xic);
@@ -4667,14 +5028,15 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
- Error err = context_vulkan->window_create(id, p_vsync_mode, 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, win_rect.size.width, win_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);
+ Error err = gl_manager->window_create(id, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window");
+ window_set_vsync_mode(p_vsync_mode, id);
}
#endif
@@ -4708,7 +5070,108 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
return id;
}
-DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+static bool _is_xim_style_supported(const ::XIMStyle &p_style) {
+ const ::XIMStyle supported_preedit = XIMPreeditCallbacks | XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
+ const ::XIMStyle supported_status = XIMStatusNothing | XIMStatusNone;
+
+ // Check preedit style is supported
+ if ((p_style & supported_preedit) == 0) {
+ return false;
+ }
+
+ // Check status style is supported
+ if ((p_style & supported_status) == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMStyle &p_style_b) {
+ if (p_style_a == 0) {
+ return p_style_b;
+ }
+ if (p_style_b == 0) {
+ return p_style_a;
+ }
+
+ const ::XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks | XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
+ const ::XIMStyle status = XIMStatusArea | XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone;
+
+ ::XIMStyle a = p_style_a & preedit;
+ ::XIMStyle b = p_style_b & preedit;
+ if (a != b) {
+ // Compare preedit styles.
+ if ((a | b) & XIMPreeditCallbacks) {
+ return a == XIMPreeditCallbacks ? p_style_a : p_style_b;
+ } else if ((a | b) & XIMPreeditPosition) {
+ return a == XIMPreeditPosition ? p_style_a : p_style_b;
+ } else if ((a | b) & XIMPreeditArea) {
+ return a == XIMPreeditArea ? p_style_a : p_style_b;
+ } else if ((a | b) & XIMPreeditNothing) {
+ return a == XIMPreeditNothing ? p_style_a : p_style_b;
+ }
+ } else {
+ // Preedit styles are the same, compare status styles.
+ a = p_style_a & status;
+ b = p_style_b & status;
+
+ if ((a | b) & XIMStatusCallbacks) {
+ return a == XIMStatusCallbacks ? p_style_a : p_style_b;
+ } else if ((a | b) & XIMStatusArea) {
+ return a == XIMStatusArea ? p_style_a : p_style_b;
+ } else if ((a | b) & XIMStatusNothing) {
+ return a == XIMStatusNothing ? p_style_a : p_style_b;
+ }
+ }
+ return p_style_a;
+}
+
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
+ KeyMappingX11::initialize();
+
+#ifdef DEBUG_ENABLED
+ int dylibloader_verbose = 1;
+#else
+ int dylibloader_verbose = 0;
+#endif
+ if (initialize_xlib(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xlib dynamically.");
+ }
+
+ if (initialize_xcursor(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load XCursor dynamically.");
+ }
+
+ initialize_xkbcommon(dylibloader_verbose); // Optional, used for key_label.
+
+ if (initialize_xext(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xext dynamically.");
+ }
+
+ if (initialize_xinerama(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xinerama dynamically.");
+ }
+
+ if (initialize_xrandr(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xrandr dynamically.");
+ }
+
+ if (initialize_xrender(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xrender dynamically.");
+ }
+
+ if (initialize_xinput2(dylibloader_verbose) != 0) {
+ r_error = ERR_UNAVAILABLE;
+ ERR_FAIL_MSG("Can't load Xinput2 dynamically.");
+ }
+
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -4815,11 +5278,13 @@ 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;
+ const ::XIMStyle &style = xim_styles->supported_styles[i];
+
+ if (!_is_xim_style_supported(style)) {
+ continue;
}
+
+ xim_style = _get_best_xim_style(xim_style, style);
}
XFree(xim_styles);
@@ -4909,7 +5374,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
gl_manager = memnew(GLManager_X11(p_resolution, opengl_api_type));
- if (gl_manager->initialize() != OK) {
+ if (gl_manager->initialize(x11_display) != OK) {
memdelete(gl_manager);
gl_manager = nullptr;
r_error = ERR_UNAVAILABLE;
@@ -4932,12 +5397,14 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
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);
-
+ Point2i window_position;
if (p_position != nullptr) {
window_position = *p_position;
+ } else {
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = SCREEN_PRIMARY;
+ }
+ window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2;
}
WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution));
@@ -4951,8 +5418,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
show_window(main_window);
- XSync(x11_display, False);
- _validate_mode_on_map(main_window);
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {