diff options
author | Pedro J. Estébanez <pedrojrulez@gmail.com> | 2018-02-24 03:04:30 +0100 |
---|---|---|
committer | Pedro J. Estébanez <pedrojrulez@gmail.com> | 2018-04-30 19:03:38 +0200 |
commit | de9d40a9537bafa613dc54f2200b7509ad6fa9e3 (patch) | |
tree | 746f887af7e07a5e89878500fc713cd5548409ac /main | |
parent | 3a5b25d5b489ad88c2861c9c37b56469580fbf03 (diff) | |
download | redot-engine-de9d40a9537bafa613dc54f2200b7509ad6fa9e3.tar.gz |
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.
** Summary of changes to settings: **
- `display/window/handheld/emulate_touchscreen` becomes `input/pointing_devices/emulate_touch_from_mouse`
- New setting: `input/pointing_devices/emulate_mouse_from_touch`
Diffstat (limited to 'main')
-rw-r--r-- | main/input_default.cpp | 118 | ||||
-rw-r--r-- | main/input_default.h | 15 | ||||
-rw-r--r-- | main/main.cpp | 15 |
3 files changed, 127 insertions, 21 deletions
diff --git a/main/input_default.cpp b/main/input_default.cpp index 1c73ecf2d2..4f0915a4f3 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -250,6 +250,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; @@ -273,25 +278,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_from_mouse && !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_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) { Ref<InputEventScreenDrag> drag_event; drag_event.instance(); @@ -303,6 +313,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()) { @@ -485,14 +547,44 @@ void InputDefault::action_release(const StringName &p_action) { action_state[p_action] = action; } -void InputDefault::set_emulate_touch(bool p_emulate) { +void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) { + + emulate_touch_from_mouse = p_emulate; +} + +bool InputDefault::is_emulating_touch_from_mouse() const { + + return emulate_touch_from_mouse; +} + +// 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_touch = p_emulate; + emulate_mouse_from_touch = p_emulate; } -bool InputDefault::is_emulating_touchscreen() const { +bool InputDefault::is_emulating_mouse_from_touch() const { - return emulate_touch; + return emulate_mouse_from_touch; } Input::CursorShape InputDefault::get_default_cursor_shape() { @@ -529,7 +621,9 @@ void InputDefault::set_mouse_in_window(bool p_in_window) { InputDefault::InputDefault() { mouse_button_mask = 0; - emulate_touch = false; + emulate_touch_from_mouse = false; + emulate_mouse_from_touch = false; + mouse_from_touch_index = -1; main_loop = NULL; hat_map_default[HAT_UP].type = TYPE_BUTTON; diff --git a/main/input_default.h b/main/input_default.h index 384b04cf41..827c9dbe2a 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -59,7 +59,10 @@ class InputDefault : public Input { Map<StringName, Action> action_state; - bool emulate_touch; + bool emulate_touch_from_mouse; + 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; @@ -223,8 +228,12 @@ public: void iteration(float p_step); - void set_emulate_touch(bool p_emulate); - virtual bool is_emulating_touchscreen() const; + void set_emulate_touch_from_mouse(bool p_emulate); + virtual bool is_emulating_touch_from_mouse() const; + void ensure_touch_mouse_raised(); + + void set_emulate_mouse_from_touch(bool p_emulate); + virtual bool is_emulating_mouse_from_touch() const; virtual CursorShape get_default_cursor_shape(); virtual void set_default_cursor_shape(CursorShape p_shape); diff --git a/main/main.cpp b/main/main.cpp index a59ca3da3b..a22ae944a7 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1141,13 +1141,16 @@ 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 || project_manager)) { - //only if no touchscreen ui hint, set emulation - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); - if (id) - id->set_emulate_touch(true); + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + if (bool(GLOBAL_DEF("input/pointing_devices/emulate_touch_from_mouse", false)) && !(editor || project_manager)) { + if (!OS::get_singleton()->has_touchscreen_ui_hint()) { + //only if no touchscreen ui hint, set emulation + id->set_emulate_touch_from_mouse(true); + } } + + id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input/pointing_devices/emulate_mouse_from_touch", true))); } MAIN_PRINT("Main: Load Remaps"); |