Skip to content

Commit 68335be

Browse files
committed
Add support for joypad battery and connection info
This PR adds the ability to get information about the joypad's battery state (no battery, charging, fully charged), battery percentage, connection state (wired, wireless).
1 parent 1eb0374 commit 68335be

8 files changed

Lines changed: 175 additions & 0 deletions

File tree

core/core_constants.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,16 @@ void register_global_constants() {
570570
BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, SDL_MAX);
571571
BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, MAX);
572572

573+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, UNKNOWN);
574+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, ON_BATTERY);
575+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, NO_BATTERY);
576+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, CHARGING);
577+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyPowerState, JOY_POWER, FULL_BATTERY);
578+
579+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, UNKNOWN);
580+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, WIRED);
581+
BIND_CORE_ENUM_CLASS_CONSTANT(JoyConnectionState, JOY_CONNECTION, WIRELESS);
582+
573583
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NONE);
574584
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_OFF);
575585
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_ON);

core/input/input.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ void Input::_bind_methods() {
183183
ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer);
184184
ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer);
185185
ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope);
186+
ClassDB::bind_method(D_METHOD("get_joy_power_state", "device"), &Input::get_joy_power_state);
187+
ClassDB::bind_method(D_METHOD("get_joy_battery_percent", "device"), &Input::get_joy_battery_percent);
188+
ClassDB::bind_method(D_METHOD("get_joy_connection_state", "device"), &Input::get_joy_connection_state);
186189
ClassDB::bind_method(D_METHOD("set_joy_light", "device", "color"), &Input::set_joy_light);
187190
ClassDB::bind_method(D_METHOD("has_joy_light", "device"), &Input::has_joy_light);
188191
ClassDB::bind_method(D_METHOD("get_last_mouse_velocity"), &Input::get_last_mouse_velocity);
@@ -798,6 +801,33 @@ Vector3 Input::get_gyroscope() const {
798801
return gyroscope;
799802
}
800803

804+
JoyPowerState Input::get_joy_power_state(int p_device) const {
805+
_THREAD_SAFE_METHOD_
806+
const Joypad *joypad = joy_names.getptr(p_device);
807+
if (joypad == nullptr) {
808+
return JoyPowerState::UNKNOWN;
809+
}
810+
return joypad->power_state;
811+
}
812+
813+
int Input::get_joy_battery_percent(int p_device) const {
814+
_THREAD_SAFE_METHOD_
815+
const Joypad *joypad = joy_names.getptr(p_device);
816+
if (joypad == nullptr) {
817+
return -1;
818+
}
819+
return joypad->battery_percent;
820+
}
821+
822+
JoyConnectionState Input::get_joy_connection_state(int p_device) const {
823+
_THREAD_SAFE_METHOD_
824+
const Joypad *joypad = joy_names.getptr(p_device);
825+
if (joypad == nullptr) {
826+
return JoyConnectionState::UNKNOWN;
827+
}
828+
return joypad->connection_state;
829+
}
830+
801831
void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
802832
// This function does the final delivery of the input event to user land.
803833
// 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) {
13561386
gyroscope = p_gyroscope;
13571387
}
13581388

1389+
void Input::set_joy_power_state(int p_device, JoyPowerState p_state) {
1390+
_THREAD_SAFE_METHOD_
1391+
Joypad *joypad = joy_names.getptr(p_device);
1392+
if (joypad == nullptr) {
1393+
return;
1394+
}
1395+
joypad->power_state = p_state;
1396+
}
1397+
1398+
void Input::set_joy_battery_percent(int p_device, int p_percent) {
1399+
_THREAD_SAFE_METHOD_
1400+
Joypad *joypad = joy_names.getptr(p_device);
1401+
if (joypad == nullptr || p_percent < -1 || p_percent > 100) {
1402+
return;
1403+
}
1404+
joypad->battery_percent = p_percent;
1405+
}
1406+
1407+
void Input::set_joy_connection_state(int p_device, JoyConnectionState p_state) {
1408+
_THREAD_SAFE_METHOD_
1409+
Joypad *joypad = joy_names.getptr(p_device);
1410+
if (joypad == nullptr) {
1411+
return;
1412+
}
1413+
joypad->connection_state = p_state;
1414+
}
1415+
13591416
void Input::set_mouse_position(const Point2 &p_posf) {
13601417
mouse_pos = p_posf;
13611418
}

core/input/input.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ class Input : public Object {
217217
HatMask last_hat = HatMask::CENTER;
218218
int mapping = -1;
219219
int hat_current = 0;
220+
int8_t battery_percent = -1;
221+
JoyPowerState power_state = JoyPowerState::UNKNOWN;
222+
JoyConnectionState connection_state = JoyConnectionState::UNKNOWN;
220223
Dictionary info;
221224
bool has_light = false;
222225
bool has_vibration = false;
@@ -385,6 +388,10 @@ class Input : public Object {
385388
Vector3 get_magnetometer() const;
386389
Vector3 get_gyroscope() const;
387390

391+
JoyPowerState get_joy_power_state(int p_device) const;
392+
int get_joy_battery_percent(int p_device) const;
393+
JoyConnectionState get_joy_connection_state(int p_device) const;
394+
388395
Point2 get_mouse_position() const;
389396
Vector2 get_last_mouse_velocity();
390397
Vector2 get_last_mouse_screen_velocity();
@@ -399,6 +406,11 @@ class Input : public Object {
399406
void set_accelerometer(const Vector3 &p_accel);
400407
void set_magnetometer(const Vector3 &p_magnetometer);
401408
void set_gyroscope(const Vector3 &p_gyroscope);
409+
410+
void set_joy_power_state(int p_device, JoyPowerState p_state);
411+
void set_joy_battery_percent(int p_device, int p_percent);
412+
void set_joy_connection_state(int p_device, JoyConnectionState p_state);
413+
402414
void set_joy_axis(int p_device, JoyAxis p_axis, float p_value);
403415

404416
void set_joy_features(int p_device, JoypadFeatures *p_features);

core/input/input_enums.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ enum class JoyButton {
109109
MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons.
110110
};
111111

112+
// See SDL_PowerState. SDL_POWERSTATE_ERROR equivalent was omitted for simplicity.
113+
enum class JoyPowerState {
114+
UNKNOWN = 0,
115+
ON_BATTERY = 1,
116+
NO_BATTERY = 2,
117+
CHARGING = 3,
118+
FULL_BATTERY = 4,
119+
};
120+
121+
// See SDL_JoystickConnectionState. SDL_JOYSTICK_CONNECTION_INVALID equivalent was omitted for simplicity.
122+
enum class JoyConnectionState {
123+
UNKNOWN = 0,
124+
WIRED = 1,
125+
WIRELESS = 2,
126+
};
127+
112128
enum class MIDIMessage {
113129
NONE = 0,
114130
NOTE_OFF = 0x8,

core/variant/variant_caster.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ enum class HatDir;
3737
enum class HatMask;
3838
enum class JoyAxis;
3939
enum class JoyButton;
40+
enum class JoyPowerState;
41+
enum class JoyConnectionState;
4042

4143
enum class MIDIMessage;
4244
enum class MouseButton;
@@ -105,6 +107,8 @@ VARIANT_ENUM_CAST(HatDir);
105107
VARIANT_BITFIELD_CAST(HatMask);
106108
VARIANT_ENUM_CAST(JoyAxis);
107109
VARIANT_ENUM_CAST(JoyButton);
110+
VARIANT_ENUM_CAST(JoyPowerState);
111+
VARIANT_ENUM_CAST(JoyConnectionState);
108112

109113
VARIANT_ENUM_CAST(MIDIMessage);
110114
VARIANT_ENUM_CAST(MouseButton);

doc/classes/@GlobalScope.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,6 +2572,30 @@
25722572
<constant name="JOY_AXIS_MAX" value="10" enum="JoyAxis">
25732573
The maximum number of game controller axes: OpenVR supports up to 5 Joysticks making a total of 10 axes.
25742574
</constant>
2575+
<constant name="JOY_POWER_UNKNOWN" value="0" enum="JoyPowerState">
2576+
An invalid or unknown joypad power state.
2577+
</constant>
2578+
<constant name="JOY_POWER_ON_BATTERY" value="1" enum="JoyPowerState">
2579+
The joypad is not plugged in and is running on the battery.
2580+
</constant>
2581+
<constant name="JOY_POWER_NO_BATTERY" value="2" enum="JoyPowerState">
2582+
The joypad is plugged in and has no battery available.
2583+
</constant>
2584+
<constant name="JOY_POWER_CHARGING" value="3" enum="JoyPowerState">
2585+
The joypad is plugged in and its battery is currently charging.
2586+
</constant>
2587+
<constant name="JOY_POWER_FULL_BATTERY" value="4" enum="JoyPowerState">
2588+
The joypad is plugged in and its battery is fully charged.
2589+
</constant>
2590+
<constant name="JOY_CONNECTION_UNKNOWN" value="0" enum="JoyConnectionState">
2591+
An invalid or unknown joypad connection state.
2592+
</constant>
2593+
<constant name="JOY_CONNECTION_WIRED" value="1" enum="JoyConnectionState">
2594+
The joypad is currently using a wired connection to the device.
2595+
</constant>
2596+
<constant name="JOY_CONNECTION_WIRELESS" value="2" enum="JoyConnectionState">
2597+
The joypad is currently using a wireless connection to the device.
2598+
</constant>
25752599
<constant name="MIDI_MESSAGE_NONE" value="0" enum="MIDIMessage">
25762600
Does not correspond to any MIDI message. This is the default value of [member InputEventMIDI.message].
25772601
</constant>

doc/classes/Input.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,22 @@
140140
Returns the current value of the joypad axis at index [param axis].
141141
</description>
142142
</method>
143+
<method name="get_joy_battery_percent" qualifiers="const" experimental="">
144+
<return type="int" />
145+
<param index="0" name="device" type="int" />
146+
<description>
147+
Returns the battery percentage of the requested joypad, if available.
148+
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
149+
</description>
150+
</method>
151+
<method name="get_joy_connection_state" qualifiers="const" experimental="">
152+
<return type="int" enum="JoyConnectionState" />
153+
<param index="0" name="device" type="int" />
154+
<description>
155+
Returns the joypad's connection state (i.e. if it's wired or wireless).
156+
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
157+
</description>
158+
</method>
143159
<method name="get_joy_gravity" qualifiers="const" experimental="">
144160
<return type="Vector3" />
145161
<param index="0" name="device" type="int" />
@@ -217,6 +233,14 @@
217233
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.
218234
</description>
219235
</method>
236+
<method name="get_joy_power_state" qualifiers="const" experimental="">
237+
<return type="int" enum="JoyPowerState" />
238+
<param index="0" name="device" type="int" />
239+
<description>
240+
Returns the joypad's power state (i.e. if it's currently charging, being used on battery, etc.).
241+
[b]Note:[/b] This feature is only supported on Windows, Linux, and macOS.
242+
</description>
243+
</method>
220244
<method name="get_joy_vibration_duration">
221245
<return type="float" />
222246
<param index="0" name="device" type="int" />

drivers/sdl/joypad_sdl.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,29 @@ void JoypadSDL::process_events() {
203203
joypads[joy_id].guid,
204204
joypad_info);
205205

206+
// Querying more information about the joypad
207+
208+
int joy_battery_percent;
209+
SDL_PowerState joy_power_state = SDL_GetJoystickPowerInfo(joy, &joy_battery_percent);
210+
SDL_JoystickConnectionState joy_connection_state = SDL_GetJoystickConnectionState(joy);
211+
if (joy_power_state == SDL_POWERSTATE_ERROR) {
212+
joy_power_state = SDL_POWERSTATE_UNKNOWN;
213+
}
214+
if (joy_connection_state == SDL_JOYSTICK_CONNECTION_INVALID) {
215+
joy_connection_state = SDL_JOYSTICK_CONNECTION_UNKNOWN;
216+
}
217+
206218
Input::get_singleton()->set_joy_features(joy_id, &joypads[joy_id]);
207219

208220
if (joypads[joy_id].supports_motion_sensors) {
209221
// Data rate for all sensors should be the same.
210222
Input::get_singleton()->set_joy_motion_sensors_rate(joy_id, SDL_GetGamepadSensorDataRate(gamepad, SDL_SENSOR_ACCEL));
211223
}
224+
225+
// Godot constants are intentionally the same as SDL's
226+
Input::get_singleton()->set_joy_power_state(joy_id, static_cast<JoyPowerState>(joy_power_state));
227+
Input::get_singleton()->set_joy_battery_percent(joy_id, joy_battery_percent);
228+
Input::get_singleton()->set_joy_connection_state(joy_id, static_cast<JoyConnectionState>(joy_connection_state));
212229
}
213230
// An event for an attached joypad
214231
} 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() {
254271
);
255272
break;
256273

274+
case SDL_EVENT_JOYSTICK_BATTERY_UPDATED: {
275+
// Gamepads also can have battery, so no SKIP_EVENT_FOR_GAMEPAD here
276+
277+
SDL_PowerState joy_power_state = sdl_event.jbattery.state;
278+
if (joy_power_state == SDL_POWERSTATE_ERROR) {
279+
joy_power_state = SDL_POWERSTATE_UNKNOWN;
280+
}
281+
Input::get_singleton()->set_joy_power_state(joy_id, static_cast<JoyPowerState>(sdl_event.jbattery.state)); // Godot constants are intentionally the same as SDL's
282+
Input::get_singleton()->set_joy_battery_percent(joy_id, sdl_event.jbattery.percent);
283+
} break;
284+
257285
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
258286
float axis_value;
259287

0 commit comments

Comments
 (0)