Implement universal translation of touch to mouse

Now generating mouse events from touch is optional (on by default) and it's performed by `InputDefault` instead of having each OS abstraction doing it. (*)

The translation algorithm waits for a touch index to be pressed and tracks it translating its events to mouse events until it is raised, while ignoring other pointers.

Furthermore, to avoid an stuck "touch mouse", since not all platforms may report touches raised when the window is unfocused, it checks if touches are still down by the time it's focused again and if so it resets the state of the emulated mouse.

*: In the case of Windows, since it already provides touch-to-mouse translation by itself, "echo" mouse events are filtered out to have it working like the rest.

On X11 a little hack has been needed to avoid a case of a spurious mouse motion event that is generated during touch interaction.

Plus: Improve/fix tracking of current mouse position.
This commit is contained in:
Pedro J. Estébanez 2018-02-24 03:04:30 +01:00
parent 74e72c995d
commit e10a2e5999
16 changed files with 210 additions and 214 deletions

View File

@ -118,6 +118,7 @@ public:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
virtual bool is_emulating_touchscreen() const = 0;
virtual bool is_emulating_mouse_from_touch() const = 0;
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
virtual void set_mouse_in_window(bool p_in_window) = 0;

View File

@ -249,6 +249,11 @@ Vector3 InputDefault::get_gyroscope() const {
void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
_parse_input_event_impl(p_event, false);
}
void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
_THREAD_SAFE_METHOD_
Ref<InputEventKey> k = p_event;
@ -272,25 +277,30 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
mouse_button_mask &= ~(1 << (mb->get_button_index() - 1));
}
if (main_loop && emulate_touch && mb->get_button_index() == 1) {
Point2 pos = mb->get_global_position();
if (mouse_pos != pos) {
set_mouse_position(pos);
}
if (main_loop && emulate_touch && !p_is_emulated && mb->get_button_index() == 1) {
Ref<InputEventScreenTouch> touch_event;
touch_event.instance();
touch_event->set_pressed(mb->is_pressed());
touch_event->set_position(mb->get_position());
main_loop->input_event(touch_event);
}
Point2 pos = mb->get_global_position();
if (mouse_pos != pos) {
set_mouse_position(pos);
}
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (main_loop && emulate_touch && mm->get_button_mask() & 1) {
Point2 pos = mm->get_global_position();
if (mouse_pos != pos) {
set_mouse_position(pos);
}
if (main_loop && emulate_touch && !p_is_emulated && mm->get_button_mask() & 1) {
Ref<InputEventScreenDrag> drag_event;
drag_event.instance();
@ -302,6 +312,58 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
}
}
if (emulate_mouse_from_touch) {
Ref<InputEventScreenTouch> st = p_event;
if (st.is_valid()) {
bool translate = false;
if (st->is_pressed()) {
if (mouse_from_touch_index == -1) {
translate = true;
mouse_from_touch_index = st->get_index();
}
} else {
if (st->get_index() == mouse_from_touch_index) {
translate = true;
mouse_from_touch_index = -1;
}
}
if (translate) {
Ref<InputEventMouseButton> button_event;
button_event.instance();
button_event->set_position(st->get_position());
button_event->set_global_position(st->get_position());
button_event->set_pressed(st->is_pressed());
button_event->set_button_index(BUTTON_LEFT);
if (st->is_pressed()) {
button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1));
} else {
button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1));
}
_parse_input_event_impl(button_event, true);
}
}
Ref<InputEventScreenDrag> sd = p_event;
if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) {
Ref<InputEventMouseMotion> motion_event;
motion_event.instance();
motion_event->set_position(sd->get_position());
motion_event->set_global_position(sd->get_position());
motion_event->set_relative(sd->get_relative());
motion_event->set_speed(sd->get_speed());
motion_event->set_button_mask(mouse_button_mask);
_parse_input_event_impl(motion_event, true);
}
}
Ref<InputEventJoypadButton> jb = p_event;
if (jb.is_valid()) {
@ -498,6 +560,36 @@ bool InputDefault::is_emulating_touchscreen() const {
return emulate_touch;
}
// Calling this whenever the game window is focused helps unstucking the "touch mouse"
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
void InputDefault::ensure_touch_mouse_raised() {
if (mouse_from_touch_index != -1) {
mouse_from_touch_index = -1;
Ref<InputEventMouseButton> button_event;
button_event.instance();
button_event->set_position(mouse_pos);
button_event->set_global_position(mouse_pos);
button_event->set_pressed(false);
button_event->set_button_index(BUTTON_LEFT);
button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1));
_parse_input_event_impl(button_event, true);
}
}
void InputDefault::set_emulate_mouse_from_touch(bool p_emulate) {
emulate_mouse_from_touch = p_emulate;
}
bool InputDefault::is_emulating_mouse_from_touch() const {
return emulate_mouse_from_touch;
}
void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (Engine::get_singleton()->is_editor_hint())
return;
@ -815,6 +907,8 @@ InputDefault::InputDefault() {
mouse_button_mask = 0;
emulate_touch = false;
emulate_mouse_from_touch = false;
mouse_from_touch_index = -1;
main_loop = NULL;
hat_map_default[HAT_UP].type = TYPE_BUTTON;

View File

@ -60,6 +60,9 @@ class InputDefault : public Input {
Map<StringName, Action> action_state;
bool emulate_touch;
bool emulate_mouse_from_touch;
int mouse_from_touch_index;
struct VibrationInfo {
float weak_magnitude;
@ -175,6 +178,8 @@ private:
void _axis_event(int p_device, int p_axis, float p_value);
float _handle_deadzone(int p_device, int p_axis, float p_value);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
public:
virtual bool is_key_pressed(int p_scancode) const;
virtual bool is_mouse_button_pressed(int p_button) const;
@ -225,6 +230,10 @@ public:
void set_emulate_touch(bool p_emulate);
virtual bool is_emulating_touchscreen() const;
void ensure_touch_mouse_raised();
void set_emulate_mouse_from_touch(bool p_emulate);
virtual bool is_emulating_mouse_from_touch() const;
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
virtual void set_mouse_in_window(bool p_in_window);

View File

@ -1143,15 +1143,18 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
GLOBAL_DEF("application/config/icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp"));
if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false))) {
if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !editor) {
//only if no touchscreen ui hint, set emulation
InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
if (id)
if (id) {
if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false)) && !editor) {
if (!OS::get_singleton()->has_touchscreen_ui_hint()) {
//only if no touchscreen ui hint, set emulation
id->set_emulate_touch(true);
}
}
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("display/window/handheld/emulate_mouse_from_touch", true)));
}
MAIN_PRINT("Main: Load Remaps");
MAIN_PRINT("Main: Load Scene Types");

View File

@ -332,17 +332,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
if (touch.size()) {
//end all if exist
{
Ref<InputEventMouseButton> ev;
ev.instance();
ev->set_button_index(BUTTON_LEFT);
ev->set_button_mask(BUTTON_MASK_LEFT);
ev->set_pressed(false);
ev->set_position(touch[0].pos);
ev->set_global_position(touch[0].pos);
input->parse_input_event(ev);
}
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
@ -360,21 +349,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
touch[i].pos = p_points[i].pos;
}
{
//send mouse
Ref<InputEventMouseButton> ev;
ev.instance();
// ev.type = Ref<InputEvent>::MOUSE_BUTTON;
ev->set_button_index(BUTTON_LEFT);
ev->set_button_mask(BUTTON_MASK_LEFT);
ev->set_pressed(true);
ev->set_position(touch[0].pos);
ev->set_global_position(touch[0].pos);
input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y));
last_mouse = touch[0].pos;
input->parse_input_event(ev);
}
//send touch
for (int i = 0; i < touch.size(); i++) {
@ -389,19 +363,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
} break;
case 1: { //motion
if (p_points.size()) {
//send mouse, should look for point 0?
Ref<InputEventMouseMotion> ev;
ev.instance();
ev->set_button_mask(BUTTON_MASK_LEFT);
ev->set_position(p_points[0].pos);
input->set_mouse_position(Point2(ev->get_position().x, ev->get_position().y));
ev->set_speed(input->get_last_mouse_speed());
ev->set_relative(p_points[0].pos - last_mouse);
last_mouse = p_points[0].pos;
input->parse_input_event(ev);
}
ERR_FAIL_COND(touch.size() != p_points.size());
for (int i = 0; i < touch.size(); i++) {
@ -434,16 +395,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
if (touch.size()) {
//end all if exist
Ref<InputEventMouseButton> ev;
ev.instance();
ev->set_button_index(BUTTON_LEFT);
ev->set_button_mask(BUTTON_MASK_LEFT);
ev->set_pressed(false);
ev->set_position(touch[0].pos);
ev->set_global_position(touch[0].pos);
input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y));
input->parse_input_event(ev);
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;

View File

@ -93,7 +93,6 @@ public:
private:
Vector<TouchPos> touch;
Point2 last_mouse;
GFXInitFunc gfx_init_func;
void *gfx_init_ud;

View File

@ -489,7 +489,7 @@ static void clear_touches() {
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1, tid == 0);
OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
};
};
}
@ -506,10 +506,9 @@ static void clear_touches() {
continue;
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
int first = get_first_id(touch);
CGPoint touchPoint = [touch locationInView:self];
CGPoint prev_point = [touch previousLocationInView:self];
OSIPhone::get_singleton()->mouse_move(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, first == tid);
OSIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
};
};
}
@ -525,9 +524,9 @@ static void clear_touches() {
continue;
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
int rem = remove_touch(touch);
remove_touch(touch);
CGPoint touchPoint = [touch locationInView:self];
OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false, rem == 0);
OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
};
};
}

View File

@ -191,7 +191,7 @@ void OSIPhone::key(uint32_t p_key, bool p_pressed) {
queue_event(ev);
};
void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse) {
void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
if (!GLOBAL_DEF("debug/disable_touch", false)) {
Ref<InputEventScreenTouch> ev;
@ -203,28 +203,10 @@ void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_
queue_event(ev);
};
mouse_list.pressed[p_idx] = p_pressed;
if (p_use_as_mouse) {
Ref<InputEventMouseButton> ev;
ev.instance();
ev->set_position(Vector2(p_x, p_y));
ev->set_global_position(Vector2(p_x, p_y));
//mouse_list.pressed[p_idx] = p_pressed;
input->set_mouse_position(ev->get_position());
ev->set_button_index(BUTTON_LEFT);
ev->set_doubleclick(p_doubleclick);
ev->set_pressed(p_pressed);
queue_event(ev);
};
touch_list.pressed[p_idx] = p_pressed;
};
void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse) {
void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
if (!GLOBAL_DEF("debug/disable_touch", false)) {
@ -235,21 +217,6 @@ void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_
ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
queue_event(ev);
};
if (p_use_as_mouse) {
Ref<InputEventMouseMotion> ev;
ev.instance();
ev->set_position(Vector2(p_x, p_y));
ev->set_global_position(Vector2(p_x, p_y));
ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
input->set_mouse_position(ev->get_position());
ev->set_speed(input->get_last_mouse_speed());
ev->set_button_mask(BUTTON_LEFT); // pressed
queue_event(ev);
};
};
void OSIPhone::queue_event(const Ref<InputEvent> &p_event) {
@ -263,10 +230,10 @@ void OSIPhone::touches_cancelled() {
for (int i = 0; i < MAX_MOUSE_COUNT; i++) {
if (mouse_list.pressed[i]) {
if (touch_list.pressed[i]) {
// send a mouse_up outside the screen
mouse_button(i, -1, -1, false, false, false);
touch_press(i, -1, -1, false, false);
};
};
};
@ -377,7 +344,7 @@ Point2 OSIPhone::get_mouse_position() const {
int OSIPhone::get_mouse_button_state() const {
return mouse_list.pressed[0];
return false;
};
void OSIPhone::set_window_title(const String &p_title){};

View File

@ -106,7 +106,7 @@ private:
};
};
MouseList mouse_list;
MouseList touch_list;
Vector3 last_accel;
@ -127,8 +127,8 @@ public:
uint8_t get_orientations() const;
void mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse);
void mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse);
void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
void touches_cancelled();
void key(uint32_t p_key, bool p_pressed);
void set_virtual_keyboard_height(int p_height);
@ -151,7 +151,6 @@ public:
virtual void set_mouse_grab(bool p_grab);
virtual bool is_mouse_grab_enabled() const;
virtual Point2 get_mouse_position() const;
virtual int get_mouse_button_state() const;
virtual void set_window_title(const String &p_title);
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");

View File

@ -277,23 +277,6 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *
_input->parse_input_event(ev);
}
if (touch_event->touches[lowest_id_index].isChanged) {
Ref<InputEventMouseButton> ev_mouse;
ev_mouse.instance();
ev_mouse->set_button_mask(_input->get_mouse_button_mask());
dom2godot_mod(touch_event, ev_mouse);
const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY));
ev_mouse->set_global_position(ev_mouse->get_position());
ev_mouse->set_button_index(BUTTON_LEFT);
ev_mouse->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
_input->parse_input_event(ev_mouse);
}
return true;
}
@ -319,24 +302,6 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t
_input->parse_input_event(ev);
}
if (touch_event->touches[lowest_id_index].isChanged) {
Ref<InputEventMouseMotion> ev_mouse;
ev_mouse.instance();
dom2godot_mod(touch_event, ev_mouse);
ev_mouse->set_button_mask(_input->get_mouse_button_mask());
const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY));
ev_mouse->set_global_position(ev_mouse->get_position());
ev_mouse->set_relative(ev_mouse->get_position() - _input->get_mouse_position());
_input->set_mouse_position(ev_mouse->get_position());
ev_mouse->set_speed(_input->get_last_mouse_speed());
_input->parse_input_event(ev_mouse);
}
return true;
}

View File

@ -85,8 +85,7 @@ App::App() :
mWindowHeight(0),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
mEglSurface(EGL_NO_SURFACE),
number_of_contacts(0) {
mEglSurface(EGL_NO_SURFACE) {
}
// The first method called when the IFrameworkView is being created.
@ -271,10 +270,7 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
last_touch_y[screen_touch->get_index()] = pos.Y;
os->input_event(screen_touch);
if (number_of_contacts > 1)
return;
}; // fallthrought of sorts
} else {
Ref<InputEventMouseButton> mouse_button;
mouse_button.instance();
@ -302,17 +298,16 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
mouse_button->set_pressed(false);
os->input_event(mouse_button);
}
}
};
void App::OnPointerPressed(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
number_of_contacts++;
pointer_event(sender, args, true);
};
void App::OnPointerReleased(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
number_of_contacts--;
pointer_event(sender, args, false);
};
@ -351,7 +346,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint;
Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os);
if (point->IsInContact && _is_touch(point)) {
if (_is_touch(point)) {
Ref<InputEventScreenDrag> screen_drag;
screen_drag.instance();
@ -361,10 +356,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
screen_drag->set_relative(Vector2(screen_drag->get_position().x - last_touch_x[screen_drag->get_index()], screen_drag->get_position().y - last_touch_y[screen_drag->get_index()]));
os->input_event(screen_drag);
if (number_of_contacts > 1)
return;
}; // fallthrought of sorts
} else {
// In case the mouse grabbed, MouseMoved will handle this
if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED)
@ -381,6 +373,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
os->input_event(mouse_motion);
}
}
void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) {

View File

@ -107,7 +107,6 @@ namespace GodotUWP
int last_touch_x[32]; // 20 fingers, index 31 reserved for the mouse
int last_touch_y[32];
int number_of_contacts;
Windows::Foundation::Point last_mouse_pos;
};
}

View File

@ -361,6 +361,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
} break;
case WM_MOUSEMOVE: {
if (input->is_emulating_mouse_from_touch()) {
// Universal translation enabled; ignore OS translation
LPARAM extra = GetMessageExtraInfo();
if (IsPenEvent(extra)) {
break;
}
}
if (outside) {
//mouse enter
@ -386,18 +394,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
break;
/*
LPARAM extra = GetMessageExtraInfo();
if (IsPenEvent(extra)) {
int idx = extra & 0x7f;
_drag_event(idx, uMsg, wParam, lParam);
if (idx != 0) {
return 0;
};
// fallthrough for mouse event
};
*/
Ref<InputEventMouseMotion> mm;
mm.instance();
@ -467,18 +463,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
/*case WM_XBUTTONDOWN:
case WM_XBUTTONUP: */ {
/*
if (input->is_emulating_mouse_from_touch()) {
// Universal translation enabled; ignore OS translation
LPARAM extra = GetMessageExtraInfo();
if (IsPenEvent(extra)) {
int idx = extra & 0x7f;
_touch_event(idx, uMsg, wParam, lParam);
if (idx != 0) {
return 0;
};
// fallthrough for mouse event
};
*/
break;
}
}
Ref<InputEventMouseButton> mb;
mb.instance();

View File

@ -1626,6 +1626,11 @@ void OS_X11::process_xevents() {
if (touch.state.has(index)) // Defensive
break;
touch.state[index] = pos;
if (touch.state.size() == 1) {
// X11 may send a motion event when a touch gesture begins, that would result
// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
touch.mouse_pos_to_filter = pos;
}
input->parse_input_event(st);
} else {
if (!touch.state.has(index)) // Defensive
@ -1832,6 +1837,18 @@ void OS_X11::process_xevents() {
// to be able to send relative motion events.
Point2i pos(event.xmotion.x, event.xmotion.y);
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false;
// Adding some tolerance to match better Point2i to Vector2
if (touch.state.size() && Vector2(pos).distance_squared_to(touch.mouse_pos_to_filter) < 2) {
filter = true;
}
// Invalidate to avoid filtering a possible legitimate similar event coming later
touch.mouse_pos_to_filter = Vector2(1e10, 1e10);
if (filter) {
break;
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
if (pos == Point2i(current_videomode.width / 2, current_videomode.height / 2)) {

View File

@ -127,6 +127,7 @@ class OS_X11 : public OS_Unix {
Vector<int> devices;
XIEventMask event_mask;
Map<int, Vector2> state;
Vector2 mouse_pos_to_filter;
} touch;
#endif

View File

@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "io/marshalls.h"
#include "io/resource_loader.h"
#include "main/input_default.h"
#include "message_queue.h"
#include "node.h"
#include "os/keyboard.h"
@ -618,6 +619,13 @@ void SceneTree::_notification(int p_notification) {
case NOTIFICATION_WM_FOCUS_IN:
case NOTIFICATION_WM_FOCUS_OUT: {
if (p_notification == NOTIFICATION_WM_FOCUS_IN) {
InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
if (id) {
id->ensure_touch_mouse_raised();
}
}
get_root()->propagate_notification(p_notification);
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {