rpcsx/rpcs3/Input/skateboard_pad_handler.cpp
DH 62ad27d1e2 split rpcs3 and hle libraries
merge rpcs3 utilities
2025-04-08 19:46:57 +03:00

403 lines
14 KiB
C++

#include "stdafx.h"
#include "skateboard_pad_handler.h"
#include "Emu/Io/pad_config.h"
LOG_CHANNEL(skateboard_log, "Skateboard");
using namespace reports;
namespace
{
constexpr id_pair SKATEBOARD_ID_0 = {0x12BA, 0x0400}; // Tony Hawk RIDE Skateboard
constexpr id_pair SKATEBOARD_ID_1 = {0x1430, 0x0100}; // Tony Hawk SHRED Skateboard
enum button_flags : u16
{
square = 0x0001,
cross = 0x0002,
circle = 0x0004,
triangle = 0x0008,
select = 0x0100,
start = 0x0200,
ps = 0x1000,
};
enum dpad_states : u8
{
up = 0x00,
up_right = 0x01,
left = 0x02,
down_right = 0x03,
down = 0x04,
down_left = 0x05,
right = 0x06,
up_left = 0x07,
none = 0x0F,
};
// Default data if dongle is connected but the skateboard is turned off:
static constexpr std::array<u8, sizeof(skateboard_input_report)> not_connected_state = {0x00, 0x00, 0x0F, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02};
static constexpr std::array<u8, sizeof(skateboard_input_report)> disconnected_state = {0x00, 0x00, 0x0F, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
} // namespace
skateboard_pad_handler::skateboard_pad_handler()
: hid_pad_handler<skateboard_device>(pad_handler::skateboard, {SKATEBOARD_ID_0, SKATEBOARD_ID_1})
{
// Unique names for the config files and our pad settings dialog
button_list =
{
{skateboard_key_codes::none, ""},
{skateboard_key_codes::left, "Left"},
{skateboard_key_codes::right, "Right"},
{skateboard_key_codes::up, "Up"},
{skateboard_key_codes::down, "Down"},
{skateboard_key_codes::cross, "Cross"},
{skateboard_key_codes::square, "Square"},
{skateboard_key_codes::circle, "Circle"},
{skateboard_key_codes::triangle, "Triangle"},
{skateboard_key_codes::start, "Start"},
{skateboard_key_codes::select, "Select"},
{skateboard_key_codes::ps, "PS"},
{skateboard_key_codes::ir_left, "IR Left"},
{skateboard_key_codes::ir_right, "IR Right"},
{skateboard_key_codes::ir_nose, "IR Nose"},
{skateboard_key_codes::ir_tail, "IR Tail"},
{skateboard_key_codes::tilt_left, "Tilt Left"},
{skateboard_key_codes::tilt_right, "Tilt Right"}};
init_configs();
// Define border values
thumb_max = 255;
trigger_min = 0;
trigger_max = 255;
// Set capabilities
b_has_config = true;
b_has_rumble = false;
b_has_motion = true;
b_has_deadzones = false;
b_has_led = false;
b_has_rgb = false;
b_has_player_led = false;
b_has_battery = false;
b_has_battery_led = false;
b_has_pressure_intensity_button = false;
b_has_orientation = true;
m_name_string = "Skateboard #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
m_trigger_threshold = trigger_max / 2;
m_thumb_threshold = thumb_max / 2;
}
skateboard_pad_handler::~skateboard_pad_handler()
{
}
void skateboard_pad_handler::init_config(cfg_pad* cfg)
{
if (!cfg)
return;
// Set default button mapping
cfg->ls_left.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_down.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_right.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_up.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_left.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_down.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_right.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_up.def = ::at32(button_list, skateboard_key_codes::none);
cfg->start.def = ::at32(button_list, skateboard_key_codes::start);
cfg->select.def = ::at32(button_list, skateboard_key_codes::select);
cfg->ps.def = ::at32(button_list, skateboard_key_codes::ps);
cfg->square.def = ::at32(button_list, skateboard_key_codes::square);
cfg->cross.def = ::at32(button_list, skateboard_key_codes::cross);
cfg->circle.def = ::at32(button_list, skateboard_key_codes::circle);
cfg->triangle.def = ::at32(button_list, skateboard_key_codes::triangle);
cfg->left.def = ::at32(button_list, skateboard_key_codes::left);
cfg->down.def = ::at32(button_list, skateboard_key_codes::down);
cfg->right.def = ::at32(button_list, skateboard_key_codes::right);
cfg->up.def = ::at32(button_list, skateboard_key_codes::up);
cfg->r1.def = ::at32(button_list, skateboard_key_codes::none);
cfg->r2.def = ::at32(button_list, skateboard_key_codes::none);
cfg->r3.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l1.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l2.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l3.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ir_nose.def = ::at32(button_list, skateboard_key_codes::ir_nose);
cfg->ir_tail.def = ::at32(button_list, skateboard_key_codes::ir_tail);
cfg->ir_left.def = ::at32(button_list, skateboard_key_codes::ir_left);
cfg->ir_right.def = ::at32(button_list, skateboard_key_codes::ir_right);
cfg->tilt_left.def = ::at32(button_list, skateboard_key_codes::tilt_left);
cfg->tilt_right.def = ::at32(button_list, skateboard_key_codes::tilt_right);
cfg->orientation_reset_button.def = ::at32(button_list, skateboard_key_codes::none);
// Set default misc variables
cfg->lstick_anti_deadzone.def = 0;
cfg->rstick_anti_deadzone.def = 0;
cfg->lstickdeadzone.def = 40; // between 0 and 255
cfg->rstickdeadzone.def = 40; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255
// apply defaults
cfg->from_default();
}
void skateboard_pad_handler::check_add_device(hid_device* hidDevice, hid_enumerated_device_view path, std::wstring_view wide_serial)
{
if (!hidDevice)
{
return;
}
skateboard_device* device = nullptr;
for (auto& controller : m_controllers)
{
ensure(controller.second);
if (!controller.second->hidDevice)
{
device = controller.second.get();
break;
}
}
if (!device)
{
return;
}
if (hid_set_nonblocking(hidDevice, 1) == -1)
{
skateboard_log.error("check_add_device: hid_set_nonblocking failed! Reason: %s", hid_error(hidDevice));
hid_close(hidDevice);
return;
}
device->hidDevice = hidDevice;
device->path = path;
// Activate
if (send_output_report(device) == -1)
{
skateboard_log.error("check_add_device: send_output_report failed! Reason: %s", hid_error(hidDevice));
}
std::string serial;
for (wchar_t ch : wide_serial)
serial += static_cast<uchar>(ch);
skateboard_log.notice("Added device: serial='%s', path='%s'", serial, device->path);
}
skateboard_pad_handler::DataStatus skateboard_pad_handler::get_data(skateboard_device* device)
{
if (!device)
return DataStatus::ReadError;
std::array<u8, sizeof(skateboard_input_report)> buf{};
int res = hid_read(device->hidDevice, buf.data(), buf.size());
if (res == -1)
{
// looks like controller disconnected or read error
skateboard_log.error("get_data ReadError: %s", hid_error(device->hidDevice));
return DataStatus::ReadError;
}
if (res != static_cast<int>(sizeof(skateboard_input_report)))
return DataStatus::NoNewData;
if (std::memcmp(&device->report, buf.data(), sizeof(skateboard_input_report)) == 0)
return DataStatus::NoNewData;
// Get the new data
std::memcpy(&device->report, buf.data(), sizeof(skateboard_input_report));
// Check the skateboard's power state based on the input report
device->skateboard_is_on =
(std::memcmp(not_connected_state.data(), buf.data(), not_connected_state.size()) != 0 && // This usually means that the device hasn't been connected to the dongle yet.
std::memcmp(disconnected_state.data(), buf.data(), disconnected_state.size()) != 0); // This usually means that the device was disconnected from the dongle.
return DataStatus::NewData;
}
PadHandlerBase::connection skateboard_pad_handler::update_connection(const std::shared_ptr<PadDevice>& device)
{
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev || dev->path == hid_enumerated_device_default)
return connection::disconnected;
if (dev->hidDevice == nullptr)
{
// try to reconnect
#ifdef ANDROID
if (hid_device* hid_dev = hid_libusb_wrap_sys_device(dev->path, -1))
#else
if (hid_device* hid_dev = hid_open_path(dev->path.c_str()))
#endif
{
if (hid_set_nonblocking(hid_dev, 1) == -1)
{
skateboard_log.error("Reconnecting Device %s: hid_set_nonblocking failed with error %s", dev->path, hid_error(hid_dev));
}
dev->hidDevice = hid_dev;
}
else
{
// nope, not there
skateboard_log.error("Device %s: disconnected", dev->path);
return connection::disconnected;
}
}
if (get_data(dev) == DataStatus::ReadError)
{
// this also can mean disconnected, either way deal with it on next loop and reconnect
dev->close();
return connection::no_data;
}
if (!dev->skateboard_is_on)
{
// This means that the dongle is still connected, but the skateboard is turned off.
// There is no need to reconnect the hid device again, we just have to check the input report for proper data.
// The game should get the proper disconnected state anyway.
return connection::disconnected;
}
return connection::connected;
}
std::unordered_map<u64, u16> skateboard_pad_handler::get_button_values(const std::shared_ptr<PadDevice>& device)
{
std::unordered_map<u64, u16> key_buf;
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev)
return key_buf;
const skateboard_input_report& input = dev->report;
// D-Pad
key_buf[skateboard_key_codes::left] = (input.d_pad == dpad_states::left || input.d_pad == dpad_states::up_left || input.d_pad == dpad_states::down_left) ? 255 : 0;
key_buf[skateboard_key_codes::right] = (input.d_pad == dpad_states::right || input.d_pad == dpad_states::up_right || input.d_pad == dpad_states::down_right) ? 255 : 0;
key_buf[skateboard_key_codes::up] = (input.d_pad == dpad_states::up || input.d_pad == dpad_states::up_left || input.d_pad == dpad_states::up_right) ? 255 : 0;
key_buf[skateboard_key_codes::down] = (input.d_pad == dpad_states::down || input.d_pad == dpad_states::down_left || input.d_pad == dpad_states::down_right) ? 255 : 0;
// Face buttons
key_buf[skateboard_key_codes::cross] = (input.buttons & button_flags::cross) ? 255 : 0;
key_buf[skateboard_key_codes::square] = (input.buttons & button_flags::square) ? 255 : 0;
key_buf[skateboard_key_codes::circle] = (input.buttons & button_flags::circle) ? 255 : 0;
key_buf[skateboard_key_codes::triangle] = (input.buttons & button_flags::triangle) ? 255 : 0;
key_buf[skateboard_key_codes::start] = (input.buttons & button_flags::start) ? 255 : 0;
key_buf[skateboard_key_codes::select] = (input.buttons & button_flags::select) ? 255 : 0;
key_buf[skateboard_key_codes::ps] = (input.buttons & button_flags::ps) ? 255 : 0;
// Infrared
key_buf[skateboard_key_codes::ir_nose] = input.pressure_triangle;
key_buf[skateboard_key_codes::ir_tail] = input.pressure_circle;
key_buf[skateboard_key_codes::ir_left] = input.pressure_cross;
key_buf[skateboard_key_codes::ir_right] = input.pressure_square;
key_buf[skateboard_key_codes::tilt_left] = input.pressure_l1;
key_buf[skateboard_key_codes::tilt_right] = input.pressure_r1;
// NOTE: Axes X, Y, Z and RZ are always 128, which is the default anyway, so setting the values is omitted.
return key_buf;
}
void skateboard_pad_handler::get_extended_info(const pad_ensemble& binding)
{
const auto& device = binding.device;
const auto& pad = binding.pad;
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev || !pad)
return;
const skateboard_input_report& input = dev->report;
pad->m_sensors[0].m_value = Clamp0To1023(input.large_axes[0]);
pad->m_sensors[1].m_value = Clamp0To1023(input.large_axes[1]);
pad->m_sensors[2].m_value = Clamp0To1023(input.large_axes[2]);
pad->m_sensors[3].m_value = Clamp0To1023(input.large_axes[3]);
set_raw_orientation(*pad);
}
pad_preview_values skateboard_pad_handler::get_preview_values(const std::unordered_map<u64, u16>& /*data*/)
{
// There is no proper user interface for skateboard values yet
return {};
}
int skateboard_pad_handler::send_output_report(skateboard_device* device)
{
if (!device || !device->hidDevice)
return -2;
const cfg_pad* config = device->config;
if (config == nullptr)
return -2; // hid_write returns -1 on error
// The output report contents are still unknown
skateboard_output_report report{};
return hid_write(device->hidDevice, report.data.data(), report.data.size());
}
void skateboard_pad_handler::apply_pad_data(const pad_ensemble& binding)
{
const auto& device = binding.device;
const auto& pad = binding.pad;
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev || !dev->hidDevice || !dev->config || !pad)
return;
dev->new_output_data = false;
// Disabled until needed
// const auto now = steady_clock::now();
// const auto elapsed = now - dev->last_output;
// if (dev->new_output_data || elapsed > min_output_interval)
//{
// if (const int res = send_output_report(dev); res >= 0)
// {
// dev->new_output_data = false;
// dev->last_output = now;
// }
// else if (res == -1)
// {
// skateboard_log.error("apply_pad_data: send_output_report failed! error=%s", hid_error(dev->hidDevice));
// }
// }
}
void skateboard_pad_handler::SetPadData(const std::string& padId, u8 player_id, u8 /*large_motor*/, u8 /*small_motor*/, s32 /*r*/, s32 /*g*/, s32 /*b*/, bool /*player_led*/, bool /*battery_led*/, u32 /*battery_led_brightness*/)
{
std::shared_ptr<skateboard_device> device = get_hid_device(padId);
if (device == nullptr || device->hidDevice == nullptr)
return;
device->player_id = player_id;
device->config = get_config(padId);
ensure(device->config);
// Disabled until needed
// if (send_output_report(device.get()) == -1)
//{
// skateboard_log.error("SetPadData: send_output_report failed! Reason: %s", hid_error(device->hidDevice));
//}
}