summaryrefslogtreecommitdiff
path: root/platform/linuxbsd
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linuxbsd')
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp4
-rw-r--r--platform/linuxbsd/detect.py6
-rw-r--r--platform/linuxbsd/display_server_x11.cpp735
-rw-r--r--platform/linuxbsd/display_server_x11.h24
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp4
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/platform_config.h12
7 files changed, 560 insertions, 227 deletions
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index b3553e961a..e2b88b7704 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -67,7 +67,7 @@ static void handle_crash(int sig) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
}
- fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
for (size_t i = 1; i < size; i++) {
@@ -109,7 +109,7 @@ static void handle_crash(int sig) {
output.erase(output.length() - 1, 1);
}
- fprintf(stderr, "[%ld] %s (%ls)\n", (long int)i, fname, output.c_str());
+ fprintf(stderr, "[%ld] %s (%s)\n", (long int)i, fname, output.utf8().get_data());
}
free(strings);
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 3eb4c44bc1..f5e2c72bbc 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -35,6 +35,11 @@ def can_build():
print("xinerama not found.. x11 disabled.")
return False
+ x11_error = os.system("pkg-config xext --modversion > /dev/null ")
+ if x11_error:
+ print("xext not found.. x11 disabled.")
+ return False
+
x11_error = os.system("pkg-config xrandr --modversion > /dev/null ")
if x11_error:
print("xrandr not found.. x11 disabled.")
@@ -194,6 +199,7 @@ def configure(env):
env.ParseConfig("pkg-config x11 --cflags --libs")
env.ParseConfig("pkg-config xcursor --cflags --libs")
env.ParseConfig("pkg-config xinerama --cflags --libs")
+ env.ParseConfig("pkg-config xext --cflags --libs")
env.ParseConfig("pkg-config xrandr --cflags --libs")
env.ParseConfig("pkg-config xrender --cflags --libs")
env.ParseConfig("pkg-config xi --cflags --libs")
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index e05d540c50..176878bc12 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -50,6 +50,7 @@
#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
@@ -83,6 +84,13 @@
#define VALUATOR_TILTX 3
#define VALUATOR_TILTY 4
+//#define DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#define DEBUG_LOG_X11(...) printf(__VA_ARGS__)
+#else
+#define DEBUG_LOG_X11(...)
+#endif
+
static const double abs_resolution_mult = 10000.0;
static const double abs_resolution_range_mult = 10.0;
@@ -314,23 +322,19 @@ bool DisplayServerX11::_refresh_device_info() {
}
void DisplayServerX11::_flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
-
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
-
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
+ // Block events polling while flushing motion events.
+ MutexLock mutex_lock(events_mutex);
+
+ for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) {
+ XEvent &event = polled_events[event_index];
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+ if (event_data->evtype == XI_RawMotion) {
+ XFreeEventData(x11_display, &event.xcookie);
+ polled_events.remove(event_index--);
+ continue;
}
- } else {
+ XFreeEventData(x11_display, &event.xcookie);
break;
}
}
@@ -443,12 +447,25 @@ int DisplayServerX11::mouse_get_button_state() const {
void DisplayServerX11::clipboard_set(const String &p_text) {
_THREAD_SAFE_METHOD_
- internal_clipboard = p_text;
+ {
+ // The clipboard content can be accessed while polling for events.
+ MutexLock mutex_lock(events_mutex);
+ internal_clipboard = p_text;
+ }
+
XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
-static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
+ return True;
+ } else {
+ return False;
+ }
+}
+
+String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const {
String ret;
Atom type;
@@ -456,23 +473,27 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
int format, result;
unsigned long len, bytes_left, dummy;
unsigned char *data;
- Window Sown = XGetSelectionOwner(x11_display, p_source);
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
- if (Sown == x11_window) {
- return p_internal_clipboard;
- };
+ if (selection_owner == x11_window) {
+ return internal_clipboard;
+ }
- if (Sown != None) {
- XConvertSelection(x11_display, p_source, target, selection,
- x11_window, CurrentTime);
- XFlush(x11_display);
- while (true) {
+ if (selection_owner != None) {
+ {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ XConvertSelection(x11_display, p_source, target, selection,
+ x11_window, CurrentTime);
+
+ XFlush(x11_display);
+
+ // Blocking wait for predicate to be True
+ // and remove the event from the queue.
XEvent event;
- XNextEvent(x11_display, &event);
- if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
- break;
- };
- };
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+ }
//
// Do not get any data, see how much data is there
@@ -506,14 +527,14 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
return ret;
}
-static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const {
String ret;
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
if (utf8_atom != None) {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
+ ret = _clipboard_get_impl(p_source, x11_window, utf8_atom);
}
- if (ret == "") {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ if (ret.empty()) {
+ ret = _clipboard_get_impl(p_source, x11_window, XA_STRING);
}
return ret;
}
@@ -522,11 +543,11 @@ String DisplayServerX11::clipboard_get() const {
_THREAD_SAFE_METHOD_
String ret;
- ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window);
- if (ret == "") {
- ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- };
+ if (ret.empty()) {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
+ }
return ret;
}
@@ -697,6 +718,8 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
WindowData &wd = windows[p_id];
+ DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+
while (wd.transient_children.size()) {
window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
}
@@ -714,6 +737,7 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
windows.erase(p_id);
@@ -733,15 +757,31 @@ ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) co
}
DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const {
-#warning This is an incorrect implementation, if windows overlap, it should return the topmost visible one or none if occluded by a foreign window
-
+ 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()) {
- Rect2i win_rect = Rect2i(window_get_position(E->key()), window_get_size(E->key()));
+ const WindowData &wd = E->get();
+
+ // Discard windows with no focus.
+ if (wd.focus_order == 0) {
+ continue;
+ }
+
+ // Find topmost window which contains the given position.
+ WindowID window_id = E->key();
+ Rect2i win_rect = Rect2i(window_get_position(window_id), window_get_size(window_id));
if (win_rect.has_point(p_position)) {
- return E->key();
+ // For siblings, pick the window which was focused last.
+ if ((parent_window != wd.transient_parent) || (wd.focus_order > focus_order)) {
+ found_window = window_id;
+ parent_window = wd.transient_parent;
+ focus_order = wd.focus_order;
+ }
}
}
- return INVALID_WINDOW_ID;
+
+ return found_window;
}
void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) {
@@ -757,6 +797,38 @@ void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window
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) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ const WindowData &wd = windows[p_window];
+
+ int event_base, error_base;
+ const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base);
+ if (ext_okay) {
+ Region region;
+ if (p_region.size() == 0) {
+ region = XCreateRegion();
+ XRectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = window_get_real_size(p_window).x;
+ rect.height = window_get_real_size(p_window).y;
+ XUnionRectWithRegion(&rect, region, region);
+ } else {
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size());
+ for (int i = 0; i < p_region.size(); i++) {
+ points[i].x = p_region[i].x;
+ points[i].y = p_region[i].y;
+ }
+ region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ memfree(points);
+ }
+ XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ }
+}
+
void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -850,24 +922,34 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd_window = windows[p_window];
- ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+ WindowID prev_parent = wd_window.transient_parent;
+ ERR_FAIL_COND(prev_parent == p_parent);
ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
if (p_parent == INVALID_WINDOW_ID) {
//remove transient
- ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
- ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+ ERR_FAIL_COND(prev_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(prev_parent));
- WindowData &wd_parent = windows[wd_window.transient_parent];
+ WindowData &wd_parent = windows[prev_parent];
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
XSetTransientForHint(x11_display, wd_window.x11_window, None);
+
+ // Set focus to parent sub window to avoid losing all focus with nested menus.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd_window.menu_type && !wd_window.no_focus) {
+ if (!wd_parent.no_focus) {
+ XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ }
} else {
ERR_FAIL_COND(!windows.has(p_parent));
- ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ ERR_FAIL_COND_MSG(prev_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
WindowData &wd_parent = windows[p_parent];
wd_window.transient_parent = p_parent;
@@ -1581,10 +1663,16 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
return;
}
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
if (p_active) {
- XSetICFocus(wd.xic);
+ {
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
window_set_ime_position(wd.im_position, p_window);
} else {
+ MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
}
}
@@ -1605,7 +1693,14 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
spot.x = short(p_pos.x);
spot.y = short(p_pos.y);
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, nullptr);
- XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+
+ {
+ // Block events polling during this call
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+ }
+
XFree(preedit_attr);
}
@@ -1925,7 +2020,7 @@ unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button
return last_button_state;
}
-void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -2062,7 +2157,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
/* Phase 4, determine if event must be filtered */
// This seems to be a side-effect of using XIM.
- // XEventFilter looks like a core X11 function,
+ // XFilterEvent looks like a core X11 function,
// but it's actually just used to see if we must
// ignore a deadkey, or events XIM determines
// must not reach the actual gui.
@@ -2096,17 +2191,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// Echo characters in X11 are a keyrelease and a keypress
// one after the other with the (almot) same timestamp.
- // To detect them, i use XPeekEvent and check that their
- // difference in time is below a threshold.
+ // To detect them, i compare to the next event in list and
+ // check that their difference in time is below a threshold.
if (xkeyevent->type != KeyPress) {
p_echo = false;
// make sure there are events pending,
// so this call won't block.
- if (XPending(x11_display) > 0) {
- XEvent peek_event;
- XPeekEvent(x11_display, &peek_event);
+ if (p_event_index + 1 < p_events.size()) {
+ XEvent &peek_event = p_events[p_event_index + 1];
// I'm using a threshold of 5 msecs,
// since sometimes there seems to be a little
@@ -2121,9 +2215,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
KeySym rk;
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr);
if (rk == keysym_keycode) {
- XEvent event;
- XNextEvent(x11_display, &event); //erase next event
- _handle_key_event(p_window, (XKeyEvent *)&event, true);
+ // Consume to next event.
+ ++p_event_index;
+ _handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, true);
return; //ignore current, echo next
}
}
@@ -2178,6 +2272,66 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->accumulate_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];
+ data[0] = XInternAtom(x11_display, "TARGETS", 0);
+ data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
+ data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
+ data[3] = XInternAtom(x11_display, "TEXT", 0);
+ data[4] = XA_STRING;
+ data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
+ data[6] = XInternAtom(x11_display, "text/plain", 0);
+
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (unsigned char *)&data,
+ sizeof(data) / sizeof(data[0]));
+ respond.xselection.property = p_event->property;
+
+ } else {
+ char *targetname = XGetAtomName(x11_display, p_event->target);
+ printf("No Target '%s'\n", targetname);
+ if (targetname) {
+ XFree(targetname);
+ }
+ respond.xselection.property = None;
+ }
+
+ respond.xselection.type = SelectionNotify;
+ respond.xselection.display = p_event->display;
+ respond.xselection.requestor = p_event->requestor;
+ respond.xselection.selection = p_event->selection;
+ respond.xselection.target = p_event->target;
+ respond.xselection.time = p_event->time;
+
+ XSendEvent(x11_display, p_event->requestor, True, NoEventMask, &respond);
+ XFlush(x11_display);
+}
+
void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
WARN_PRINT("Input method stopped");
@@ -2290,9 +2444,73 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
}
}
+void DisplayServerX11::_poll_events_thread(void *ud) {
+ DisplayServerX11 *display_server = (DisplayServerX11 *)ud;
+ display_server->_poll_events();
+}
+
+Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) {
+ // Just accept all events.
+ return True;
+}
+
+void DisplayServerX11::_poll_events() {
+ int x11_fd = ConnectionNumber(x11_display);
+ fd_set in_fds;
+
+ while (!events_thread_done) {
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+
+ // Wait for next event or timeout.
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+ if (num_ready_fds < 0) {
+ ERR_PRINT("_poll_events: select error: " + itos(errno));
+ }
+
+ // Process events from the queue.
+ {
+ MutexLock mutex_lock(events_mutex);
+
+ // Non-blocking wait for next event
+ // and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
+ // Check if the input manager wants to process the event.
+ if (XFilterEvent(&ev, None)) {
+ // Event has been filtered by the Input Manager,
+ // it has to be ignored and a new one will be received.
+ continue;
+ }
+
+ // Handle selection request events directly in the event thread, because
+ // communication through the x server takes several events sent back and forth
+ // and we don't want to block other programs while processing only one each frame.
+ if (ev.type == SelectionRequest) {
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ continue;
+ }
+
+ polled_events.push_back(ev);
+ }
+ }
+ }
+}
+
void DisplayServerX11::process_events() {
_THREAD_SAFE_METHOD_
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+ static int frame = 0;
+ ++frame;
+#endif
+
if (app_focused) {
//verify that one of the windows has focus, else send focus out notification
bool focus_found = false;
@@ -2309,6 +2527,7 @@ void DisplayServerX11::process_events() {
if (delta > 250) {
//X11 can go between windows and have no focus for a while, when creating them or something else. Use this as safety to avoid unnecessary focus in/outs.
if (OS::get_singleton()->get_main_loop()) {
+ DEBUG_LOG_X11("All focus lost, triggering NOTIFICATION_APPLICATION_FOCUS_OUT\n");
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
}
app_focused = false;
@@ -2327,9 +2546,16 @@ void DisplayServerX11::process_events() {
xi.tilt = Vector2();
xi.pressure_supported = false;
- while (XPending(x11_display) > 0) {
- XEvent event;
- XNextEvent(x11_display, &event);
+ LocalVector<XEvent> events;
+ {
+ // Block events polling while flushing events.
+ MutexLock mutex_lock(events_mutex);
+ events = polled_events;
+ polled_events.clear();
+ }
+
+ for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
+ XEvent &event = events[event_index];
WindowID window_id = MAIN_WINDOW_ID;
@@ -2341,10 +2567,6 @@ void DisplayServerX11::process_events() {
}
}
- if (XFilterEvent(&event, None)) {
- continue;
- }
-
if (XGetEventData(x11_display, &event.xcookie)) {
if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
@@ -2507,32 +2729,74 @@ void DisplayServerX11::process_events() {
XFreeEventData(x11_display, &event.xcookie);
switch (event.type) {
- case Expose:
+ case MapNotify: {
+ DEBUG_LOG_X11("[%u] MapNotify window=%lu (%u) \n", frame, event.xmap.window, window_id);
+
+ const WindowData &wd = windows[window_id];
+
+ // Set focus when menu window is started.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ } break;
+
+ case Expose: {
+ DEBUG_LOG_X11("[%u] Expose window=%lu (%u), count='%u' \n", frame, event.xexpose.window, window_id, event.xexpose.count);
+
Main::force_redraw();
- break;
+ } break;
+
+ case NoExpose: {
+ DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id);
- case NoExpose:
windows[window_id].minimized = true;
- break;
+ } break;
case VisibilityNotify: {
+ DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state);
+
XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
windows[window_id].minimized = (visibility->state == VisibilityFullyObscured);
} break;
+
case LeaveNotify: {
+ DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
} break;
+
case EnterNotify: {
+ DEBUG_LOG_X11("[%u] EnterNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
- case FocusIn:
- windows[window_id].focused = true;
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
+
+ case FocusIn: {
+ DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = true;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
+
+ // Keep track of focus order for overlapping windows.
+ static unsigned int focus_order = 0;
+ wd.focus_order = ++focus_order;
+
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_IN);
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
@@ -2556,9 +2820,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 (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
- }
if (!app_focused) {
if (OS::get_singleton()->get_main_loop()) {
@@ -2566,12 +2827,24 @@ void DisplayServerX11::process_events() {
}
app_focused = true;
}
- break;
+ } break;
+
+ case FocusOut: {
+ DEBUG_LOG_X11("[%u] FocusOut window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = false;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XUnsetICFocus(wd.xic);
+ }
- case FocusOut:
- windows[window_id].focused = false;
Input::get_singleton()->release_pressed_events();
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -2600,14 +2873,23 @@ void DisplayServerX11::process_events() {
}
xi.state.clear();
#endif
- if (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
+ } break;
+
+ case ConfigureNotify: {
+ DEBUG_LOG_X11("[%u] ConfigureNotify window=%lu (%u), event=%lu, above=%lu, override_redirect=%u \n", frame, event.xconfigure.window, window_id, event.xconfigure.event, event.xconfigure.above, event.xconfigure.override_redirect);
+
+ const WindowData &wd = windows[window_id];
+
+ // Set focus when menu window is re-used.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
}
- break;
- case ConfigureNotify:
_window_changed(&event);
- break;
+ } break;
+
case ButtonPress:
case ButtonRelease: {
/* exit in case of a mouse button press */
@@ -2634,7 +2916,18 @@ void DisplayServerX11::process_events() {
mb->set_pressed((event.type == ButtonPress));
+ const WindowData &wd = windows[window_id];
+
if (event.type == ButtonPress) {
+ DEBUG_LOG_X11("[%u] ButtonPress window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ // Ensure window focus on click.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (!wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+
uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;
if (mb->get_button_index() == last_click_button_index) {
@@ -2653,6 +2946,33 @@ void DisplayServerX11::process_events() {
last_click_ms += diff;
last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
+ } else {
+ DEBUG_LOG_X11("[%u] ButtonRelease window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ if (!wd.focused) {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ WindowID window_id_other = E->key();
+ if (wd_other.focused) {
+ if (window_id_other != window_id) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child);
+
+ mb->set_window_id(window_id_other);
+ mb->set_position(Vector2(x, y));
+ mb->set_global_position(mb->get_position());
+ Input::get_singleton()->accumulate_input_event(mb);
+ }
+ break;
+ }
+ }
+ }
}
Input::get_singleton()->accumulate_input_event(mb);
@@ -2670,11 +2990,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (XPending(x11_display) > 0) {
- XEvent tevent;
- XPeekEvent(x11_display, &tevent);
- if (tevent.type == MotionNotify) {
- XNextEvent(x11_display, &event);
+ if (event_index + 1 < events.size()) {
+ const XEvent &next_event = events[event_index + 1];
+ if (next_event.type == MotionNotify) {
+ ++event_index;
+ event = next_event;
} else {
break;
}
@@ -2702,6 +3022,9 @@ void DisplayServerX11::process_events() {
break;
}
+ const WindowData &wd = windows[window_id];
+ bool focused = wd.focused;
+
if (mouse_mode == MOUSE_MODE_CAPTURED) {
if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {
break;
@@ -2710,7 +3033,7 @@ void DisplayServerX11::process_events() {
Point2i new_center = pos;
pos = last_mouse_pos + xi.relative_motion;
center = new_center;
- do_mouse_warp = windows[window_id].focused; // warp the cursor if we're focused in
+ do_mouse_warp = focused; // warp the cursor if we're focused in
}
if (!last_mouse_pos_valid) {
@@ -2752,14 +3075,11 @@ void DisplayServerX11::process_events() {
}
mm->set_tilt(xi.tilt);
- // Make the absolute position integral so it doesn't look _too_ weird :)
- Point2i posi(pos);
-
_get_key_modifier_state(event.xmotion.state, mm);
mm->set_button_mask(mouse_get_button_state());
- mm->set_position(posi);
- mm->set_global_position(posi);
- Input::get_singleton()->set_mouse_position(posi);
+ mm->set_position(pos);
+ mm->set_global_position(pos);
+ Input::get_singleton()->set_mouse_position(pos);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2770,8 +3090,32 @@ void DisplayServerX11::process_events() {
// Don't propagate the motion event unless we have focus
// this is so that the relative motion doesn't get messed up
// after we regain focus.
- if (windows[window_id].focused || !mouse_mode_grab) {
+ if (focused) {
Input::get_singleton()->accumulate_input_event(mm);
+ } else {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ if (wd_other.focused) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xmotion.x, event.xmotion.y, &x, &y, &child);
+
+ Point2i pos_focused(x, y);
+
+ mm->set_window_id(E->key());
+ mm->set_position(pos_focused);
+ mm->set_global_position(pos_focused);
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+ Input::get_singleton()->accumulate_input_event(mm);
+
+ break;
+ }
+ }
}
} break;
@@ -2781,67 +3125,7 @@ void DisplayServerX11::process_events() {
// key event is a little complex, so
// it will be handled in its own function.
- _handle_key_event(window_id, (XKeyEvent *)&event);
- } break;
- case SelectionRequest: {
- XSelectionRequestEvent *req;
- XEvent e, respond;
- e = event;
-
- req = &(e.xselectionrequest);
- if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- req->target == XInternAtom(x11_display, "TEXT", 0) ||
- req->target == XA_STRING ||
- req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- req->target == XInternAtom(x11_display, "text/plain", 0)) {
- CharString clip = clipboard_get().utf8();
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- req->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = req->property;
- } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) {
- Atom data[7];
- data[0] = XInternAtom(x11_display, "TARGETS", 0);
- data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
- data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
- data[3] = XInternAtom(x11_display, "TEXT", 0);
- data[4] = XA_STRING;
- data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
- data[6] = XInternAtom(x11_display, "text/plain", 0);
-
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char *)&data,
- sizeof(data) / sizeof(data[0]));
- respond.xselection.property = req->property;
-
- } else {
- char *targetname = XGetAtomName(x11_display, req->target);
- printf("No Target '%s'\n", targetname);
- if (targetname) {
- XFree(targetname);
- }
- respond.xselection.property = None;
- }
-
- respond.xselection.type = SelectionNotify;
- respond.xselection.display = req->display;
- respond.xselection.requestor = req->requestor;
- respond.xselection.selection = req->selection;
- respond.xselection.target = req->target;
- respond.xselection.time = req->time;
- XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond);
- XFlush(x11_display);
+ _handle_key_event(window_id, (XKeyEvent *)&event, events, event_index);
} break;
case SelectionNotify:
@@ -3148,11 +3432,38 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- WindowID id;
+ WindowID id = window_id_counter++;
+ WindowData &wd = windows[id];
+
+ if ((id != MAIN_WINDOW_ID) && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
+ wd.menu_type = true;
+ }
+
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ wd.menu_type = true;
+ wd.no_focus = true;
+ }
+
+ // Setup for menu subwindows:
+ // - override_redirect forces the WM not to interfere with the window, to avoid delays due to
+ // handling decorations and placement.
+ // On the other hand, focus changes need to be handled manually when this is set.
+ // - save_under is a hint for the WM to keep the content of windows behind to avoid repaint.
+ if (wd.menu_type) {
+ windowAttributes.override_redirect = True;
+ windowAttributes.save_under = True;
+ valuemask |= CWOverrideRedirect | CWSaveUnder;
+ }
+
{
- WindowData wd;
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
+ // Enable receiving notification when the window is initialized (MapNotify)
+ // so the focus can be set at the right time.
+ if (wd.menu_type && !wd.no_focus) {
+ XSelectInput(x11_display, wd.x11_window, StructureNotifyMask);
+ }
+
//associate PID
// make PID known to X11
{
@@ -3209,6 +3520,10 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
if (xim && xim_style) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+
wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)nullptr);
if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) {
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
@@ -3227,58 +3542,26 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
_update_context(wd);
- id = window_id_counter++;
-
- windows[id] = wd;
-
- {
- bool make_utility = false;
-
- if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
-
- make_utility = true;
- }
- if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
- make_utility = true;
- }
-
- if (make_utility) {
- //this one seems to disable the fade animations for regular windows
- //but has the drawback that will not get focus by default, so
- //we need to force it, unless no focus requested
-
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
-
- XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
-
- if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
- //but as utility appears unfocused, it needs to be forcefuly focused, unless no focus requested
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = 0;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = wd.x11_window;
- xev.xclient.message_type = net_active_window;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = 1;
- xev.xclient.data.l[1] = CurrentTime;
+ 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);
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- } else {
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
+ 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);
- }
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
}
_update_size_hints(id);
@@ -3299,8 +3582,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XFree(visualInfo);
}
- WindowData &wd = windows[id];
-
window_set_mode(p_mode, id);
//sync size
@@ -3394,7 +3675,12 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
if (!xrandr_handle) {
err = dlerror();
- fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ // For some arcane reason, NetBSD now ships libXrandr.so.3 while the rest of the world has libXrandr.so.2...
+ // In case this happens for other X11 platforms in the future, let's give it a try too before failing.
+ xrandr_handle = dlopen("libXrandr.so.3", RTLD_LAZY);
+ if (!xrandr_handle) {
+ fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ }
} else {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
@@ -3740,12 +4026,19 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
+ events_thread = Thread::create(_poll_events_thread, this);
+
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
r_error = OK;
}
DisplayServerX11::~DisplayServerX11() {
+ events_thread_done = true;
+ Thread::wait_to_finish(events_thread);
+ memdelete(events_thread);
+ events_thread = nullptr;
+
//destroy all windows
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
#ifdef VULKAN_ENABLED
@@ -3754,11 +4047,13 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
- if (E->get().xic) {
- XDestroyIC(E->get().xic);
+ WindowData &wd = E->get();
+ if (wd.xic) {
+ XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
- XUnmapWindow(x11_display, E->get().x11_window);
- XDestroyWindow(x11_display, E->get().x11_window);
+ XUnmapWindow(x11_display, wd.x11_window);
+ XDestroyWindow(x11_display, wd.x11_window);
}
//destroy drivers
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 8e1f941bbf..740bf81fd9 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -36,6 +36,7 @@
#include "servers/display_server.h"
#include "core/input/input.h"
+#include "core/local_vector.h"
#include "drivers/alsa/audio_driver_alsa.h"
#include "drivers/alsamidi/midi_driver_alsamidi.h"
#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
@@ -132,6 +133,9 @@ class DisplayServerX11 : public DisplayServer {
ObjectID instance_id;
+ bool menu_type = false;
+ bool no_focus = false;
+
//better to guess on the fly, given WM can change it
//WindowMode mode;
bool fullscreen = false; //OS can't exit from this mode
@@ -141,6 +145,8 @@ class DisplayServerX11 : public DisplayServer {
Vector2i last_position_before_fs;
bool focused = false;
bool minimized = false;
+
+ unsigned int focus_order = 0;
};
Map<WindowID, WindowData> windows;
@@ -197,7 +203,11 @@ class DisplayServerX11 : public DisplayServer {
MouseMode mouse_mode;
Point2i center;
- void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
+ void _handle_selection_request_event(XSelectionRequestEvent *p_event);
+
+ String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
+ String _clipboard_get(Atom p_source, Window x11_window) const;
//bool minimized;
//bool window_has_focus;
@@ -247,6 +257,16 @@ class DisplayServerX11 : public DisplayServer {
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ mutable Mutex events_mutex;
+ Thread *events_thread = nullptr;
+ bool events_thread_done = false;
+ LocalVector<XEvent> polled_events;
+ static void _poll_events_thread(void *ud);
+ void _poll_events();
+
+ static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
+
protected:
void _window_changed(XEvent *event);
@@ -286,6 +306,8 @@ public:
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);
+
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 8c6f3b1167..e00a32e3ba 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -88,7 +88,9 @@ void OS_LinuxBSD::finalize() {
#endif
#ifdef JOYDEV_ENABLED
- memdelete(joypad);
+ if (joypad) {
+ memdelete(joypad);
+ }
#endif
}
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 4295721c68..cd4fbd9db5 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -48,7 +48,7 @@ class OS_LinuxBSD : public OS_Unix {
bool force_quit;
#ifdef JOYDEV_ENABLED
- JoypadLinux *joypad;
+ JoypadLinux *joypad = nullptr;
#endif
#ifdef ALSA_ENABLED
diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h
index 764666681f..571ad03db0 100644
--- a/platform/linuxbsd/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
@@ -31,7 +31,15 @@
#ifdef __linux__
#include <alloca.h>
#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#include <stdlib.h> // alloca
+// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms,
+// include NetBSD, use pthread_setname_np. NetBSD's version however requires
+// a different format, we handle this directly in thread_posix.
+#ifdef __NetBSD__
+#define PTHREAD_NETBSD_SET_NAME
+#else
#define PTHREAD_BSD_SET_NAME
#endif
+#endif