Merge pull request #42341 from nekomatata/x11-events-thread-3.2
[3.2] Fix issues related to delay when processing events on Linux
This commit is contained in:
commit
904773149d
4 changed files with 514 additions and 130 deletions
|
@ -172,6 +172,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
|
||||||
} \
|
} \
|
||||||
} while (0); // (*)
|
} while (0); // (*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `m_index` is greater than or equal to `m_size`,
|
||||||
|
* prints a generic error message and returns the value specified in `m_retval`.
|
||||||
|
* This macro should be preferred to `ERR_FAIL_COND_V` for unsigned bounds checking.
|
||||||
|
*/
|
||||||
|
#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \
|
||||||
|
do { \
|
||||||
|
if (unlikely((m_index) >= (m_size))) { \
|
||||||
|
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0); // (*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `m_index` is greater than or equal to `m_size`,
|
* If `m_index` is greater than or equal to `m_size`,
|
||||||
* prints a generic error message and returns the value specified in `m_retval`.
|
* prints a generic error message and returns the value specified in `m_retval`.
|
||||||
|
@ -226,6 +239,20 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
|
||||||
} \
|
} \
|
||||||
} while (0); // (*)
|
} while (0); // (*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `m_index` is greater than or equal to `m_size`,
|
||||||
|
* crashes the engine immediately with a generic error message.
|
||||||
|
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||||
|
* This macro should be preferred to `CRASH_COND` for bounds checking.
|
||||||
|
*/
|
||||||
|
#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
|
||||||
|
do { \
|
||||||
|
if (unlikely((m_index) >= (m_size))) { \
|
||||||
|
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
|
||||||
|
GENERATE_TRAP \
|
||||||
|
} \
|
||||||
|
} while (0); // (*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `m_param` is `null`, prints a generic error message and returns from the function.
|
* If `m_param` is `null`, prints a generic error message and returns from the function.
|
||||||
*/
|
*/
|
||||||
|
|
246
core/local_vector.h
Normal file
246
core/local_vector.h
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* local_vector.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2020 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LOCAL_VECTOR_H
|
||||||
|
#define LOCAL_VECTOR_H
|
||||||
|
|
||||||
|
#include "core/error_macros.h"
|
||||||
|
#include "core/os/copymem.h"
|
||||||
|
#include "core/os/memory.h"
|
||||||
|
#include "core/sort_array.h"
|
||||||
|
#include "core/vector.h"
|
||||||
|
|
||||||
|
template <class T, class U = uint32_t, bool force_trivial = false>
|
||||||
|
class LocalVector {
|
||||||
|
private:
|
||||||
|
U count = 0;
|
||||||
|
U capacity = 0;
|
||||||
|
T *data = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
T *ptr() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *ptr() const {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void push_back(T p_elem) {
|
||||||
|
if (unlikely(count == capacity)) {
|
||||||
|
if (capacity == 0) {
|
||||||
|
capacity = 1;
|
||||||
|
} else {
|
||||||
|
capacity <<= 1;
|
||||||
|
}
|
||||||
|
data = (T *)memrealloc(data, capacity * sizeof(T));
|
||||||
|
CRASH_COND_MSG(!data, "Out of memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!__has_trivial_constructor(T) && !force_trivial) {
|
||||||
|
memnew_placement(&data[count++], T(p_elem));
|
||||||
|
} else {
|
||||||
|
data[count++] = p_elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(U p_index) {
|
||||||
|
ERR_FAIL_UNSIGNED_INDEX(p_index, count);
|
||||||
|
count--;
|
||||||
|
for (U i = p_index; i < count; i++) {
|
||||||
|
data[i] = data[i + 1];
|
||||||
|
}
|
||||||
|
if (!__has_trivial_destructor(T) && !force_trivial) {
|
||||||
|
data[count].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(const T &p_val) {
|
||||||
|
int64_t idx = find(p_val);
|
||||||
|
if (idx >= 0) {
|
||||||
|
remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void invert() {
|
||||||
|
for (U i = 0; i < count / 2; i++) {
|
||||||
|
SWAP(data[i], data[count - i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void clear() { resize(0); }
|
||||||
|
_FORCE_INLINE_ void reset() {
|
||||||
|
clear();
|
||||||
|
if (data) {
|
||||||
|
memfree(data);
|
||||||
|
data = nullptr;
|
||||||
|
capacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ bool empty() const { return count == 0; }
|
||||||
|
_FORCE_INLINE_ void reserve(U p_size) {
|
||||||
|
p_size = nearest_power_of_2_templated(p_size);
|
||||||
|
if (p_size > capacity) {
|
||||||
|
capacity = p_size;
|
||||||
|
data = (T *)memrealloc(data, capacity * sizeof(T));
|
||||||
|
CRASH_COND_MSG(!data, "Out of memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ U size() const { return count; }
|
||||||
|
void resize(U p_size) {
|
||||||
|
if (p_size < count) {
|
||||||
|
if (!__has_trivial_destructor(T) && !force_trivial) {
|
||||||
|
for (U i = p_size; i < count; i++) {
|
||||||
|
data[i].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count = p_size;
|
||||||
|
} else if (p_size > count) {
|
||||||
|
if (unlikely(p_size > capacity)) {
|
||||||
|
if (capacity == 0) {
|
||||||
|
capacity = 1;
|
||||||
|
}
|
||||||
|
while (capacity < p_size) {
|
||||||
|
capacity <<= 1;
|
||||||
|
}
|
||||||
|
data = (T *)memrealloc(data, capacity * sizeof(T));
|
||||||
|
CRASH_COND_MSG(!data, "Out of memory");
|
||||||
|
}
|
||||||
|
if (!__has_trivial_constructor(T) && !force_trivial) {
|
||||||
|
for (U i = count; i < p_size; i++) {
|
||||||
|
memnew_placement(&data[i], T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count = p_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ const T &operator[](U p_index) const {
|
||||||
|
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
|
||||||
|
return data[p_index];
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ T &operator[](U p_index) {
|
||||||
|
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
|
||||||
|
return data[p_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(U p_pos, T p_val) {
|
||||||
|
ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1);
|
||||||
|
if (p_pos == count) {
|
||||||
|
push_back(p_val);
|
||||||
|
} else {
|
||||||
|
resize(count + 1);
|
||||||
|
for (U i = count; i > p_pos; i--) {
|
||||||
|
data[i] = data[i - 1];
|
||||||
|
}
|
||||||
|
data[p_pos] = p_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t find(const T &p_val, U p_from = 0) const {
|
||||||
|
for (U i = 0; i < count; i++) {
|
||||||
|
if (data[i] == p_val) {
|
||||||
|
return int64_t(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
void sort_custom() {
|
||||||
|
U len = count;
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SortArray<T, C> sorter;
|
||||||
|
sorter.sort(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort() {
|
||||||
|
sort_custom<_DefaultComparator<T> >();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ordered_insert(T p_val) {
|
||||||
|
U i;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (p_val < data[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insert(i, p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Vector<T>() const {
|
||||||
|
Vector<T> ret;
|
||||||
|
ret.resize(size());
|
||||||
|
T *w = ret.ptrw();
|
||||||
|
copymem(w, data, sizeof(T) * count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
|
||||||
|
Vector<uint8_t> ret;
|
||||||
|
ret.resize(count * sizeof(T));
|
||||||
|
uint8_t *w = ret.ptrw();
|
||||||
|
copymem(w, data, sizeof(T) * count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ LocalVector() {}
|
||||||
|
_FORCE_INLINE_ LocalVector(const LocalVector &p_from) {
|
||||||
|
resize(p_from.size());
|
||||||
|
for (U i = 0; i < p_from.count; i++) {
|
||||||
|
data[i] = p_from.data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline LocalVector &operator=(const LocalVector &p_from) {
|
||||||
|
resize(p_from.size());
|
||||||
|
for (U i = 0; i < p_from.count; i++) {
|
||||||
|
data[i] = p_from.data[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline LocalVector &operator=(const Vector<T> &p_from) {
|
||||||
|
resize(p_from.size());
|
||||||
|
for (U i = 0; i < count; i++) {
|
||||||
|
data[i] = p_from[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ~LocalVector() {
|
||||||
|
if (data) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOCAL_VECTOR_H
|
|
@ -118,9 +118,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
|
||||||
last_keyrelease_time = 0;
|
last_keyrelease_time = 0;
|
||||||
xdnd_version = 0;
|
xdnd_version = 0;
|
||||||
|
|
||||||
if (get_render_thread_mode() == RENDER_SEPARATE_THREAD) {
|
XInitThreads();
|
||||||
XInitThreads();
|
|
||||||
}
|
events_mutex = Mutex::create();
|
||||||
|
|
||||||
/** XLIB INITIALIZATION **/
|
/** XLIB INITIALIZATION **/
|
||||||
x11_display = XOpenDisplay(NULL);
|
x11_display = XOpenDisplay(NULL);
|
||||||
|
@ -484,7 +484,6 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
|
||||||
im_position = Vector2();
|
im_position = Vector2();
|
||||||
|
|
||||||
if (xim && xim_style) {
|
if (xim && xim_style) {
|
||||||
|
|
||||||
xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
|
xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
|
||||||
if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
|
if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
|
||||||
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
|
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
|
||||||
|
@ -620,6 +619,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events_thread = Thread::create(_poll_events_thread, this);
|
||||||
|
|
||||||
update_real_mouse_position();
|
update_real_mouse_position();
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -752,13 +753,20 @@ void OS_X11::set_ime_active(const bool p_active) {
|
||||||
|
|
||||||
im_active = p_active;
|
im_active = p_active;
|
||||||
|
|
||||||
if (!xic)
|
if (!xic) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block events polling while changing input focus
|
||||||
|
// because it triggers some event polling internally.
|
||||||
if (p_active) {
|
if (p_active) {
|
||||||
XSetICFocus(xic);
|
{
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
XSetICFocus(xic);
|
||||||
|
}
|
||||||
set_ime_position(im_position);
|
set_ime_position(im_position);
|
||||||
} else {
|
} else {
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
XUnsetICFocus(xic);
|
XUnsetICFocus(xic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -774,7 +782,14 @@ void OS_X11::set_ime_position(const Point2 &p_pos) {
|
||||||
spot.x = short(p_pos.x);
|
spot.x = short(p_pos.x);
|
||||||
spot.y = short(p_pos.y);
|
spot.y = short(p_pos.y);
|
||||||
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
|
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
|
||||||
XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
|
|
||||||
|
{
|
||||||
|
// Block events polling during this call
|
||||||
|
// because it triggers some event polling internally.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
XFree(preedit_attr);
|
XFree(preedit_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,6 +809,10 @@ String OS_X11::get_unique_id() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::finalize() {
|
void OS_X11::finalize() {
|
||||||
|
events_thread_done = true;
|
||||||
|
Thread::wait_to_finish(events_thread);
|
||||||
|
memdelete(events_thread);
|
||||||
|
events_thread = nullptr;
|
||||||
|
|
||||||
if (main_loop)
|
if (main_loop)
|
||||||
memdelete(main_loop);
|
memdelete(main_loop);
|
||||||
|
@ -918,23 +937,19 @@ void OS_X11::warp_mouse_position(const Point2 &p_to) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::flush_mouse_motion() {
|
void OS_X11::flush_mouse_motion() {
|
||||||
while (true) {
|
// Block events polling while flushing motion events.
|
||||||
if (XPending(x11_display) > 0) {
|
MutexLock mutex_lock(events_mutex);
|
||||||
XEvent event;
|
|
||||||
XPeekEvent(x11_display, &event);
|
|
||||||
|
|
||||||
if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
|
for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) {
|
||||||
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
|
XEvent &event = polled_events[event_index];
|
||||||
|
if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
|
||||||
if (event_data->evtype == XI_RawMotion) {
|
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
|
||||||
XNextEvent(x11_display, &event);
|
if (event_data->evtype == XI_RawMotion) {
|
||||||
} else {
|
XFreeEventData(x11_display, &event.xcookie);
|
||||||
break;
|
polled_events.remove(event_index--);
|
||||||
}
|
continue;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
XFreeEventData(x11_display, &event.xcookie);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1732,7 +1747,7 @@ unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11
|
||||||
return last_button_state;
|
return last_button_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
|
||||||
|
|
||||||
// X11 functions don't know what const is
|
// X11 functions don't know what const is
|
||||||
XKeyEvent *xkeyevent = p_event;
|
XKeyEvent *xkeyevent = p_event;
|
||||||
|
@ -1859,7 +1874,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
||||||
/* Phase 4, determine if event must be filtered */
|
/* Phase 4, determine if event must be filtered */
|
||||||
|
|
||||||
// This seems to be a side-effect of using XIM.
|
// 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
|
// but it's actually just used to see if we must
|
||||||
// ignore a deadkey, or events XIM determines
|
// ignore a deadkey, or events XIM determines
|
||||||
// must not reach the actual gui.
|
// must not reach the actual gui.
|
||||||
|
@ -1887,8 +1902,8 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
||||||
|
|
||||||
// Echo characters in X11 are a keyrelease and a keypress
|
// Echo characters in X11 are a keyrelease and a keypress
|
||||||
// one after the other with the (almot) same timestamp.
|
// one after the other with the (almot) same timestamp.
|
||||||
// To detect them, i use XPeekEvent and check that their
|
// To detect them, i compare to the next event in list and
|
||||||
// difference in time is below a threshold.
|
// check that their difference in time is below a threshold.
|
||||||
|
|
||||||
if (xkeyevent->type != KeyPress) {
|
if (xkeyevent->type != KeyPress) {
|
||||||
|
|
||||||
|
@ -1896,9 +1911,8 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
||||||
|
|
||||||
// make sure there are events pending,
|
// make sure there are events pending,
|
||||||
// so this call won't block.
|
// so this call won't block.
|
||||||
if (XPending(x11_display) > 0) {
|
if (p_event_index + 1 < p_events.size()) {
|
||||||
XEvent peek_event;
|
XEvent &peek_event = p_events[p_event_index + 1];
|
||||||
XPeekEvent(x11_display, &peek_event);
|
|
||||||
|
|
||||||
// I'm using a threshold of 5 msecs,
|
// I'm using a threshold of 5 msecs,
|
||||||
// since sometimes there seems to be a little
|
// since sometimes there seems to be a little
|
||||||
|
@ -1911,9 +1925,9 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
||||||
KeySym rk;
|
KeySym rk;
|
||||||
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, NULL);
|
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, NULL);
|
||||||
if (rk == keysym_keycode) {
|
if (rk == keysym_keycode) {
|
||||||
XEvent event;
|
// Consume to next event.
|
||||||
XNextEvent(x11_display, &event); //erase next event
|
++p_event_index;
|
||||||
handle_key_event((XKeyEvent *)&event, true);
|
_handle_key_event((XKeyEvent *)&peek_event, p_events, p_event_index, true);
|
||||||
return; //ignore current, echo next
|
return; //ignore current, echo next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1965,6 +1979,66 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
|
||||||
input->accumulate_input_event(k);
|
input->accumulate_input_event(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OS_X11::_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 = OS::get_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);
|
||||||
|
}
|
||||||
|
|
||||||
struct Property {
|
struct Property {
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
int format, nitems;
|
int format, nitems;
|
||||||
|
@ -2043,6 +2117,65 @@ void OS_X11::_window_changed(XEvent *event) {
|
||||||
current_videomode.height = event->xconfigure.height;
|
current_videomode.height = event->xconfigure.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OS_X11::_poll_events_thread(void *ud) {
|
||||||
|
OS_X11 *os = (OS_X11 *)ud;
|
||||||
|
os->_poll_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool OS_X11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) {
|
||||||
|
// Just accept all events.
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_X11::_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, NULL)) {
|
||||||
|
// 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 OS_X11::process_xevents() {
|
void OS_X11::process_xevents() {
|
||||||
|
|
||||||
//printf("checking events %i\n", XPending(x11_display));
|
//printf("checking events %i\n", XPending(x11_display));
|
||||||
|
@ -2056,13 +2189,16 @@ void OS_X11::process_xevents() {
|
||||||
xi.tilt = Vector2();
|
xi.tilt = Vector2();
|
||||||
xi.pressure_supported = false;
|
xi.pressure_supported = false;
|
||||||
|
|
||||||
while (XPending(x11_display) > 0) {
|
LocalVector<XEvent> events;
|
||||||
XEvent event;
|
{
|
||||||
XNextEvent(x11_display, &event);
|
// Block events polling while flushing events.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
events = polled_events;
|
||||||
|
polled_events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (XFilterEvent(&event, None)) {
|
for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
|
||||||
continue;
|
XEvent &event = events[event_index];
|
||||||
}
|
|
||||||
|
|
||||||
if (XGetEventData(x11_display, &event.xcookie)) {
|
if (XGetEventData(x11_display, &event.xcookie)) {
|
||||||
|
|
||||||
|
@ -2273,6 +2409,9 @@ void OS_X11::process_xevents() {
|
||||||
}*/
|
}*/
|
||||||
#endif
|
#endif
|
||||||
if (xic) {
|
if (xic) {
|
||||||
|
// Block events polling while changing input focus
|
||||||
|
// because it triggers some event polling internally.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
XSetICFocus(xic);
|
XSetICFocus(xic);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2309,6 +2448,9 @@ void OS_X11::process_xevents() {
|
||||||
xi.state.clear();
|
xi.state.clear();
|
||||||
#endif
|
#endif
|
||||||
if (xic) {
|
if (xic) {
|
||||||
|
// Block events polling while changing input focus
|
||||||
|
// because it triggers some event polling internally.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
XUnsetICFocus(xic);
|
XUnsetICFocus(xic);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2381,11 +2523,11 @@ void OS_X11::process_xevents() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XPending(x11_display) > 0) {
|
if (event_index + 1 < events.size()) {
|
||||||
XEvent tevent;
|
const XEvent &next_event = events[event_index + 1];
|
||||||
XPeekEvent(x11_display, &tevent);
|
if (next_event.type == MotionNotify) {
|
||||||
if (tevent.type == MotionNotify) {
|
++event_index;
|
||||||
XNextEvent(x11_display, &event);
|
event = next_event;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2492,68 +2634,7 @@ void OS_X11::process_xevents() {
|
||||||
|
|
||||||
// key event is a little complex, so
|
// key event is a little complex, so
|
||||||
// it will be handled in its own function.
|
// it will be handled in its own function.
|
||||||
handle_key_event((XKeyEvent *)&event);
|
_handle_key_event((XKeyEvent *)&event, events, event_index);
|
||||||
} 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 = OS::get_clipboard().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);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case SelectionNotify:
|
case SelectionNotify:
|
||||||
|
@ -2694,14 +2775,25 @@ bool OS_X11::can_draw() const {
|
||||||
};
|
};
|
||||||
|
|
||||||
void OS_X11::set_clipboard(const String &p_text) {
|
void OS_X11::set_clipboard(const String &p_text) {
|
||||||
|
{
|
||||||
OS::set_clipboard(p_text);
|
// The clipboard content can be accessed while polling for events.
|
||||||
|
MutexLock mutex_lock(events_mutex);
|
||||||
|
OS::set_clipboard(p_text);
|
||||||
|
}
|
||||||
|
|
||||||
XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
|
XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
|
||||||
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime);
|
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
|
Bool OS_X11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
|
||||||
|
if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
|
||||||
|
return True;
|
||||||
|
} else {
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String OS_X11::_get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const {
|
||||||
|
|
||||||
String ret;
|
String ret;
|
||||||
|
|
||||||
|
@ -2710,24 +2802,27 @@ static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x
|
||||||
int format, result;
|
int format, result;
|
||||||
unsigned long len, bytes_left, dummy;
|
unsigned long len, bytes_left, dummy;
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
Window Sown = XGetSelectionOwner(x11_display, p_source);
|
Window selection_owner = XGetSelectionOwner(x11_display, p_source);
|
||||||
|
|
||||||
if (Sown == x11_window) {
|
if (selection_owner == x11_window) {
|
||||||
|
|
||||||
return p_internal_clipboard;
|
return OS::get_clipboard();
|
||||||
};
|
}
|
||||||
|
|
||||||
if (Sown != None) {
|
if (selection_owner != None) {
|
||||||
XConvertSelection(x11_display, p_source, target, selection,
|
{
|
||||||
x11_window, CurrentTime);
|
// Block events polling while processing selection events.
|
||||||
XFlush(x11_display);
|
MutexLock mutex_lock(events_mutex);
|
||||||
while (true) {
|
|
||||||
|
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;
|
XEvent event;
|
||||||
XNextEvent(x11_display, &event);
|
XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
|
||||||
if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
|
}
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Do not get any data, see how much data is there
|
// Do not get any data, see how much data is there
|
||||||
|
@ -2758,14 +2853,14 @@ static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
|
String OS_X11::_get_clipboard(Atom p_source, Window x11_window) const {
|
||||||
String ret;
|
String ret;
|
||||||
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
|
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
|
||||||
if (utf8_atom != None) {
|
if (utf8_atom != None) {
|
||||||
ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
|
ret = _get_clipboard_impl(p_source, x11_window, utf8_atom);
|
||||||
}
|
}
|
||||||
if (ret == "") {
|
if (ret.empty()) {
|
||||||
ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
|
ret = _get_clipboard_impl(p_source, x11_window, XA_STRING);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -2773,10 +2868,10 @@ static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_di
|
||||||
String OS_X11::get_clipboard() const {
|
String OS_X11::get_clipboard() const {
|
||||||
|
|
||||||
String ret;
|
String ret;
|
||||||
ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard());
|
ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window);
|
||||||
|
|
||||||
if (ret == "") {
|
if (ret.empty()) {
|
||||||
ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard());
|
ret = _get_clipboard(XA_PRIMARY, x11_window);
|
||||||
};
|
};
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#define OS_X11_H
|
#define OS_X11_H
|
||||||
|
|
||||||
#include "context_gl_x11.h"
|
#include "context_gl_x11.h"
|
||||||
|
#include "core/local_vector.h"
|
||||||
#include "core/os/input.h"
|
#include "core/os/input.h"
|
||||||
#include "crash_handler_x11.h"
|
#include "crash_handler_x11.h"
|
||||||
#include "drivers/alsa/audio_driver_alsa.h"
|
#include "drivers/alsa/audio_driver_alsa.h"
|
||||||
|
@ -155,7 +156,22 @@ class OS_X11 : public OS_Unix {
|
||||||
MouseMode mouse_mode;
|
MouseMode mouse_mode;
|
||||||
Point2i center;
|
Point2i center;
|
||||||
|
|
||||||
void handle_key_event(XKeyEvent *p_event, bool p_echo = false);
|
void _handle_key_event(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 _get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const;
|
||||||
|
String _get_clipboard(Atom p_source, Window x11_window) const;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
void process_xevents();
|
void process_xevents();
|
||||||
virtual void delete_main_loop();
|
virtual void delete_main_loop();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue