mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
403 lines
14 KiB
C++
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));
|
|
//}
|
|
}
|