From 5c97302cca3854fdb4f6c6563c9b020e052b75d6 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Tue, 5 Mar 2024 15:17:51 -0300 Subject: [PATCH] Readd and improve methods to get the name of gamepad inputs --- core/input/input.cpp | 240 ++++++++++++++++++++++++++++++++++++++++-- core/input/input.h | 18 +++- doc/classes/Input.xml | 52 +++++++++ 3 files changed, 299 insertions(+), 11 deletions(-) diff --git a/core/input/input.cpp b/core/input/input.cpp index 3de0ed39ec77..d346bbd0f4b9 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -72,6 +72,138 @@ static const char *_joy_axes[(size_t)JoyAxis::SDL_MAX] = { "righttrigger", }; +static const char *_joy_button_names_unknown[(size_t)JoyButton::SDL_MAX] = { + "Bottom Action", + "Right Action", + "Left Action", + "Top Action", + "Select", + "Guide", + "Start", + "Left Stick", + "Right Stick", + "Left Shoulder", + "Right Shoulder", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Misc", + "Paddle 1", + "Paddle 2", + "Paddle 3", + "Paddle 4", + "Touchpad", +}; + +static const char *_joy_button_names_nintendo_generic[(size_t)JoyButton::SDL_MAX] = { + "B", + "A", + "Y", + "X", + "-", + "", + "+", + "", + "", + "L", + "R", + "", + "", + "", + "", + "Capture", + "", + "", + "", + "", + "", +}; + +static const char *_joy_button_names_playstation_generic[(size_t)JoyButton::SDL_MAX] = { + "Cross", + "Circle", + "Square", + "Triangle", + "Select", + "PS", + "", + "L3", + "R3", + "L1", + "R1", + "", + "", + "", + "", + "Microphone", + "", + "", + "", + "", + "", +}; + +static const char *_joy_button_names_xbox_generic[(size_t)JoyButton::SDL_MAX] = { + "A", + "B", + "X", + "Y", + "Back", + "Home", + "Menu", + "L/LS", + "R/RS", + "LB", + "RB", + "", + "", + "", + "", + "Share", + "", + "", + "", + "", + "", +}; + +static const char *_joy_axis_names_unknown[(size_t)JoyAxis::SDL_MAX] = { + "Left Stick X-Axis", + "Left Stick Y-Axis", + "Right Stick X-Axis", + "Right Stick Y-Axis", + "Left Trigger", + "Right Trigger", +}; + +static const char *_joy_axis_names_nintendo_generic[(size_t)JoyAxis::SDL_MAX] = { + "", + "", + "", + "", + "ZL", + "ZR", +}; + +static const char *_joy_axis_names_playstation_generic[(size_t)JoyAxis::SDL_MAX] = { + "", + "", + "", + "", + "L2", + "R2", +}; + +static const char *_joy_axis_names_xbox_generic[(size_t)JoyAxis::SDL_MAX] = { + "", + "", + "", + "", + "LT", + "RT", +}; + Input *Input::singleton = nullptr; void (*Input::set_mouse_mode_func)(Input::MouseMode) = nullptr; @@ -112,12 +244,15 @@ void Input::_bind_methods() { 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_scheme", "device"), &Input::get_joy_scheme); ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info); ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device); 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", "scheme"), &Input::get_joy_button_string, DEFVAL(JoyScheme::JOY_SCHEME_UNKNOWN)); + ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis", "value", "scheme"), &Input::get_joy_axis_string, DEFVAL(0), DEFVAL(JoyScheme::JOY_SCHEME_UNKNOWN)); 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)); @@ -178,6 +313,11 @@ void Input::_bind_methods() { BIND_ENUM_CONSTANT(CURSOR_HSPLIT); BIND_ENUM_CONSTANT(CURSOR_HELP); + BIND_ENUM_CONSTANT(JOY_SCHEME_UNKNOWN); + BIND_ENUM_CONSTANT(JOY_SCHEME_NINTENDO_GENERIC); + BIND_ENUM_CONSTANT(JOY_SCHEME_PLAYSTATION_GENERIC); + BIND_ENUM_CONSTANT(JOY_SCHEME_XBOX_GENERIC); + ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); } @@ -417,9 +557,26 @@ float Input::get_joy_axis(int p_device, JoyAxis p_axis) const { } } -String Input::get_joy_name(int p_idx) { +String Input::get_joy_name(int p_device) { _THREAD_SAFE_METHOD_ - return joy_names[p_idx].name; + return joy_names[p_device].name; +} + +Input::JoyScheme Input::get_joy_scheme(int p_device) { + String joy_name = get_joy_name(p_device).to_lower(); + + if (joy_name.contains("nintendo") || joy_name.contains("joy-con") || joy_name.contains("gamecube")) { + return JOY_SCHEME_NINTENDO_GENERIC; + } + if (joy_name.contains("sony") || joy_name.contains("playstation") || joy_name.contains("dualshock") || + joy_name.contains("ps1") || joy_name.contains("ps2") || joy_name.contains("ps3") || joy_name.contains("ps4") || joy_name.contains("ps5")) { + return JOY_SCHEME_PLAYSTATION_GENERIC; + } + if (joy_name.contains("microsoft") || joy_name.contains("xbox")) { + return JOY_SCHEME_XBOX_GENERIC; + } + + return JOY_SCHEME_UNKNOWN; } Vector2 Input::get_joy_vibration_strength(int p_device) { @@ -446,6 +603,73 @@ float Input::get_joy_vibration_duration(int p_device) { } } +String Input::get_joy_button_string(JoyButton p_button, JoyScheme p_scheme) { + String button_name; + switch (p_scheme) { + case JOY_SCHEME_NINTENDO_GENERIC: { + button_name = _joy_button_names_nintendo_generic[(int)p_button]; + } break; + case JOY_SCHEME_PLAYSTATION_GENERIC: { + button_name = _joy_button_names_playstation_generic[(int)p_button]; + } break; + case JOY_SCHEME_XBOX_GENERIC: { + button_name = _joy_button_names_xbox_generic[(int)p_button]; + } break; + default: { + } + } + + if (button_name.is_empty()) { + button_name = _joy_button_names_unknown[(int)p_button]; + } + + return button_name; +} + +String Input::get_joy_axis_string(JoyAxis p_axis, float p_value, JoyScheme p_scheme) { + if (p_axis == JoyAxis::TRIGGER_LEFT || p_axis == JoyAxis::TRIGGER_RIGHT) { + String axis_name; + switch (p_scheme) { + case JOY_SCHEME_NINTENDO_GENERIC: { + axis_name = _joy_axis_names_nintendo_generic[(int)p_axis]; + } break; + case JOY_SCHEME_PLAYSTATION_GENERIC: { + axis_name = _joy_axis_names_playstation_generic[(int)p_axis]; + } break; + case JOY_SCHEME_XBOX_GENERIC: { + axis_name = _joy_axis_names_xbox_generic[(int)p_axis]; + } break; + default: { + } + } + + if (axis_name.is_empty()) { + axis_name = _joy_axis_names_unknown[(int)p_axis]; + } + + return axis_name; + } + + if (p_value != 0.0) { + String stick; + if (p_axis == JoyAxis::LEFT_X || p_axis == JoyAxis::LEFT_Y) { + stick = "Left Stick "; + } else if (p_axis == JoyAxis::RIGHT_X || p_axis == JoyAxis::RIGHT_Y) { + stick = "Right Stick "; + } + + if (p_axis == JoyAxis::LEFT_X || p_axis == JoyAxis::RIGHT_X) { + stick += p_value < 0 ? "Left" : "Right"; + } else if (p_axis == JoyAxis::LEFT_Y || p_axis == JoyAxis::RIGHT_Y) { + stick += p_value < 0 ? "Up" : "Down"; + } + + return stick; + } + + return _joy_axis_names_unknown[(int)p_axis]; +} + static String _hex_str(uint8_t p_byte) { static const char *dict = "0123456789abcdef"; char ret[3]; @@ -457,13 +681,13 @@ static String _hex_str(uint8_t p_byte) { return ret; } -void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid, const Dictionary &p_joypad_info) { +void Input::joy_connection_changed(int p_device, bool p_connected, const String &p_name, const String &p_guid, const Dictionary &p_joypad_info) { _THREAD_SAFE_METHOD_ // Clear the pressed status if a Joypad gets disconnected. if (!p_connected) { for (KeyValue &E : action_states) { - HashMap::Iterator it = E.value.device_states.find(p_idx); + HashMap::Iterator it = E.value.device_states.find(p_device); if (it) { E.value.device_states.remove(it); _update_action_cache(E.key, E.value); @@ -497,17 +721,17 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_ } else { js.connected = false; for (int i = 0; i < (int)JoyButton::MAX; i++) { - JoyButton c = _combine_device((JoyButton)i, p_idx); + JoyButton c = _combine_device((JoyButton)i, p_device); joy_buttons_pressed.erase(c); } for (int i = 0; i < (int)JoyAxis::MAX; i++) { - set_joy_axis(p_idx, (JoyAxis)i, 0.0f); + set_joy_axis(p_device, (JoyAxis)i, 0.0f); } } - joy_names[p_idx] = js; + joy_names[p_device] = js; // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread. - call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected); + call_deferred("emit_signal", SNAME("joy_connection_changed"), p_device, p_connected); } Vector3 Input::get_gravity() const { diff --git a/core/input/input.h b/core/input/input.h index d1f284e8f7fc..3aaae530c924 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -31,6 +31,7 @@ #ifndef INPUT_H #define INPUT_H +#include "core/input/input_enums.h" #include "core/input/input_event.h" #include "core/object/object.h" #include "core/os/keyboard.h" @@ -74,7 +75,14 @@ class Input : public Object { CURSOR_VSPLIT, CURSOR_HSPLIT, CURSOR_HELP, - CURSOR_MAX + CURSOR_MAX, + }; + + enum JoyScheme { + JOY_SCHEME_UNKNOWN, + JOY_SCHEME_NINTENDO_GENERIC, + JOY_SCHEME_PLAYSTATION_GENERIC, + JOY_SCHEME_XBOX_GENERIC, }; enum { @@ -293,12 +301,15 @@ class Input : public Object { Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; float get_joy_axis(int p_device, JoyAxis p_axis) const; - String get_joy_name(int p_idx); + String get_joy_name(int p_device); + JoyScheme get_joy_scheme(int p_device); TypedArray 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, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); + String get_joy_button_string(JoyButton p_button, JoyScheme p_scheme = JoyScheme::JOY_SCHEME_UNKNOWN); + String get_joy_axis_string(JoyAxis p_axis, float p_value = 0, JoyScheme p_scheme = JoyScheme::JOY_SCHEME_UNKNOWN); + void joy_connection_changed(int p_device, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); Vector3 get_gravity() const; Vector3 get_accelerometer() const; @@ -374,5 +385,6 @@ class Input : public Object { VARIANT_ENUM_CAST(Input::MouseMode); VARIANT_ENUM_CAST(Input::CursorShape); +VARIANT_ENUM_CAST(Input::JoyScheme); #endif // INPUT_H diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index e622a6bce302..3160eab9481d 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -114,6 +114,38 @@ Returns the current value of the joypad axis at given index (see [enum JoyAxis]). + + + + + + + Returns the string equivalent of the given [param axis]. If [param value] is set to anything besides [code]0[/code], the axis orientation will be replaced with the direction. If [param scheme] is set, it will adapt the result to the given control scheme (see [method get_joy_scheme]). + [b]Examples:[/b] + [codeblocks] + [gdscript] + get_joy_axis_string(JoyAxis.LEFT_X) # Returns "Left Stick X-Axis". + get_joy_axis_string(JoyAxis.LEFT_X, -0.5) # Returns "Left Stick Left". + get_joy_axis_string(JoyAxis.TRIGGER_RIGHT, 0.5, Input.JoyScheme.XBOX) # Returns "RT". + [/gdscript] + [/codeblocks] + + + + + + + + Returns the string equivalent of the given gamepad [param button]. If [param scheme] is set, it will adapt the result to the given control scheme (see [method get_joy_scheme]). + [b]Examples:[/b] + [codeblocks] + [gdscript] + get_joy_button_string(JoyButton.A) # Returns "Bottom Action". + get_joy_button_string(JoyButton.A, Input.JoyScheme.JOY_SCHEME_PLAYSTATION_GENERIC) # Returns "Cross". + [/gdscript] + [/codeblocks] + + @@ -142,6 +174,14 @@ Returns the name of the joypad at the specified device index, e.g. [code]PS4 Controller[/code]. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names. + + + + + Returns the device's control scheme. + [b]Note:[/b] On non-official gamepads, the most likely result will be [constant JOY_SCHEME_UNKNOWN]. + + @@ -509,5 +549,17 @@ Help cursor. Usually a question mark. + + Unspecified gamepad button layout. + + + Generic button layout used by Nintendo gamepads. + + + Generic button layout used by PlayStation gamepads. + + + Generic button layout used by Xbox gamepads. +