diff --git a/core/core_constants.cpp b/core/core_constants.cpp index b99da1fe2d9..8a834029842 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -570,6 +570,16 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, SDL_MAX); BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, MAX); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, UNKNOWN); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, ON_BATTERY); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, NO_BATTERY); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, CHARGING); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, FULL_BATTERY); + + BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, UNKNOWN); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, WIRED); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, WIRELESS); + BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NONE); BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_OFF); BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_ON); diff --git a/core/input/input.cpp b/core/input/input.cpp index 512f402158f..5f70b54919b 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -183,6 +183,9 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope); + ClassDB::bind_method(D_METHOD("get_joy_power_state", "device"), &Input::get_joy_power_state); + ClassDB::bind_method(D_METHOD("get_joy_battery_percent", "device"), &Input::get_joy_battery_percent); + ClassDB::bind_method(D_METHOD("get_joy_connection_state", "device"), &Input::get_joy_connection_state); ClassDB::bind_method(D_METHOD("set_joy_light", "device", "color"), &Input::set_joy_light); ClassDB::bind_method(D_METHOD("has_joy_light", "device"), &Input::has_joy_light); ClassDB::bind_method(D_METHOD("get_last_mouse_velocity"), &Input::get_last_mouse_velocity); @@ -798,6 +801,33 @@ Vector3 Input::get_gyroscope() const { return gyroscope; } +JoyPowerState Input::get_joy_power_state(int p_device) const { + _THREAD_SAFE_METHOD_ + const Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr) { + return JoyPowerState::UNKNOWN; + } + return joypad->power_state; +} + +int Input::get_joy_battery_percent(int p_device) const { + _THREAD_SAFE_METHOD_ + const Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr) { + return -1; + } + return joypad->battery_percent; +} + +JoyConnectionState Input::get_joy_connection_state(int p_device) const { + _THREAD_SAFE_METHOD_ + const Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr) { + return JoyConnectionState::UNKNOWN; + } + return joypad->connection_state; +} + void Input::_parse_input_event_impl(const Ref &p_event, bool p_is_emulated) { // This function does the final delivery of the input event to user land. // Regardless where the event came from originally, this has to happen on the main thread. @@ -1356,6 +1386,33 @@ void Input::set_gyroscope(const Vector3 &p_gyroscope) { gyroscope = p_gyroscope; } +void Input::set_joy_power_state(int p_device, JoyPowerState p_state) { + _THREAD_SAFE_METHOD_ + Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr) { + return; + } + joypad->power_state = p_state; +} + +void Input::set_joy_battery_percent(int p_device, int p_percent) { + _THREAD_SAFE_METHOD_ + Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr || p_percent < -1 || p_percent > 100) { + return; + } + joypad->battery_percent = p_percent; +} + +void Input::set_joy_connection_state(int p_device, JoyConnectionState p_state) { + _THREAD_SAFE_METHOD_ + Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr) { + return; + } + joypad->connection_state = p_state; +} + void Input::set_mouse_position(const Point2 &p_posf) { mouse_pos = p_posf; } diff --git a/core/input/input.h b/core/input/input.h index 6d732877efb..c07a6b7e8df 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -217,6 +217,9 @@ class Input : public Object { HatMask last_hat = HatMask::CENTER; int mapping = -1; int hat_current = 0; + int8_t battery_percent = -1; + JoyPowerState power_state = JoyPowerState::UNKNOWN; + JoyConnectionState connection_state = JoyConnectionState::UNKNOWN; Dictionary info; bool has_light = false; bool has_vibration = false; @@ -385,6 +388,10 @@ class Input : public Object { Vector3 get_magnetometer() const; Vector3 get_gyroscope() const; + JoyPowerState get_joy_power_state(int p_device) const; + int get_joy_battery_percent(int p_device) const; + JoyConnectionState get_joy_connection_state(int p_device) const; + Point2 get_mouse_position() const; Vector2 get_last_mouse_velocity(); Vector2 get_last_mouse_screen_velocity(); @@ -399,6 +406,11 @@ class Input : public Object { void set_accelerometer(const Vector3 &p_accel); void set_magnetometer(const Vector3 &p_magnetometer); void set_gyroscope(const Vector3 &p_gyroscope); + + void set_joy_power_state(int p_device, JoyPowerState p_state); + void set_joy_battery_percent(int p_device, int p_percent); + void set_joy_connection_state(int p_device, JoyConnectionState p_state); + void set_joy_axis(int p_device, JoyAxis p_axis, float p_value); void set_joy_features(int p_device, JoypadFeatures *p_features); diff --git a/core/input/input_enums.h b/core/input/input_enums.h index b0bd1540064..cf5108ebd63 100644 --- a/core/input/input_enums.h +++ b/core/input/input_enums.h @@ -109,6 +109,22 @@ enum class JoyButton { MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons. }; +// See SDL_PowerState. SDL_POWERSTATE_ERROR equivalent was omitted for simplicity. +enum class JoyPowerState { + UNKNOWN = 0, + ON_BATTERY = 1, + NO_BATTERY = 2, + CHARGING = 3, + FULL_BATTERY = 4, +}; + +// See SDL_JoystickConnectionState. SDL_JOYSTICK_CONNECTION_INVALID equivalent was omitted for simplicity. +enum class JoyConnectionState { + UNKNOWN = 0, + WIRED = 1, + WIRELESS = 2, +}; + enum class MIDIMessage { NONE = 0, NOTE_OFF = 0x8, diff --git a/core/variant/variant_caster.h b/core/variant/variant_caster.h index 0815a423181..9a64194ab86 100644 --- a/core/variant/variant_caster.h +++ b/core/variant/variant_caster.h @@ -37,6 +37,8 @@ enum class HatDir; enum class HatMask; enum class JoyAxis; enum class JoyButton; +enum class JoyPowerState; +enum class JoyConnectionState; enum class MIDIMessage; enum class MouseButton; @@ -105,6 +107,8 @@ VARIANT_ENUM_CAST(HatDir); VARIANT_BITFIELD_CAST(HatMask); VARIANT_ENUM_CAST(JoyAxis); VARIANT_ENUM_CAST(JoyButton); +VARIANT_ENUM_CAST(JoyPowerState); +VARIANT_ENUM_CAST(JoyConnectionState); VARIANT_ENUM_CAST(MIDIMessage); VARIANT_ENUM_CAST(MouseButton); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index b212f3ca91e..2ba07963755 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2572,6 +2572,30 @@ The maximum number of game controller axes: OpenVR supports up to 5 Joysticks making a total of 10 axes. + + An invalid or unknown joypad power state. + + + The joypad is not plugged in and is running on the battery. + + + The joypad is plugged in and has no battery available. + + + The joypad is plugged in and its battery is currently charging. + + + The joypad is plugged in and its battery is fully charged. + + + An invalid or unknown joypad connection state. + + + The joypad is currently using a wired connection to the device. + + + The joypad is currently using a wireless connection to the device. + Does not correspond to any MIDI message. This is the default value of [member InputEventMIDI.message]. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index ee7d3aa6408..5c48ad773aa 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -140,6 +140,22 @@ Returns the current value of the joypad axis at index [param axis]. + + + + + Returns the battery percentage of the requested joypad, if available. + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + + + + + + Returns the joypad's connection state (i.e. if it's wired or wireless). + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + @@ -217,6 +233,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 joypad's power state (i.e. if it's currently charging, being used on battery, etc.). + [b]Note:[/b] This feature is only supported on Windows, Linux, and macOS. + + diff --git a/drivers/sdl/joypad_sdl.cpp b/drivers/sdl/joypad_sdl.cpp index 669d766f7be..88973d2ab92 100644 --- a/drivers/sdl/joypad_sdl.cpp +++ b/drivers/sdl/joypad_sdl.cpp @@ -203,12 +203,29 @@ void JoypadSDL::process_events() { joypads[joy_id].guid, joypad_info); + // Querying more information about the joypad + + int joy_battery_percent; + SDL_PowerState joy_power_state = SDL_GetJoystickPowerInfo(joy, &joy_battery_percent); + SDL_JoystickConnectionState joy_connection_state = SDL_GetJoystickConnectionState(joy); + if (joy_power_state == SDL_POWERSTATE_ERROR) { + joy_power_state = SDL_POWERSTATE_UNKNOWN; + } + if (joy_connection_state == SDL_JOYSTICK_CONNECTION_INVALID) { + joy_connection_state = SDL_JOYSTICK_CONNECTION_UNKNOWN; + } + Input::get_singleton()->set_joy_features(joy_id, &joypads[joy_id]); if (joypads[joy_id].supports_motion_sensors) { // Data rate for all sensors should be the same. Input::get_singleton()->set_joy_motion_sensors_rate(joy_id, SDL_GetGamepadSensorDataRate(gamepad, SDL_SENSOR_ACCEL)); } + + // Godot constants are intentionally the same as SDL's + Input::get_singleton()->set_joy_power_state(joy_id, static_cast(joy_power_state)); + Input::get_singleton()->set_joy_battery_percent(joy_id, joy_battery_percent); + Input::get_singleton()->set_joy_connection_state(joy_id, static_cast(joy_connection_state)); } // An event for an attached joypad } else if (sdl_event.type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && sdl_event.type < SDL_EVENT_FINGER_DOWN && sdl_instance_id_to_joypad_id.has(sdl_event.jdevice.which)) { @@ -254,6 +271,17 @@ void JoypadSDL::process_events() { ); break; + case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: { + // Gamepads also can have battery, so no SKIP_EVENT_FOR_GAMEPAD here + + SDL_PowerState joy_power_state = sdl_event.jbattery.state; + if (joy_power_state == SDL_POWERSTATE_ERROR) { + joy_power_state = SDL_POWERSTATE_UNKNOWN; + } + Input::get_singleton()->set_joy_power_state(joy_id, static_cast(sdl_event.jbattery.state)); // Godot constants are intentionally the same as SDL's + Input::get_singleton()->set_joy_battery_percent(joy_id, sdl_event.jbattery.percent); + } break; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: { float axis_value;