From 8e6960a69e0202cb1e6f3b3dc9f9bb34e1c1d889 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Wed, 4 Mar 2020 13:36:09 -0300 Subject: Refactored input, goes all via windows now. Also renamed Input to InputFilter because all it does is filter events. --- core/debugger/remote_debugger.cpp | 12 +- core/input/input.cpp | 1320 -------------------- core/input/input.h | 335 ----- core/input/input_filter.cpp | 1309 +++++++++++++++++++ core/input/input_filter.h | 337 +++++ core/os/main_loop.cpp | 30 - core/os/main_loop.h | 11 - core/os/midi_driver.cpp | 4 +- core/os/os.cpp | 2 +- core/register_core_types.cpp | 6 +- editor/animation_track_editor.cpp | 4 +- editor/code_editor.cpp | 6 +- editor/editor_audio_buses.cpp | 6 +- editor/editor_help.cpp | 4 +- editor/editor_node.cpp | 24 +- editor/editor_plugin_settings.cpp | 2 +- editor/editor_spin_slider.cpp | 26 +- editor/export_template_manager.cpp | 4 +- editor/plugins/animation_blend_space_2d_editor.cpp | 2 +- .../plugins/animation_blend_tree_editor_plugin.cpp | 2 +- editor/plugins/animation_player_editor_plugin.cpp | 4 +- editor/plugins/animation_state_machine_editor.cpp | 2 +- editor/plugins/animation_tree_editor_plugin.cpp | 2 +- editor/plugins/asset_library_editor_plugin.cpp | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 28 +- editor/plugins/collision_polygon_editor_plugin.cpp | 4 +- editor/plugins/curve_editor_plugin.cpp | 4 +- editor/plugins/polygon_2d_editor_plugin.cpp | 4 +- editor/plugins/script_editor_plugin.cpp | 6 +- editor/plugins/shader_editor_plugin.cpp | 2 +- editor/plugins/spatial_editor_plugin.cpp | 24 +- editor/plugins/texture_region_editor_plugin.cpp | 8 +- editor/plugins/tile_map_editor_plugin.cpp | 6 +- editor/plugins/tile_set_editor_plugin.cpp | 6 +- editor/plugins/visual_shader_editor_plugin.cpp | 6 +- editor/project_manager.cpp | 7 +- editor/property_editor.cpp | 6 +- editor/scene_tree_dock.cpp | 6 +- main/main.cpp | 11 +- modules/arkit/arkit_interface.mm | 2 +- modules/gdnative/arvr/arvr_interface_gdnative.cpp | 12 +- modules/gdnative/nativescript/nativescript.cpp | 4 +- modules/gridmap/grid_map_editor_plugin.cpp | 2 +- modules/mobile_vr/mobile_vr_interface.cpp | 4 +- modules/visual_script/visual_script_editor.cpp | 28 +- modules/visual_script/visual_script_nodes.cpp | 10 +- platform/android/java_godot_lib_jni.cpp | 4 +- platform/android/os_android.cpp | 6 +- platform/android/os_android.h | 2 +- platform/haiku/haiku_direct_window.cpp | 2 +- platform/haiku/haiku_direct_window.h | 2 +- platform/haiku/os_haiku.h | 2 +- platform/iphone/os_iphone.h | 4 +- platform/javascript/os_javascript.cpp | 12 +- platform/javascript/os_javascript.h | 2 +- platform/linuxbsd/display_server_x11.cpp | 124 +- platform/linuxbsd/display_server_x11.h | 14 +- platform/linuxbsd/joypad_linux.cpp | 22 +- platform/linuxbsd/joypad_linux.h | 10 +- platform/linuxbsd/os_linuxbsd.cpp | 2 +- platform/linuxbsd/os_linuxbsd.h | 2 +- platform/osx/joypad_osx.cpp | 2 +- platform/osx/joypad_osx.h | 2 +- platform/osx/os_osx.h | 2 +- platform/server/os_server.h | 2 +- platform/uwp/joypad_uwp.h | 2 +- platform/uwp/os_uwp.h | 2 +- platform/windows/os_windows.cpp | 14 +- platform/windows/os_windows.h | 2 +- scene/2d/canvas_item.cpp | 2 +- scene/2d/touch_screen_button.cpp | 12 +- scene/3d/arvr_nodes.cpp | 8 +- scene/gui/color_picker.cpp | 6 +- scene/gui/control.cpp | 5 - scene/gui/graph_edit.cpp | 20 +- scene/gui/line_edit.cpp | 4 +- scene/gui/popup_menu.cpp | 6 +- scene/gui/spin_box.cpp | 9 +- scene/gui/text_edit.cpp | 8 +- scene/gui/tree.cpp | 18 +- scene/main/node.cpp | 3 +- scene/main/node.h | 15 +- scene/main/scene_tree.cpp | 193 +-- scene/main/scene_tree.h | 14 +- scene/main/viewport.cpp | 178 ++- scene/main/viewport.h | 8 +- scene/main/window.cpp | 90 +- scene/main/window.h | 10 + scene/scene_string_names.cpp | 4 + scene/scene_string_names.h | 4 + servers/arvr/arvr_positional_tracker.cpp | 2 +- servers/display_server.cpp | 37 +- servers/display_server.h | 25 +- 93 files changed, 2309 insertions(+), 2256 deletions(-) delete mode 100644 core/input/input.cpp delete mode 100644 core/input/input.h create mode 100644 core/input/input_filter.cpp create mode 100644 core/input/input_filter.h diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 1cf18239ab..b9db8ab1b0 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -33,7 +33,7 @@ #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" -#include "core/input/input.h" +#include "core/input/input_filter.h" #include "core/os/os.h" #include "core/project_settings.h" #include "core/script_language.h" @@ -659,9 +659,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug. - Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode(); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + InputFilter::MouseMode mouse_mode = InputFilter::get_singleton()->get_mouse_mode(); + if (mouse_mode != InputFilter::MOUSE_MODE_VISIBLE) + InputFilter::get_singleton()->set_mouse_mode(InputFilter::MOUSE_MODE_VISIBLE); uint64_t loop_begin_usec = 0; uint64_t loop_time_sec = 0; @@ -779,8 +779,8 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { send_message("debug_exit", Array()); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) - Input::get_singleton()->set_mouse_mode(mouse_mode); + if (mouse_mode != InputFilter::MOUSE_MODE_VISIBLE) + InputFilter::get_singleton()->set_mouse_mode(mouse_mode); } void RemoteDebugger::poll_events(bool p_is_idle) { diff --git a/core/input/input.cpp b/core/input/input.cpp deleted file mode 100644 index d40feffe97..0000000000 --- a/core/input/input.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -/*************************************************************************/ -/* input.cpp */ -/*************************************************************************/ -/* 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. */ -/*************************************************************************/ - -#include "input.h" - -#include "core/input/default_controller_mappings.h" -#include "core/input/input_map.h" -#include "core/os/os.h" -#include "core/project_settings.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - -Input *Input::singleton = NULL; - -void (*Input::set_mouse_mode_func)(Input::MouseMode) = nullptr; -Input::MouseMode (*Input::get_mouse_mode_func)() = nullptr; -void (*Input::warp_mouse_func)(const Vector2 &p_to_pos) = nullptr; -Input::CursorShape (*Input::get_current_cursor_shape_func)() = nullptr; -void (*Input::set_custom_mouse_cursor_func)(const RES &, Input::CursorShape, const Vector2 &) = nullptr; - -Input *Input::get_singleton() { - - return singleton; -} - -void Input::set_mouse_mode(MouseMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 4); - set_mouse_mode_func(p_mode); -} - -Input::MouseMode Input::get_mouse_mode() const { - - return get_mouse_mode_func(); -} - -void Input::_bind_methods() { - - ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_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"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); - ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed); - ClassDB::bind_method(D_METHOD("is_joy_known", "device"), &Input::is_joy_known); - ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); - ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); - ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); - ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); - ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); - ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); - ClassDB::bind_method(D_METHOD("get_joy_button_string", "button_index"), &Input::get_joy_button_string); - ClassDB::bind_method(D_METHOD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); - ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); - ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); - ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); - ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); - ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); - ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); - //ClassDB::bind_method(D_METHOD("get_mouse_position"),&Input::get_mouse_position); - this is not the function you want - ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &Input::get_last_mouse_speed); - ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask); - ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); - ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode); - ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position); - ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); - ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); - ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); - ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); - ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); - ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); - ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); - - BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); - BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); - BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); - BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); - - BIND_ENUM_CONSTANT(CURSOR_ARROW); - BIND_ENUM_CONSTANT(CURSOR_IBEAM); - BIND_ENUM_CONSTANT(CURSOR_POINTING_HAND); - BIND_ENUM_CONSTANT(CURSOR_CROSS); - BIND_ENUM_CONSTANT(CURSOR_WAIT); - BIND_ENUM_CONSTANT(CURSOR_BUSY); - BIND_ENUM_CONSTANT(CURSOR_DRAG); - BIND_ENUM_CONSTANT(CURSOR_CAN_DROP); - BIND_ENUM_CONSTANT(CURSOR_FORBIDDEN); - BIND_ENUM_CONSTANT(CURSOR_VSIZE); - BIND_ENUM_CONSTANT(CURSOR_HSIZE); - BIND_ENUM_CONSTANT(CURSOR_BDIAGSIZE); - BIND_ENUM_CONSTANT(CURSOR_FDIAGSIZE); - BIND_ENUM_CONSTANT(CURSOR_MOVE); - BIND_ENUM_CONSTANT(CURSOR_VSPLIT); - BIND_ENUM_CONSTANT(CURSOR_HSPLIT); - BIND_ENUM_CONSTANT(CURSOR_HELP); - - ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); -} - -void Input::get_argument_options(const StringName &p_function, int p_idx, List *r_options) const { -#ifdef TOOLS_ENABLED - - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; - - String pf = p_function; - if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { - - List pinfo; - ProjectSettings::get_singleton()->get_property_list(&pinfo); - - for (List::Element *E = pinfo.front(); E; E = E->next()) { - const PropertyInfo &pi = E->get(); - - if (!pi.name.begins_with("input/")) - continue; - - String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); - r_options->push_back(quote_style + name + quote_style); - } - } -#endif -} - -void Input::SpeedTrack::update(const Vector2 &p_delta_p) { - - uint64_t tick = OS::get_singleton()->get_ticks_usec(); - uint32_t tdiff = tick - last_tick; - float delta_t = tdiff / 1000000.0; - last_tick = tick; - - accum += p_delta_p; - accum_t += delta_t; - - if (accum_t > max_ref_frame * 10) - accum_t = max_ref_frame * 10; - - while (accum_t >= min_ref_frame) { - - float slice_t = min_ref_frame / accum_t; - Vector2 slice = accum * slice_t; - accum = accum - slice; - accum_t -= min_ref_frame; - - speed = (slice / min_ref_frame).linear_interpolate(speed, min_ref_frame / max_ref_frame); - } -} - -void Input::SpeedTrack::reset() { - last_tick = OS::get_singleton()->get_ticks_usec(); - speed = Vector2(); - accum_t = 0; -} - -Input::SpeedTrack::SpeedTrack() { - - min_ref_frame = 0.1; - max_ref_frame = 0.3; - reset(); -} - -bool Input::is_key_pressed(int p_keycode) const { - - _THREAD_SAFE_METHOD_ - return keys_pressed.has(p_keycode); -} - -bool Input::is_mouse_button_pressed(int p_button) const { - - _THREAD_SAFE_METHOD_ - return (mouse_button_mask & (1 << (p_button - 1))) != 0; -} - -static int _combine_device(int p_value, int p_device) { - - return p_value | (p_device << 20); -} - -bool Input::is_joy_button_pressed(int p_device, int p_button) const { - - _THREAD_SAFE_METHOD_ - return joy_buttons_pressed.has(_combine_device(p_button, p_device)); -} - -bool Input::is_action_pressed(const StringName &p_action) const { - - return action_state.has(p_action) && action_state[p_action].pressed; -} - -bool Input::is_action_just_pressed(const StringName &p_action) const { - - const Map::Element *E = action_state.find(p_action); - if (!E) - return false; - - if (Engine::get_singleton()->is_in_physics_frame()) { - return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); - } else { - return E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); - } -} - -bool Input::is_action_just_released(const StringName &p_action) const { - - const Map::Element *E = action_state.find(p_action); - if (!E) - return false; - - if (Engine::get_singleton()->is_in_physics_frame()) { - return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); - } else { - return !E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); - } -} - -float Input::get_action_strength(const StringName &p_action) const { - const Map::Element *E = action_state.find(p_action); - if (!E) - return 0.0f; - - return E->get().strength; -} - -float Input::get_joy_axis(int p_device, int p_axis) const { - - _THREAD_SAFE_METHOD_ - int c = _combine_device(p_axis, p_device); - if (_joy_axis.has(c)) { - return _joy_axis[c]; - } else { - return 0; - } -} - -String Input::get_joy_name(int p_idx) { - - _THREAD_SAFE_METHOD_ - return joy_names[p_idx].name; -}; - -Vector2 Input::get_joy_vibration_strength(int p_device) { - if (joy_vibration.has(p_device)) { - return Vector2(joy_vibration[p_device].weak_magnitude, joy_vibration[p_device].strong_magnitude); - } else { - return Vector2(0, 0); - } -} - -uint64_t Input::get_joy_vibration_timestamp(int p_device) { - if (joy_vibration.has(p_device)) { - return joy_vibration[p_device].timestamp; - } else { - return 0; - } -} - -float Input::get_joy_vibration_duration(int p_device) { - if (joy_vibration.has(p_device)) { - return joy_vibration[p_device].duration; - } else { - return 0.f; - } -} - -static String _hex_str(uint8_t p_byte) { - - static const char *dict = "0123456789abcdef"; - char ret[3]; - ret[2] = 0; - - ret[0] = dict[p_byte >> 4]; - ret[1] = dict[p_byte & 0xf]; - - return ret; -}; - -void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { - - _THREAD_SAFE_METHOD_ - Joypad js; - js.name = p_connected ? p_name : ""; - js.uid = p_connected ? p_guid : ""; - - if (p_connected) { - - String uidname = p_guid; - if (p_guid == "") { - int uidlen = MIN(p_name.length(), 16); - for (int i = 0; i < uidlen; i++) { - uidname = uidname + _hex_str(p_name[i]); - }; - }; - js.uid = uidname; - js.connected = true; - int mapping = fallback_mapping; - for (int i = 0; i < map_db.size(); i++) { - if (js.uid == map_db[i].uid) { - mapping = i; - js.name = map_db[i].name; - }; - }; - js.mapping = mapping; - } else { - js.connected = false; - for (int i = 0; i < JOY_BUTTON_MAX; i++) { - - if (i < JOY_AXIS_MAX) - set_joy_axis(p_idx, i, 0.0f); - - int c = _combine_device(i, p_idx); - joy_buttons_pressed.erase(c); - }; - }; - joy_names[p_idx] = js; - - emit_signal("joy_connection_changed", p_idx, p_connected); -}; - -Vector3 Input::get_gravity() const { - - _THREAD_SAFE_METHOD_ - return gravity; -} - -Vector3 Input::get_accelerometer() const { - - _THREAD_SAFE_METHOD_ - return accelerometer; -} - -Vector3 Input::get_magnetometer() const { - - _THREAD_SAFE_METHOD_ - return magnetometer; -} - -Vector3 Input::get_gyroscope() const { - - _THREAD_SAFE_METHOD_ - return gyroscope; -} - -void Input::parse_drop_files(const Vector &p_files) { - if (main_loop) { - main_loop->drop_files(p_files); - } -} -void Input::parse_notification(int p_notification) { - if (main_loop) { - main_loop->notification(p_notification); - } -} - -void Input::parse_input_event(const Ref &p_event) { - - _parse_input_event_impl(p_event, false); -} - -void Input::_parse_input_event_impl(const Ref &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 - // as true mouse events. The only difference is the situation is flagged as emulated so they are not - // emulated back to touch events in an endless loop. - // - 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 k = p_event; - if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) { - if (k->is_pressed()) - keys_pressed.insert(k->get_keycode()); - else - keys_pressed.erase(k->get_keycode()); - } - - Ref mb = p_event; - - if (mb.is_valid()) { - - if (mb->is_pressed()) { - mouse_button_mask |= (1 << (mb->get_button_index() - 1)); - } else { - mouse_button_mask &= ~(1 << (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 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); - } - } - - Ref mm = p_event; - - if (mm.is_valid()) { - - 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 drag_event; - drag_event.instance(); - - drag_event->set_position(mm->get_position()); - drag_event->set_relative(mm->get_relative()); - drag_event->set_speed(mm->get_speed()); - - main_loop->input_event(drag_event); - } - } - - Ref st = p_event; - - if (st.is_valid()) { - - if (st->is_pressed()) { - SpeedTrack &track = touch_speed_track[st->get_index()]; - track.reset(); - } else { - // Since a pointer index may not occur again (OSs may or may not reuse them), - // imperatively remove it from the map to keep no fossil entries in it - touch_speed_track.erase(st->get_index()); - } - - if (emulate_mouse_from_touch) { - - 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 button_event; - button_event.instance(); - - button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); - 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 sd = p_event; - - if (sd.is_valid()) { - - SpeedTrack &track = touch_speed_track[sd->get_index()]; - track.update(sd->get_relative()); - sd->set_speed(track.speed); - - if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) { - - Ref motion_event; - motion_event.instance(); - - motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); - 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 jb = p_event; - - if (jb.is_valid()) { - - int c = _combine_device(jb->get_button_index(), jb->get_device()); - - if (jb->is_pressed()) - joy_buttons_pressed.insert(c); - else - joy_buttons_pressed.erase(c); - } - - Ref jm = p_event; - - if (jm.is_valid()) { - set_joy_axis(jm->get_device(), jm->get_axis(), jm->get_axis_value()); - } - - Ref ge = p_event; - - if (ge.is_valid()) { - - if (main_loop) { - main_loop->input_event(ge); - } - } - - for (const Map::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { - if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - - // Save the action's state - if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { - Action action; - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); - action.pressed = p_event->is_action_pressed(E->key()); - action.strength = 0.f; - action_state[E->key()] = action; - } - action_state[E->key()].strength = p_event->get_action_strength(E->key()); - } - } - - if (main_loop) - main_loop->input_event(p_event); -} - -void Input::set_joy_axis(int p_device, int p_axis, float p_value) { - - _THREAD_SAFE_METHOD_ - int c = _combine_device(p_axis, p_device); - _joy_axis[c] = p_value; -} - -void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { - _THREAD_SAFE_METHOD_ - if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { - return; - } - VibrationInfo vibration; - vibration.weak_magnitude = p_weak_magnitude; - vibration.strong_magnitude = p_strong_magnitude; - vibration.duration = p_duration; - vibration.timestamp = OS::get_singleton()->get_ticks_usec(); - joy_vibration[p_device] = vibration; -} - -void Input::stop_joy_vibration(int p_device) { - _THREAD_SAFE_METHOD_ - VibrationInfo vibration; - vibration.weak_magnitude = 0; - vibration.strong_magnitude = 0; - vibration.duration = 0; - vibration.timestamp = OS::get_singleton()->get_ticks_usec(); - joy_vibration[p_device] = vibration; -} - -void Input::vibrate_handheld(int p_duration_ms) { - OS::get_singleton()->vibrate_handheld(p_duration_ms); -} - -void Input::set_gravity(const Vector3 &p_gravity) { - - _THREAD_SAFE_METHOD_ - - gravity = p_gravity; -} - -void Input::set_accelerometer(const Vector3 &p_accel) { - - _THREAD_SAFE_METHOD_ - - accelerometer = p_accel; -} - -void Input::set_magnetometer(const Vector3 &p_magnetometer) { - - _THREAD_SAFE_METHOD_ - - magnetometer = p_magnetometer; -} - -void Input::set_gyroscope(const Vector3 &p_gyroscope) { - - _THREAD_SAFE_METHOD_ - - gyroscope = p_gyroscope; -} - -void Input::set_main_loop(MainLoop *p_main_loop) { - main_loop = p_main_loop; -} - -void Input::set_mouse_position(const Point2 &p_posf) { - - mouse_speed_track.update(p_posf - mouse_pos); - mouse_pos = p_posf; -} - -Point2 Input::get_mouse_position() const { - - return mouse_pos; -} -Point2 Input::get_last_mouse_speed() const { - - return mouse_speed_track.speed; -} - -int 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(); -} - -void Input::warp_mouse_position(const Vector2 &p_to) { - warp_mouse_func(p_to); -} -Point2i Input::warp_mouse_motion(const Ref &p_motion, const Rect2 &p_rect) { - - // The relative distance reported for the next event after a warp is in the boundaries of the - // size of the rect on that axis, but it may be greater, in which case there's not problem as fmod() - // will warp it, but if the pointer has moved in the opposite direction between the pointer relocation - // and the subsequent event, the reported relative distance will be less than the size of the rect - // and thus fmod() will be disabled for handling the situation. - // And due to this mouse warping mechanism being stateless, we need to apply some heuristics to - // detect the warp: if the relative distance is greater than the half of the size of the relevant rect - // (checked per each axis), it will be considered as the consequence of a former pointer warp. - - const Point2i rel_sgn(p_motion->get_relative().x >= 0.0f ? 1 : -1, p_motion->get_relative().y >= 0.0 ? 1 : -1); - const Size2i warp_margin = p_rect.size * 0.5f; - const Point2i rel_warped( - Math::fmod(p_motion->get_relative().x + rel_sgn.x * warp_margin.x, p_rect.size.x) - rel_sgn.x * warp_margin.x, - Math::fmod(p_motion->get_relative().y + rel_sgn.y * warp_margin.y, p_rect.size.y) - rel_sgn.y * warp_margin.y); - - const Point2i pos_local = p_motion->get_global_position() - p_rect.position; - const Point2i pos_warped(Math::fposmod(pos_local.x, p_rect.size.x), Math::fposmod(pos_local.y, p_rect.size.y)); - if (pos_warped != pos_local) { - warp_mouse_position(pos_warped + p_rect.position); - } - - return rel_warped; -} - -void Input::iteration(float p_step) { -} - -void Input::action_press(const StringName &p_action, float p_strength) { - - Action action; - - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); - action.pressed = true; - action.strength = p_strength; - - action_state[p_action] = action; -} - -void Input::action_release(const StringName &p_action) { - - Action action; - - action.physics_frame = Engine::get_singleton()->get_physics_frames(); - action.idle_frame = Engine::get_singleton()->get_idle_frames(); - action.pressed = false; - action.strength = 0.f; - - action_state[p_action] = action; -} - -void Input::set_emulate_touch_from_mouse(bool p_emulate) { - - emulate_touch_from_mouse = p_emulate; -} - -bool Input::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 Input::ensure_touch_mouse_raised() { - - if (mouse_from_touch_index != -1) { - mouse_from_touch_index = -1; - - Ref button_event; - button_event.instance(); - - button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); - 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 Input::set_emulate_mouse_from_touch(bool p_emulate) { - - emulate_mouse_from_touch = p_emulate; -} - -bool Input::is_emulating_mouse_from_touch() const { - - return emulate_mouse_from_touch; -} - -Input::CursorShape Input::get_default_cursor_shape() const { - - return default_shape; -} - -void Input::set_default_cursor_shape(CursorShape p_shape) { - - if (default_shape == p_shape) - return; - - default_shape = p_shape; - // The default shape is set in Viewport::_gui_input_event. To instantly - // see the shape in the viewport we need to trigger a mouse motion event. - Ref mm; - mm.instance(); - mm->set_position(mouse_pos); - mm->set_global_position(mouse_pos); - parse_input_event(mm); -} - -Input::CursorShape Input::get_current_cursor_shape() const { - - return get_current_cursor_shape_func(); -} - -void Input::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - if (Engine::get_singleton()->is_editor_hint()) - return; - - set_custom_mouse_cursor_func(p_cursor, p_shape, p_hotspot); -} - -void Input::accumulate_input_event(const Ref &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - if (!use_accumulated_input) { - parse_input_event(p_event); - return; - } - if (!accumulated_events.empty() && accumulated_events.back()->get()->accumulate(p_event)) { - return; //event was accumulated, exit - } - - accumulated_events.push_back(p_event); -} -void Input::flush_accumulated_events() { - - while (accumulated_events.front()) { - parse_input_event(accumulated_events.front()->get()); - accumulated_events.pop_front(); - } -} - -void Input::set_use_accumulated_input(bool p_enable) { - - use_accumulated_input = p_enable; -} - -void Input::release_pressed_events() { - - flush_accumulated_events(); // this is needed to release actions strengths - - keys_pressed.clear(); - joy_buttons_pressed.clear(); - _joy_axis.clear(); - - for (Map::Element *E = action_state.front(); E; E = E->next()) { - if (E->get().pressed) - action_release(E->key()); - } -} - -void Input::joy_button(int p_device, int p_button, bool p_pressed) { - - _THREAD_SAFE_METHOD_; - Joypad &joy = joy_names[p_device]; - //printf("got button %i, mapping is %i\n", p_button, joy.mapping); - if (joy.last_buttons[p_button] == p_pressed) { - return; - } - joy.last_buttons[p_button] = p_pressed; - if (joy.mapping == -1) { - _button_event(p_device, p_button, p_pressed); - return; - } - - const Map::Element *el = map_db[joy.mapping].buttons.find(p_button); - if (!el) { - //don't process un-mapped events for now, it could mess things up badly for devices with additional buttons/axis - //return _button_event(p_last_id, p_device, p_button, p_pressed); - return; - } - - JoyEvent map = el->get(); - if (map.type == TYPE_BUTTON) { - //fake additional axis event for triggers - if (map.index == JOY_L2 || map.index == JOY_R2) { - float value = p_pressed ? 1.0f : 0.0f; - int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; - _axis_event(p_device, axis, value); - } - _button_event(p_device, map.index, p_pressed); - return; - } - - if (map.type == TYPE_AXIS) { - _axis_event(p_device, map.index, p_pressed ? 1.0 : 0.0); - } - // no event? -} - -void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { - - _THREAD_SAFE_METHOD_; - - ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX); - - Joypad &joy = joy_names[p_device]; - - if (joy.last_axis[p_axis] == p_value.value) { - return; - } - - if (p_value.value > joy.last_axis[p_axis]) { - - if (p_value.value < joy.last_axis[p_axis] + joy.filter) { - - return; - } - } else if (p_value.value > joy.last_axis[p_axis] - joy.filter) { - - return; - } - - //when changing direction quickly, insert fake event to release pending inputmap actions - float last = joy.last_axis[p_axis]; - if (p_value.min == 0 && (last < 0.25 || last > 0.75) && (last - 0.5) * (p_value.value - 0.5) < 0) { - JoyAxis jx; - jx.min = p_value.min; - jx.value = p_value.value < 0.5 ? 0.6 : 0.4; - joy_axis(p_device, p_axis, jx); - } else if (ABS(last) > 0.5 && last * p_value.value < 0) { - JoyAxis jx; - jx.min = p_value.min; - jx.value = p_value.value < 0 ? 0.1 : -0.1; - joy_axis(p_device, p_axis, jx); - } - - joy.last_axis[p_axis] = p_value.value; - float val = p_value.min == 0 ? -1.0f + 2.0f * p_value.value : p_value.value; - - if (joy.mapping == -1) { - _axis_event(p_device, p_axis, val); - return; - }; - - const Map::Element *el = map_db[joy.mapping].axis.find(p_axis); - if (!el) { - //return _axis_event(p_last_id, p_device, p_axis, p_value); - return; - }; - - JoyEvent map = el->get(); - - if (map.type == TYPE_BUTTON) { - //send axis event for triggers - if (map.index == JOY_L2 || map.index == JOY_R2) { - float value = p_value.min == 0 ? p_value.value : 0.5f + p_value.value / 2.0f; - int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; - _axis_event(p_device, axis, value); - } - - if (map.index == JOY_DPAD_UP || map.index == JOY_DPAD_DOWN) { - bool pressed = p_value.value != 0.0f; - int button = p_value.value < 0 ? JOY_DPAD_UP : JOY_DPAD_DOWN; - - if (!pressed) { - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_UP, p_device))) { - _button_event(p_device, JOY_DPAD_UP, false); - } - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_DOWN, p_device))) { - _button_event(p_device, JOY_DPAD_DOWN, false); - } - } - if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { - return; - } - _button_event(p_device, button, true); - return; - } - if (map.index == JOY_DPAD_LEFT || map.index == JOY_DPAD_RIGHT) { - bool pressed = p_value.value != 0.0f; - int button = p_value.value < 0 ? JOY_DPAD_LEFT : JOY_DPAD_RIGHT; - - if (!pressed) { - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_LEFT, p_device))) { - _button_event(p_device, JOY_DPAD_LEFT, false); - } - if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_RIGHT, p_device))) { - _button_event(p_device, JOY_DPAD_RIGHT, false); - } - } - if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { - return; - } - _button_event(p_device, button, true); - return; - } - float deadzone = p_value.min == 0 ? 0.5f : 0.0f; - bool pressed = p_value.value > deadzone; - if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) { - // button already pressed or released, this is an axis bounce value - return; - } - _button_event(p_device, map.index, pressed); - return; - } - - if (map.type == TYPE_AXIS) { - - _axis_event(p_device, map.index, val); - return; - } - //printf("invalid mapping\n"); -} - -void Input::joy_hat(int p_device, int p_val) { - - _THREAD_SAFE_METHOD_; - const Joypad &joy = joy_names[p_device]; - - const JoyEvent *map; - - if (joy.mapping == -1) { - map = hat_map_default; - } else { - map = map_db[joy.mapping].hat; - }; - - int cur_val = joy_names[p_device].hat_current; - - if ((p_val & HAT_MASK_UP) != (cur_val & HAT_MASK_UP)) { - _button_event(p_device, map[HAT_UP].index, p_val & HAT_MASK_UP); - } - - if ((p_val & HAT_MASK_RIGHT) != (cur_val & HAT_MASK_RIGHT)) { - _button_event(p_device, map[HAT_RIGHT].index, p_val & HAT_MASK_RIGHT); - } - if ((p_val & HAT_MASK_DOWN) != (cur_val & HAT_MASK_DOWN)) { - _button_event(p_device, map[HAT_DOWN].index, p_val & HAT_MASK_DOWN); - } - if ((p_val & HAT_MASK_LEFT) != (cur_val & HAT_MASK_LEFT)) { - _button_event(p_device, map[HAT_LEFT].index, p_val & HAT_MASK_LEFT); - } - - joy_names[p_device].hat_current = p_val; -} - -void Input::_button_event(int p_device, int p_index, bool p_pressed) { - - Ref ievent; - ievent.instance(); - ievent->set_device(p_device); - ievent->set_button_index(p_index); - ievent->set_pressed(p_pressed); - - parse_input_event(ievent); -} - -void Input::_axis_event(int p_device, int p_axis, float p_value) { - - Ref ievent; - ievent.instance(); - ievent->set_device(p_device); - ievent->set_axis(p_axis); - ievent->set_axis_value(p_value); - - parse_input_event(ievent); -}; - -Input::JoyEvent Input::_find_to_event(String p_to) { - - // string names of the SDL buttons in the same order as input_event.h godot buttons - static const char *buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", NULL }; - - static const char *axis[] = { "leftx", "lefty", "rightx", "righty", NULL }; - - JoyEvent ret; - ret.type = -1; - ret.index = 0; - - int i = 0; - while (buttons[i]) { - - if (p_to == buttons[i]) { - ret.type = TYPE_BUTTON; - ret.index = i; - ret.value = 0; - return ret; - }; - ++i; - }; - - i = 0; - while (axis[i]) { - - if (p_to == axis[i]) { - ret.type = TYPE_AXIS; - ret.index = i; - ret.value = 0; - return ret; - }; - ++i; - }; - - return ret; -}; - -void Input::parse_mapping(String p_mapping) { - - _THREAD_SAFE_METHOD_; - JoyDeviceMapping mapping; - for (int i = 0; i < HAT_MAX; ++i) - mapping.hat[i].index = 1024 + i; - - Vector entry = p_mapping.split(","); - if (entry.size() < 2) { - return; - } - - CharString uid; - uid.resize(17); - - mapping.uid = entry[0]; - mapping.name = entry[1]; - - int idx = 1; - while (++idx < entry.size()) { - - if (entry[idx] == "") - continue; - - String from = entry[idx].get_slice(":", 1).replace(" ", ""); - String to = entry[idx].get_slice(":", 0).replace(" ", ""); - - JoyEvent to_event = _find_to_event(to); - if (to_event.type == -1) - continue; - - String etype = from.substr(0, 1); - if (etype == "a") { - - int aid = from.substr(1, from.length() - 1).to_int(); - mapping.axis[aid] = to_event; - - } else if (etype == "b") { - - int bid = from.substr(1, from.length() - 1).to_int(); - mapping.buttons[bid] = to_event; - - } else if (etype == "h") { - - int hat_value = from.get_slice(".", 1).to_int(); - switch (hat_value) { - case 1: - mapping.hat[HAT_UP] = to_event; - break; - case 2: - mapping.hat[HAT_RIGHT] = to_event; - break; - case 4: - mapping.hat[HAT_DOWN] = to_event; - break; - case 8: - mapping.hat[HAT_LEFT] = to_event; - break; - }; - }; - }; - map_db.push_back(mapping); - //printf("added mapping with uuid %ls\n", mapping.uid.c_str()); -}; - -void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { - parse_mapping(p_mapping); - if (p_update_existing) { - Vector entry = p_mapping.split(","); - String uid = entry[0]; - for (int i = 0; i < joy_names.size(); i++) { - if (uid == joy_names[i].uid) { - joy_names[i].mapping = map_db.size() - 1; - } - } - } -} - -void Input::remove_joy_mapping(String p_guid) { - for (int i = map_db.size() - 1; i >= 0; i--) { - if (p_guid == map_db[i].uid) { - map_db.remove(i); - } - } - for (int i = 0; i < joy_names.size(); i++) { - if (joy_names[i].uid == p_guid) { - joy_names[i].mapping = -1; - } - } -} - -void Input::set_fallback_mapping(String p_guid) { - - for (int i = 0; i < map_db.size(); i++) { - if (map_db[i].uid == p_guid) { - fallback_mapping = i; - return; - } - } -} - -//platforms that use the remapping system can override and call to these ones -bool Input::is_joy_known(int p_device) { - int mapping = joy_names[p_device].mapping; - return mapping != -1 ? (mapping != fallback_mapping) : false; -} - -String Input::get_joy_guid(int p_device) const { - ERR_FAIL_COND_V(!joy_names.has(p_device), ""); - return joy_names[p_device].uid; -} - -Array Input::get_connected_joypads() { - Array ret; - Map::Element *elem = joy_names.front(); - while (elem) { - if (elem->get().connected) { - ret.push_back(elem->key()); - } - elem = elem->next(); - } - return ret; -} - -static const char *_buttons[JOY_BUTTON_MAX] = { - "Face Button Bottom", - "Face Button Right", - "Face Button Left", - "Face Button Top", - "L", - "R", - "L2", - "R2", - "L3", - "R3", - "Select", - "Start", - "DPAD Up", - "DPAD Down", - "DPAD Left", - "DPAD Right" -}; - -static const char *_axes[JOY_AXIS_MAX] = { - "Left Stick X", - "Left Stick Y", - "Right Stick X", - "Right Stick Y", - "", - "", - "L2", - "R2", - "", - "" -}; - -String Input::get_joy_button_string(int p_button) { - ERR_FAIL_INDEX_V(p_button, JOY_BUTTON_MAX, ""); - return _buttons[p_button]; -} - -int Input::get_joy_button_index_from_string(String p_button) { - for (int i = 0; i < JOY_BUTTON_MAX; i++) { - if (p_button == _buttons[i]) { - return i; - } - } - ERR_FAIL_V(-1); -} - -int Input::get_unused_joy_id() { - for (int i = 0; i < JOYPADS_MAX; i++) { - if (!joy_names.has(i) || !joy_names[i].connected) { - return i; - } - } - return -1; -} - -String Input::get_joy_axis_string(int p_axis) { - ERR_FAIL_INDEX_V(p_axis, JOY_AXIS_MAX, ""); - return _axes[p_axis]; -} - -int Input::get_joy_axis_index_from_string(String p_axis) { - for (int i = 0; i < JOY_AXIS_MAX; i++) { - if (p_axis == _axes[i]) { - return i; - } - } - ERR_FAIL_V(-1); -} - -Input::Input() { - - singleton = this; - use_accumulated_input = true; - mouse_button_mask = 0; - mouse_window = 0; - emulate_touch_from_mouse = false; - emulate_mouse_from_touch = false; - mouse_from_touch_index = -1; - main_loop = NULL; - default_shape = CURSOR_ARROW; - - hat_map_default[HAT_UP].type = TYPE_BUTTON; - hat_map_default[HAT_UP].index = JOY_DPAD_UP; - hat_map_default[HAT_UP].value = 0; - - hat_map_default[HAT_RIGHT].type = TYPE_BUTTON; - hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT; - hat_map_default[HAT_RIGHT].value = 0; - - hat_map_default[HAT_DOWN].type = TYPE_BUTTON; - hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN; - hat_map_default[HAT_DOWN].value = 0; - - hat_map_default[HAT_LEFT].type = TYPE_BUTTON; - hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT; - hat_map_default[HAT_LEFT].value = 0; - - fallback_mapping = -1; - - // Parse default mappings. - { - int i = 0; - while (DefaultControllerMappings::mappings[i]) { - parse_mapping(DefaultControllerMappings::mappings[i++]); - } - } - - // If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides. - String env_mapping = OS::get_singleton()->get_environment("SDL_GAMECONTROLLERCONFIG"); - if (env_mapping != "") { - Vector entries = env_mapping.split("\n"); - for (int i = 0; i < entries.size(); i++) { - if (entries[i] == "") - continue; - parse_mapping(entries[i]); - } - } -} - -////////////////////////////////////////////////////////// diff --git a/core/input/input.h b/core/input/input.h deleted file mode 100644 index 6299255a92..0000000000 --- a/core/input/input.h +++ /dev/null @@ -1,335 +0,0 @@ -/*************************************************************************/ -/* input.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 INPUT_H -#define INPUT_H - -#include "core/object.h" -#include "core/os/main_loop.h" -#include "core/os/thread_safe.h" - -class Input : public Object { - - GDCLASS(Input, Object); - _THREAD_SAFE_CLASS_ - - static Input *singleton; - -public: - enum MouseMode { - MOUSE_MODE_VISIBLE, - MOUSE_MODE_HIDDEN, - MOUSE_MODE_CAPTURED, - MOUSE_MODE_CONFINED - }; - -#undef CursorShape - enum CursorShape { - CURSOR_ARROW, - CURSOR_IBEAM, - CURSOR_POINTING_HAND, - CURSOR_CROSS, - CURSOR_WAIT, - CURSOR_BUSY, - CURSOR_DRAG, - CURSOR_CAN_DROP, - CURSOR_FORBIDDEN, - CURSOR_VSIZE, - CURSOR_HSIZE, - CURSOR_BDIAGSIZE, - CURSOR_FDIAGSIZE, - CURSOR_MOVE, - CURSOR_VSPLIT, - CURSOR_HSPLIT, - CURSOR_HELP, - CURSOR_MAX - }; - - enum HatMask { - HAT_MASK_CENTER = 0, - HAT_MASK_UP = 1, - HAT_MASK_RIGHT = 2, - HAT_MASK_DOWN = 4, - HAT_MASK_LEFT = 8, - }; - - enum HatDir { - HAT_UP, - HAT_RIGHT, - HAT_DOWN, - HAT_LEFT, - HAT_MAX, - }; - - enum { - JOYPADS_MAX = 16, - }; - - struct JoyAxis { - int min; - float value; - }; - -private: - int mouse_button_mask; - - Set keys_pressed; - Set joy_buttons_pressed; - Map _joy_axis; - //Map custom_action_press; - Vector3 gravity; - Vector3 accelerometer; - Vector3 magnetometer; - Vector3 gyroscope; - Vector2 mouse_pos; - int64_t mouse_window; - MainLoop *main_loop; - - struct Action { - uint64_t physics_frame; - uint64_t idle_frame; - bool pressed; - float strength; - }; - - Map action_state; - - bool emulate_touch_from_mouse; - bool emulate_mouse_from_touch; - - int mouse_from_touch_index; - - struct VibrationInfo { - float weak_magnitude; - float strong_magnitude; - float duration; // Duration in seconds - uint64_t timestamp; - }; - - Map joy_vibration; - - struct SpeedTrack { - - uint64_t last_tick; - Vector2 speed; - Vector2 accum; - float accum_t; - float min_ref_frame; - float max_ref_frame; - - void update(const Vector2 &p_delta_p); - void reset(); - SpeedTrack(); - }; - - struct Joypad { - StringName name; - StringName uid; - bool connected; - bool last_buttons[JOY_BUTTON_MAX + 19]; //apparently SDL specifies 35 possible buttons on android - float last_axis[JOY_AXIS_MAX]; - float filter; - int last_hat; - int mapping; - int hat_current; - - Joypad() { - for (int i = 0; i < JOY_AXIS_MAX; i++) { - - last_axis[i] = 0.0f; - } - for (int i = 0; i < JOY_BUTTON_MAX + 19; i++) { - - last_buttons[i] = false; - } - connected = false; - last_hat = HAT_MASK_CENTER; - filter = 0.01f; - mapping = -1; - hat_current = 0; - } - }; - - SpeedTrack mouse_speed_track; - Map touch_speed_track; - Map joy_names; - int fallback_mapping; - - CursorShape default_shape; - - enum JoyType { - TYPE_BUTTON, - TYPE_AXIS, - TYPE_HAT, - TYPE_MAX, - }; - - struct JoyEvent { - int type; - int index; - int value; - }; - - struct JoyDeviceMapping { - - String uid; - String name; - Map buttons; - Map axis; - JoyEvent hat[HAT_MAX]; - }; - - JoyEvent hat_map_default[HAT_MAX]; - - Vector map_db; - - JoyEvent _find_to_event(String p_to); - void _button_event(int p_device, int p_index, bool p_pressed); - 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 &p_event, bool p_is_emulated); - - List> accumulated_events; - bool use_accumulated_input; - friend class DisplayServer; - - static void (*set_mouse_mode_func)(MouseMode); - static MouseMode (*get_mouse_mode_func)(); - static void (*warp_mouse_func)(const Vector2 &p_to_pos); - - static CursorShape (*get_current_cursor_shape_func)(); - static void (*set_custom_mouse_cursor_func)(const RES &, CursorShape, const Vector2 &); - -protected: - static void _bind_methods(); - -public: - void set_mouse_mode(MouseMode p_mode); - MouseMode get_mouse_mode() const; - void get_argument_options(const StringName &p_function, int p_idx, List *r_options) const; - - static Input *get_singleton(); - - bool is_key_pressed(int p_keycode) const; - bool is_mouse_button_pressed(int p_button) const; - bool is_joy_button_pressed(int p_device, int p_button) const; - bool is_action_pressed(const StringName &p_action) const; - bool is_action_just_pressed(const StringName &p_action) const; - bool is_action_just_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - - float get_joy_axis(int p_device, int p_axis) const; - String get_joy_name(int p_idx); - Array get_connected_joypads(); - Vector2 get_joy_vibration_strength(int p_device); - float get_joy_vibration_duration(int p_device); - uint64_t get_joy_vibration_timestamp(int p_device); - void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); - void parse_joypad_mapping(String p_mapping, bool p_update_existing); - - Vector3 get_gravity() const; - Vector3 get_accelerometer() const; - Vector3 get_magnetometer() const; - Vector3 get_gyroscope() const; - - Point2 get_mouse_position() const; - Point2 get_last_mouse_speed() const; - int get_mouse_button_mask() const; - - void warp_mouse_position(const Vector2 &p_to); - Point2i warp_mouse_motion(const Ref &p_motion, const Rect2 &p_rect); - - void parse_drop_files(const Vector &p_files); - void parse_notification(int p_notification); - void parse_input_event(const Ref &p_event); - - void set_gravity(const Vector3 &p_gravity); - void set_accelerometer(const Vector3 &p_accel); - void set_magnetometer(const Vector3 &p_magnetometer); - void set_gyroscope(const Vector3 &p_gyroscope); - void set_joy_axis(int p_device, int p_axis, float p_value); - - void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); - void stop_joy_vibration(int p_device); - void vibrate_handheld(int p_duration_ms = 500); - - void set_main_loop(MainLoop *p_main_loop); - void set_mouse_position(const Point2 &p_posf); - - void action_press(const StringName &p_action, float p_strength = 1.f); - void action_release(const StringName &p_action); - - void iteration(float p_step); - - void set_emulate_touch_from_mouse(bool p_emulate); - bool is_emulating_touch_from_mouse() const; - void ensure_touch_mouse_raised(); - - void set_emulate_mouse_from_touch(bool p_emulate); - bool is_emulating_mouse_from_touch() const; - - CursorShape get_default_cursor_shape() const; - void set_default_cursor_shape(CursorShape p_shape); - CursorShape get_current_cursor_shape() const; - void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); - - void parse_mapping(String p_mapping); - void joy_button(int p_device, int p_button, bool p_pressed); - void joy_axis(int p_device, int p_axis, const JoyAxis &p_value); - void joy_hat(int p_device, int p_val); - - void add_joy_mapping(String p_mapping, bool p_update_existing = false); - void remove_joy_mapping(String p_guid); - - String get_joy_button_string(int p_button); - String get_joy_axis_string(int p_axis); - int get_joy_axis_index_from_string(String p_axis); - int get_joy_button_index_from_string(String p_button); - - int get_unused_joy_id(); - - bool is_joy_known(int p_device); - String get_joy_guid(int p_device) const; - void set_fallback_mapping(String p_guid); - - void accumulate_input_event(const Ref &p_event); - void flush_accumulated_events(); - void set_use_accumulated_input(bool p_enable); - - void release_pressed_events(); - - Input(); -}; - -VARIANT_ENUM_CAST(Input::MouseMode); -VARIANT_ENUM_CAST(Input::CursorShape); - -#endif // INPUT_H diff --git a/core/input/input_filter.cpp b/core/input/input_filter.cpp new file mode 100644 index 0000000000..088f7effd6 --- /dev/null +++ b/core/input/input_filter.cpp @@ -0,0 +1,1309 @@ +/*************************************************************************/ +/* input_filter.cpp */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ + +#include "input_filter.h" + +#include "core/input/default_controller_mappings.h" +#include "core/input/input_map.h" +#include "core/os/os.h" +#include "core/project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + +InputFilter *InputFilter::singleton = NULL; + +void (*InputFilter::set_mouse_mode_func)(InputFilter::MouseMode) = nullptr; +InputFilter::MouseMode (*InputFilter::get_mouse_mode_func)() = nullptr; +void (*InputFilter::warp_mouse_func)(const Vector2 &p_to_pos) = nullptr; +InputFilter::CursorShape (*InputFilter::get_current_cursor_shape_func)() = nullptr; +void (*InputFilter::set_custom_mouse_cursor_func)(const RES &, InputFilter::CursorShape, const Vector2 &) = nullptr; + +InputFilter *InputFilter::get_singleton() { + + return singleton; +} + +void InputFilter::set_mouse_mode(MouseMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 4); + set_mouse_mode_func(p_mode); +} + +InputFilter::MouseMode InputFilter::get_mouse_mode() const { + + return get_mouse_mode_func(); +} + +void InputFilter::_bind_methods() { + + ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &InputFilter::is_key_pressed); + ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &InputFilter::is_mouse_button_pressed); + ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &InputFilter::is_joy_button_pressed); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &InputFilter::is_action_pressed); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &InputFilter::is_action_just_pressed); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &InputFilter::is_action_just_released); + ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputFilter::get_action_strength); + ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &InputFilter::add_joy_mapping, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &InputFilter::remove_joy_mapping); + ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &InputFilter::joy_connection_changed); + ClassDB::bind_method(D_METHOD("is_joy_known", "device"), &InputFilter::is_joy_known); + ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &InputFilter::get_joy_axis); + ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &InputFilter::get_joy_name); + ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &InputFilter::get_joy_guid); + ClassDB::bind_method(D_METHOD("get_connected_joypads"), &InputFilter::get_connected_joypads); + ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &InputFilter::get_joy_vibration_strength); + ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &InputFilter::get_joy_vibration_duration); + ClassDB::bind_method(D_METHOD("get_joy_button_string", "button_index"), &InputFilter::get_joy_button_string); + ClassDB::bind_method(D_METHOD("get_joy_button_index_from_string", "button"), &InputFilter::get_joy_button_index_from_string); + ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis_index"), &InputFilter::get_joy_axis_string); + ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &InputFilter::get_joy_axis_index_from_string); + ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &InputFilter::start_joy_vibration, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &InputFilter::stop_joy_vibration); + ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &InputFilter::vibrate_handheld, DEFVAL(500)); + ClassDB::bind_method(D_METHOD("get_gravity"), &InputFilter::get_gravity); + ClassDB::bind_method(D_METHOD("get_accelerometer"), &InputFilter::get_accelerometer); + ClassDB::bind_method(D_METHOD("get_magnetometer"), &InputFilter::get_magnetometer); + ClassDB::bind_method(D_METHOD("get_gyroscope"), &InputFilter::get_gyroscope); + ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &InputFilter::get_last_mouse_speed); + ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &InputFilter::get_mouse_button_mask); + ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &InputFilter::set_mouse_mode); + ClassDB::bind_method(D_METHOD("get_mouse_mode"), &InputFilter::get_mouse_mode); + ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &InputFilter::warp_mouse_position); + ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &InputFilter::action_press, DEFVAL(1.f)); + ClassDB::bind_method(D_METHOD("action_release", "action"), &InputFilter::action_release); + ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &InputFilter::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); + ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &InputFilter::get_current_cursor_shape); + ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &InputFilter::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &InputFilter::parse_input_event); + ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &InputFilter::set_use_accumulated_input); + + BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); + BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); + BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); + BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); + + BIND_ENUM_CONSTANT(CURSOR_ARROW); + BIND_ENUM_CONSTANT(CURSOR_IBEAM); + BIND_ENUM_CONSTANT(CURSOR_POINTING_HAND); + BIND_ENUM_CONSTANT(CURSOR_CROSS); + BIND_ENUM_CONSTANT(CURSOR_WAIT); + BIND_ENUM_CONSTANT(CURSOR_BUSY); + BIND_ENUM_CONSTANT(CURSOR_DRAG); + BIND_ENUM_CONSTANT(CURSOR_CAN_DROP); + BIND_ENUM_CONSTANT(CURSOR_FORBIDDEN); + BIND_ENUM_CONSTANT(CURSOR_VSIZE); + BIND_ENUM_CONSTANT(CURSOR_HSIZE); + BIND_ENUM_CONSTANT(CURSOR_BDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_FDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_MOVE); + BIND_ENUM_CONSTANT(CURSOR_VSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HELP); + + ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); +} + +void InputFilter::get_argument_options(const StringName &p_function, int p_idx, List *r_options) const { +#ifdef TOOLS_ENABLED + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + + String pf = p_function; + if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { + + List pinfo; + ProjectSettings::get_singleton()->get_property_list(&pinfo); + + for (List::Element *E = pinfo.front(); E; E = E->next()) { + const PropertyInfo &pi = E->get(); + + if (!pi.name.begins_with("input/")) + continue; + + String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); + r_options->push_back(quote_style + name + quote_style); + } + } +#endif +} + +void InputFilter::SpeedTrack::update(const Vector2 &p_delta_p) { + + uint64_t tick = OS::get_singleton()->get_ticks_usec(); + uint32_t tdiff = tick - last_tick; + float delta_t = tdiff / 1000000.0; + last_tick = tick; + + accum += p_delta_p; + accum_t += delta_t; + + if (accum_t > max_ref_frame * 10) + accum_t = max_ref_frame * 10; + + while (accum_t >= min_ref_frame) { + + float slice_t = min_ref_frame / accum_t; + Vector2 slice = accum * slice_t; + accum = accum - slice; + accum_t -= min_ref_frame; + + speed = (slice / min_ref_frame).linear_interpolate(speed, min_ref_frame / max_ref_frame); + } +} + +void InputFilter::SpeedTrack::reset() { + last_tick = OS::get_singleton()->get_ticks_usec(); + speed = Vector2(); + accum_t = 0; +} + +InputFilter::SpeedTrack::SpeedTrack() { + + min_ref_frame = 0.1; + max_ref_frame = 0.3; + reset(); +} + +bool InputFilter::is_key_pressed(int p_keycode) const { + + _THREAD_SAFE_METHOD_ + return keys_pressed.has(p_keycode); +} + +bool InputFilter::is_mouse_button_pressed(int p_button) const { + + _THREAD_SAFE_METHOD_ + return (mouse_button_mask & (1 << (p_button - 1))) != 0; +} + +static int _combine_device(int p_value, int p_device) { + + return p_value | (p_device << 20); +} + +bool InputFilter::is_joy_button_pressed(int p_device, int p_button) const { + + _THREAD_SAFE_METHOD_ + return joy_buttons_pressed.has(_combine_device(p_button, p_device)); +} + +bool InputFilter::is_action_pressed(const StringName &p_action) const { + + return action_state.has(p_action) && action_state[p_action].pressed; +} + +bool InputFilter::is_action_just_pressed(const StringName &p_action) const { + + const Map::Element *E = action_state.find(p_action); + if (!E) + return false; + + if (Engine::get_singleton()->is_in_physics_frame()) { + return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); + } else { + return E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); + } +} + +bool InputFilter::is_action_just_released(const StringName &p_action) const { + + const Map::Element *E = action_state.find(p_action); + if (!E) + return false; + + if (Engine::get_singleton()->is_in_physics_frame()) { + return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); + } else { + return !E->get().pressed && E->get().idle_frame == Engine::get_singleton()->get_idle_frames(); + } +} + +float InputFilter::get_action_strength(const StringName &p_action) const { + const Map::Element *E = action_state.find(p_action); + if (!E) + return 0.0f; + + return E->get().strength; +} + +float InputFilter::get_joy_axis(int p_device, int p_axis) const { + + _THREAD_SAFE_METHOD_ + int c = _combine_device(p_axis, p_device); + if (_joy_axis.has(c)) { + return _joy_axis[c]; + } else { + return 0; + } +} + +String InputFilter::get_joy_name(int p_idx) { + + _THREAD_SAFE_METHOD_ + return joy_names[p_idx].name; +}; + +Vector2 InputFilter::get_joy_vibration_strength(int p_device) { + if (joy_vibration.has(p_device)) { + return Vector2(joy_vibration[p_device].weak_magnitude, joy_vibration[p_device].strong_magnitude); + } else { + return Vector2(0, 0); + } +} + +uint64_t InputFilter::get_joy_vibration_timestamp(int p_device) { + if (joy_vibration.has(p_device)) { + return joy_vibration[p_device].timestamp; + } else { + return 0; + } +} + +float InputFilter::get_joy_vibration_duration(int p_device) { + if (joy_vibration.has(p_device)) { + return joy_vibration[p_device].duration; + } else { + return 0.f; + } +} + +static String _hex_str(uint8_t p_byte) { + + static const char *dict = "0123456789abcdef"; + char ret[3]; + ret[2] = 0; + + ret[0] = dict[p_byte >> 4]; + ret[1] = dict[p_byte & 0xf]; + + return ret; +}; + +void InputFilter::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { + + _THREAD_SAFE_METHOD_ + Joypad js; + js.name = p_connected ? p_name : ""; + js.uid = p_connected ? p_guid : ""; + + if (p_connected) { + + String uidname = p_guid; + if (p_guid == "") { + int uidlen = MIN(p_name.length(), 16); + for (int i = 0; i < uidlen; i++) { + uidname = uidname + _hex_str(p_name[i]); + }; + }; + js.uid = uidname; + js.connected = true; + int mapping = fallback_mapping; + for (int i = 0; i < map_db.size(); i++) { + if (js.uid == map_db[i].uid) { + mapping = i; + js.name = map_db[i].name; + }; + }; + js.mapping = mapping; + } else { + js.connected = false; + for (int i = 0; i < JOY_BUTTON_MAX; i++) { + + if (i < JOY_AXIS_MAX) + set_joy_axis(p_idx, i, 0.0f); + + int c = _combine_device(i, p_idx); + joy_buttons_pressed.erase(c); + }; + }; + joy_names[p_idx] = js; + + emit_signal("joy_connection_changed", p_idx, p_connected); +}; + +Vector3 InputFilter::get_gravity() const { + + _THREAD_SAFE_METHOD_ + return gravity; +} + +Vector3 InputFilter::get_accelerometer() const { + + _THREAD_SAFE_METHOD_ + return accelerometer; +} + +Vector3 InputFilter::get_magnetometer() const { + + _THREAD_SAFE_METHOD_ + return magnetometer; +} + +Vector3 InputFilter::get_gyroscope() const { + + _THREAD_SAFE_METHOD_ + return gyroscope; +} + +void InputFilter::parse_input_event(const Ref &p_event) { + + _parse_input_event_impl(p_event, false); +} + +void InputFilter::_parse_input_event_impl(const Ref &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 + // as true mouse events. The only difference is the situation is flagged as emulated so they are not + // emulated back to touch events in an endless loop. + // - 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 k = p_event; + if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) { + if (k->is_pressed()) + keys_pressed.insert(k->get_keycode()); + else + keys_pressed.erase(k->get_keycode()); + } + + Ref mb = p_event; + + if (mb.is_valid()) { + + if (mb->is_pressed()) { + mouse_button_mask |= (1 << (mb->get_button_index() - 1)); + } else { + mouse_button_mask &= ~(1 << (mb->get_button_index() - 1)); + } + + Point2 pos = mb->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) { + Ref touch_event; + touch_event.instance(); + touch_event->set_pressed(mb->is_pressed()); + touch_event->set_position(mb->get_position()); + event_dispatch_function(touch_event); + } + } + + Ref mm = p_event; + + if (mm.is_valid()) { + + Point2 pos = mm->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) { + Ref drag_event; + drag_event.instance(); + + drag_event->set_position(mm->get_position()); + drag_event->set_relative(mm->get_relative()); + drag_event->set_speed(mm->get_speed()); + + event_dispatch_function(drag_event); + } + } + + Ref st = p_event; + + if (st.is_valid()) { + + if (st->is_pressed()) { + SpeedTrack &track = touch_speed_track[st->get_index()]; + track.reset(); + } else { + // Since a pointer index may not occur again (OSs may or may not reuse them), + // imperatively remove it from the map to keep no fossil entries in it + touch_speed_track.erase(st->get_index()); + } + + if (emulate_mouse_from_touch) { + + 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 button_event; + button_event.instance(); + + button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); + 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 sd = p_event; + + if (sd.is_valid()) { + + SpeedTrack &track = touch_speed_track[sd->get_index()]; + track.update(sd->get_relative()); + sd->set_speed(track.speed); + + if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) { + + Ref motion_event; + motion_event.instance(); + + motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); + 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 jb = p_event; + + if (jb.is_valid()) { + + int c = _combine_device(jb->get_button_index(), jb->get_device()); + + if (jb->is_pressed()) + joy_buttons_pressed.insert(c); + else + joy_buttons_pressed.erase(c); + } + + Ref jm = p_event; + + if (jm.is_valid()) { + set_joy_axis(jm->get_device(), jm->get_axis(), jm->get_axis_value()); + } + + Ref ge = p_event; + + if (ge.is_valid()) { + + if (event_dispatch_function) { + event_dispatch_function(ge); + } + } + + for (const Map::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { + if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { + + // Save the action's state + if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { + Action action; + action.physics_frame = Engine::get_singleton()->get_physics_frames(); + action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.pressed = p_event->is_action_pressed(E->key()); + action.strength = 0.f; + action_state[E->key()] = action; + } + action_state[E->key()].strength = p_event->get_action_strength(E->key()); + } + } + + if (event_dispatch_function) + event_dispatch_function(p_event); +} + +void InputFilter::set_joy_axis(int p_device, int p_axis, float p_value) { + + _THREAD_SAFE_METHOD_ + int c = _combine_device(p_axis, p_device); + _joy_axis[c] = p_value; +} + +void InputFilter::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { + _THREAD_SAFE_METHOD_ + if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { + return; + } + VibrationInfo vibration; + vibration.weak_magnitude = p_weak_magnitude; + vibration.strong_magnitude = p_strong_magnitude; + vibration.duration = p_duration; + vibration.timestamp = OS::get_singleton()->get_ticks_usec(); + joy_vibration[p_device] = vibration; +} + +void InputFilter::stop_joy_vibration(int p_device) { + _THREAD_SAFE_METHOD_ + VibrationInfo vibration; + vibration.weak_magnitude = 0; + vibration.strong_magnitude = 0; + vibration.duration = 0; + vibration.timestamp = OS::get_singleton()->get_ticks_usec(); + joy_vibration[p_device] = vibration; +} + +void InputFilter::vibrate_handheld(int p_duration_ms) { + OS::get_singleton()->vibrate_handheld(p_duration_ms); +} + +void InputFilter::set_gravity(const Vector3 &p_gravity) { + + _THREAD_SAFE_METHOD_ + + gravity = p_gravity; +} + +void InputFilter::set_accelerometer(const Vector3 &p_accel) { + + _THREAD_SAFE_METHOD_ + + accelerometer = p_accel; +} + +void InputFilter::set_magnetometer(const Vector3 &p_magnetometer) { + + _THREAD_SAFE_METHOD_ + + magnetometer = p_magnetometer; +} + +void InputFilter::set_gyroscope(const Vector3 &p_gyroscope) { + + _THREAD_SAFE_METHOD_ + + gyroscope = p_gyroscope; +} + +void InputFilter::set_mouse_position(const Point2 &p_posf) { + + mouse_speed_track.update(p_posf - mouse_pos); + mouse_pos = p_posf; +} + +Point2 InputFilter::get_mouse_position() const { + + return mouse_pos; +} +Point2 InputFilter::get_last_mouse_speed() const { + + return mouse_speed_track.speed; +} + +int InputFilter::get_mouse_button_mask() const { + + return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state(); +} + +void InputFilter::warp_mouse_position(const Vector2 &p_to) { + warp_mouse_func(p_to); +} + +Point2i InputFilter::warp_mouse_motion(const Ref &p_motion, const Rect2 &p_rect) { + + // The relative distance reported for the next event after a warp is in the boundaries of the + // size of the rect on that axis, but it may be greater, in which case there's not problem as fmod() + // will warp it, but if the pointer has moved in the opposite direction between the pointer relocation + // and the subsequent event, the reported relative distance will be less than the size of the rect + // and thus fmod() will be disabled for handling the situation. + // And due to this mouse warping mechanism being stateless, we need to apply some heuristics to + // detect the warp: if the relative distance is greater than the half of the size of the relevant rect + // (checked per each axis), it will be considered as the consequence of a former pointer warp. + + const Point2i rel_sgn(p_motion->get_relative().x >= 0.0f ? 1 : -1, p_motion->get_relative().y >= 0.0 ? 1 : -1); + const Size2i warp_margin = p_rect.size * 0.5f; + const Point2i rel_warped( + Math::fmod(p_motion->get_relative().x + rel_sgn.x * warp_margin.x, p_rect.size.x) - rel_sgn.x * warp_margin.x, + Math::fmod(p_motion->get_relative().y + rel_sgn.y * warp_margin.y, p_rect.size.y) - rel_sgn.y * warp_margin.y); + + const Point2i pos_local = p_motion->get_global_position() - p_rect.position; + const Point2i pos_warped(Math::fposmod(pos_local.x, p_rect.size.x), Math::fposmod(pos_local.y, p_rect.size.y)); + if (pos_warped != pos_local) { + warp_mouse_position(pos_warped + p_rect.position); + } + + return rel_warped; +} + +void InputFilter::iteration(float p_step) { +} + +void InputFilter::action_press(const StringName &p_action, float p_strength) { + + Action action; + + action.physics_frame = Engine::get_singleton()->get_physics_frames(); + action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.pressed = true; + action.strength = p_strength; + + action_state[p_action] = action; +} + +void InputFilter::action_release(const StringName &p_action) { + + Action action; + + action.physics_frame = Engine::get_singleton()->get_physics_frames(); + action.idle_frame = Engine::get_singleton()->get_idle_frames(); + action.pressed = false; + action.strength = 0.f; + + action_state[p_action] = action; +} + +void InputFilter::set_emulate_touch_from_mouse(bool p_emulate) { + + emulate_touch_from_mouse = p_emulate; +} + +bool InputFilter::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 InputFilter::ensure_touch_mouse_raised() { + + if (mouse_from_touch_index != -1) { + mouse_from_touch_index = -1; + + Ref button_event; + button_event.instance(); + + button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); + 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 InputFilter::set_emulate_mouse_from_touch(bool p_emulate) { + + emulate_mouse_from_touch = p_emulate; +} + +bool InputFilter::is_emulating_mouse_from_touch() const { + + return emulate_mouse_from_touch; +} + +InputFilter::CursorShape InputFilter::get_default_cursor_shape() const { + + return default_shape; +} + +void InputFilter::set_default_cursor_shape(CursorShape p_shape) { + + if (default_shape == p_shape) + return; + + default_shape = p_shape; + // The default shape is set in Viewport::_gui_input_event. To instantly + // see the shape in the viewport we need to trigger a mouse motion event. + Ref mm; + mm.instance(); + mm->set_position(mouse_pos); + mm->set_global_position(mouse_pos); + parse_input_event(mm); +} + +InputFilter::CursorShape InputFilter::get_current_cursor_shape() const { + + return get_current_cursor_shape_func(); +} + +void InputFilter::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + if (Engine::get_singleton()->is_editor_hint()) + return; + + set_custom_mouse_cursor_func(p_cursor, p_shape, p_hotspot); +} + +void InputFilter::accumulate_input_event(const Ref &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (!use_accumulated_input) { + parse_input_event(p_event); + return; + } + if (!accumulated_events.empty() && accumulated_events.back()->get()->accumulate(p_event)) { + return; //event was accumulated, exit + } + + accumulated_events.push_back(p_event); +} +void InputFilter::flush_accumulated_events() { + + while (accumulated_events.front()) { + parse_input_event(accumulated_events.front()->get()); + accumulated_events.pop_front(); + } +} + +void InputFilter::set_use_accumulated_input(bool p_enable) { + + use_accumulated_input = p_enable; +} + +void InputFilter::release_pressed_events() { + + flush_accumulated_events(); // this is needed to release actions strengths + + keys_pressed.clear(); + joy_buttons_pressed.clear(); + _joy_axis.clear(); + + for (Map::Element *E = action_state.front(); E; E = E->next()) { + if (E->get().pressed) + action_release(E->key()); + } +} + +void InputFilter::set_event_dispatch_function(EventDispatchFunc p_function) { + event_dispatch_function = p_function; +} + +void InputFilter::joy_button(int p_device, int p_button, bool p_pressed) { + + _THREAD_SAFE_METHOD_; + Joypad &joy = joy_names[p_device]; + //printf("got button %i, mapping is %i\n", p_button, joy.mapping); + if (joy.last_buttons[p_button] == p_pressed) { + return; + } + joy.last_buttons[p_button] = p_pressed; + if (joy.mapping == -1) { + _button_event(p_device, p_button, p_pressed); + return; + } + + const Map::Element *el = map_db[joy.mapping].buttons.find(p_button); + if (!el) { + //don't process un-mapped events for now, it could mess things up badly for devices with additional buttons/axis + //return _button_event(p_last_id, p_device, p_button, p_pressed); + return; + } + + JoyEvent map = el->get(); + if (map.type == TYPE_BUTTON) { + //fake additional axis event for triggers + if (map.index == JOY_L2 || map.index == JOY_R2) { + float value = p_pressed ? 1.0f : 0.0f; + int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; + _axis_event(p_device, axis, value); + } + _button_event(p_device, map.index, p_pressed); + return; + } + + if (map.type == TYPE_AXIS) { + _axis_event(p_device, map.index, p_pressed ? 1.0 : 0.0); + } + // no event? +} + +void InputFilter::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { + + _THREAD_SAFE_METHOD_; + + ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX); + + Joypad &joy = joy_names[p_device]; + + if (joy.last_axis[p_axis] == p_value.value) { + return; + } + + if (p_value.value > joy.last_axis[p_axis]) { + + if (p_value.value < joy.last_axis[p_axis] + joy.filter) { + + return; + } + } else if (p_value.value > joy.last_axis[p_axis] - joy.filter) { + + return; + } + + //when changing direction quickly, insert fake event to release pending inputmap actions + float last = joy.last_axis[p_axis]; + if (p_value.min == 0 && (last < 0.25 || last > 0.75) && (last - 0.5) * (p_value.value - 0.5) < 0) { + JoyAxis jx; + jx.min = p_value.min; + jx.value = p_value.value < 0.5 ? 0.6 : 0.4; + joy_axis(p_device, p_axis, jx); + } else if (ABS(last) > 0.5 && last * p_value.value < 0) { + JoyAxis jx; + jx.min = p_value.min; + jx.value = p_value.value < 0 ? 0.1 : -0.1; + joy_axis(p_device, p_axis, jx); + } + + joy.last_axis[p_axis] = p_value.value; + float val = p_value.min == 0 ? -1.0f + 2.0f * p_value.value : p_value.value; + + if (joy.mapping == -1) { + _axis_event(p_device, p_axis, val); + return; + }; + + const Map::Element *el = map_db[joy.mapping].axis.find(p_axis); + if (!el) { + //return _axis_event(p_last_id, p_device, p_axis, p_value); + return; + }; + + JoyEvent map = el->get(); + + if (map.type == TYPE_BUTTON) { + //send axis event for triggers + if (map.index == JOY_L2 || map.index == JOY_R2) { + float value = p_value.min == 0 ? p_value.value : 0.5f + p_value.value / 2.0f; + int axis = map.index == JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2; + _axis_event(p_device, axis, value); + } + + if (map.index == JOY_DPAD_UP || map.index == JOY_DPAD_DOWN) { + bool pressed = p_value.value != 0.0f; + int button = p_value.value < 0 ? JOY_DPAD_UP : JOY_DPAD_DOWN; + + if (!pressed) { + if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_UP, p_device))) { + _button_event(p_device, JOY_DPAD_UP, false); + } + if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_DOWN, p_device))) { + _button_event(p_device, JOY_DPAD_DOWN, false); + } + } + if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { + return; + } + _button_event(p_device, button, true); + return; + } + if (map.index == JOY_DPAD_LEFT || map.index == JOY_DPAD_RIGHT) { + bool pressed = p_value.value != 0.0f; + int button = p_value.value < 0 ? JOY_DPAD_LEFT : JOY_DPAD_RIGHT; + + if (!pressed) { + if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_LEFT, p_device))) { + _button_event(p_device, JOY_DPAD_LEFT, false); + } + if (joy_buttons_pressed.has(_combine_device(JOY_DPAD_RIGHT, p_device))) { + _button_event(p_device, JOY_DPAD_RIGHT, false); + } + } + if (pressed == joy_buttons_pressed.has(_combine_device(button, p_device))) { + return; + } + _button_event(p_device, button, true); + return; + } + float deadzone = p_value.min == 0 ? 0.5f : 0.0f; + bool pressed = p_value.value > deadzone; + if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) { + // button already pressed or released, this is an axis bounce value + return; + } + _button_event(p_device, map.index, pressed); + return; + } + + if (map.type == TYPE_AXIS) { + + _axis_event(p_device, map.index, val); + return; + } + //printf("invalid mapping\n"); +} + +void InputFilter::joy_hat(int p_device, int p_val) { + + _THREAD_SAFE_METHOD_; + const Joypad &joy = joy_names[p_device]; + + const JoyEvent *map; + + if (joy.mapping == -1) { + map = hat_map_default; + } else { + map = map_db[joy.mapping].hat; + }; + + int cur_val = joy_names[p_device].hat_current; + + if ((p_val & HAT_MASK_UP) != (cur_val & HAT_MASK_UP)) { + _button_event(p_device, map[HAT_UP].index, p_val & HAT_MASK_UP); + } + + if ((p_val & HAT_MASK_RIGHT) != (cur_val & HAT_MASK_RIGHT)) { + _button_event(p_device, map[HAT_RIGHT].index, p_val & HAT_MASK_RIGHT); + } + if ((p_val & HAT_MASK_DOWN) != (cur_val & HAT_MASK_DOWN)) { + _button_event(p_device, map[HAT_DOWN].index, p_val & HAT_MASK_DOWN); + } + if ((p_val & HAT_MASK_LEFT) != (cur_val & HAT_MASK_LEFT)) { + _button_event(p_device, map[HAT_LEFT].index, p_val & HAT_MASK_LEFT); + } + + joy_names[p_device].hat_current = p_val; +} + +void InputFilter::_button_event(int p_device, int p_index, bool p_pressed) { + + Ref ievent; + ievent.instance(); + ievent->set_device(p_device); + ievent->set_button_index(p_index); + ievent->set_pressed(p_pressed); + + parse_input_event(ievent); +} + +void InputFilter::_axis_event(int p_device, int p_axis, float p_value) { + + Ref ievent; + ievent.instance(); + ievent->set_device(p_device); + ievent->set_axis(p_axis); + ievent->set_axis_value(p_value); + + parse_input_event(ievent); +}; + +InputFilter::JoyEvent InputFilter::_find_to_event(String p_to) { + + // string names of the SDL buttons in the same order as input_event.h godot buttons + static const char *buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", NULL }; + + static const char *axis[] = { "leftx", "lefty", "rightx", "righty", NULL }; + + JoyEvent ret; + ret.type = -1; + ret.index = 0; + + int i = 0; + while (buttons[i]) { + + if (p_to == buttons[i]) { + ret.type = TYPE_BUTTON; + ret.index = i; + ret.value = 0; + return ret; + }; + ++i; + }; + + i = 0; + while (axis[i]) { + + if (p_to == axis[i]) { + ret.type = TYPE_AXIS; + ret.index = i; + ret.value = 0; + return ret; + }; + ++i; + }; + + return ret; +}; + +void InputFilter::parse_mapping(String p_mapping) { + + _THREAD_SAFE_METHOD_; + JoyDeviceMapping mapping; + for (int i = 0; i < HAT_MAX; ++i) + mapping.hat[i].index = 1024 + i; + + Vector entry = p_mapping.split(","); + if (entry.size() < 2) { + return; + } + + CharString uid; + uid.resize(17); + + mapping.uid = entry[0]; + mapping.name = entry[1]; + + int idx = 1; + while (++idx < entry.size()) { + + if (entry[idx] == "") + continue; + + String from = entry[idx].get_slice(":", 1).replace(" ", ""); + String to = entry[idx].get_slice(":", 0).replace(" ", ""); + + JoyEvent to_event = _find_to_event(to); + if (to_event.type == -1) + continue; + + String etype = from.substr(0, 1); + if (etype == "a") { + + int aid = from.substr(1, from.length() - 1).to_int(); + mapping.axis[aid] = to_event; + + } else if (etype == "b") { + + int bid = from.substr(1, from.length() - 1).to_int(); + mapping.buttons[bid] = to_event; + + } else if (etype == "h") { + + int hat_value = from.get_slice(".", 1).to_int(); + switch (hat_value) { + case 1: + mapping.hat[HAT_UP] = to_event; + break; + case 2: + mapping.hat[HAT_RIGHT] = to_event; + break; + case 4: + mapping.hat[HAT_DOWN] = to_event; + break; + case 8: + mapping.hat[HAT_LEFT] = to_event; + break; + }; + }; + }; + map_db.push_back(mapping); + //printf("added mapping with uuid %ls\n", mapping.uid.c_str()); +}; + +void InputFilter::add_joy_mapping(String p_mapping, bool p_update_existing) { + parse_mapping(p_mapping); + if (p_update_existing) { + Vector entry = p_mapping.split(","); + String uid = entry[0]; + for (int i = 0; i < joy_names.size(); i++) { + if (uid == joy_names[i].uid) { + joy_names[i].mapping = map_db.size() - 1; + } + } + } +} + +void InputFilter::remove_joy_mapping(String p_guid) { + for (int i = map_db.size() - 1; i >= 0; i--) { + if (p_guid == map_db[i].uid) { + map_db.remove(i); + } + } + for (int i = 0; i < joy_names.size(); i++) { + if (joy_names[i].uid == p_guid) { + joy_names[i].mapping = -1; + } + } +} + +void InputFilter::set_fallback_mapping(String p_guid) { + + for (int i = 0; i < map_db.size(); i++) { + if (map_db[i].uid == p_guid) { + fallback_mapping = i; + return; + } + } +} + +//platforms that use the remapping system can override and call to these ones +bool InputFilter::is_joy_known(int p_device) { + int mapping = joy_names[p_device].mapping; + return mapping != -1 ? (mapping != fallback_mapping) : false; +} + +String InputFilter::get_joy_guid(int p_device) const { + ERR_FAIL_COND_V(!joy_names.has(p_device), ""); + return joy_names[p_device].uid; +} + +Array InputFilter::get_connected_joypads() { + Array ret; + Map::Element *elem = joy_names.front(); + while (elem) { + if (elem->get().connected) { + ret.push_back(elem->key()); + } + elem = elem->next(); + } + return ret; +} + +static const char *_buttons[JOY_BUTTON_MAX] = { + "Face Button Bottom", + "Face Button Right", + "Face Button Left", + "Face Button Top", + "L", + "R", + "L2", + "R2", + "L3", + "R3", + "Select", + "Start", + "DPAD Up", + "DPAD Down", + "DPAD Left", + "DPAD Right" +}; + +static const char *_axes[JOY_AXIS_MAX] = { + "Left Stick X", + "Left Stick Y", + "Right Stick X", + "Right Stick Y", + "", + "", + "L2", + "R2", + "", + "" +}; + +String InputFilter::get_joy_button_string(int p_button) { + ERR_FAIL_INDEX_V(p_button, JOY_BUTTON_MAX, ""); + return _buttons[p_button]; +} + +int InputFilter::get_joy_button_index_from_string(String p_button) { + for (int i = 0; i < JOY_BUTTON_MAX; i++) { + if (p_button == _buttons[i]) { + return i; + } + } + ERR_FAIL_V(-1); +} + +int InputFilter::get_unused_joy_id() { + for (int i = 0; i < JOYPADS_MAX; i++) { + if (!joy_names.has(i) || !joy_names[i].connected) { + return i; + } + } + return -1; +} + +String InputFilter::get_joy_axis_string(int p_axis) { + ERR_FAIL_INDEX_V(p_axis, JOY_AXIS_MAX, ""); + return _axes[p_axis]; +} + +int InputFilter::get_joy_axis_index_from_string(String p_axis) { + for (int i = 0; i < JOY_AXIS_MAX; i++) { + if (p_axis == _axes[i]) { + return i; + } + } + ERR_FAIL_V(-1); +} + +InputFilter::InputFilter() { + + singleton = this; + use_accumulated_input = true; + mouse_button_mask = 0; + mouse_window = 0; + emulate_touch_from_mouse = false; + emulate_mouse_from_touch = false; + mouse_from_touch_index = -1; + event_dispatch_function = nullptr; + default_shape = CURSOR_ARROW; + + hat_map_default[HAT_UP].type = TYPE_BUTTON; + hat_map_default[HAT_UP].index = JOY_DPAD_UP; + hat_map_default[HAT_UP].value = 0; + + hat_map_default[HAT_RIGHT].type = TYPE_BUTTON; + hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT; + hat_map_default[HAT_RIGHT].value = 0; + + hat_map_default[HAT_DOWN].type = TYPE_BUTTON; + hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN; + hat_map_default[HAT_DOWN].value = 0; + + hat_map_default[HAT_LEFT].type = TYPE_BUTTON; + hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT; + hat_map_default[HAT_LEFT].value = 0; + + fallback_mapping = -1; + + // Parse default mappings. + { + int i = 0; + while (DefaultControllerMappings::mappings[i]) { + parse_mapping(DefaultControllerMappings::mappings[i++]); + } + } + + // If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides. + String env_mapping = OS::get_singleton()->get_environment("SDL_GAMECONTROLLERCONFIG"); + if (env_mapping != "") { + Vector entries = env_mapping.split("\n"); + for (int i = 0; i < entries.size(); i++) { + if (entries[i] == "") + continue; + parse_mapping(entries[i]); + } + } +} + +////////////////////////////////////////////////////////// diff --git a/core/input/input_filter.h b/core/input/input_filter.h new file mode 100644 index 0000000000..908a005228 --- /dev/null +++ b/core/input/input_filter.h @@ -0,0 +1,337 @@ +/*************************************************************************/ +/* input_filter.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 INPUT_H +#define INPUT_H + +#include "core/input/input_event.h" +#include "core/object.h" +#include "core/os/thread_safe.h" + +class InputFilter : public Object { + + GDCLASS(InputFilter, Object); + _THREAD_SAFE_CLASS_ + + static InputFilter *singleton; + +public: + enum MouseMode { + MOUSE_MODE_VISIBLE, + MOUSE_MODE_HIDDEN, + MOUSE_MODE_CAPTURED, + MOUSE_MODE_CONFINED + }; + +#undef CursorShape + enum CursorShape { + CURSOR_ARROW, + CURSOR_IBEAM, + CURSOR_POINTING_HAND, + CURSOR_CROSS, + CURSOR_WAIT, + CURSOR_BUSY, + CURSOR_DRAG, + CURSOR_CAN_DROP, + CURSOR_FORBIDDEN, + CURSOR_VSIZE, + CURSOR_HSIZE, + CURSOR_BDIAGSIZE, + CURSOR_FDIAGSIZE, + CURSOR_MOVE, + CURSOR_VSPLIT, + CURSOR_HSPLIT, + CURSOR_HELP, + CURSOR_MAX + }; + + enum HatMask { + HAT_MASK_CENTER = 0, + HAT_MASK_UP = 1, + HAT_MASK_RIGHT = 2, + HAT_MASK_DOWN = 4, + HAT_MASK_LEFT = 8, + }; + + enum HatDir { + HAT_UP, + HAT_RIGHT, + HAT_DOWN, + HAT_LEFT, + HAT_MAX, + }; + + enum { + JOYPADS_MAX = 16, + }; + + struct JoyAxis { + int min; + float value; + }; + + typedef void (*EventDispatchFunc)(const Ref &p_event); + +private: + int mouse_button_mask; + + Set keys_pressed; + Set joy_buttons_pressed; + Map _joy_axis; + //Map custom_action_press; + Vector3 gravity; + Vector3 accelerometer; + Vector3 magnetometer; + Vector3 gyroscope; + Vector2 mouse_pos; + int64_t mouse_window; + + struct Action { + uint64_t physics_frame; + uint64_t idle_frame; + bool pressed; + float strength; + }; + + Map action_state; + + bool emulate_touch_from_mouse; + bool emulate_mouse_from_touch; + + int mouse_from_touch_index; + + struct VibrationInfo { + float weak_magnitude; + float strong_magnitude; + float duration; // Duration in seconds + uint64_t timestamp; + }; + + Map joy_vibration; + + struct SpeedTrack { + + uint64_t last_tick; + Vector2 speed; + Vector2 accum; + float accum_t; + float min_ref_frame; + float max_ref_frame; + + void update(const Vector2 &p_delta_p); + void reset(); + SpeedTrack(); + }; + + struct Joypad { + StringName name; + StringName uid; + bool connected; + bool last_buttons[JOY_BUTTON_MAX + 19]; //apparently SDL specifies 35 possible buttons on android + float last_axis[JOY_AXIS_MAX]; + float filter; + int last_hat; + int mapping; + int hat_current; + + Joypad() { + for (int i = 0; i < JOY_AXIS_MAX; i++) { + + last_axis[i] = 0.0f; + } + for (int i = 0; i < JOY_BUTTON_MAX + 19; i++) { + + last_buttons[i] = false; + } + connected = false; + last_hat = HAT_MASK_CENTER; + filter = 0.01f; + mapping = -1; + hat_current = 0; + } + }; + + SpeedTrack mouse_speed_track; + Map touch_speed_track; + Map joy_names; + int fallback_mapping; + + CursorShape default_shape; + + enum JoyType { + TYPE_BUTTON, + TYPE_AXIS, + TYPE_HAT, + TYPE_MAX, + }; + + struct JoyEvent { + int type; + int index; + int value; + }; + + struct JoyDeviceMapping { + + String uid; + String name; + Map buttons; + Map axis; + JoyEvent hat[HAT_MAX]; + }; + + JoyEvent hat_map_default[HAT_MAX]; + + Vector map_db; + + JoyEvent _find_to_event(String p_to); + void _button_event(int p_device, int p_index, bool p_pressed); + 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 &p_event, bool p_is_emulated); + + List> accumulated_events; + bool use_accumulated_input; + friend class DisplayServer; + + static void (*set_mouse_mode_func)(MouseMode); + static MouseMode (*get_mouse_mode_func)(); + static void (*warp_mouse_func)(const Vector2 &p_to_pos); + + static CursorShape (*get_current_cursor_shape_func)(); + static void (*set_custom_mouse_cursor_func)(const RES &, CursorShape, const Vector2 &); + + EventDispatchFunc event_dispatch_function; + +protected: + static void _bind_methods(); + +public: + void set_mouse_mode(MouseMode p_mode); + MouseMode get_mouse_mode() const; + void get_argument_options(const StringName &p_function, int p_idx, List *r_options) const; + + static InputFilter *get_singleton(); + + bool is_key_pressed(int p_keycode) const; + bool is_mouse_button_pressed(int p_button) const; + bool is_joy_button_pressed(int p_device, int p_button) const; + bool is_action_pressed(const StringName &p_action) const; + bool is_action_just_pressed(const StringName &p_action) const; + bool is_action_just_released(const StringName &p_action) const; + float get_action_strength(const StringName &p_action) const; + + float get_joy_axis(int p_device, int p_axis) const; + String get_joy_name(int p_idx); + Array get_connected_joypads(); + Vector2 get_joy_vibration_strength(int p_device); + float get_joy_vibration_duration(int p_device); + uint64_t get_joy_vibration_timestamp(int p_device); + void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); + void parse_joypad_mapping(String p_mapping, bool p_update_existing); + + Vector3 get_gravity() const; + Vector3 get_accelerometer() const; + Vector3 get_magnetometer() const; + Vector3 get_gyroscope() const; + + Point2 get_mouse_position() const; + Point2 get_last_mouse_speed() const; + int get_mouse_button_mask() const; + + void warp_mouse_position(const Vector2 &p_to); + Point2i warp_mouse_motion(const Ref &p_motion, const Rect2 &p_rect); + + void parse_input_event(const Ref &p_event); + + void set_gravity(const Vector3 &p_gravity); + void set_accelerometer(const Vector3 &p_accel); + void set_magnetometer(const Vector3 &p_magnetometer); + void set_gyroscope(const Vector3 &p_gyroscope); + void set_joy_axis(int p_device, int p_axis, float p_value); + + void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); + void stop_joy_vibration(int p_device); + void vibrate_handheld(int p_duration_ms = 500); + + void set_mouse_position(const Point2 &p_posf); + + void action_press(const StringName &p_action, float p_strength = 1.f); + void action_release(const StringName &p_action); + + void iteration(float p_step); + + void set_emulate_touch_from_mouse(bool p_emulate); + bool is_emulating_touch_from_mouse() const; + void ensure_touch_mouse_raised(); + + void set_emulate_mouse_from_touch(bool p_emulate); + bool is_emulating_mouse_from_touch() const; + + CursorShape get_default_cursor_shape() const; + void set_default_cursor_shape(CursorShape p_shape); + CursorShape get_current_cursor_shape() const; + void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = InputFilter::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); + + void parse_mapping(String p_mapping); + void joy_button(int p_device, int p_button, bool p_pressed); + void joy_axis(int p_device, int p_axis, const JoyAxis &p_value); + void joy_hat(int p_device, int p_val); + + void add_joy_mapping(String p_mapping, bool p_update_existing = false); + void remove_joy_mapping(String p_guid); + + String get_joy_button_string(int p_button); + String get_joy_axis_string(int p_axis); + int get_joy_axis_index_from_string(String p_axis); + int get_joy_button_index_from_string(String p_button); + + int get_unused_joy_id(); + + bool is_joy_known(int p_device); + String get_joy_guid(int p_device) const; + void set_fallback_mapping(String p_guid); + + void accumulate_input_event(const Ref &p_event); + void flush_accumulated_events(); + void set_use_accumulated_input(bool p_enable); + + void release_pressed_events(); + + void set_event_dispatch_function(EventDispatchFunc p_function); + + InputFilter(); +}; + +VARIANT_ENUM_CAST(InputFilter::MouseMode); +VARIANT_ENUM_CAST(InputFilter::CursorShape); + +#endif // INPUT_H diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index ab43ce4af7..4e0743809b 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -34,30 +34,18 @@ void MainLoop::_bind_methods() { - ClassDB::bind_method(D_METHOD("input_event", "event"), &MainLoop::input_event); - ClassDB::bind_method(D_METHOD("input_text", "text"), &MainLoop::input_text); ClassDB::bind_method(D_METHOD("init"), &MainLoop::init); ClassDB::bind_method(D_METHOD("iteration", "delta"), &MainLoop::iteration); ClassDB::bind_method(D_METHOD("idle", "delta"), &MainLoop::idle); ClassDB::bind_method(D_METHOD("finish"), &MainLoop::finish); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - BIND_VMETHOD(MethodInfo("_input_text", PropertyInfo(Variant::STRING, "text"))); BIND_VMETHOD(MethodInfo("_initialize")); BIND_VMETHOD(MethodInfo(Variant::BOOL, "_iteration", PropertyInfo(Variant::FLOAT, "delta"))); BIND_VMETHOD(MethodInfo(Variant::BOOL, "_idle", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "from_screen"))); BIND_VMETHOD(MethodInfo("_finalize")); BIND_VMETHOD(MethodInfo("_global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta"))); - BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER); - BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); - BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN); - BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT); - BIND_CONSTANT(NOTIFICATION_WM_QUIT_REQUEST); - BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST); - BIND_CONSTANT(NOTIFICATION_WM_UNFOCUS_REQUEST); BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED); BIND_CONSTANT(NOTIFICATION_WM_ABOUT); @@ -80,18 +68,6 @@ MainLoop::MainLoop() { MainLoop::~MainLoop() { } -void MainLoop::input_text(const String &p_text) { - - if (get_script_instance()) - get_script_instance()->call("_input_text", p_text); -} - -void MainLoop::input_event(const Ref &p_event) { - - if (get_script_instance()) - get_script_instance()->call("_input_event", p_event); -} - void MainLoop::init() { if (init_script.is_valid()) @@ -115,12 +91,6 @@ bool MainLoop::idle(float p_time) { return false; } -void MainLoop::drop_files(const Vector &p_files, int p_from_screen) { - - if (get_script_instance()) - get_script_instance()->call("_drop_files", p_files, p_from_screen); -} - void MainLoop::global_menu_action(const Variant &p_id, const Variant &p_meta) { if (get_script_instance()) diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 2b7ec8fc5d..51cdd3e827 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -48,13 +48,6 @@ protected: public: enum { //make sure these are replicated in Node - NOTIFICATION_WM_MOUSE_ENTER = 1002, - NOTIFICATION_WM_MOUSE_EXIT = 1003, - NOTIFICATION_WM_FOCUS_IN = 1004, - NOTIFICATION_WM_FOCUS_OUT = 1005, - NOTIFICATION_WM_QUIT_REQUEST = 1006, - NOTIFICATION_WM_GO_BACK_REQUEST = 1007, - NOTIFICATION_WM_UNFOCUS_REQUEST = 1008, NOTIFICATION_OS_MEMORY_WARNING = 1009, NOTIFICATION_TRANSLATION_CHANGED = 1010, NOTIFICATION_WM_ABOUT = 1011, @@ -64,15 +57,11 @@ public: NOTIFICATION_APP_PAUSED = 1015, }; - virtual void input_event(const Ref &p_event); - virtual void input_text(const String &p_text); - virtual void init(); virtual bool iteration(float p_time); virtual bool idle(float p_time); virtual void finish(); - virtual void drop_files(const Vector &p_files, int p_from_screen = 0); virtual void global_menu_action(const Variant &p_id, const Variant &p_meta); void set_init_script(const Ref