summaryrefslogtreecommitdiff
path: root/core/input/input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/input/input.cpp')
-rw-r--r--core/input/input.cpp153
1 files changed, 105 insertions, 48 deletions
diff --git a/core/input/input.cpp b/core/input/input.cpp
index cb65d2569c..b2164b8e76 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* input.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* input.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "input.h"
@@ -93,6 +93,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed);
ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed);
ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed);
+ ClassDB::bind_method(D_METHOD("is_key_label_pressed", "keycode"), &Input::is_key_label_pressed);
ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed);
ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed);
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact_match"), &Input::is_action_pressed, DEFVAL(false));
@@ -230,14 +231,17 @@ Input::VelocityTrack::VelocityTrack() {
bool Input::is_anything_pressed() const {
_THREAD_SAFE_METHOD_
+ if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty() || !mouse_button_mask.is_empty()) {
+ return true;
+ }
+
for (const KeyValue<StringName, Input::Action> &E : action_state) {
if (E.value.pressed) {
return true;
}
}
- return !keys_pressed.is_empty() ||
- !joy_buttons_pressed.is_empty() ||
- mouse_button_mask > MouseButton::NONE;
+
+ return false;
}
bool Input::is_key_pressed(Key p_keycode) const {
@@ -250,9 +254,14 @@ bool Input::is_physical_key_pressed(Key p_keycode) const {
return physical_keys_pressed.has(p_keycode);
}
+bool Input::is_key_label_pressed(Key p_keycode) const {
+ _THREAD_SAFE_METHOD_
+ return key_label_pressed.has(p_keycode);
+}
+
bool Input::is_mouse_button_pressed(MouseButton p_button) const {
_THREAD_SAFE_METHOD_
- return (mouse_button_mask & mouse_button_to_mask(p_button)) != MouseButton::NONE;
+ return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
}
static JoyAxis _combine_device(JoyAxis p_value, int p_device) {
@@ -343,8 +352,8 @@ float Input::get_axis(const StringName &p_negative_action, const StringName &p_p
Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone) const {
Vector2 vector = Vector2(
- get_action_raw_strength(p_positive_x) - get_action_raw_strength(p_negative_x),
- get_action_raw_strength(p_positive_y) - get_action_raw_strength(p_negative_y));
+ get_action_strength(p_positive_x) - get_action_strength(p_negative_x),
+ get_action_strength(p_positive_y) - get_action_strength(p_negative_y));
if (p_deadzone < 0.0f) {
// If the deadzone isn't specified, get it from the average of the actions.
@@ -499,14 +508,21 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
physical_keys_pressed.erase(k->get_physical_keycode());
}
}
+ if (k.is_valid() && !k->is_echo() && k->get_key_label() != Key::NONE) {
+ if (k->is_pressed()) {
+ key_label_pressed.insert(k->get_key_label());
+ } else {
+ key_label_pressed.erase(k->get_key_label());
+ }
+ }
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
if (mb->is_pressed()) {
- mouse_button_mask |= mouse_button_to_mask(mb->get_button_index());
+ mouse_button_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- mouse_button_mask &= ~mouse_button_to_mask(mb->get_button_index());
+ mouse_button_mask.clear_flag(mouse_button_to_mask(mb->get_button_index()));
}
Point2 pos = mb->get_global_position();
@@ -520,6 +536,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
touch_event->set_pressed(mb->is_pressed());
touch_event->set_position(mb->get_position());
touch_event->set_double_tap(mb->is_double_click());
+ touch_event->set_device(InputEvent::DEVICE_ID_EMULATION);
event_dispatch_function(touch_event);
}
}
@@ -534,13 +551,17 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Vector2 relative = mm->get_relative();
mouse_velocity_track.update(relative);
- if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
+ if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
Ref<InputEventScreenDrag> drag_event;
drag_event.instantiate();
drag_event->set_position(position);
drag_event->set_relative(relative);
+ drag_event->set_tilt(mm->get_tilt());
+ drag_event->set_pen_inverted(mm->get_pen_inverted());
+ drag_event->set_pressure(mm->get_pressure());
drag_event->set_velocity(get_last_mouse_velocity());
+ drag_event->set_device(InputEvent::DEVICE_ID_EMULATION);
event_dispatch_function(drag_event);
}
@@ -576,17 +597,20 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventMouseButton> button_event;
button_event.instantiate();
- button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ button_event->set_device(InputEvent::DEVICE_ID_EMULATION);
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(MouseButton::LEFT);
button_event->set_double_click(st->is_double_tap());
+
+ BitField<MouseButtonMask> ev_bm = mouse_button_mask;
if (st->is_pressed()) {
- button_event->set_button_mask(MouseButton(mouse_button_mask | MouseButton::MASK_LEFT));
+ ev_bm.set_flag(MouseButtonMask::LEFT);
} else {
- button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));
+ ev_bm.clear_flag(MouseButtonMask::LEFT);
}
+ button_event->set_button_mask(ev_bm);
_parse_input_event_impl(button_event, true);
}
@@ -604,7 +628,10 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventMouseMotion> motion_event;
motion_event.instantiate();
- motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ motion_event->set_device(InputEvent::DEVICE_ID_EMULATION);
+ motion_event->set_tilt(sd->get_tilt());
+ motion_event->set_pen_inverted(sd->get_pen_inverted());
+ motion_event->set_pressure(sd->get_pressure());
motion_event->set_position(sd->get_position());
motion_event->set_global_position(sd->get_position());
motion_event->set_relative(sd->get_relative());
@@ -734,7 +761,7 @@ Point2 Input::get_last_mouse_velocity() {
return mouse_velocity_track.velocity;
}
-MouseButton Input::get_mouse_button_mask() const {
+BitField<MouseButtonMask> Input::get_mouse_button_mask() const {
return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state();
}
@@ -810,12 +837,14 @@ void Input::ensure_touch_mouse_raised() {
Ref<InputEventMouseButton> button_event;
button_event.instantiate();
- button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ button_event->set_device(InputEvent::DEVICE_ID_EMULATION);
button_event->set_position(mouse_pos);
button_event->set_global_position(mouse_pos);
button_event->set_pressed(false);
button_event->set_button_index(MouseButton::LEFT);
- button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));
+ BitField<MouseButtonMask> ev_bm = mouse_button_mask;
+ ev_bm.clear_flag(MouseButtonMask::LEFT);
+ button_event->set_button_mask(ev_bm);
_parse_input_event_impl(button_event, true);
}
@@ -845,6 +874,7 @@ void Input::set_default_cursor_shape(CursorShape p_shape) {
mm.instantiate();
mm->set_position(mouse_pos);
mm->set_global_position(mouse_pos);
+ mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
parse_input_event(mm);
}
@@ -867,6 +897,31 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
+#ifdef DEBUG_ENABLED
+ uint64_t curr_frame = Engine::get_singleton()->get_process_frames();
+ if (curr_frame != last_parsed_frame) {
+ frame_parsed_events.clear();
+ last_parsed_frame = curr_frame;
+ frame_parsed_events.insert(p_event);
+ } else if (frame_parsed_events.has(p_event)) {
+ // It would be technically safe to send the same event in cases such as:
+ // - After an explicit flush.
+ // - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes.
+ // - If platform doesn't use buffering and event accumulation is disabled.
+ // - If platform doesn't use buffering and the event type is not accumulable.
+ // However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times
+ // of the possibilities of the target platform, project settings and engine internals, which may change
+ // without prior notice.
+ // Therefore, the guideline is, "don't send the same event object more than once per frame".
+ WARN_PRINT_ONCE(
+ "An input event object is being parsed more than once in the same frame, which is unsafe.\n"
+ "If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame.\n"
+ "You can call duplicate() on the event to get a new instance with identical values.");
+ } else {
+ frame_parsed_events.insert(p_event);
+ }
+#endif
+
if (use_accumulated_input) {
if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) {
buffered_events.push_back(p_event);
@@ -908,6 +963,7 @@ void Input::release_pressed_events() {
keys_pressed.clear();
physical_keys_pressed.clear();
+ key_label_pressed.clear();
joy_buttons_pressed.clear();
_joy_axis.clear();
@@ -1016,7 +1072,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
}
}
-void Input::joy_hat(int p_device, HatMask p_val) {
+void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
_THREAD_SAFE_METHOD_;
const Joypad &joy = joy_names[p_device];
@@ -1309,8 +1365,9 @@ void Input::parse_mapping(String p_mapping) {
JoyButton output_button = _get_output_button(output);
JoyAxis output_axis = _get_output_axis(output);
- ERR_CONTINUE_MSG(output_button == JoyButton::INVALID && output_axis == JoyAxis::INVALID,
- vformat("Unrecognized output string \"%s\" in mapping:\n%s", output, p_mapping));
+ if (output_button == JoyButton::INVALID && output_axis == JoyAxis::INVALID) {
+ print_verbose(vformat("Unrecognized output string \"%s\" in mapping:\n%s", output, p_mapping));
+ }
ERR_CONTINUE_MSG(output_button != JoyButton::INVALID && output_axis != JoyAxis::INVALID,
vformat("Output string \"%s\" matched both button and axis in mapping:\n%s", output, p_mapping));