From 58a54f534e6b19a5b51410422500085ecf29dc8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= <pedrojrulez@gmail.com>
Date: Sun, 20 Sep 2020 21:30:34 +0200
Subject: [PATCH] Improve input event accumulation

- API has been simplified: all events now go through `parse_input_event()`. Whether they are accumulated or not depends on the `use_accumulated_input` flag.
- Event accumulation is now thread-safe (it was not needed so far, but it prepares the ground for the following changes).
- Touch drag events now support accumulation.
---
 core/os/input.h                 |  1 -
 core/os/input_event.cpp         | 16 ++++++++++++++++
 core/os/input_event.h           |  2 ++
 main/input_default.cpp          | 18 ++++++++----------
 main/input_default.h            |  1 -
 platform/osx/os_osx.mm          |  2 +-
 platform/windows/os_windows.cpp | 20 ++++++++++----------
 platform/x11/os_x11.cpp         | 16 ++++++++--------
 8 files changed, 45 insertions(+), 31 deletions(-)

diff --git a/core/os/input.h b/core/os/input.h
index 86791546f47..f3d1e322b07 100644
--- a/core/os/input.h
+++ b/core/os/input.h
@@ -136,7 +136,6 @@ public:
 	virtual int get_joy_axis_index_from_string(String p_axis) = 0;
 
 	virtual void parse_input_event(const Ref<InputEvent> &p_event) = 0;
-	virtual void accumulate_input_event(const Ref<InputEvent> &p_event) = 0;
 	virtual void flush_accumulated_events() = 0;
 	virtual void set_use_accumulated_input(bool p_enable) = 0;
 
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 3203194a548..a7189714132 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -966,6 +966,22 @@ String InputEventScreenDrag::as_text() const {
 	return "InputEventScreenDrag : index=" + itos(index) + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + ")";
 }
 
+bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) {
+	Ref<InputEventScreenDrag> drag = p_event;
+	if (drag.is_null())
+		return false;
+
+	if (get_index() != drag->get_index()) {
+		return false;
+	}
+
+	set_position(drag->get_position());
+	set_speed(drag->get_speed());
+	relative += drag->get_relative();
+
+	return true;
+}
+
 void InputEventScreenDrag::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenDrag::set_index);
 	ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenDrag::get_index);
diff --git a/core/os/input_event.h b/core/os/input_event.h
index c599b62a086..4db239e98ca 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -502,6 +502,8 @@ public:
 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
 	virtual String as_text() const;
 
+	virtual bool accumulate(const Ref<InputEvent> &p_event);
+
 	InputEventScreenDrag();
 };
 
diff --git a/main/input_default.cpp b/main/input_default.cpp
index 72965f43b38..e40a001d108 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -315,10 +315,6 @@ Vector3 InputDefault::get_gyroscope() const {
 	return gyroscope;
 }
 
-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) {
 	// Notes on mouse-touch emulation:
 	// - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects
@@ -327,8 +323,6 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool
 	// - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't
 	//   require additional handling by this class.
 
-	_THREAD_SAFE_METHOD_
-
 	Ref<InputEventKey> k = p_event;
 	if (k.is_valid() && !k->is_echo() && k->get_scancode() != 0) {
 		if (k->is_pressed()) {
@@ -697,11 +691,13 @@ void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_sh
 	OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
 }
 
-void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) {
+void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
+	_THREAD_SAFE_METHOD_
+
 	ERR_FAIL_COND(p_event.is_null());
 
 	if (!use_accumulated_input) {
-		parse_input_event(p_event);
+		_parse_input_event_impl(p_event, false);
 		return;
 	}
 	if (!accumulated_events.empty() && accumulated_events.back()->get()->accumulate(p_event)) {
@@ -711,8 +707,10 @@ void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) {
 	accumulated_events.push_back(p_event);
 }
 void InputDefault::flush_accumulated_events() {
+	_THREAD_SAFE_METHOD_
+
 	while (accumulated_events.front()) {
-		parse_input_event(accumulated_events.front()->get());
+		_parse_input_event_impl(accumulated_events.front()->get(), false);
 		accumulated_events.pop_front();
 	}
 }
@@ -736,7 +734,7 @@ void InputDefault::release_pressed_events() {
 }
 
 InputDefault::InputDefault() {
-	use_accumulated_input = true;
+	use_accumulated_input = false;
 	mouse_button_mask = 0;
 	emulate_touch_from_mouse = false;
 	emulate_mouse_from_touch = false;
diff --git a/main/input_default.h b/main/input_default.h
index dcafb4bb38c..7402444c734 100644
--- a/main/input_default.h
+++ b/main/input_default.h
@@ -302,7 +302,6 @@ public:
 	String get_joy_guid_remapped(int p_device) const;
 	void set_fallback_mapping(String p_guid);
 
-	virtual void accumulate_input_event(const Ref<InputEvent> &p_event);
 	virtual void flush_accumulated_events();
 	virtual void set_use_accumulated_input(bool p_enable);
 
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 472910d670a..4355a835db1 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -3216,7 +3216,7 @@ void OS_OSX::process_key_events() {
 
 void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
 	Ref<InputEvent> ev = p_event;
-	input->accumulate_input_event(ev);
+	input->parse_input_event(ev);
 }
 
 void OS_OSX::force_process_input() {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index ec4db7ed053..32807d0a5d2 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -283,7 +283,7 @@ void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) {
 	event->set_position(Vector2(p_x, p_y));
 
 	if (main_loop) {
-		input->accumulate_input_event(event);
+		input->parse_input_event(event);
 	}
 };
 
@@ -303,7 +303,7 @@ void OS_Windows::_drag_event(float p_x, float p_y, int idx) {
 	event->set_relative(Vector2(p_x, p_y) - curr->get());
 
 	if (main_loop)
-		input->accumulate_input_event(event);
+		input->parse_input_event(event);
 
 	curr->get() = Vector2(p_x, p_y);
 };
@@ -487,7 +487,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 				}
 
 				if (window_has_focus && main_loop && mm->get_relative() != Vector2())
-					input->accumulate_input_event(mm);
+					input->parse_input_event(mm);
 			}
 			delete[] lpb;
 		} break;
@@ -575,7 +575,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 					old_x = mm->get_position().x;
 					old_y = mm->get_position().y;
 					if (window_has_focus && main_loop)
-						input->accumulate_input_event(mm);
+						input->parse_input_event(mm);
 				}
 				return 0;
 			}
@@ -719,7 +719,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 			old_x = mm->get_position().x;
 			old_y = mm->get_position().y;
 			if (window_has_focus && main_loop)
-				input->accumulate_input_event(mm);
+				input->parse_input_event(mm);
 			return 0;
 		} break;
 		case WM_MOUSEMOVE: {
@@ -821,7 +821,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 			old_x = mm->get_position().x;
 			old_y = mm->get_position().y;
 			if (window_has_focus && main_loop)
-				input->accumulate_input_event(mm);
+				input->parse_input_event(mm);
 
 		} break;
 		case WM_LBUTTONDOWN:
@@ -984,14 +984,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 			mb->set_global_position(mb->get_position());
 
 			if (main_loop) {
-				input->accumulate_input_event(mb);
+				input->parse_input_event(mb);
 				if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) {
 					//send release for mouse wheel
 					Ref<InputEventMouseButton> mbd = mb->duplicate();
 					last_button_state &= ~(1 << (mbd->get_button_index() - 1));
 					mbd->set_button_mask(last_button_state);
 					mbd->set_pressed(false);
-					input->accumulate_input_event(mbd);
+					input->parse_input_event(mbd);
 				}
 			}
 		} break;
@@ -1222,7 +1222,7 @@ void OS_Windows::process_key_events() {
 					if (k->get_unicode() < 32)
 						k->set_unicode(0);
 
-					input->accumulate_input_event(k);
+					input->parse_input_event(k);
 				}
 
 				//do nothing
@@ -1261,7 +1261,7 @@ void OS_Windows::process_key_events() {
 
 				k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
 
-				input->accumulate_input_event(k);
+				input->parse_input_event(k);
 
 			} break;
 		}
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 4467fe7c69a..3d64d5674a5 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -1928,7 +1928,7 @@ void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events
 					k->set_shift(true);
 				}
 
-				input->accumulate_input_event(k);
+				input->parse_input_event(k);
 			}
 			memfree(utf8string);
 			return;
@@ -2075,7 +2075,7 @@ void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector<XEvent> &p_events
 	}
 
 	//printf("key: %x\n",k->get_scancode());
-	input->accumulate_input_event(k);
+	input->parse_input_event(k);
 }
 
 Atom OS_X11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
@@ -2491,13 +2491,13 @@ void OS_X11::process_xevents() {
 								// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
 								xi.mouse_pos_to_filter = pos;
 							}
-							input->accumulate_input_event(st);
+							input->parse_input_event(st);
 						} else {
 							if (!xi.state.has(index)) { // Defensive
 								break;
 							}
 							xi.state.erase(index);
-							input->accumulate_input_event(st);
+							input->parse_input_event(st);
 						}
 					} break;
 
@@ -2513,7 +2513,7 @@ void OS_X11::process_xevents() {
 							sd->set_index(index);
 							sd->set_position(pos);
 							sd->set_relative(pos - curr_pos_elem->value());
-							input->accumulate_input_event(sd);
+							input->parse_input_event(sd);
 
 							curr_pos_elem->value() = pos;
 						}
@@ -2607,7 +2607,7 @@ void OS_X11::process_xevents() {
 					st.instance();
 					st->set_index(E->key());
 					st->set_position(E->get());
-					input->accumulate_input_event(st);
+					input->parse_input_event(st);
 				}
 				xi.state.clear();
 #endif
@@ -2668,7 +2668,7 @@ void OS_X11::process_xevents() {
 					}
 				}
 
-				input->accumulate_input_event(mb);
+				input->parse_input_event(mb);
 
 			} break;
 			case MotionNotify: {
@@ -2783,7 +2783,7 @@ void OS_X11::process_xevents() {
 				// this is so that the relative motion doesn't get messed up
 				// after we regain focus.
 				if (window_has_focus || !mouse_mode_grab) {
-					input->accumulate_input_event(mm);
+					input->parse_input_event(mm);
 				}
 
 			} break;