#include "device/gamepad/gamepad_pad_state_provider.h"
#include <cmath>
#include <memory>
#include <optional>
#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_provider.h"
#include "device/gamepad/public/cpp/gamepads.h"
namespace device {
namespace {
const float kMinAxisResetValue = 0.1f;
}
PadState::PadState() = default;
PadState::~PadState() = default;
GamepadPadStateProvider::GamepadPadStateProvider() {
pad_states_ = base::HeapArray<PadState>::WithSize(Gamepads::kItemsLengthCap);
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
ClearPadState(pad_states_[i]);
}
GamepadPadStateProvider::~GamepadPadStateProvider() = default;
PadState* GamepadPadStateProvider::GetPadState(GamepadSource source,
int source_id,
bool new_gamepad_recognized) {
std::optional<size_t> empty_slot_index;
std::optional<size_t> unrecognized_slot_index;
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
auto& state = pad_states_[i];
if (state.source == source && state.source_id == source_id) {
state.is_active = true;
return &state;
}
if (!empty_slot_index && state.source == GamepadSource::kNone)
empty_slot_index = i;
if (!state.is_recognized)
unrecognized_slot_index = i;
}
if (!empty_slot_index && unrecognized_slot_index && new_gamepad_recognized) {
auto& state = pad_states_[*unrecognized_slot_index];
DisconnectUnrecognizedGamepad(state.source, state.source_id);
empty_slot_index = unrecognized_slot_index;
}
if (!empty_slot_index) {
return nullptr;
}
auto& empty_slot = pad_states_[*empty_slot_index];
empty_slot.pad_index = *empty_slot_index;
empty_slot.source = source;
empty_slot.source_id = source_id;
empty_slot.is_active = true;
empty_slot.is_newly_active = true;
empty_slot.is_initialized = false;
empty_slot.is_recognized = new_gamepad_recognized;
return &empty_slot;
}
PadState* GamepadPadStateProvider::GetConnectedPadState(uint32_t pad_index) {
if (pad_index >= Gamepads::kItemsLengthCap)
return nullptr;
PadState& pad_state = pad_states_[pad_index];
if (pad_state.source == GamepadSource::kNone)
return nullptr;
return &pad_state;
}
void GamepadPadStateProvider::ClearPadState(PadState& state) {
UNSAFE_TODO(memset(&state, 0, sizeof(PadState)));
}
void GamepadPadStateProvider::InitializeDataFetcher(
GamepadDataFetcher* fetcher) {
fetcher->InitializeProvider(this);
}
void GamepadPadStateProvider::MapAndSanitizeGamepadData(PadState* pad_state,
Gamepad* pad,
bool sanitize) {
DCHECK(pad_state);
DCHECK(pad);
if (!pad_state->data.connected) {
UNSAFE_TODO(memset(pad, 0, sizeof(Gamepad)));
return;
}
if (pad_state->mapper)
pad_state->mapper(pad_state->data, pad);
else
*pad = pad_state->data;
pad->connected = true;
if (!sanitize)
return;
uint32_t full_axis_mask = (1 << pad->axes_length) - 1;
if (pad_state->axis_mask != full_axis_mask) {
for (size_t axis = 0; axis < pad->axes_length; ++axis) {
if (!(pad_state->axis_mask & 1 << axis)) {
if (fabs(UNSAFE_TODO(pad->axes[axis])) < kMinAxisResetValue) {
pad_state->axis_mask |= 1 << axis;
} else {
UNSAFE_TODO(pad->axes[axis]) = 0.0f;
}
}
}
}
uint32_t full_button_mask = (1 << pad->buttons_length) - 1;
if (pad_state->button_mask != full_button_mask) {
for (size_t button = 0; button < pad->buttons_length; ++button) {
if (!(pad_state->button_mask & 1 << button)) {
if (!UNSAFE_TODO(pad->buttons[button]).pressed) {
pad_state->button_mask |= 1 << button;
} else {
UNSAFE_TODO(pad->buttons[button]).pressed = false;
UNSAFE_TODO(pad->buttons[button]).value = 0.0f;
}
}
}
}
}
}