mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-14 01:27:00 +00:00
Pad Refactoring
Adds a window to setup multiple input types as once All controllers are now handled by a single thread [hcorion] evdev refactor
This commit is contained in:
parent
a6ba7ed21c
commit
0457f23b13
@ -3,7 +3,7 @@
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "pad_thread.h"
|
||||
#include "cellPad.h"
|
||||
|
||||
extern logs::channel sys_io;
|
||||
@ -12,7 +12,7 @@ s32 cellPadInit(u32 max_connect)
|
||||
{
|
||||
sys_io.warning("cellPadInit(max_connect=%d)", max_connect);
|
||||
|
||||
const auto handler = fxm::import<PadHandlerBase>(Emu.GetCallbacks().get_pad_handler);
|
||||
const auto handler = fxm::import<pad_thread>(Emu.GetCallbacks().get_pad_handler);
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_ALREADY_INITIALIZED;
|
||||
@ -26,7 +26,7 @@ s32 cellPadEnd()
|
||||
{
|
||||
sys_io.notice("cellPadEnd()");
|
||||
|
||||
if (!fxm::remove<PadHandlerBase>())
|
||||
if (!fxm::remove<pad_thread>())
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
return CELL_OK;
|
||||
@ -36,7 +36,7 @@ s32 cellPadClearBuf(u32 port_no)
|
||||
{
|
||||
sys_io.trace("cellPadClearBuf(port_no=%d)", port_no);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -51,20 +51,20 @@ s32 cellPadClearBuf(u32 port_no)
|
||||
//Set 'm_buffer_cleared' to force a resend of everything
|
||||
//might as well also reset everything in our pad 'buffer' to nothing as well
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
Pad& pad = pads[port_no];
|
||||
auto& pads = handler->GetPads();
|
||||
auto pad = pads[port_no];
|
||||
|
||||
pad.m_buffer_cleared = true;
|
||||
pad.m_analog_left_x = pad.m_analog_left_y = pad.m_analog_right_x = pad.m_analog_right_y = 128;
|
||||
pad->m_buffer_cleared = true;
|
||||
pad->m_analog_left_x = pad->m_analog_left_y = pad->m_analog_right_x = pad->m_analog_right_y = 128;
|
||||
|
||||
pad.m_digital_1 = pad.m_digital_2 = 0;
|
||||
pad.m_press_right = pad.m_press_left = pad.m_press_up = pad.m_press_down = 0;
|
||||
pad.m_press_triangle = pad.m_press_circle = pad.m_press_cross = pad.m_press_square = 0;
|
||||
pad.m_press_L1 = pad.m_press_L2 = pad.m_press_R1 = pad.m_press_R2 = 0;
|
||||
pad->m_digital_1 = pad->m_digital_2 = 0;
|
||||
pad->m_press_right = pad->m_press_left = pad->m_press_up = pad->m_press_down = 0;
|
||||
pad->m_press_triangle = pad->m_press_circle = pad->m_press_cross = pad->m_press_square = 0;
|
||||
pad->m_press_L1 = pad->m_press_L2 = pad->m_press_R1 = pad->m_press_R2 = 0;
|
||||
|
||||
//~399 on sensor y is a level non moving controller
|
||||
pad.m_sensor_y = 399;
|
||||
pad.m_sensor_x = pad.m_sensor_z = pad.m_sensor_g = 512;
|
||||
pad->m_sensor_y = 399;
|
||||
pad->m_sensor_x = pad->m_sensor_z = pad->m_sensor_g = 512;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -73,12 +73,12 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
{
|
||||
sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
|
||||
@ -89,39 +89,39 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
Pad& pad = pads[port_no];
|
||||
auto pad = pads[port_no];
|
||||
|
||||
u16 d1Initial, d2Initial;
|
||||
d1Initial = pad.m_digital_1;
|
||||
d2Initial = pad.m_digital_2;
|
||||
d1Initial = pad->m_digital_1;
|
||||
d2Initial = pad->m_digital_2;
|
||||
bool btnChanged = false;
|
||||
for(Button& button : pad.m_buttons)
|
||||
for(Button& button : pad->m_buttons)
|
||||
{
|
||||
//here we check btns, and set pad accordingly,
|
||||
//if something changed, set btnChanged
|
||||
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
|
||||
{
|
||||
if (button.m_pressed) pad.m_digital_1 |= button.m_outKeyCode;
|
||||
else pad.m_digital_1 &= ~button.m_outKeyCode;
|
||||
if (button.m_pressed) pad->m_digital_1 |= button.m_outKeyCode;
|
||||
else pad->m_digital_1 &= ~button.m_outKeyCode;
|
||||
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_LEFT:
|
||||
if (pad.m_press_left != button.m_value) btnChanged = true;
|
||||
pad.m_press_left = button.m_value;
|
||||
if (pad->m_press_left != button.m_value) btnChanged = true;
|
||||
pad->m_press_left = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_DOWN:
|
||||
if (pad.m_press_down != button.m_value) btnChanged = true;
|
||||
pad.m_press_down = button.m_value;
|
||||
if (pad->m_press_down != button.m_value) btnChanged = true;
|
||||
pad->m_press_down = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_RIGHT:
|
||||
if (pad.m_press_right != button.m_value) btnChanged = true;
|
||||
pad.m_press_right = button.m_value;
|
||||
if (pad->m_press_right != button.m_value) btnChanged = true;
|
||||
pad->m_press_right = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_UP:
|
||||
if (pad.m_press_up != button.m_value) btnChanged = true;
|
||||
pad.m_press_up = button.m_value;
|
||||
if (pad->m_press_up != button.m_value) btnChanged = true;
|
||||
pad->m_press_up = button.m_value;
|
||||
break;
|
||||
//These arent pressure btns
|
||||
case CELL_PAD_CTRL_R3:
|
||||
@ -133,42 +133,42 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
}
|
||||
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
if (button.m_pressed) pad.m_digital_2 |= button.m_outKeyCode;
|
||||
else pad.m_digital_2 &= ~button.m_outKeyCode;
|
||||
if (button.m_pressed) pad->m_digital_2 |= button.m_outKeyCode;
|
||||
else pad->m_digital_2 &= ~button.m_outKeyCode;
|
||||
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_SQUARE:
|
||||
if (pad.m_press_square != button.m_value) btnChanged = true;
|
||||
pad.m_press_square = button.m_value;
|
||||
if (pad->m_press_square != button.m_value) btnChanged = true;
|
||||
pad->m_press_square = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CROSS:
|
||||
if (pad.m_press_cross != button.m_value) btnChanged = true;
|
||||
pad.m_press_cross = button.m_value;
|
||||
if (pad->m_press_cross != button.m_value) btnChanged = true;
|
||||
pad->m_press_cross = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CIRCLE:
|
||||
if (pad.m_press_circle != button.m_value) btnChanged = true;
|
||||
pad.m_press_circle = button.m_value;
|
||||
if (pad->m_press_circle != button.m_value) btnChanged = true;
|
||||
pad->m_press_circle = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_TRIANGLE:
|
||||
if (pad.m_press_triangle != button.m_value) btnChanged = true;
|
||||
pad.m_press_triangle = button.m_value;
|
||||
if (pad->m_press_triangle != button.m_value) btnChanged = true;
|
||||
pad->m_press_triangle = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R1:
|
||||
if (pad.m_press_R1 != button.m_value) btnChanged = true;
|
||||
pad.m_press_R1 = button.m_value;
|
||||
if (pad->m_press_R1 != button.m_value) btnChanged = true;
|
||||
pad->m_press_R1 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L1:
|
||||
if (pad.m_press_L1 != button.m_value) btnChanged = true;
|
||||
pad.m_press_L1 = button.m_value;
|
||||
if (pad->m_press_L1 != button.m_value) btnChanged = true;
|
||||
pad->m_press_L1 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R2:
|
||||
if (pad.m_press_R2 != button.m_value) btnChanged = true;
|
||||
pad.m_press_R2 = button.m_value;
|
||||
if (pad->m_press_R2 != button.m_value) btnChanged = true;
|
||||
pad->m_press_R2 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L2:
|
||||
if (pad.m_press_L2 != button.m_value) btnChanged = true;
|
||||
pad.m_press_L2 = button.m_value;
|
||||
if (pad->m_press_L2 != button.m_value) btnChanged = true;
|
||||
pad->m_press_L2 = button.m_value;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
@ -182,55 +182,55 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
}
|
||||
}
|
||||
|
||||
for (const AnalogStick& stick : pad.m_sticks)
|
||||
for (const AnalogStick& stick : pad->m_sticks)
|
||||
{
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
|
||||
if (pad.m_analog_left_x != stick.m_value) btnChanged = true;
|
||||
pad.m_analog_left_x = stick.m_value;
|
||||
if (pad->m_analog_left_x != stick.m_value) btnChanged = true;
|
||||
pad->m_analog_left_x = stick.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
||||
if (pad.m_analog_left_y != stick.m_value) btnChanged = true;
|
||||
pad.m_analog_left_y = stick.m_value;
|
||||
if (pad->m_analog_left_y != stick.m_value) btnChanged = true;
|
||||
pad->m_analog_left_y = stick.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
|
||||
if (pad.m_analog_right_x != stick.m_value) btnChanged = true;
|
||||
pad.m_analog_right_x = stick.m_value;
|
||||
if (pad->m_analog_right_x != stick.m_value) btnChanged = true;
|
||||
pad->m_analog_right_x = stick.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
|
||||
if (pad.m_analog_right_y != stick.m_value) btnChanged = true;
|
||||
pad.m_analog_right_y = stick.m_value;
|
||||
if (pad->m_analog_right_y != stick.m_value) btnChanged = true;
|
||||
pad->m_analog_right_y = stick.m_value;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const AnalogSensor& sensor : pad.m_sensors)
|
||||
for (const AnalogSensor& sensor : pad->m_sensors)
|
||||
{
|
||||
switch (sensor.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_SENSOR_X:
|
||||
if (pad.m_sensor_x != sensor.m_value) btnChanged = true;
|
||||
pad.m_sensor_x = sensor.m_value;
|
||||
if (pad->m_sensor_x != sensor.m_value) btnChanged = true;
|
||||
pad->m_sensor_x = sensor.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_SENSOR_Y:
|
||||
if (pad.m_sensor_y != sensor.m_value) btnChanged = true;
|
||||
pad.m_sensor_y = sensor.m_value;
|
||||
if (pad->m_sensor_y != sensor.m_value) btnChanged = true;
|
||||
pad->m_sensor_y = sensor.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_SENSOR_Z:
|
||||
if (pad.m_sensor_z != sensor.m_value) btnChanged = true;
|
||||
pad.m_sensor_z = sensor.m_value;
|
||||
if (pad->m_sensor_z != sensor.m_value) btnChanged = true;
|
||||
pad->m_sensor_z = sensor.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_SENSOR_G:
|
||||
if (pad.m_sensor_g != sensor.m_value) btnChanged = true;
|
||||
pad.m_sensor_g = sensor.m_value;
|
||||
if (pad->m_sensor_g != sensor.m_value) btnChanged = true;
|
||||
pad->m_sensor_g = sensor.m_value;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (d1Initial != pad.m_digital_1 || d2Initial != pad.m_digital_2)
|
||||
if (d1Initial != pad->m_digital_1 || d2Initial != pad->m_digital_2)
|
||||
{
|
||||
btnChanged = true;
|
||||
}
|
||||
@ -238,9 +238,9 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
//not sure if this should officially change with capabilities/portsettings :(
|
||||
data->len = 24;
|
||||
|
||||
if (pad.m_buffer_cleared)
|
||||
if (pad->m_buffer_cleared)
|
||||
{
|
||||
pad.m_buffer_cleared = false;
|
||||
pad->m_buffer_cleared = false;
|
||||
}
|
||||
else if (!btnChanged)
|
||||
{
|
||||
@ -250,28 +250,28 @@ s32 cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
// bits 15-8 reserved, 7-4 = 0x7, 3-0: data->len/2;
|
||||
data->button[1] = (0x7 << 4) | std::min(data->len / 2, 15);
|
||||
//lets still send new data anyway, not sure whats expected still
|
||||
data->button[CELL_PAD_BTN_OFFSET_DIGITAL1] = pad.m_digital_1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_DIGITAL2] = pad.m_digital_2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = pad.m_analog_right_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = pad.m_analog_right_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = pad.m_analog_left_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = pad.m_analog_left_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] = pad.m_press_right;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] = pad.m_press_left;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_UP] = pad.m_press_up;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] = pad.m_press_down;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] = pad.m_press_triangle;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] = pad.m_press_circle;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad.m_press_cross;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad.m_press_square;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad.m_press_L1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad.m_press_L2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad.m_press_R1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad.m_press_R2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_X] = pad.m_sensor_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = pad.m_sensor_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = pad.m_sensor_z;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad.m_sensor_g;
|
||||
data->button[CELL_PAD_BTN_OFFSET_DIGITAL1] = pad->m_digital_1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_DIGITAL2] = pad->m_digital_2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = pad->m_analog_right_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = pad->m_analog_right_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = pad->m_analog_left_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = pad->m_analog_left_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] = pad->m_press_right;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] = pad->m_press_left;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_UP] = pad->m_press_up;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] = pad->m_press_down;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] = pad->m_press_triangle;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] = pad->m_press_circle;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad->m_press_cross;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad->m_press_square;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad->m_press_L1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad->m_press_R1;
|
||||
data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad->m_press_R2;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_X] = pad->m_sensor_x;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = pad->m_sensor_y;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = pad->m_sensor_z;
|
||||
data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -280,7 +280,7 @@ s32 cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
{
|
||||
sys_io.trace("cellPadPeriphGetInfo(info=*0x%x)", info);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -293,7 +293,7 @@ s32 cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
info->now_connect = rinfo.now_connect;
|
||||
info->system_info = rinfo.system_info;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
// TODO: Support other types of controllers
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i)
|
||||
@ -301,10 +301,10 @@ s32 cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
if (i >= pads.size())
|
||||
break;
|
||||
|
||||
info->port_status[i] = pads[i].m_port_status;
|
||||
info->port_setting[i] = pads[i].m_port_setting;
|
||||
info->device_capability[i] = pads[i].m_device_capability;
|
||||
info->device_type[i] = pads[i].m_device_type;
|
||||
info->port_status[i] = pads[i]->m_port_status;
|
||||
info->port_setting[i] = pads[i]->m_port_setting;
|
||||
info->device_capability[i] = pads[i]->m_device_capability;
|
||||
info->device_type[i] = pads[i]->m_device_type;
|
||||
info->pclass_type[i] = CELL_PAD_PCLASS_TYPE_STANDARD;
|
||||
info->pclass_profile[i] = 0x0;
|
||||
}
|
||||
@ -315,7 +315,7 @@ s32 cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
s32 cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data)
|
||||
{
|
||||
sys_io.trace("cellPadPeriphGetData(port_no=%d, data=*0x%x)", port_no, data);
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -342,7 +342,7 @@ s32 cellPadGetDataExtra(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadDa
|
||||
{
|
||||
sys_io.trace("cellPadGetDataExtra(port_no=%d, device_type=*0x%x, device_type=*0x%x)", port_no, device_type, data);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -370,7 +370,7 @@ s32 cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
|
||||
{
|
||||
sys_io.trace("cellPadSetActDirect(port_no=%d, param=*0x%x)", port_no, param);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -391,7 +391,7 @@ s32 cellPadGetInfo(vm::ptr<CellPadInfo> info)
|
||||
{
|
||||
sys_io.trace("cellPadGetInfo(info=*0x%x)", info);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -403,15 +403,15 @@ s32 cellPadGetInfo(vm::ptr<CellPadInfo> info)
|
||||
info->now_connect = rinfo.now_connect;
|
||||
info->system_info = rinfo.system_info;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
for (u32 i=0; i<CELL_MAX_PADS; ++i)
|
||||
{
|
||||
if (i >= pads.size())
|
||||
break;
|
||||
|
||||
info->status[i] = pads[i].m_port_status;
|
||||
pads[i].m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
info->status[i] = pads[i]->m_port_status;
|
||||
pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
info->product_id[i] = 0x0268;
|
||||
info->vendor_id[i] = 0x054C;
|
||||
}
|
||||
@ -423,7 +423,7 @@ s32 cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
|
||||
{
|
||||
sys_io.trace("cellPadGetInfo2(info=*0x%x)", info);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -435,18 +435,18 @@ s32 cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
|
||||
info->now_connect = rinfo.now_connect;
|
||||
info->system_info = rinfo.system_info;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
for (u32 i=0; i<CELL_PAD_MAX_PORT_NUM; ++i)
|
||||
{
|
||||
if (i >= pads.size())
|
||||
break;
|
||||
|
||||
info->port_status[i] = pads[i].m_port_status;
|
||||
pads[i].m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
info->port_setting[i] = pads[i].m_port_setting;
|
||||
info->device_capability[i] = pads[i].m_device_capability;
|
||||
info->device_type[i] = pads[i].m_device_type;
|
||||
info->port_status[i] = pads[i]->m_port_status;
|
||||
pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
info->port_setting[i] = pads[i]->m_port_setting;
|
||||
info->device_capability[i] = pads[i]->m_device_capability;
|
||||
info->device_type[i] = pads[i]->m_device_type;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@ -456,7 +456,7 @@ s32 cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellCapabilityInfo> info)
|
||||
{
|
||||
sys_io.trace("cellPadGetCapabilityInfo(port_no=%d, data_addr:=0x%x)", port_no, info.addr());
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -468,10 +468,10 @@ s32 cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellCapabilityInfo> info)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const std::vector<Pad>& pads = handler->GetPads();
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
//Should return the same as device capability mask, psl1ght has it backwards in pad.h
|
||||
info->info[0] = pads[port_no].m_device_capability;
|
||||
//Should return the same as device capability mask, psl1ght has it backwards in pad->h
|
||||
info->info[0] = pads[port_no]->m_device_capability;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -480,7 +480,7 @@ s32 cellPadSetPortSetting(u32 port_no, u32 port_setting)
|
||||
{
|
||||
sys_io.trace("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -492,8 +492,8 @@ s32 cellPadSetPortSetting(u32 port_no, u32 port_setting)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
pads[port_no].m_port_setting = port_setting;
|
||||
auto& pads = handler->GetPads();
|
||||
pads[port_no]->m_port_setting = port_setting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -502,7 +502,7 @@ s32 cellPadInfoPressMode(u32 port_no)
|
||||
{
|
||||
sys_io.trace("cellPadInfoPressMode(port_no=%d)", port_no);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -514,16 +514,16 @@ s32 cellPadInfoPressMode(u32 port_no)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const std::vector<Pad>& pads = handler->GetPads();
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
return (pads[port_no].m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) > 0;
|
||||
return (pads[port_no]->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) > 0;
|
||||
}
|
||||
|
||||
s32 cellPadInfoSensorMode(u32 port_no)
|
||||
{
|
||||
sys_io.trace("cellPadInfoSensorMode(port_no=%d)", port_no);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -535,16 +535,16 @@ s32 cellPadInfoSensorMode(u32 port_no)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const std::vector<Pad>& pads = handler->GetPads();
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
return (pads[port_no].m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) > 0;
|
||||
return (pads[port_no]->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) > 0;
|
||||
}
|
||||
|
||||
s32 cellPadSetPressMode(u32 port_no, u32 mode)
|
||||
{
|
||||
sys_io.trace("cellPadSetPressMode(port_no=%d, mode=%d)", port_no, mode);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -559,12 +559,12 @@ s32 cellPadSetPressMode(u32 port_no, u32 mode)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
if (mode)
|
||||
pads[port_no].m_port_setting |= CELL_PAD_SETTING_PRESS_ON;
|
||||
pads[port_no]->m_port_setting |= CELL_PAD_SETTING_PRESS_ON;
|
||||
else
|
||||
pads[port_no].m_port_setting &= ~CELL_PAD_SETTING_PRESS_ON;
|
||||
pads[port_no]->m_port_setting &= ~CELL_PAD_SETTING_PRESS_ON;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -573,7 +573,7 @@ s32 cellPadSetSensorMode(u32 port_no, u32 mode)
|
||||
{
|
||||
sys_io.trace("cellPadSetSensorMode(port_no=%d, mode=%d)", port_no, mode);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -588,12 +588,12 @@ s32 cellPadSetSensorMode(u32 port_no, u32 mode)
|
||||
if (port_no >= rinfo.now_connect)
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
std::vector<Pad>& pads = handler->GetPads();
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
if (mode)
|
||||
pads[port_no].m_port_setting |= CELL_PAD_SETTING_SENSOR_ON;
|
||||
pads[port_no]->m_port_setting |= CELL_PAD_SETTING_SENSOR_ON;
|
||||
else
|
||||
pads[port_no].m_port_setting &= ~CELL_PAD_SETTING_SENSOR_ON;
|
||||
pads[port_no]->m_port_setting &= ~CELL_PAD_SETTING_SENSOR_ON;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -602,7 +602,7 @@ s32 cellPadLddRegisterController()
|
||||
{
|
||||
sys_io.todo("cellPadLddRegisterController()");
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -614,7 +614,7 @@ s32 cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
|
||||
{
|
||||
sys_io.todo("cellPadLddDataInsert(handle=%d, data=*0x%x)", handle, data);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -626,7 +626,7 @@ s32 cellPadLddGetPortNo(s32 handle)
|
||||
{
|
||||
sys_io.todo("cellPadLddGetPortNo(handle=%d)", handle);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
@ -638,7 +638,7 @@ s32 cellPadLddUnregisterController(s32 handle)
|
||||
{
|
||||
sys_io.todo("cellPadLddUnregisterController(handle=%d)", handle);
|
||||
|
||||
const auto handler = fxm::get<PadHandlerBase>();
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
@ -5,10 +5,25 @@
|
||||
class NullPadHandler final : public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
void Init(const u32 max_connect) override
|
||||
bool Init() override
|
||||
{
|
||||
memset(&m_info, 0, sizeof(PadInfo));
|
||||
m_info.max_connect = max_connect;
|
||||
m_pads.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ListDevices() override
|
||||
{
|
||||
std::vector<std::string> nulllist;
|
||||
nulllist.push_back("Default Null Device");
|
||||
return nulllist;
|
||||
}
|
||||
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadProc() override
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "../../Utilities/types.h"
|
||||
|
||||
// TODO: HLE info (constants, structs, etc.) should not be available here
|
||||
|
||||
enum PortStatus
|
||||
@ -190,6 +194,14 @@ struct Pad
|
||||
u16 m_sensor_z;
|
||||
u16 m_sensor_g;
|
||||
|
||||
void Init(u32 port_status, u32 port_setting, u32 device_capability, u32 device_type)
|
||||
{
|
||||
m_port_status = port_status;
|
||||
m_port_setting = port_setting;
|
||||
m_device_capability = device_capability;
|
||||
m_device_type = device_type;
|
||||
}
|
||||
|
||||
Pad(u32 port_status, u32 port_setting, u32 device_capability, u32 device_type)
|
||||
: m_buffer_cleared(true)
|
||||
, m_port_status(port_status)
|
||||
@ -226,79 +238,23 @@ struct Pad
|
||||
}
|
||||
};
|
||||
|
||||
struct PadInfo
|
||||
{
|
||||
u32 max_connect;
|
||||
u32 now_connect;
|
||||
u32 system_info;
|
||||
};
|
||||
|
||||
class PadHandlerBase
|
||||
{
|
||||
protected:
|
||||
PadInfo m_info;
|
||||
std::vector<Pad> m_pads;
|
||||
bool b_has_config = false;
|
||||
|
||||
public:
|
||||
virtual void Init(const u32 max_connect) = 0;
|
||||
virtual bool Init() { return true; };
|
||||
virtual ~PadHandlerBase() = default;
|
||||
|
||||
//Set value to set pressure/axi to certain level, otherwise 0/255 default
|
||||
void Key(const u32 code, bool pressed, u16 value=255)
|
||||
{
|
||||
for(Pad& pad : m_pads)
|
||||
{
|
||||
for (Button& button : pad.m_buttons)
|
||||
{
|
||||
if (button.m_keyCode != code)
|
||||
continue;
|
||||
|
||||
if (value >= 256){ value = 255; }
|
||||
|
||||
//Todo: Is this flush necessary once games hit decent speeds?
|
||||
if (button.m_pressed && !pressed)
|
||||
{
|
||||
button.m_flush = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
button.m_pressed = pressed;
|
||||
if (pressed)
|
||||
button.m_value = value;
|
||||
else
|
||||
button.m_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(AnalogStick& stick : pad.m_sticks)
|
||||
{
|
||||
if (stick.m_keyCodeMax != code && stick.m_keyCodeMin != code)
|
||||
continue;
|
||||
|
||||
//slightly less hack job for key based analog stick
|
||||
// should also fix/make transitions when using keys smoother
|
||||
// the logic here is that when a key is released,
|
||||
// if we are at the opposite end of the axis, dont reset to middle
|
||||
if (stick.m_keyCodeMax == code)
|
||||
{
|
||||
if (pressed) stick.m_value = 255;
|
||||
else if (stick.m_value==0) stick.m_value = 0;
|
||||
else stick.m_value = 128;
|
||||
}
|
||||
if (stick.m_keyCodeMin == code)
|
||||
{
|
||||
if (pressed) stick.m_value = 0;
|
||||
else if (stick.m_value == 255) stick.m_value = 255;
|
||||
else stick.m_value = 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual PadInfo& GetInfo() { return m_info; }
|
||||
virtual std::vector<Pad>& GetPads() { return m_pads; }
|
||||
virtual void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) {};
|
||||
std::vector<Button>& GetButtons(const u32 pad) { return m_pads[pad].m_buttons; }
|
||||
std::vector<AnalogStick>& GetSticks(const u32 pad) { return m_pads[pad].m_sticks; }
|
||||
//Does it have GUI Config?
|
||||
bool has_config() { return b_has_config; };
|
||||
//Sets window to config the controller(optional)
|
||||
virtual void ConfigController(std::string device) {};
|
||||
//Return list of devices for that handler
|
||||
virtual std::vector<std::string> ListDevices() = 0;
|
||||
//Callback called during pad_thread::ThreadFunc
|
||||
virtual void ThreadProc() = 0;
|
||||
//Binds a Pad to a device
|
||||
virtual bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) = 0;
|
||||
};
|
||||
|
@ -159,7 +159,7 @@ struct EmuCallbacks
|
||||
std::function<void()> exit;
|
||||
std::function<std::shared_ptr<class KeyboardHandlerBase>()> get_kb_handler;
|
||||
std::function<std::shared_ptr<class MouseHandlerBase>()> get_mouse_handler;
|
||||
std::function<std::shared_ptr<class PadHandlerBase>()> get_pad_handler;
|
||||
std::function<std::shared_ptr<class pad_thread>()> get_pad_handler;
|
||||
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
|
||||
std::function<std::shared_ptr<class GSRender>()> get_gs_render;
|
||||
std::function<std::shared_ptr<class AudioThread>()> get_audio;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
// VFS type
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
const auto THREAD_SLEEP = 1ms; //ds4 has new data every ~4ms,
|
||||
const auto THREAD_SLEEP = 1ms; //ds4 has new data every ~4ms,
|
||||
const auto THREAD_SLEEP_INACTIVE = 100ms;
|
||||
|
||||
const u32 DS4_ACC_RES_PER_G = 8192;
|
||||
@ -57,12 +57,12 @@ namespace
|
||||
// compute angle and len of given point to be used for squircle radius
|
||||
const f32 angle = std::atan2(y, x);
|
||||
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
|
||||
|
||||
|
||||
// now find len/point on the given squircle from our current angle and radius in polar coords
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / 8.f) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
// we now have len and angle, convert to cartisian
|
||||
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
|
||||
@ -125,134 +125,18 @@ namespace
|
||||
return (u32)(((u32)buf[0] << 0) + ((u32)buf[1] << 8) + ((u32)buf[2] << 16) + ((u32)buf[3] << 24));
|
||||
}
|
||||
}
|
||||
|
||||
ds4_pad_handler::~ds4_pad_handler()
|
||||
ds4_pad_handler::ds4_pad_handler() : is_init(false)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void ds4_pad_handler::Init(const u32 max_connect)
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof m_info);
|
||||
m_info.max_connect = max_connect;
|
||||
|
||||
for (u32 i = 0, max = std::min(max_connect, u32(MAX_GAMEPADS)); i != max; ++i)
|
||||
{
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
auto & pad = m_pads.back();
|
||||
|
||||
// 'keycode' here is just 0 as we have to manually calculate this
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2);
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_UP);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_DOWN);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_LEFT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_SQUARE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_CROSS);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_CIRCLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_TRIANGLE);
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R1);
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_SELECT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_START);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_L3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_R3);
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
|
||||
pad.m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_X, 512);
|
||||
pad.m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Y, 399);
|
||||
pad.m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Z, 512);
|
||||
pad.m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_G, 512);
|
||||
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
pad.m_vibrateMotors.emplace_back(true, 0);
|
||||
pad.m_vibrateMotors.emplace_back(false, 0);
|
||||
}
|
||||
|
||||
ds4Thread = std::make_shared<ds4_thread>();
|
||||
ds4Thread->on_init(ds4Thread);
|
||||
}
|
||||
|
||||
PadInfo& ds4_pad_handler::GetInfo()
|
||||
{
|
||||
if (ds4Thread)
|
||||
{
|
||||
auto info = ds4Thread->GetConnectedControllers();
|
||||
|
||||
m_info.now_connect = 0;
|
||||
|
||||
int i = 0;
|
||||
for (auto & pad : m_pads)
|
||||
{
|
||||
|
||||
if (info[i])
|
||||
{
|
||||
m_info.now_connect++;
|
||||
if (last_connection_status[i] == false)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = true;
|
||||
pad.m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last_connection_status[i] == true)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = false;
|
||||
pad.m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return m_info;
|
||||
}
|
||||
|
||||
std::vector<Pad>& ds4_pad_handler::GetPads()
|
||||
{
|
||||
if (ds4Thread)
|
||||
ProcessData();
|
||||
|
||||
return m_pads;
|
||||
}
|
||||
|
||||
void ds4_pad_handler::Close()
|
||||
{
|
||||
if (ds4Thread)
|
||||
ds4Thread.reset();
|
||||
|
||||
m_pads.clear();
|
||||
}
|
||||
|
||||
void ds4_pad_handler::ProcessData()
|
||||
{
|
||||
|
||||
if (!ds4Thread)
|
||||
return;
|
||||
|
||||
auto data = ds4Thread->GetControllerData();
|
||||
|
||||
int i = 0;
|
||||
for (auto & pad : m_pads)
|
||||
for (auto &bind : bindings)
|
||||
{
|
||||
|
||||
auto buf = data[i];
|
||||
std::shared_ptr<DS4Device> device = bind.first;
|
||||
auto pad = bind.second;
|
||||
auto buf = device->padData;
|
||||
|
||||
// these are added with previous value and divided to 'smooth' out the readings
|
||||
// the ds4 seems to rapidly flicker sometimes between two values and this seems to stop that
|
||||
@ -260,114 +144,114 @@ void ds4_pad_handler::ProcessData()
|
||||
u16 lx, ly;
|
||||
//std::tie(lx, ly) = ConvertToSquarePoint(buf[1], buf[2]);
|
||||
std::tie(lx, ly) = ConvertToSquirclePoint(buf[1], buf[2]);
|
||||
pad.m_sticks[0].m_value = (lx + pad.m_sticks[0].m_value) / 2; // LX
|
||||
pad.m_sticks[1].m_value = (ly + pad.m_sticks[1].m_value) / 2; // LY
|
||||
|
||||
pad->m_sticks[0].m_value = (lx + pad->m_sticks[0].m_value) / 2; // LX
|
||||
pad->m_sticks[1].m_value = (ly + pad->m_sticks[1].m_value) / 2; // LY
|
||||
|
||||
u16 rx, ry;
|
||||
//std::tie(rx, ry) = ConvertToSquarePoint(buf[3], buf[4]);
|
||||
std::tie(rx, ry) = ConvertToSquirclePoint(buf[3], buf[4]);
|
||||
pad.m_sticks[2].m_value = (rx + pad.m_sticks[2].m_value) / 2; // RX
|
||||
pad.m_sticks[3].m_value = (ry + pad.m_sticks[3].m_value) / 2; // RY
|
||||
pad->m_sticks[2].m_value = (rx + pad->m_sticks[2].m_value) / 2; // RX
|
||||
pad->m_sticks[3].m_value = (ry + pad->m_sticks[3].m_value) / 2; // RY
|
||||
|
||||
// l2 r2
|
||||
pad.m_buttons[0].m_pressed = buf[8] > 0;
|
||||
pad.m_buttons[0].m_value = buf[8];
|
||||
pad.m_buttons[1].m_pressed = buf[9] > 0;
|
||||
pad.m_buttons[1].m_value = buf[9];
|
||||
// l2 r2
|
||||
pad->m_buttons[0].m_pressed = buf[8] > 0;
|
||||
pad->m_buttons[0].m_value = buf[8];
|
||||
pad->m_buttons[1].m_pressed = buf[9] > 0;
|
||||
pad->m_buttons[1].m_value = buf[9];
|
||||
|
||||
// bleh, dpad in buffer is stored in a different state
|
||||
// bleh, dpad in buffer is stored in a different state
|
||||
u8 dpadState = buf[5] & 0xf;
|
||||
switch (dpadState)
|
||||
{
|
||||
case 0x08: // none pressed
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
case 0x07: // NW...left and up
|
||||
pad.m_buttons[2].m_pressed = true;
|
||||
pad.m_buttons[2].m_value = 255;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = true;
|
||||
pad.m_buttons[4].m_value = 255;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = true;
|
||||
pad->m_buttons[2].m_value = 255;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = true;
|
||||
pad->m_buttons[4].m_value = 255;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
case 0x06: // W..left
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = true;
|
||||
pad.m_buttons[4].m_value = 255;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = true;
|
||||
pad->m_buttons[4].m_value = 255;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
case 0x05: // SW..left down
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = true;
|
||||
pad.m_buttons[3].m_value = 255;
|
||||
pad.m_buttons[4].m_pressed = true;
|
||||
pad.m_buttons[4].m_value = 255;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = true;
|
||||
pad->m_buttons[3].m_value = 255;
|
||||
pad->m_buttons[4].m_pressed = true;
|
||||
pad->m_buttons[4].m_value = 255;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
case 0x04: // S..down
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = true;
|
||||
pad.m_buttons[3].m_value = 255;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = true;
|
||||
pad->m_buttons[3].m_value = 255;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
case 0x03: // SE..down and right
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = true;
|
||||
pad.m_buttons[3].m_value = 255;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = true;
|
||||
pad.m_buttons[5].m_value = 255;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = true;
|
||||
pad->m_buttons[3].m_value = 255;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = true;
|
||||
pad->m_buttons[5].m_value = 255;
|
||||
break;
|
||||
case 0x02: // E... right
|
||||
pad.m_buttons[2].m_pressed = false;
|
||||
pad.m_buttons[2].m_value = 0;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = true;
|
||||
pad.m_buttons[5].m_value = 255;
|
||||
pad->m_buttons[2].m_pressed = false;
|
||||
pad->m_buttons[2].m_value = 0;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = true;
|
||||
pad->m_buttons[5].m_value = 255;
|
||||
break;
|
||||
case 0x01: // NE.. up right
|
||||
pad.m_buttons[2].m_pressed = true;
|
||||
pad.m_buttons[2].m_value = 255;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = true;
|
||||
pad.m_buttons[5].m_value = 255;
|
||||
pad->m_buttons[2].m_pressed = true;
|
||||
pad->m_buttons[2].m_value = 255;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = true;
|
||||
pad->m_buttons[5].m_value = 255;
|
||||
break;
|
||||
case 0x00: // n.. up
|
||||
pad.m_buttons[2].m_pressed = true;
|
||||
pad.m_buttons[2].m_value = 255;
|
||||
pad.m_buttons[3].m_pressed = false;
|
||||
pad.m_buttons[3].m_value = 0;
|
||||
pad.m_buttons[4].m_pressed = false;
|
||||
pad.m_buttons[4].m_value = 0;
|
||||
pad.m_buttons[5].m_pressed = false;
|
||||
pad.m_buttons[5].m_value = 0;
|
||||
pad->m_buttons[2].m_pressed = true;
|
||||
pad->m_buttons[2].m_value = 255;
|
||||
pad->m_buttons[3].m_pressed = false;
|
||||
pad->m_buttons[3].m_value = 0;
|
||||
pad->m_buttons[4].m_pressed = false;
|
||||
pad->m_buttons[4].m_value = 0;
|
||||
pad->m_buttons[5].m_pressed = false;
|
||||
pad->m_buttons[5].m_value = 0;
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("ds4 dpad state encountered unexpected input");
|
||||
@ -377,31 +261,31 @@ void ds4_pad_handler::ProcessData()
|
||||
for (int i = 4; i < 8; ++i)
|
||||
{
|
||||
const bool pressed = ((buf[5] & (1 << i)) != 0);
|
||||
pad.m_buttons[6 + i - 4].m_pressed = pressed;
|
||||
pad.m_buttons[6 + i - 4].m_value = pressed ? 255 : 0;
|
||||
pad->m_buttons[6 + i - 4].m_pressed = pressed;
|
||||
pad->m_buttons[6 + i - 4].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
|
||||
// L1, R1
|
||||
const bool l1press = ((buf[6] & (1 << 0)) != 0);
|
||||
pad.m_buttons[10].m_pressed = l1press;
|
||||
pad.m_buttons[10].m_value = l1press ? 255 : 0;
|
||||
pad->m_buttons[10].m_pressed = l1press;
|
||||
pad->m_buttons[10].m_value = l1press ? 255 : 0;
|
||||
|
||||
const bool l2press = ((buf[6] & (1 << 1)) != 0);
|
||||
pad.m_buttons[11].m_pressed = l2press;
|
||||
pad.m_buttons[11].m_value = l2press ? 255 : 0;
|
||||
pad->m_buttons[11].m_pressed = l2press;
|
||||
pad->m_buttons[11].m_value = l2press ? 255 : 0;
|
||||
|
||||
// select, start, l3, r3
|
||||
for (int i = 4; i < 8; ++i)
|
||||
{
|
||||
const bool pressed = ((buf[6] & (1 << i)) != 0);
|
||||
pad.m_buttons[12 + i - 4].m_pressed = pressed;
|
||||
pad.m_buttons[12 + i - 4].m_value = pressed ? 255 : 0;
|
||||
pad->m_buttons[12 + i - 4].m_pressed = pressed;
|
||||
pad->m_buttons[12 + i - 4].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
for (int i = 6; i < 16; i++)
|
||||
{
|
||||
if (pad.m_buttons[i].m_pressed)
|
||||
if (pad->m_buttons[i].m_pressed)
|
||||
{
|
||||
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
break;
|
||||
@ -422,10 +306,10 @@ void ds4_pad_handler::ProcessData()
|
||||
accelY = accelY * 113 + 512;
|
||||
accelZ = accelZ * 113 + 512;
|
||||
|
||||
pad.m_sensors[0].m_value = Clamp0To1023(accelX);
|
||||
pad.m_sensors[1].m_value = Clamp0To1023(accelY);
|
||||
pad.m_sensors[2].m_value = Clamp0To1023(accelZ);
|
||||
|
||||
pad->m_sensors[0].m_value = Clamp0To1023(accelX);
|
||||
pad->m_sensors[1].m_value = Clamp0To1023(accelY);
|
||||
pad->m_sensors[2].m_value = Clamp0To1023(accelZ);
|
||||
|
||||
// gyroX is yaw, which is all that we need
|
||||
f32 gyroX = (((s16)((u16)(buf[16] << 8) | buf[15])) / static_cast<f32>(DS4_GYRO_RES_PER_DEG_S)) * -1;
|
||||
//const int gyroY = ((u16)(buf[14] << 8) | buf[13]) / 256;
|
||||
@ -434,73 +318,25 @@ void ds4_pad_handler::ProcessData()
|
||||
// convert to ds3
|
||||
gyroX = gyroX * (123.f / 90.f) + 512;
|
||||
|
||||
pad.m_sensors[3].m_value = Clamp0To1023(gyroX);
|
||||
|
||||
i++;
|
||||
pad->m_sensors[3].m_value = Clamp0To1023(gyroX);
|
||||
}
|
||||
}
|
||||
|
||||
void ds4_pad_handler::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor)
|
||||
void ds4_pad_handler::UpdateRumble()
|
||||
{
|
||||
if (pad > m_pads.size())
|
||||
return;
|
||||
|
||||
m_pads[pad].m_vibrateMotors[0].m_value = largeMotor;
|
||||
m_pads[pad].m_vibrateMotors[1].m_value = smallMotor ? 255 : 0;
|
||||
|
||||
if (!ds4Thread)
|
||||
return;
|
||||
|
||||
ds4Thread->SetRumbleData(pad, largeMotor, smallMotor ? 255 : 0);
|
||||
}
|
||||
|
||||
void ds4_thread::SetRumbleData(u32 port, u8 largeVibrate, u8 smallVibrate)
|
||||
{
|
||||
semaphore_lock lock(mutex);
|
||||
|
||||
// todo: give unique identifier to this instead of port
|
||||
|
||||
u32 i = 0;
|
||||
for (auto & controller : controllers)
|
||||
for (auto &bind : bindings)
|
||||
{
|
||||
if (i == port)
|
||||
{
|
||||
controller.second.newVibrateData = controller.second.largeVibrate != largeVibrate || controller.second.smallVibrate != smallVibrate;
|
||||
controller.second.largeVibrate = largeVibrate;
|
||||
controller.second.smallVibrate = smallVibrate;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
std::shared_ptr<DS4Device> device = bind.first;
|
||||
auto thepad = bind.second;
|
||||
|
||||
device->newVibrateData = device->largeVibrate != thepad->m_vibrateMotors[0].m_value || device->smallVibrate != (thepad->m_vibrateMotors[1].m_value ? 255 : 0);
|
||||
device->largeVibrate = thepad->m_vibrateMotors[0].m_value;
|
||||
device->smallVibrate = (thepad->m_vibrateMotors[1].m_value ? 255 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<bool, MAX_GAMEPADS> ds4_thread::GetConnectedControllers()
|
||||
{
|
||||
std::array<bool, MAX_GAMEPADS> rtnData{};
|
||||
int i = 0;
|
||||
|
||||
semaphore_lock lock(mutex);
|
||||
|
||||
for (const auto & cont : controllers)
|
||||
rtnData[i++] = cont.second.hidDevice != nullptr;
|
||||
|
||||
return rtnData;
|
||||
}
|
||||
|
||||
std::array<std::array<u8, 64>, MAX_GAMEPADS> ds4_thread::GetControllerData()
|
||||
{
|
||||
std::array<std::array<u8, 64>, MAX_GAMEPADS> rtnData;
|
||||
|
||||
int i = 0;
|
||||
semaphore_lock lock(mutex);
|
||||
|
||||
for (const auto & data : padData)
|
||||
rtnData[i++] = data;
|
||||
|
||||
return rtnData;
|
||||
}
|
||||
|
||||
bool ds4_thread::GetCalibrationData(DS4Device* ds4Dev)
|
||||
bool ds4_pad_handler::GetCalibrationData(std::shared_ptr<DS4Device> ds4Dev)
|
||||
{
|
||||
std::array<u8, 64> buf;
|
||||
if (ds4Dev->btCon)
|
||||
@ -588,11 +424,11 @@ bool ds4_thread::GetCalibrationData(DS4Device* ds4Dev)
|
||||
return true;
|
||||
}
|
||||
|
||||
void ds4_thread::CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo)
|
||||
void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo)
|
||||
{
|
||||
std::string serial = "";
|
||||
DS4Device ds4Dev;
|
||||
ds4Dev.hidDevice = hidDevice;
|
||||
std::shared_ptr<DS4Device> ds4Dev = std::make_shared<DS4Device>();
|
||||
ds4Dev->hidDevice = hidDevice;
|
||||
// There isnt a nice 'portable' way with hidapi to detect bt vs wired as the pid/vid's are the same
|
||||
// Let's try getting 0x81 feature report, which should will return mac address on wired, and should error on bluetooth
|
||||
std::array<u8, 64> buf{};
|
||||
@ -603,80 +439,45 @@ void ds4_thread::CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevIn
|
||||
}
|
||||
else
|
||||
{
|
||||
ds4Dev.btCon = true;
|
||||
ds4Dev->btCon = true;
|
||||
std::wstring wSerial(hidDevInfo->serial_number);
|
||||
serial = std::string(wSerial.begin(), wSerial.end());
|
||||
}
|
||||
|
||||
if (!GetCalibrationData(&ds4Dev))
|
||||
if (!GetCalibrationData(ds4Dev))
|
||||
{
|
||||
LOG_ERROR(HLE, "[DS4] Failed getting calibration data, ignoring controller!");
|
||||
hid_close(hidDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
ds4Dev.path = hidDevInfo->path;
|
||||
ds4Dev->path = hidDevInfo->path;
|
||||
|
||||
hid_set_nonblocking(hidDevice, 1);
|
||||
controllers.emplace(serial, ds4Dev);
|
||||
}
|
||||
|
||||
void ds4_thread::on_init(const std::shared_ptr<void>& _this)
|
||||
ds4_pad_handler::~ds4_pad_handler()
|
||||
{
|
||||
const int res = hid_init();
|
||||
if (res != 0)
|
||||
fmt::throw_exception("hidapi-init error.threadproc");
|
||||
|
||||
// get all the possible controllers at start
|
||||
for (auto pid : ds4Pids)
|
||||
for (auto& controller : controllers)
|
||||
{
|
||||
hid_device_info* devInfo = hid_enumerate(DS4_VID, pid);
|
||||
hid_device_info* head = devInfo;
|
||||
while (devInfo)
|
||||
{
|
||||
|
||||
if (controllers.size() >= MAX_GAMEPADS)
|
||||
break;
|
||||
|
||||
hid_device* dev = hid_open_path(devInfo->path);
|
||||
if (dev)
|
||||
CheckAddDevice(dev, devInfo);
|
||||
|
||||
devInfo = devInfo->next;
|
||||
}
|
||||
|
||||
hid_free_enumeration(head);
|
||||
}
|
||||
|
||||
if (controllers.size() == 0)
|
||||
LOG_ERROR(HLE, "[DS4] No controllers found!");
|
||||
else
|
||||
LOG_SUCCESS(HLE, "[DS4] Controllers found: %d", controllers.size());
|
||||
|
||||
named_thread::on_init(_this);
|
||||
}
|
||||
|
||||
ds4_thread::~ds4_thread()
|
||||
{
|
||||
for (auto & controller : controllers)
|
||||
{
|
||||
if (controller.second.hidDevice)
|
||||
hid_close(controller.second.hidDevice);
|
||||
if (controller.second->hidDevice)
|
||||
hid_close(controller.second->hidDevice);
|
||||
}
|
||||
hid_exit();
|
||||
}
|
||||
|
||||
void ds4_thread::SendVibrateData(const DS4Device& device)
|
||||
void ds4_pad_handler::SendVibrateData(const std::shared_ptr<DS4Device> device)
|
||||
{
|
||||
std::array<u8, 78> outputBuf{0};
|
||||
// write rumble state
|
||||
if (device.btCon)
|
||||
if (device->btCon)
|
||||
{
|
||||
outputBuf[0] = 0x11;
|
||||
outputBuf[1] = 0xC4;
|
||||
outputBuf[3] = 0x07;
|
||||
outputBuf[6] = device.smallVibrate;
|
||||
outputBuf[7] = device.largeVibrate;
|
||||
outputBuf[6] = device->smallVibrate;
|
||||
outputBuf[7] = device->largeVibrate;
|
||||
outputBuf[8] = 0x00; // red
|
||||
outputBuf[9] = 0x00; // green
|
||||
outputBuf[10] = 0xff; // blue
|
||||
@ -690,124 +491,228 @@ void ds4_thread::SendVibrateData(const DS4Device& device)
|
||||
outputBuf[76] = (crcCalc >> 16) & 0xFF;
|
||||
outputBuf[77] = (crcCalc >> 24) & 0xFF;
|
||||
|
||||
hid_write_control(device.hidDevice, outputBuf.data(), DS4_OUTPUT_REPORT_0x11_SIZE);
|
||||
hid_write_control(device->hidDevice, outputBuf.data(), DS4_OUTPUT_REPORT_0x11_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputBuf[0] = 0x05;
|
||||
outputBuf[1] = 0x07;
|
||||
outputBuf[4] = device.smallVibrate;
|
||||
outputBuf[5] = device.largeVibrate;
|
||||
outputBuf[4] = device->smallVibrate;
|
||||
outputBuf[5] = device->largeVibrate;
|
||||
outputBuf[6] = 0x00; // red
|
||||
outputBuf[7] = 0x00; // green
|
||||
outputBuf[8] = 0xff; // blue
|
||||
|
||||
hid_write(device.hidDevice, outputBuf.data(), DS4_OUTPUT_REPORT_0x05_SIZE);
|
||||
hid_write(device->hidDevice, outputBuf.data(), DS4_OUTPUT_REPORT_0x05_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void ds4_thread::on_task()
|
||||
bool ds4_pad_handler::Init()
|
||||
{
|
||||
while (!Emu.IsStopped())
|
||||
if (is_init) return true;
|
||||
|
||||
const int res = hid_init();
|
||||
if (res != 0)
|
||||
fmt::throw_exception("hidapi-init error.threadproc");
|
||||
|
||||
// get all the possible controllers at start
|
||||
for (auto pid : ds4Pids)
|
||||
{
|
||||
if (Emu.IsPaused())
|
||||
hid_device_info* devInfo = hid_enumerate(DS4_VID, pid);
|
||||
hid_device_info* head = devInfo;
|
||||
while (devInfo)
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
if (controllers.size() >= MAX_GAMEPADS) break;
|
||||
|
||||
hid_device* dev = hid_open_path(devInfo->path);
|
||||
if (dev) CheckAddDevice(dev, devInfo);
|
||||
devInfo = devInfo->next;
|
||||
}
|
||||
hid_free_enumeration(head);
|
||||
}
|
||||
|
||||
if (controllers.size() == 0)
|
||||
LOG_ERROR(HLE, "[DS4] No controllers found!");
|
||||
else
|
||||
LOG_SUCCESS(HLE, "[DS4] Controllers found: %d", controllers.size());
|
||||
|
||||
is_init = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ds4_pad_handler::ListDevices()
|
||||
{
|
||||
std::vector<std::string> ds4_pads_list;
|
||||
|
||||
if (!Init()) return ds4_pads_list;
|
||||
|
||||
for (auto& pad : controllers)
|
||||
{
|
||||
ds4_pads_list.emplace_back("Ds4 Pad #" + pad.first);
|
||||
}
|
||||
|
||||
return ds4_pads_list;
|
||||
}
|
||||
|
||||
bool ds4_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
size_t pos = device.find("Ds4 Pad #");
|
||||
|
||||
if (pos == std::string::npos) return false;
|
||||
|
||||
std::string pad_serial = device.substr(pos + 9);
|
||||
|
||||
std::shared_ptr<DS4Device> device_id = nullptr;
|
||||
|
||||
for (auto& cur_control : controllers)
|
||||
{
|
||||
if (pad_serial == cur_control.first)
|
||||
{
|
||||
device_id = cur_control.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (device_id == nullptr) return false;
|
||||
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_CONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
// 'keycode' here is just 0 as we have to manually calculate this
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_TRIANGLE);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R1);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, 0, CELL_PAD_CTRL_R3);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_X, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Y, 399);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Z, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_G, 512);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
|
||||
bindings.emplace_back(device_id, pad);
|
||||
}
|
||||
|
||||
void ds4_pad_handler::ThreadProc()
|
||||
{
|
||||
UpdateRumble();
|
||||
|
||||
std::array<u8, 78> buf{};
|
||||
|
||||
for (auto &bind : bindings)
|
||||
{
|
||||
std::shared_ptr<DS4Device> device = bind.first;
|
||||
auto thepad = bind.second;
|
||||
|
||||
if (device->hidDevice == nullptr)
|
||||
{
|
||||
// try to reconnect
|
||||
hid_device* dev = hid_open_path(device->path.c_str());
|
||||
if (dev)
|
||||
{
|
||||
hid_set_nonblocking(dev, 1);
|
||||
device->hidDevice = dev;
|
||||
thepad->m_port_status = CELL_PAD_STATUS_CONNECTED|CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nope, not there
|
||||
thepad->m_port_status = CELL_PAD_STATUS_DISCONNECTED|CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const int res = hid_read(device->hidDevice, buf.data(), device->btCon ? 78 : 64);
|
||||
if (res == -1)
|
||||
{
|
||||
// looks like controller disconnected or read error, deal with it on next loop
|
||||
hid_close(device->hidDevice);
|
||||
device->hidDevice = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 online = 0;
|
||||
u32 i = 0;
|
||||
|
||||
std::array<u8, 78> buf{};
|
||||
|
||||
|
||||
for (auto & controller : controllers)
|
||||
if (device->newVibrateData)
|
||||
{
|
||||
semaphore_lock lock(mutex);
|
||||
|
||||
if (controller.second.hidDevice == nullptr)
|
||||
{
|
||||
// try to reconnect
|
||||
hid_device* dev = hid_open_path(controller.second.path.c_str());
|
||||
if (dev)
|
||||
{
|
||||
hid_set_nonblocking(dev, 1);
|
||||
controller.second.hidDevice = dev;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nope, not there
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
online++;
|
||||
|
||||
const int res = hid_read(controller.second.hidDevice, buf.data(), controller.second.btCon ? 78 : 64);
|
||||
if (res == -1)
|
||||
{
|
||||
// looks like controller disconnected or read error, deal with it on next loop
|
||||
hid_close(controller.second.hidDevice);
|
||||
controller.second.hidDevice = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// no data? keep going
|
||||
if (res == 0)
|
||||
continue;
|
||||
|
||||
// bt controller sends this until 0x02 feature report is sent back (happens on controller init/restart)
|
||||
if (controller.second.btCon && buf[0] == 0x1)
|
||||
{
|
||||
// tells controller to send 0x11 reports
|
||||
std::array<u8, 64> buf{};
|
||||
buf[0] = 0x2;
|
||||
hid_get_feature_report(controller.second.hidDevice, buf.data(), buf.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
// check report and set offset
|
||||
if (controller.second.btCon && buf[0] == 0x11 && res == 78)
|
||||
{
|
||||
offset = 2;
|
||||
|
||||
const u8 btHdr = 0xA1;
|
||||
const u32 crcHdr = CRCPP::CRC::Calculate(&btHdr, 1, crcTable);
|
||||
const u32 crcCalc = CRCPP::CRC::Calculate(buf.data(), (DS4_INPUT_REPORT_0x11_SIZE - 4), crcTable, crcHdr);
|
||||
const u32 crcReported = GetU32LEData(&buf[DS4_INPUT_REPORT_0x11_SIZE - 4]);
|
||||
if (crcCalc != crcReported) {
|
||||
LOG_WARNING(HLE, "[DS4] Data packet CRC check failed, ignoring! Received 0x%x, Expected 0x%x", crcReported, crcCalc);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if (!controller.second.btCon && buf[0] == 0x01 && res == 64)
|
||||
offset = 0;
|
||||
else
|
||||
continue;
|
||||
|
||||
int calibOffset = offset + DS4_INPUT_REPORT_GYRO_X_OFFSET;
|
||||
for (int i = 0; i < DS4CalibIndex::COUNT; ++i)
|
||||
{
|
||||
const s16 rawValue = GetS16LEData(&buf[calibOffset]);
|
||||
const s16 calValue = ApplyCalibration(rawValue, controller.second.calibData[i]);
|
||||
buf[calibOffset++] = ((u16)calValue >> 0) & 0xFF;
|
||||
buf[calibOffset++] = ((u16)calValue >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
memcpy(padData[i].data(), &buf[offset], 64);
|
||||
|
||||
if (controller.second.newVibrateData)
|
||||
{
|
||||
SendVibrateData(controller.second);
|
||||
controller.second.newVibrateData = false;
|
||||
}
|
||||
|
||||
i++;
|
||||
SendVibrateData(device);
|
||||
device->newVibrateData = false;
|
||||
}
|
||||
std::this_thread::sleep_for((online > 0) ? THREAD_SLEEP : THREAD_SLEEP_INACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
// no data? keep going
|
||||
if (res == 0)
|
||||
continue;
|
||||
|
||||
// bt controller sends this until 0x02 feature report is sent back (happens on controller init/restart)
|
||||
if (device->btCon && buf[0] == 0x1)
|
||||
{
|
||||
// tells controller to send 0x11 reports
|
||||
std::array<u8, 64> buf_error{};
|
||||
buf_error[0] = 0x2;
|
||||
hid_get_feature_report(device->hidDevice, buf_error.data(), buf_error.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
// check report and set offset
|
||||
if (device->btCon && buf[0] == 0x11 && res == 78)
|
||||
{
|
||||
offset = 2;
|
||||
|
||||
const u8 btHdr = 0xA1;
|
||||
const u32 crcHdr = CRCPP::CRC::Calculate(&btHdr, 1, crcTable);
|
||||
const u32 crcCalc = CRCPP::CRC::Calculate(buf.data(), (DS4_INPUT_REPORT_0x11_SIZE - 4), crcTable, crcHdr);
|
||||
const u32 crcReported = GetU32LEData(&buf[DS4_INPUT_REPORT_0x11_SIZE - 4]);
|
||||
if (crcCalc != crcReported) {
|
||||
LOG_WARNING(HLE, "[DS4] Data packet CRC check failed, ignoring! Received 0x%x, Expected 0x%x", crcReported, crcCalc);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if (!device->btCon && buf[0] == 0x01 && res == 64)
|
||||
offset = 0;
|
||||
else
|
||||
continue;
|
||||
|
||||
int calibOffset = offset + DS4_INPUT_REPORT_GYRO_X_OFFSET;
|
||||
for (int i = 0; i < DS4CalibIndex::COUNT; ++i)
|
||||
{
|
||||
const s16 rawValue = GetS16LEData(&buf[calibOffset]);
|
||||
const s16 calValue = ApplyCalibration(rawValue, device->calibData[i]);
|
||||
buf[calibOffset++] = ((u16)calValue >> 0) & 0xFF;
|
||||
buf[calibOffset++] = ((u16)calValue >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
memcpy(device->padData.data(), &buf[offset], 64);
|
||||
}
|
||||
|
||||
ProcessData();
|
||||
}
|
||||
|
@ -5,13 +5,12 @@
|
||||
#include "Utilities/CRC.h"
|
||||
#include "hidapi.h"
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
const u32 MAX_GAMEPADS = 7;
|
||||
|
||||
|
||||
class ds4_thread final : public named_thread
|
||||
class ds4_pad_handler final : public PadHandlerBase
|
||||
{
|
||||
private:
|
||||
enum DS4CalibIndex
|
||||
{
|
||||
// gyro
|
||||
@ -39,48 +38,45 @@ private:
|
||||
std::string path{ "" };
|
||||
bool btCon{ false };
|
||||
std::array<DS4CalibData, DS4CalibIndex::COUNT> calibData;
|
||||
bool newVibrateData{true};
|
||||
u8 largeVibrate{0};
|
||||
u8 smallVibrate{0};
|
||||
bool newVibrateData{ true };
|
||||
u8 largeVibrate{ 0 };
|
||||
u8 smallVibrate{ 0 };
|
||||
std::array<u8, 64> padData;
|
||||
};
|
||||
|
||||
const u16 DS4_VID = 0x054C;
|
||||
|
||||
// pid's of connected ds4
|
||||
const std::array<u16, 3> ds4Pids = {{0xBA0, 0x5C4, 0x09CC }};
|
||||
const std::array<u16, 3> ds4Pids = { { 0xBA0, 0x5C4, 0x09CC } };
|
||||
|
||||
// pseudo 'controller id' to keep track of unique controllers
|
||||
std::unordered_map<std::string, DS4Device> controllers;
|
||||
|
||||
std::array<std::array<u8, 64>, MAX_GAMEPADS> padData{};
|
||||
|
||||
void on_task() override;
|
||||
|
||||
std::string get_name() const override { return "DS4 Thread"; }
|
||||
|
||||
semaphore<> mutex;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<DS4Device>> controllers;
|
||||
CRCPP::CRC::Table<u32, 32> crcTable{ CRCPP::CRC::CRC_32() };
|
||||
|
||||
public:
|
||||
void on_init(const std::shared_ptr<void>&) override;
|
||||
ds4_pad_handler();
|
||||
~ds4_pad_handler();
|
||||
|
||||
std::array<bool, MAX_GAMEPADS> GetConnectedControllers();
|
||||
bool Init() override;
|
||||
|
||||
std::array<std::array<u8, 64>, MAX_GAMEPADS> GetControllerData();
|
||||
|
||||
void SetRumbleData(u32 port, u8 largeVibrate, u8 smallVibrate);
|
||||
|
||||
|
||||
ds4_thread() = default;
|
||||
|
||||
|
||||
~ds4_thread();
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
|
||||
private:
|
||||
bool GetCalibrationData(DS4Device* ds4Device);
|
||||
bool is_init;
|
||||
|
||||
// holds internal controller state change
|
||||
std::array<bool, MAX_GAMEPADS> last_connection_status = {};
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<DS4Device>, std::shared_ptr<Pad>>> bindings;
|
||||
|
||||
private:
|
||||
void ProcessData();
|
||||
void UpdateRumble();
|
||||
bool GetCalibrationData(std::shared_ptr<DS4Device> ds4Device);
|
||||
void CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo);
|
||||
void SendVibrateData(const DS4Device& device);
|
||||
void SendVibrateData(const std::shared_ptr<DS4Device> device);
|
||||
inline s16 ApplyCalibration(s32 rawValue, const DS4CalibData& calibData)
|
||||
{
|
||||
const s32 biased = rawValue - calibData.bias;
|
||||
@ -95,28 +91,3 @@ private:
|
||||
else return static_cast<s16>(output);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ds4_pad_handler final : public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
|
||||
ds4_pad_handler() {}
|
||||
~ds4_pad_handler();
|
||||
|
||||
void Init(const u32 max_connect) override;
|
||||
void Close();
|
||||
|
||||
PadInfo& GetInfo() override;
|
||||
std::vector<Pad>& GetPads() override;
|
||||
void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) override;
|
||||
|
||||
private:
|
||||
void ProcessData();
|
||||
|
||||
// holds internal controller state change
|
||||
std::array<bool, MAX_GAMEPADS> last_connection_status = {};
|
||||
|
||||
|
||||
std::shared_ptr<ds4_thread> ds4Thread;
|
||||
};
|
||||
|
@ -15,22 +15,12 @@
|
||||
|
||||
evdev_joystick_config g_evdev_joystick_config;
|
||||
|
||||
namespace
|
||||
{
|
||||
const u32 THREAD_SLEEP_USEC = 100;
|
||||
const u32 THREAD_SLEEP_INACTIVE_USEC = 1000000;
|
||||
const u32 READ_TIMEOUT = 10;
|
||||
const u32 THREAD_TIMEOUT_USEC = 1000000;
|
||||
}
|
||||
|
||||
evdev_joystick_handler::evdev_joystick_handler() {}
|
||||
|
||||
evdev_joystick_handler::~evdev_joystick_handler() { Close(); }
|
||||
|
||||
void evdev_joystick_handler::Init(const u32 max_connect)
|
||||
bool evdev_joystick_handler::Init()
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof m_info);
|
||||
|
||||
g_evdev_joystick_config.load();
|
||||
|
||||
axistrigger = static_cast<bool>(g_evdev_joystick_config.axistrigger);
|
||||
@ -40,90 +30,13 @@ void evdev_joystick_handler::Init(const u32 max_connect)
|
||||
revaxis.emplace_back(g_evdev_joystick_config.rxreverse);
|
||||
revaxis.emplace_back(g_evdev_joystick_config.ryreverse);
|
||||
|
||||
fs::dir devdir{"/dev/input/"};
|
||||
fs::dir_entry et;
|
||||
|
||||
while (devdir.read(et))
|
||||
{
|
||||
// Check if the entry starts with event (a 5-letter word)
|
||||
if (et.name.size() > 5 && et.name.compare(0, 5,"event") == 0)
|
||||
{
|
||||
int fd = open(("/dev/input/" + et.name).c_str(), O_RDONLY|O_NONBLOCK);
|
||||
struct libevdev *dev = NULL;
|
||||
int rc = 1;
|
||||
rc = libevdev_new_from_fd(fd, &dev);
|
||||
if (rc < 0)
|
||||
{
|
||||
// If it's just a bad file descriptor, don't bother logging, but otherwise, log it.
|
||||
if (rc == -9)
|
||||
LOG_WARNING(GENERAL, "Failed to connect to device at %s, the error was: %s", "/dev/input/" + et.name, strerror(-rc));
|
||||
continue;
|
||||
}
|
||||
if (libevdev_has_event_type(dev, EV_KEY) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_X) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_Y))
|
||||
{
|
||||
// It's a joystick.
|
||||
joy_paths.emplace_back(fmt::format("/dev/input/%s", et.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_info.max_connect = std::min(max_connect, static_cast<u32>(joy_paths.size()));
|
||||
|
||||
for (u32 i = 0; i < m_info.max_connect; ++i)
|
||||
{
|
||||
joy_devs.push_back(nullptr);
|
||||
joy_axis_maps.emplace_back(ABS_RZ - ABS_X, -1);
|
||||
joy_axis.emplace_back(ABS_RZ - ABS_X, -1);
|
||||
joy_button_maps.emplace_back(KEY_MAX - BTN_JOYSTICK, -1);
|
||||
joy_hat_ids.emplace_back(-1);
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
auto& pad = m_pads.back();
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.up, CELL_PAD_CTRL_UP);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.down, CELL_PAD_CTRL_DOWN);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.left, CELL_PAD_CTRL_LEFT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lxstick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lystick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rxstick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rystick, 0, 0);
|
||||
}
|
||||
|
||||
update_devs();
|
||||
active.store(true);
|
||||
joy_thread.reset(new std::thread(std::bind(&evdev_joystick_handler::thread_func, this)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::update_devs()
|
||||
{
|
||||
int connected=0;
|
||||
|
||||
for (u32 i = 0; i < m_info.max_connect; ++i)
|
||||
if (try_open_dev(i)) ++connected;
|
||||
|
||||
m_info.now_connect = connected;
|
||||
for (u32 i = 0; i < joy_devs.size(); ++i)
|
||||
try_open_dev(i);
|
||||
}
|
||||
|
||||
inline u16 Clamp0To255(f32 input)
|
||||
@ -149,7 +62,7 @@ std::tuple<u16, u16> evdev_joystick_handler::ConvertToSquirclePoint(u16 inX, u16
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / (g_evdev_joystick_config.squirclefactor / 1000.f)) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
// we now have len and angle, convert to cartisian
|
||||
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
|
||||
@ -169,14 +82,14 @@ bool evdev_joystick_handler::try_open_dev(u32 index)
|
||||
if (was_connected)
|
||||
{
|
||||
// It was disconnected.
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
pads[index]->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
|
||||
int fd = libevdev_get_fd(dev);
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
dev = nullptr;
|
||||
}
|
||||
m_pads[index].m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
pads[index]->m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
LOG_ERROR(GENERAL, "Joystick %s is not present or accessible [previous status: %d]", path.c_str(),
|
||||
was_connected ? 1 : 0);
|
||||
return false;
|
||||
@ -203,8 +116,8 @@ bool evdev_joystick_handler::try_open_dev(u32 index)
|
||||
|
||||
if (!was_connected)
|
||||
// Connection status changed from disconnected to connected.
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
pads[index]->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
pads[index]->m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
|
||||
int buttons=0;
|
||||
for (int i=BTN_JOYSTICK; i<KEY_MAX; i++)
|
||||
@ -244,19 +157,6 @@ bool evdev_joystick_handler::try_open_dev(u32 index)
|
||||
|
||||
void evdev_joystick_handler::Close()
|
||||
{
|
||||
if (active.load())
|
||||
{
|
||||
active.store(false);
|
||||
if (!dead.load())
|
||||
{
|
||||
usleep(THREAD_TIMEOUT_USEC);
|
||||
if (!dead.load())
|
||||
LOG_ERROR(GENERAL, "EvdevJoystick thread could not stop within %d microseconds", THREAD_TIMEOUT_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
joy_thread->detach();
|
||||
|
||||
for (auto& dev : joy_devs)
|
||||
{
|
||||
if (dev != nullptr)
|
||||
@ -290,196 +190,324 @@ int evdev_joystick_handler::scale_axis(int axis, int value)
|
||||
}
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::thread_func()
|
||||
std::vector<std::string> evdev_joystick_handler::ListDevices()
|
||||
{
|
||||
while (active)
|
||||
Init();
|
||||
std::vector<std::string> evdev_joystick_list;
|
||||
fs::dir devdir{"/dev/input/"};
|
||||
fs::dir_entry et;
|
||||
|
||||
while (devdir.read(et))
|
||||
{
|
||||
update_devs();
|
||||
|
||||
for (int i=0; i<joy_devs.size(); i++)
|
||||
// Check if the entry starts with event (a 5-letter word)
|
||||
if (et.name.size() > 5 && et.name.compare(0, 5,"event") == 0)
|
||||
{
|
||||
auto& pad = m_pads[i];
|
||||
auto& dev = joy_devs[i];
|
||||
if (dev == nullptr) continue;
|
||||
|
||||
// Try to query the latest event from the joystick.
|
||||
input_event evt;
|
||||
int ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &evt);
|
||||
|
||||
// Grab any pending sync event.
|
||||
if (ret == LIBEVDEV_READ_STATUS_SYNC)
|
||||
int fd = open(("/dev/input/" + et.name).c_str(), O_RDONLY|O_NONBLOCK);
|
||||
struct libevdev *dev = NULL;
|
||||
int rc = 1;
|
||||
rc = libevdev_new_from_fd(fd, &dev);
|
||||
if (rc < 0)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Captured sync event");
|
||||
ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, &evt);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
// -EAGAIN signifies no available events, not an actual *error*.
|
||||
if (ret != -EAGAIN)
|
||||
LOG_ERROR(GENERAL, "Failed to read latest event from joystick #%d: %s [errno %d]", i, strerror(-ret), -ret);
|
||||
// If it's just a bad file descriptor, don't bother logging, but otherwise, log it.
|
||||
if (rc != -9)
|
||||
LOG_WARNING(GENERAL, "Failed to connect to device at %s, the error was: %s", "/dev/input/" + et.name, strerror(-rc));
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (evt.type)
|
||||
if (libevdev_has_event_type(dev, EV_KEY) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_X) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_Y))
|
||||
{
|
||||
case EV_SYN:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_SYN", i);
|
||||
break;
|
||||
case EV_MSC:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_MSC", i);
|
||||
break;
|
||||
case EV_KEY:
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_KEY: %d %d", i, evt.code, evt.value);
|
||||
if (evt.code < BTN_MISC)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent non-button key event %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
int button_code = joy_button_maps[i][evt.code - BTN_MISC];
|
||||
if (button_code == -1)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid button code %d", i, evt.code);
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad.m_buttons.begin(), pad.m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_keyCode == button_code; });
|
||||
if (which_button == pad.m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent button event for unmapped button %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
which_button->m_pressed = evt.value;
|
||||
which_button->m_value = evt.value ? 255 : 0;
|
||||
break;
|
||||
// It's a joystick.
|
||||
evdev_joystick_list.push_back(libevdev_get_name(dev));
|
||||
}
|
||||
case EV_ABS:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_ABS: %d %d", i, evt.code, evt.value);
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
return evdev_joystick_list;
|
||||
}
|
||||
|
||||
if (evt.code >= ABS_HAT0X && evt.code <= ABS_HAT3Y) {
|
||||
int hat = evt.code - ABS_HAT0X;
|
||||
if (hat != joy_hat_ids[i] && hat-1 != joy_hat_ids[i])
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent HAT event for invalid hat %d (expected %d)", i, hat, joy_hat_ids[i]);
|
||||
break;
|
||||
}
|
||||
bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
Init();
|
||||
|
||||
int source_axis = hat == joy_hat_ids[i] ? EVDEV_DPAD_HAT_AXIS_X : EVDEV_DPAD_HAT_AXIS_Y;
|
||||
|
||||
for (Button& bt : pad.m_buttons)
|
||||
{
|
||||
if (bt.m_keyCode != source_axis) continue;
|
||||
|
||||
if (evt.value == 0)
|
||||
{
|
||||
bt.m_pressed = false;
|
||||
bt.m_value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = -1;
|
||||
if (source_axis == EVDEV_DPAD_HAT_AXIS_X)
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_RIGHT : CELL_PAD_CTRL_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_DOWN : CELL_PAD_CTRL_UP;
|
||||
}
|
||||
|
||||
if (bt.m_outKeyCode == code)
|
||||
{
|
||||
bt.m_pressed = true;
|
||||
bt.m_value = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (axistrigger && (evt.code == ABS_Z || evt.code == ABS_RZ))
|
||||
// Now we need to find the device with the same name, and make sure not to grab any duplicates.
|
||||
fs::dir devdir{"/dev/input/"};
|
||||
fs::dir_entry et;
|
||||
while (devdir.read(et))
|
||||
{
|
||||
// Check if the entry starts with event (a 5-letter word)
|
||||
if (et.name.size() > 5 && et.name.compare(0, 5,"event") == 0)
|
||||
{
|
||||
int fd = open(("/dev/input/" + et.name).c_str(), O_RDONLY|O_NONBLOCK);
|
||||
struct libevdev *dev = NULL;
|
||||
int rc = 1;
|
||||
rc = libevdev_new_from_fd(fd, &dev);
|
||||
if (rc < 0)
|
||||
{
|
||||
// For Xbox controllers, a third axis represent the left/right triggers.
|
||||
int which_trigger=0;
|
||||
|
||||
if (evt.code == ABS_Z)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_L2;
|
||||
}
|
||||
else if (evt.code == ABS_RZ)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_R2;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid event code %d for 3rd axis", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad.m_buttons.begin(), pad.m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_outKeyCode == which_trigger; });
|
||||
if (which_button == pad.m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d's pad has no trigger %d", i, which_trigger);
|
||||
break;
|
||||
}
|
||||
|
||||
int value = scale_axis(evt.code, evt.value);
|
||||
which_button->m_pressed = value > 0;
|
||||
which_button->m_value = value;
|
||||
// If it's just a bad file descriptor, don't bother logging, but otherwise, log it.
|
||||
if (rc != -9)
|
||||
LOG_WARNING(GENERAL, "Failed to connect to device at %s, the error was: %s", "/dev/input/" + et.name, strerror(-rc));
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
else if (evt.code >= ABS_X && evt.code <= ABS_RZ)
|
||||
const std::string name = libevdev_get_name(dev);
|
||||
if (libevdev_has_event_type(dev, EV_KEY) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_X) &&
|
||||
libevdev_has_event_code(dev, EV_ABS, ABS_Y) &&
|
||||
name == device)
|
||||
{
|
||||
int axis = joy_axis_maps[i][evt.code - ABS_X];
|
||||
// It's a joystick.
|
||||
|
||||
if (axis > pad.m_sticks.size())
|
||||
// Now let's make sure we don't already have this one.
|
||||
bool alreadyIn = false;
|
||||
for (int i = 0; i < joy_paths.size(); i++)
|
||||
if (joy_paths[i] == fmt::format("/dev/input/%s", et.name))
|
||||
{
|
||||
alreadyIn = true;
|
||||
break;
|
||||
}
|
||||
if (alreadyIn == true)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent axis event for invalid axis %d", i, axis);
|
||||
break;
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_evdev_joystick_config.squirclejoysticks)
|
||||
{
|
||||
joy_axis[i][axis] = evt.value;
|
||||
if (evt.code == ABS_X || evt.code == ABS_Y)
|
||||
{
|
||||
int Xaxis = joy_axis_maps[i][ABS_X];
|
||||
int Yaxis = joy_axis_maps[i][ABS_Y];
|
||||
pad.m_sticks[Xaxis].m_value = scale_axis(ABS_X, joy_axis[i][Xaxis]);
|
||||
pad.m_sticks[Yaxis].m_value = scale_axis(ABS_Y, joy_axis[i][Yaxis]);
|
||||
|
||||
std::tie(pad.m_sticks[Xaxis].m_value, pad.m_sticks[Yaxis].m_value) =
|
||||
ConvertToSquirclePoint(pad.m_sticks[Xaxis].m_value, pad.m_sticks[Yaxis].m_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
int Xaxis = joy_axis_maps[i][ABS_RX];
|
||||
int Yaxis = joy_axis_maps[i][ABS_RY];
|
||||
pad.m_sticks[Xaxis].m_value = scale_axis(ABS_RX, joy_axis[i][Xaxis]);
|
||||
pad.m_sticks[Yaxis].m_value = scale_axis(ABS_RY, joy_axis[i][Yaxis]);
|
||||
|
||||
std::tie(pad.m_sticks[Xaxis].m_value, pad.m_sticks[Yaxis].m_value) =
|
||||
ConvertToSquirclePoint(pad.m_sticks[Xaxis].m_value, pad.m_sticks[Yaxis].m_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
pad.m_sticks[axis].m_value = scale_axis(evt.code, evt.value);
|
||||
|
||||
// Alright, now that we've confirmed we haven't added this joystick yet, les do dis.
|
||||
joy_paths.emplace_back(fmt::format("/dev/input/%s", et.name));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(GENERAL, "Unknown joystick #%d event %d", i, evt.type);
|
||||
break;
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int to_sleep = m_info.now_connect > 0 ? THREAD_SLEEP_USEC : THREAD_SLEEP_INACTIVE_USEC;
|
||||
usleep(to_sleep);
|
||||
}
|
||||
joy_devs.push_back(nullptr);
|
||||
joy_axis_maps.emplace_back(ABS_RZ - ABS_X, -1);
|
||||
joy_axis.emplace_back(ABS_RZ - ABS_X, -1);
|
||||
joy_button_maps.emplace_back(KEY_MAX - BTN_JOYSTICK, -1);
|
||||
joy_hat_ids.emplace_back(-1);
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
|
||||
dead = true;
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.up, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.down, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.left, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lxstick, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lystick, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rxstick, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rystick, 0, 0);
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
pads.emplace_back(pad);
|
||||
update_devs();
|
||||
return true;
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::ThreadProc()
|
||||
{
|
||||
update_devs();
|
||||
|
||||
for (int i=0; i<joy_devs.size(); i++)
|
||||
{
|
||||
auto pad = pads[i];
|
||||
auto& dev = joy_devs[i];
|
||||
if (dev == nullptr) continue;
|
||||
|
||||
// Try to query the latest event from the joystick.
|
||||
input_event evt;
|
||||
int ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &evt);
|
||||
|
||||
// Grab any pending sync event.
|
||||
if (ret == LIBEVDEV_READ_STATUS_SYNC)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Captured sync event");
|
||||
ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, &evt);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
// -EAGAIN signifies no available events, not an actual *error*.
|
||||
if (ret != -EAGAIN)
|
||||
LOG_ERROR(GENERAL, "Failed to read latest event from joystick #%d: %s [errno %d]", i, strerror(-ret), -ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (evt.type)
|
||||
{
|
||||
case EV_SYN:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_SYN", i);
|
||||
break;
|
||||
case EV_MSC:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_MSC", i);
|
||||
break;
|
||||
case EV_KEY:
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_KEY: %d %d", i, evt.code, evt.value);
|
||||
if (evt.code < BTN_MISC)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent non-button key event %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
int button_code = joy_button_maps[i][evt.code - BTN_MISC];
|
||||
if (button_code == -1)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid button code %d", i, evt.code);
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad->m_buttons.begin(), pad->m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_keyCode == button_code; });
|
||||
if (which_button == pad->m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent button event for unmapped button %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
which_button->m_pressed = evt.value;
|
||||
which_button->m_value = evt.value ? 255 : 0;
|
||||
break;
|
||||
}
|
||||
case EV_ABS:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_ABS: %d %d", i, evt.code, evt.value);
|
||||
|
||||
if (evt.code >= ABS_HAT0X && evt.code <= ABS_HAT3Y) {
|
||||
int hat = evt.code - ABS_HAT0X;
|
||||
if (hat != joy_hat_ids[i] && hat-1 != joy_hat_ids[i])
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent HAT event for invalid hat %d (expected %d)", i, hat, joy_hat_ids[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
int source_axis = hat == joy_hat_ids[i] ? EVDEV_DPAD_HAT_AXIS_X : EVDEV_DPAD_HAT_AXIS_Y;
|
||||
|
||||
for (Button& bt : pad->m_buttons)
|
||||
{
|
||||
if (bt.m_keyCode != source_axis) continue;
|
||||
|
||||
if (evt.value == 0)
|
||||
{
|
||||
bt.m_pressed = false;
|
||||
bt.m_value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = -1;
|
||||
if (source_axis == EVDEV_DPAD_HAT_AXIS_X)
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_RIGHT : CELL_PAD_CTRL_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_DOWN : CELL_PAD_CTRL_UP;
|
||||
}
|
||||
|
||||
if (bt.m_outKeyCode == code)
|
||||
{
|
||||
bt.m_pressed = true;
|
||||
bt.m_value = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (axistrigger && (evt.code == ABS_Z || evt.code == ABS_RZ))
|
||||
{
|
||||
// For Xbox controllers, a third axis represent the left/right triggers.
|
||||
int which_trigger=0;
|
||||
|
||||
if (evt.code == ABS_Z)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_L2;
|
||||
}
|
||||
else if (evt.code == ABS_RZ)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_R2;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid event code %d for 3rd axis", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad->m_buttons.begin(), pad->m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_outKeyCode == which_trigger; });
|
||||
if (which_button == pad->m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d's pad has no trigger %d", i, which_trigger);
|
||||
break;
|
||||
}
|
||||
|
||||
int value = scale_axis(evt.code, evt.value);
|
||||
which_button->m_pressed = value > 0;
|
||||
which_button->m_value = value;
|
||||
}
|
||||
else if (evt.code >= ABS_X && evt.code <= ABS_RZ)
|
||||
{
|
||||
int axis = joy_axis_maps[i][evt.code - ABS_X];
|
||||
|
||||
if (axis > pad->m_sticks.size())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent axis event for invalid axis %d", i, axis);
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_evdev_joystick_config.squirclejoysticks)
|
||||
{
|
||||
joy_axis[i][axis] = evt.value;
|
||||
if (evt.code == ABS_X || evt.code == ABS_Y)
|
||||
{
|
||||
int Xaxis = joy_axis_maps[i][ABS_X];
|
||||
int Yaxis = joy_axis_maps[i][ABS_Y];
|
||||
pad->m_sticks[Xaxis].m_value = scale_axis(ABS_X, joy_axis[i][Xaxis]);
|
||||
pad->m_sticks[Yaxis].m_value = scale_axis(ABS_Y, joy_axis[i][Yaxis]);
|
||||
|
||||
std::tie(pad->m_sticks[Xaxis].m_value, pad->m_sticks[Yaxis].m_value) =
|
||||
ConvertToSquirclePoint(pad->m_sticks[Xaxis].m_value, pad->m_sticks[Yaxis].m_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
int Xaxis = joy_axis_maps[i][ABS_RX];
|
||||
int Yaxis = joy_axis_maps[i][ABS_RY];
|
||||
pad->m_sticks[Xaxis].m_value = scale_axis(ABS_RX, joy_axis[i][Xaxis]);
|
||||
pad->m_sticks[Yaxis].m_value = scale_axis(ABS_RY, joy_axis[i][Yaxis]);
|
||||
|
||||
std::tie(pad->m_sticks[Xaxis].m_value, pad->m_sticks[Yaxis].m_value) =
|
||||
ConvertToSquirclePoint(pad->m_sticks[Xaxis].m_value, pad->m_sticks[Yaxis].m_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
pad->m_sticks[axis].m_value = scale_axis(evt.code, evt.value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(GENERAL, "Unknown joystick #%d event %d", i, evt.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -70,7 +70,10 @@ public:
|
||||
evdev_joystick_handler();
|
||||
~evdev_joystick_handler();
|
||||
|
||||
void Init(const u32 max_connect) override;
|
||||
bool Init() override;
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void Close();
|
||||
|
||||
private:
|
||||
@ -78,11 +81,9 @@ private:
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
|
||||
bool try_open_dev(u32 index);
|
||||
int scale_axis(int axis, int value);
|
||||
void thread_func();
|
||||
|
||||
std::unique_ptr<std::thread> joy_thread;
|
||||
mutable atomic_t<bool> active{false}, dead{false};
|
||||
std::vector<std::string> joy_paths;
|
||||
std::vector<std::shared_ptr<Pad>> pads;
|
||||
std::vector<libevdev*> joy_devs;
|
||||
std::vector<std::vector<int>> joy_button_maps;
|
||||
std::vector<std::vector<int>> joy_axis_maps;
|
||||
|
@ -4,20 +4,79 @@
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "rpcs3qt/pad_settings_dialog.h"
|
||||
|
||||
keyboard_pad_config g_kbpad_config;
|
||||
|
||||
void keyboard_pad_handler::Init(const u32 max_connect)
|
||||
bool keyboard_pad_handler::Init()
|
||||
{
|
||||
memset(&m_info, 0, sizeof(PadInfo));
|
||||
m_info.max_connect = max_connect;
|
||||
LoadSettings();
|
||||
m_info.now_connect = std::min(m_pads.size(), (size_t)max_connect);
|
||||
return true;
|
||||
}
|
||||
|
||||
keyboard_pad_handler::keyboard_pad_handler() : QObject()
|
||||
{
|
||||
b_has_config = true;
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::ConfigController(std::string device)
|
||||
{
|
||||
pad_settings_dialog dlg(this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value)
|
||||
{
|
||||
for (auto pad : bindings)
|
||||
{
|
||||
for (Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_keyCode != code)
|
||||
continue;
|
||||
|
||||
if (value >= 256) { value = 255; }
|
||||
|
||||
//Todo: Is this flush necessary once games hit decent speeds?
|
||||
if (button.m_pressed && !pressed)
|
||||
{
|
||||
button.m_flush = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
button.m_pressed = pressed;
|
||||
if (pressed)
|
||||
button.m_value = value;
|
||||
else
|
||||
button.m_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (AnalogStick& stick : pad->m_sticks)
|
||||
{
|
||||
if (stick.m_keyCodeMax != code && stick.m_keyCodeMin != code)
|
||||
continue;
|
||||
|
||||
//slightly less hack job for key based analog stick
|
||||
// should also fix/make transitions when using keys smoother
|
||||
// the logic here is that when a key is released,
|
||||
// if we are at the opposite end of the axis, dont reset to middle
|
||||
if (stick.m_keyCodeMax == code)
|
||||
{
|
||||
if (pressed) stick.m_value = 255;
|
||||
else if (stick.m_value == 0) stick.m_value = 0;
|
||||
else stick.m_value = 128;
|
||||
}
|
||||
if (stick.m_keyCodeMin == code)
|
||||
{
|
||||
if (pressed) stick.m_value = 0;
|
||||
else if (stick.m_value == 255) stick.m_value = 255;
|
||||
else stick.m_value = 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool keyboard_pad_handler::eventFilter(QObject* target, QEvent* ev)
|
||||
{
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
|
||||
@ -108,37 +167,56 @@ void keyboard_pad_handler::keyReleaseEvent(QKeyEvent* event)
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::LoadSettings()
|
||||
std::vector<std::string> keyboard_pad_handler::ListDevices()
|
||||
{
|
||||
std::vector<std::string> list_devices;
|
||||
list_devices.push_back("Keyboard");
|
||||
return list_devices;
|
||||
}
|
||||
|
||||
bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
if (device != "Keyboard") return false;
|
||||
|
||||
g_kbpad_config.load();
|
||||
|
||||
//Fixed assign change, default is both sensor and press off
|
||||
m_pads.emplace_back(
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD);
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.left, CELL_PAD_CTRL_LEFT);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.down, CELL_PAD_CTRL_DOWN);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.up, CELL_PAD_CTRL_UP);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.start, CELL_PAD_CTRL_START);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.r3, CELL_PAD_CTRL_R3);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.l3, CELL_PAD_CTRL_L3);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.left, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.down, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.up, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.start, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.select, CELL_PAD_CTRL_SELECT);
|
||||
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r1, CELL_PAD_CTRL_R1);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l1, CELL_PAD_CTRL_L1);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r2, CELL_PAD_CTRL_R2);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l2, CELL_PAD_CTRL_L2);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, g_kbpad_config.left_stick_left, g_kbpad_config.left_stick_right);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, g_kbpad_config.left_stick_up, g_kbpad_config.left_stick_down);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, g_kbpad_config.right_stick_left, g_kbpad_config.right_stick_right);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, g_kbpad_config.right_stick_up, g_kbpad_config.right_stick_down);
|
||||
|
||||
bindings.push_back(pad);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::ThreadProc()
|
||||
{
|
||||
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, g_kbpad_config.left_stick_left, g_kbpad_config.left_stick_right);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, g_kbpad_config.left_stick_up, g_kbpad_config.left_stick_down);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, g_kbpad_config.right_stick_left, g_kbpad_config.right_stick_right);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, g_kbpad_config.right_stick_up, g_kbpad_config.right_stick_down);
|
||||
}
|
||||
|
@ -56,16 +56,25 @@ struct keyboard_pad_config final : cfg::node
|
||||
class keyboard_pad_handler final : public QObject, public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
virtual void Init(const u32 max_connect) override;
|
||||
bool Init() override;
|
||||
|
||||
keyboard_pad_handler();
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
void LoadSettings();
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void ConfigController(std::string device) override;
|
||||
|
||||
protected:
|
||||
void Key(const u32 code, bool pressed, u16 value = 255);
|
||||
|
||||
private:
|
||||
QWindow* m_target = nullptr;
|
||||
std::vector<std::shared_ptr<Pad>> bindings;
|
||||
};
|
||||
|
@ -16,17 +16,18 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
mm_joystick_handler::mm_joystick_handler() : active(false), thread(nullptr)
|
||||
mm_joystick_handler::mm_joystick_handler() : is_init(false)
|
||||
{
|
||||
}
|
||||
|
||||
mm_joystick_handler::~mm_joystick_handler()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void mm_joystick_handler::Init(const u32 max_connect)
|
||||
bool mm_joystick_handler::Init()
|
||||
{
|
||||
if (is_init) return true;
|
||||
|
||||
supportedJoysticks = joyGetNumDevs();
|
||||
if (supportedJoysticks > 0)
|
||||
{
|
||||
@ -36,141 +37,120 @@ void mm_joystick_handler::Init(const u32 max_connect)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Driver doesn't support Joysticks");
|
||||
}
|
||||
|
||||
js_info.dwSize = sizeof(js_info);
|
||||
js_info.dwFlags = JOY_RETURNALL;
|
||||
joyGetDevCaps(JOYSTICKID1, &js_caps, sizeof(js_caps));
|
||||
|
||||
|
||||
bool JoyPresent = (joyGetPosEx(JOYSTICKID1, &js_info) == JOYERR_NOERROR);
|
||||
if (JoyPresent)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Found connected joystick with %u buttons and %u axes", js_caps.wNumButtons,js_caps.wNumAxes);
|
||||
LOG_NOTICE(GENERAL, "Axes info %u %u %u %u %u %u %u %u", js_caps.wXmin, js_caps.wXmax,js_caps.wYmin,js_caps.wYmax,js_caps.wZmin,js_caps.wZmax,js_caps.wRmin,js_caps.wRmax);
|
||||
std::memset(&m_info, 0, sizeof m_info);
|
||||
m_info.max_connect = max_connect;
|
||||
|
||||
for (u32 i = 0, max = std::min(max_connect, u32(1)); i != max; ++i)
|
||||
{
|
||||
g_mmjoystick_config.load();
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
auto & pad = m_pads.back();
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVFORWARD, CELL_PAD_CTRL_UP);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVBACKWARD, CELL_PAD_CTRL_DOWN);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVLEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVRIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
active = true;
|
||||
thread = CreateThread(NULL, 0, &mm_joystick_handler::ThreadProcProxy, this, 0, NULL);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!JoyPresent)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick not found");
|
||||
}
|
||||
}
|
||||
|
||||
void mm_joystick_handler::Close()
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
active = false;
|
||||
if (WaitForSingleObject(thread, THREAD_TIMEOUT) != WAIT_OBJECT_0)
|
||||
LOG_ERROR(GENERAL, "MMJoystick thread could not stop within %d milliseconds", (u32)THREAD_TIMEOUT);
|
||||
thread = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pads.clear();
|
||||
is_init = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD mm_joystick_handler::ThreadProcedure()
|
||||
std::vector<std::string> mm_joystick_handler::ListDevices()
|
||||
{
|
||||
// holds internal controller state change
|
||||
std::array<bool, CELL_PAD_MAX_PORT_NUM> last_connection_status = {};
|
||||
while (active)
|
||||
std::vector<std::string> mm_pad_list;
|
||||
|
||||
if (!Init()) return mm_pad_list;
|
||||
|
||||
mm_pad_list.push_back("MMJoy Pad");
|
||||
|
||||
return mm_pad_list;
|
||||
}
|
||||
|
||||
bool mm_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
if (!Init()) return false;
|
||||
|
||||
g_mmjoystick_config.load();
|
||||
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVFORWARD, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVBACKWARD, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVLEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVRIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
bindings.push_back(pad);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mm_joystick_handler::ThreadProc()
|
||||
{
|
||||
MMRESULT status;
|
||||
DWORD online = 0;
|
||||
|
||||
for (u32 i = 0; i != bindings.size(); ++i)
|
||||
{
|
||||
MMRESULT status;
|
||||
DWORD online = 0;
|
||||
auto pad = bindings[i];
|
||||
status = joyGetPosEx(JOYSTICKID1, &js_info);
|
||||
|
||||
for (DWORD i = 0; i != m_pads.size(); ++i)
|
||||
switch (status)
|
||||
{
|
||||
|
||||
auto & pad = m_pads[i];
|
||||
status =joyGetPosEx(JOYSTICKID1, &js_info);
|
||||
case JOYERR_UNPLUGGED:
|
||||
if (last_connection_status[i] == true)
|
||||
pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = false;
|
||||
pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
break;
|
||||
|
||||
switch (status)
|
||||
case JOYERR_NOERROR:
|
||||
++online;
|
||||
if (last_connection_status[i] == false)
|
||||
pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = true;
|
||||
pad->m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
for (DWORD j = 0; j <= 12; j++)
|
||||
{
|
||||
case JOYERR_UNPLUGGED:
|
||||
if (last_connection_status[i] == true)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = false;
|
||||
pad.m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
break;
|
||||
|
||||
case JOYERR_NOERROR:
|
||||
++online;
|
||||
if (last_connection_status[i] == false)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = true;
|
||||
pad.m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
for (DWORD j = 0; j <= 12; j++)
|
||||
{
|
||||
bool pressed = js_info.dwButtons & pad.m_buttons[j].m_keyCode;
|
||||
pad.m_buttons[j].m_pressed = pressed;
|
||||
pad.m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
for (DWORD j = 13; j <= 16; j++)//POV aka digital pad
|
||||
{
|
||||
bool pressed = js_info.dwPOV == pad.m_buttons[j].m_keyCode;
|
||||
pad.m_buttons[j].m_pressed = pressed;
|
||||
pad.m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
pad.m_sticks[0].m_value = ConvertAxis(js_info.dwXpos);
|
||||
pad.m_sticks[1].m_value = ConvertAxis(js_info.dwYpos);
|
||||
pad.m_sticks[2].m_value = ConvertAxis(js_info.dwZpos);
|
||||
pad.m_sticks[3].m_value = ConvertAxis(js_info.dwRpos);
|
||||
break;
|
||||
bool pressed = js_info.dwButtons & pad->m_buttons[j].m_keyCode;
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
for (DWORD j = 13; j <= 16; j++)//POV aka digital pad
|
||||
{
|
||||
bool pressed = js_info.dwPOV == pad->m_buttons[j].m_keyCode;
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
pad->m_sticks[0].m_value = ConvertAxis(js_info.dwXpos);
|
||||
pad->m_sticks[1].m_value = ConvertAxis(js_info.dwYpos);
|
||||
pad->m_sticks[2].m_value = ConvertAxis(js_info.dwZpos);
|
||||
pad->m_sticks[3].m_value = ConvertAxis(js_info.dwRpos);
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep((online > 0) ? THREAD_SLEEP : THREAD_SLEEP_INACTIVE);
|
||||
m_info.now_connect = online;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI mm_joystick_handler::ThreadProcProxy(LPVOID parameter)
|
||||
{
|
||||
return reinterpret_cast<mm_joystick_handler *>(parameter)->ThreadProcedure();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -56,17 +56,19 @@ public:
|
||||
mm_joystick_handler();
|
||||
~mm_joystick_handler();
|
||||
|
||||
void Init(const u32 max_connect) override;
|
||||
void Close();
|
||||
|
||||
private:
|
||||
DWORD ThreadProcedure();
|
||||
static DWORD WINAPI ThreadProcProxy(LPVOID parameter);
|
||||
bool Init() override;
|
||||
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
|
||||
private:
|
||||
bool is_init;
|
||||
u32 supportedJoysticks;
|
||||
mutable bool active;
|
||||
HANDLE thread;
|
||||
JOYINFOEX js_info;
|
||||
JOYCAPS js_caps;
|
||||
|
||||
std::vector<std::shared_ptr<Pad>> bindings;
|
||||
std::array<bool, 7> last_connection_status = {};
|
||||
|
||||
};
|
||||
|
121
rpcs3/pad_thread.cpp
Normal file
121
rpcs3/pad_thread.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "pad_thread.h"
|
||||
#include "rpcs3qt/gamepads_settings_dialog.h"
|
||||
#include "../ds4_pad_handler.h"
|
||||
#ifdef _WIN32
|
||||
#include "../xinput_pad_handler.h"
|
||||
#include "../mm_joystick_handler.h"
|
||||
#elif HAVE_LIBEVDEV
|
||||
#include "../evdev_joystick_handler.h"
|
||||
#endif
|
||||
#include "../keyboard_pad_handler.h"
|
||||
#include "../Emu/Io/Null/NullPadHandler.h"
|
||||
|
||||
|
||||
pad_thread::pad_thread(void *_curthread, void *_curwindow) : curthread(_curthread), curwindow(_curwindow)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
pad_thread::~pad_thread()
|
||||
{
|
||||
active = false;
|
||||
thread->join();
|
||||
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
void pad_thread::Init(const u32 max_connect)
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof(m_info));
|
||||
m_info.max_connect = max_connect;
|
||||
m_info.now_connect = std::min(max_connect, (u32)7); // max 7 pads
|
||||
|
||||
input_cfg.load();
|
||||
|
||||
std::shared_ptr<keyboard_pad_handler> keyptr;
|
||||
|
||||
//Always have a Null Pad Handler
|
||||
std::shared_ptr<NullPadHandler> nullpad = std::make_shared<NullPadHandler>();
|
||||
handlers.emplace(pad_handler::null, nullpad);
|
||||
|
||||
for (u32 i = 0; i < m_info.now_connect; i++)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler;
|
||||
|
||||
if (handlers.count(input_cfg.player_input[i]) != 0)
|
||||
{
|
||||
cur_pad_handler = handlers[input_cfg.player_input[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (input_cfg.player_input[i])
|
||||
{
|
||||
case pad_handler::keyboard:
|
||||
keyptr = std::make_shared<keyboard_pad_handler>();
|
||||
keyptr->moveToThread((QThread *)curthread);
|
||||
keyptr->SetTargetWindow((QWindow *)curwindow);
|
||||
cur_pad_handler = keyptr;
|
||||
break;
|
||||
case pad_handler::ds4:
|
||||
cur_pad_handler = std::make_shared<ds4_pad_handler>();
|
||||
break;
|
||||
#ifdef _MSC_VER
|
||||
case pad_handler::xinput:
|
||||
cur_pad_handler = std::make_shared<xinput_pad_handler>();
|
||||
break;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
case pad_handler::mm:
|
||||
cur_pad_handler = std::make_shared<mm_joystick_handler>();
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev:
|
||||
cur_pad_handler = std::make_shared<evdev_joystick_handler>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
handlers.emplace(input_cfg.player_input[i], cur_pad_handler);
|
||||
}
|
||||
cur_pad_handler->Init();
|
||||
|
||||
m_pads.push_back(std::make_shared<Pad>(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR,
|
||||
CELL_PAD_DEV_TYPE_STANDARD));
|
||||
|
||||
if (cur_pad_handler->bindPadToDevice(m_pads.back(), input_cfg.player_device[i]->to_string()) == false)
|
||||
{
|
||||
//Failed to bind the device to cur_pad_handler so binds to NullPadHandler
|
||||
LOG_ERROR(GENERAL, "Failed to bind device %s to handler %s", input_cfg.player_device[i]->to_string(), input_cfg.player_input[i].to_string());
|
||||
nullpad->bindPadToDevice(m_pads.back(), input_cfg.player_device[i]->to_string());
|
||||
}
|
||||
}
|
||||
|
||||
thread = std::make_shared<std::thread>(&pad_thread::ThreadFunc, this);
|
||||
}
|
||||
|
||||
void pad_thread::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) {
|
||||
if (pad > m_pads.size())
|
||||
return;
|
||||
|
||||
if (m_pads[pad]->m_vibrateMotors.size() >= 2)
|
||||
{
|
||||
m_pads[pad]->m_vibrateMotors[0].m_value = largeMotor;
|
||||
m_pads[pad]->m_vibrateMotors[1].m_value = smallMotor ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void pad_thread::ThreadFunc()
|
||||
{
|
||||
active = true;
|
||||
while (active)
|
||||
{
|
||||
for (auto& cur_pad_handler : handlers)
|
||||
{
|
||||
cur_pad_handler.second->ThreadProc();
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
}
|
43
rpcs3/pad_thread.h
Normal file
43
rpcs3/pad_thread.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "../Utilities/types.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
|
||||
struct PadInfo
|
||||
{
|
||||
u32 max_connect;
|
||||
u32 now_connect;
|
||||
u32 system_info;
|
||||
};
|
||||
|
||||
class pad_thread
|
||||
{
|
||||
public:
|
||||
pad_thread(void *_curthread, void *_curwindow); //void * instead of QThread * and QWindow * because of include in emucore
|
||||
~pad_thread();
|
||||
|
||||
void Init(const u32 max_connect);
|
||||
PadInfo& GetInfo() { return m_info; }
|
||||
std::vector<std::shared_ptr<Pad>>& GetPads() { return m_pads; }
|
||||
void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor);
|
||||
|
||||
protected:
|
||||
void ThreadFunc();
|
||||
|
||||
//List of all handlers
|
||||
std::map<pad_handler, std::shared_ptr<PadHandlerBase>> handlers;
|
||||
|
||||
//Used for pad_handler::keyboard
|
||||
void *curthread;
|
||||
void *curwindow;
|
||||
|
||||
PadInfo m_info;
|
||||
std::vector<std::shared_ptr<Pad>> m_pads;
|
||||
|
||||
bool active;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
};
|
@ -347,6 +347,7 @@
|
||||
<ClCompile Include="keyboard_pad_handler.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="mm_joystick_handler.cpp" />
|
||||
<ClCompile Include="pad_thread.cpp" />
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_about_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
@ -878,6 +879,7 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\about_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\gamepads_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_list_grid.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_list_grid_delegate.cpp" />
|
||||
<ClCompile Include="rpcs3qt\save_data_info_dialog.cpp" />
|
||||
@ -1220,6 +1222,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(NOINHERIT)\."</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="pad_thread.h" />
|
||||
<ClInclude Include="QTGeneratedFiles\ui_about_dialog.h" />
|
||||
<ClInclude Include="QTGeneratedFiles\ui_main_window.h" />
|
||||
<ClInclude Include="QTGeneratedFiles\ui_pad_settings_dialog.h" />
|
||||
@ -1243,6 +1246,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\gamepads_settings_dialog.h" />
|
||||
<ClInclude Include="rpcs3qt\game_list.h" />
|
||||
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
|
@ -494,6 +494,12 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_save_manager_dialog.cpp">
|
||||
<Filter>Generated Files\Debug - LLVM</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pad_thread.cpp">
|
||||
<Filter>Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\gamepads_settings_dialog.cpp">
|
||||
<Filter>Gui</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="\rpcs3qt\*.h">
|
||||
@ -550,6 +556,12 @@
|
||||
<ClInclude Include="rpcs3qt\save_data_dialog.h">
|
||||
<Filter>Gui\saves</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pad_thread.h">
|
||||
<Filter>Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\gamepads_settings_dialog.h">
|
||||
<Filter>Gui</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "evdev_joystick_handler.h"
|
||||
#endif
|
||||
|
||||
#include "pad_thread.h"
|
||||
|
||||
|
||||
#include "Emu/RSX/Null/NullGSRender.h"
|
||||
#include "Emu/RSX/GL/GLGSRender.h"
|
||||
@ -140,30 +142,9 @@ void rpcs3_app::InitializeCallbacks()
|
||||
}
|
||||
};
|
||||
|
||||
callbacks.get_pad_handler = [this]() -> std::shared_ptr<PadHandlerBase>
|
||||
callbacks.get_pad_handler = [this]() -> std::shared_ptr<pad_thread>
|
||||
{
|
||||
switch (pad_handler type = g_cfg.io.pad)
|
||||
{
|
||||
case pad_handler::null: return std::make_shared<NullPadHandler>();
|
||||
case pad_handler::keyboard:
|
||||
{
|
||||
keyboard_pad_handler* ret = new keyboard_pad_handler();
|
||||
ret->moveToThread(thread());
|
||||
ret->SetTargetWindow(gameWindow);
|
||||
return std::shared_ptr<PadHandlerBase>(ret);
|
||||
}
|
||||
case pad_handler::ds4: return std::make_shared<ds4_pad_handler>();
|
||||
#ifdef _MSC_VER
|
||||
case pad_handler::xinput: return std::make_shared<xinput_pad_handler>();
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
case pad_handler::mm: return std::make_shared<mm_joystick_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev: return std::make_shared<evdev_joystick_handler>();
|
||||
#endif
|
||||
default: fmt::throw_exception("Invalid pad handler: %s", type);
|
||||
}
|
||||
return std::make_shared<pad_thread>(thread(), gameWindow);
|
||||
};
|
||||
|
||||
callbacks.get_gs_frame = [this]() -> std::unique_ptr<GSFrameBase>
|
||||
|
229
rpcs3/rpcs3qt/gamepads_settings_dialog.cpp
Normal file
229
rpcs3/rpcs3qt/gamepads_settings_dialog.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
#include "gamepads_settings_dialog.h"
|
||||
#include "../Emu/Io/PadHandler.h"
|
||||
#include "../ds4_pad_handler.h"
|
||||
#ifdef _WIN32
|
||||
#include "../xinput_pad_handler.h"
|
||||
#include "../mm_joystick_handler.h"
|
||||
#elif HAVE_LIBEVDEV
|
||||
#include "../evdev_joystick_handler.h"
|
||||
#endif
|
||||
#include "../keyboard_pad_handler.h"
|
||||
#include "../Emu/Io/Null/NullPadHandler.h"
|
||||
#include "../Emu/System.h"
|
||||
|
||||
input_config input_cfg;
|
||||
|
||||
gamepads_settings_dialog::gamepads_settings_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Gamepads Settings"));
|
||||
|
||||
QVBoxLayout *dialog_layout = new QVBoxLayout();
|
||||
QHBoxLayout *all_players = new QHBoxLayout();
|
||||
|
||||
input_cfg.from_default();
|
||||
input_cfg.load();
|
||||
|
||||
auto fill_device_combo = [](QComboBox *combo)
|
||||
{
|
||||
std::vector<std::string> str_inputs = input_cfg.player_input[0].to_list();
|
||||
for (int index = 0; index < str_inputs.size(); index++)
|
||||
{
|
||||
combo->addItem(str_inputs[index].c_str());
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
QGroupBox *grp_player = new QGroupBox(QString(tr("Player %1").arg(i+1)));
|
||||
|
||||
QVBoxLayout *ppad_layout = new QVBoxLayout();
|
||||
|
||||
co_inputtype[i] = new QComboBox();
|
||||
fill_device_combo(co_inputtype[i]);
|
||||
ppad_layout->addWidget(co_inputtype[i]);
|
||||
|
||||
co_deviceID[i] = new QComboBox();
|
||||
co_deviceID[i]->setEnabled(false);
|
||||
ppad_layout->addWidget(co_deviceID[i]);
|
||||
|
||||
QHBoxLayout *button_layout = new QHBoxLayout();
|
||||
bu_config[i] = new QPushButton(tr("Config"));
|
||||
bu_config[i]->setEnabled(false);
|
||||
button_layout->addSpacing(bu_config[i]->sizeHint().width()*0.50f);
|
||||
button_layout->addWidget(bu_config[i]);
|
||||
button_layout->addSpacing(bu_config[i]->sizeHint().width()*0.50f);
|
||||
ppad_layout->addLayout(button_layout);
|
||||
|
||||
grp_player->setLayout(ppad_layout);
|
||||
all_players->addWidget(grp_player);
|
||||
|
||||
if (i == 3)
|
||||
{
|
||||
dialog_layout->addLayout(all_players);
|
||||
all_players = new QHBoxLayout();
|
||||
all_players->addStretch();
|
||||
}
|
||||
}
|
||||
|
||||
all_players->addStretch();
|
||||
dialog_layout->addLayout(all_players);
|
||||
|
||||
QHBoxLayout *buttons_layout = new QHBoxLayout();
|
||||
QPushButton *ok_button = new QPushButton(tr("OK"));
|
||||
buttons_layout->addWidget(ok_button);
|
||||
QPushButton *cancel_button = new QPushButton(tr("Cancel"));
|
||||
buttons_layout->addWidget(cancel_button);
|
||||
buttons_layout->addStretch();
|
||||
dialog_layout->addLayout(buttons_layout);
|
||||
|
||||
setLayout(dialog_layout);
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
//Set the values from config
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
for (int j = 0; j < co_inputtype[i]->count(); j++)
|
||||
{
|
||||
if (co_inputtype[i]->itemText(j).toStdString() == input_cfg.player_input[i].to_string())
|
||||
{
|
||||
co_inputtype[i]->setCurrentIndex(j);
|
||||
ChangeInputType(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < co_deviceID[i]->count(); j++)
|
||||
{
|
||||
if (co_deviceID[i]->itemText(j).toStdString() == input_cfg.player_device[i]->to_string())
|
||||
{
|
||||
co_deviceID[i]->setCurrentIndex(j);
|
||||
ChangeDevice(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
connect(co_inputtype[i], &QComboBox::currentTextChanged, [=] { ChangeInputType(i); });
|
||||
connect(co_deviceID[i], &QComboBox::currentTextChanged, [=] { ChangeDevice(i); });
|
||||
connect(bu_config[i], &QAbstractButton::clicked, [=] { ClickConfigButton(i); });
|
||||
}
|
||||
connect(ok_button, &QPushButton::pressed, this, &gamepads_settings_dialog::SaveExit);
|
||||
connect(cancel_button, &QPushButton::pressed, this, &gamepads_settings_dialog::CancelExit);
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::SaveExit()
|
||||
{
|
||||
//Check for invalid selection
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (co_deviceID[i]->currentData() == -1)
|
||||
{
|
||||
input_cfg.player_input[i].from_default();
|
||||
input_cfg.player_device[i]->from_default();
|
||||
}
|
||||
}
|
||||
|
||||
input_cfg.save();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::CancelExit()
|
||||
{
|
||||
//Reloads config from file or defaults
|
||||
input_cfg.from_default();
|
||||
input_cfg.load();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::ChangeDevice(int player)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = input_cfg.player_device[player]->from_string(co_deviceID[player]->currentText().toStdString());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
//Something went wrong
|
||||
LOG_ERROR(GENERAL, "Failed to convert device string:%s", co_deviceID[player]->currentText().toStdString().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> gamepads_settings_dialog::GetHandler(pad_handler type)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> ret_handler;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case pad_handler::null:
|
||||
ret_handler = std::make_unique<NullPadHandler>();
|
||||
break;
|
||||
case pad_handler::keyboard:
|
||||
ret_handler = std::make_unique<keyboard_pad_handler>();
|
||||
break;
|
||||
case pad_handler::ds4:
|
||||
ret_handler = std::make_unique<ds4_pad_handler>();
|
||||
break;
|
||||
#ifdef _MSC_VER
|
||||
case pad_handler::xinput:
|
||||
ret_handler = std::make_unique<xinput_pad_handler>();
|
||||
break;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
case pad_handler::mm:
|
||||
ret_handler = std::make_unique<mm_joystick_handler>();
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev:
|
||||
ret_handler = std::make_unique<evdev_joystick_handler>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret_handler;
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::ChangeInputType(int player)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = input_cfg.player_input[player].from_string(co_inputtype[player]->currentText().toStdString());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
//Something went wrong
|
||||
LOG_ERROR(GENERAL, "Failed to convert input string:%s", co_inputtype[player]->currentText().toStdString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(input_cfg.player_input[player]);
|
||||
|
||||
std::vector<std::string> list_devices = cur_pad_handler->ListDevices();
|
||||
|
||||
co_deviceID[player]->clear();
|
||||
for (int i = 0; i < list_devices.size(); i++) co_deviceID[player]->addItem(list_devices[i].c_str(), i);
|
||||
|
||||
if (list_devices.size() == 0)
|
||||
{
|
||||
co_deviceID[player]->addItem(tr("No Device Detected"), -1);
|
||||
co_deviceID[player]->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
co_deviceID[player]->setEnabled(true);
|
||||
}
|
||||
|
||||
bu_config[player]->setEnabled(cur_pad_handler->has_config());
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::ClickConfigButton(int player)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(input_cfg.player_input[player]);
|
||||
if (cur_pad_handler->has_config()) cur_pad_handler->ConfigController(*input_cfg.player_device[player]);
|
||||
}
|
78
rpcs3/rpcs3qt/gamepads_settings_dialog.h
Normal file
78
rpcs3/rpcs3qt/gamepads_settings_dialog.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "../Emu/System.h"
|
||||
#include "../../Utilities/Config.h"
|
||||
#include "../../Utilities/File.h"
|
||||
#include "../Emu/Io/PadHandler.h"
|
||||
|
||||
struct input_config final : cfg::node
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_input.yml";
|
||||
|
||||
cfg::_enum<pad_handler> player_input[7]{
|
||||
{ this, "Player 1 Input", pad_handler::keyboard },
|
||||
{ this, "Player 2 Input", pad_handler::null },
|
||||
{ this, "Player 3 Input", pad_handler::null },
|
||||
{ this, "Player 4 Input", pad_handler::null },
|
||||
{ this, "Player 5 Input", pad_handler::null },
|
||||
{ this, "Player 6 Input", pad_handler::null },
|
||||
{ this, "Player 7 Input", pad_handler::null } };
|
||||
|
||||
cfg::string player1{ this, "Player 1 Device", "Keyboard" };
|
||||
cfg::string player2{ this, "Player 2 Device", "Default Null Device" };
|
||||
cfg::string player3{ this, "Player 3 Device", "Default Null Device" };
|
||||
cfg::string player4{ this, "Player 4 Device", "Default Null Device" };
|
||||
cfg::string player5{ this, "Player 5 Device", "Default Null Device" };
|
||||
cfg::string player6{ this, "Player 6 Device", "Default Null Device" };
|
||||
cfg::string player7{ this, "Player 7 Device", "Default Null Device" };
|
||||
|
||||
cfg::string *player_device[7]{ &player1, &player2, &player3, &player4, &player5, &player6, &player7 }; // Thanks gcc!
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
|
||||
bool exist()
|
||||
{
|
||||
return fs::is_file(cfg_name);
|
||||
}
|
||||
};
|
||||
|
||||
extern input_config input_cfg;
|
||||
|
||||
class gamepads_settings_dialog : public QDialog
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type);
|
||||
void ChangeInputType(int player);
|
||||
void ChangeDevice(int player);
|
||||
void ClickConfigButton(int player);
|
||||
void SaveExit();
|
||||
void CancelExit();
|
||||
|
||||
protected:
|
||||
QComboBox *co_inputtype[7];
|
||||
QComboBox *co_deviceID[7];
|
||||
QPushButton *bu_config[7];
|
||||
|
||||
public:
|
||||
gamepads_settings_dialog(QWidget* parent);
|
||||
~gamepads_settings_dialog() = default;
|
||||
};
|
@ -24,6 +24,7 @@
|
||||
#include "main_window.h"
|
||||
#include "emu_settings.h"
|
||||
#include "about_dialog.h"
|
||||
#include "gamepads_settings_dialog.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@ -1168,9 +1169,9 @@ void main_window::CreateConnects()
|
||||
connect(ui->confIOAct, &QAction::triggered, [=]() { openSettings(3); });
|
||||
connect(ui->confSystemAct, &QAction::triggered, [=]() { openSettings(4); });
|
||||
|
||||
connect(ui->confPadAct, &QAction::triggered, this, [=]
|
||||
connect(ui->confPadsAct, &QAction::triggered, this, [=]
|
||||
{
|
||||
pad_settings_dialog dlg(guiSettings, this);
|
||||
gamepads_settings_dialog dlg(this);
|
||||
dlg.exec();
|
||||
});
|
||||
|
||||
@ -1369,7 +1370,7 @@ void main_window::CreateConnects()
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->toolbar_controls, &QAction::triggered, [=]() { pad_settings_dialog dlg(guiSettings, this); dlg.exec(); });
|
||||
connect(ui->toolbar_controls, &QAction::triggered, [=]() { gamepads_settings_dialog dlg(this); dlg.exec(); });
|
||||
connect(ui->toolbar_config, &QAction::triggered, [=]() { openSettings(0); });
|
||||
connect(ui->toolbar_list, &QAction::triggered, [=]() { ui->setlistModeListAct->trigger(); });
|
||||
connect(ui->toolbar_grid, &QAction::triggered, [=]() { ui->setlistModeGridAct->trigger(); });
|
||||
|
@ -141,7 +141,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1058</width>
|
||||
<height>26</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
@ -192,7 +192,7 @@
|
||||
<addaction name="confCPUAct"/>
|
||||
<addaction name="confGPUAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="confPadAct"/>
|
||||
<addaction name="confPadsAct"/>
|
||||
<addaction name="confAudioAct"/>
|
||||
<addaction name="confIOAct"/>
|
||||
<addaction name="confSystemAct"/>
|
||||
@ -419,9 +419,12 @@
|
||||
<string>Configure Graphics</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="confPadAct">
|
||||
<action name="confPadsAct">
|
||||
<property name="text">
|
||||
<string>Keyboard</string>
|
||||
<string>Pads</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Pads</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Configure Controls</string>
|
||||
@ -803,10 +806,13 @@
|
||||
<normaloff>:/Icons/controls.png</normaloff>:/Icons/controls.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keyboard</string>
|
||||
<string>Controls</string>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Controls</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Configure keyboard</string>
|
||||
<string>Configure controls</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="toolbar_snap">
|
||||
|
@ -18,7 +18,7 @@ static const int PadButtonWidth = 60;
|
||||
|
||||
extern keyboard_pad_config g_kbpad_config;
|
||||
|
||||
pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_settings, QWidget *parent) : QDialog(parent), ui(new Ui::pad_settings_dialog)
|
||||
pad_settings_dialog::pad_settings_dialog(keyboard_pad_handler *keyhandler, QWidget *parent) : QDialog(parent), ui(new Ui::pad_settings_dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -67,7 +67,9 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||
g_kbpad_config.load();
|
||||
UpdateLabel();
|
||||
|
||||
ui->l_controller->setPixmap(gui_settings->colorizedPixmap(*ui->l_controller->pixmap(), QColor(), GUI::get_Label_Color("l_controller"), false, true));
|
||||
gui_settings settings(this);
|
||||
|
||||
ui->l_controller->setPixmap(settings.colorizedPixmap(*ui->l_controller->pixmap(), QColor(), GUI::get_Label_Color("l_controller"), false, true));
|
||||
ui->l_controller->setMaximumSize(ui->gb_description->sizeHint().width(), ui->l_controller->maximumHeight() * ui->gb_description->sizeHint().width() / ui->l_controller->maximumWidth());
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
@ -271,49 +273,6 @@ void pad_settings_dialog::RunTimer(const u32 seconds, const u32 id)
|
||||
}
|
||||
}
|
||||
|
||||
void pad_settings_dialog::Init(const u32 max_connect)
|
||||
{
|
||||
memset(&m_info, 0, sizeof(PadInfo));
|
||||
m_info.max_connect = max_connect;
|
||||
LoadSettings();
|
||||
m_info.now_connect = std::min((u32)m_pads.size(), max_connect);
|
||||
}
|
||||
|
||||
void pad_settings_dialog::LoadSettings()
|
||||
{
|
||||
g_kbpad_config.load();
|
||||
|
||||
//Fixed assign change, default is both sensor and press off
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD);
|
||||
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.left, CELL_PAD_CTRL_LEFT);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.down, CELL_PAD_CTRL_DOWN);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.up, CELL_PAD_CTRL_UP);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.start, CELL_PAD_CTRL_START);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.r3, CELL_PAD_CTRL_R3);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.l3, CELL_PAD_CTRL_L3);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.select, CELL_PAD_CTRL_SELECT);
|
||||
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r1, CELL_PAD_CTRL_R1);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l1, CELL_PAD_CTRL_L1);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r2, CELL_PAD_CTRL_R2);
|
||||
m_pads[0].m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l2, CELL_PAD_CTRL_L2);
|
||||
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, g_kbpad_config.left_stick_left, g_kbpad_config.left_stick_right);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, g_kbpad_config.left_stick_up, g_kbpad_config.left_stick_down);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, g_kbpad_config.right_stick_left, g_kbpad_config.right_stick_right);
|
||||
m_pads[0].m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, g_kbpad_config.right_stick_up, g_kbpad_config.right_stick_down);
|
||||
}
|
||||
|
||||
const QString pad_settings_dialog::GetKeyName(const u32 keyCode)
|
||||
{
|
||||
//TODO what about numpad?
|
||||
|
@ -54,7 +54,7 @@ namespace Ui
|
||||
class pad_settings_dialog;
|
||||
}
|
||||
|
||||
class pad_settings_dialog : public QDialog, PadHandlerBase
|
||||
class pad_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -67,17 +67,15 @@ private:
|
||||
bool m_key_pressed;
|
||||
QAction *onButtonClickedAct;
|
||||
Ui::pad_settings_dialog *ui;
|
||||
keyboard_pad_handler* keyhdlr;
|
||||
|
||||
public:
|
||||
// TODO get Init to work
|
||||
virtual void Init(const u32 max_connect) override;
|
||||
explicit pad_settings_dialog(std::shared_ptr<gui_settings> gui_settings, QWidget *parent = 0);
|
||||
explicit pad_settings_dialog(keyboard_pad_handler* keyhandler, QWidget *parent = nullptr);
|
||||
~pad_settings_dialog();
|
||||
void keyPressEvent(QKeyEvent *keyEvent) override;
|
||||
void UpdateLabel();
|
||||
void UpdateTimerLabel(const u32 id);
|
||||
void SwitchButtons(const bool IsEnabled);
|
||||
void RunTimer(const u32 seconds, const u32 id);
|
||||
void LoadSettings();
|
||||
const QString GetKeyName(const u32 keyCode);
|
||||
};
|
||||
|
@ -585,9 +585,6 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> xSettings, const
|
||||
|
||||
// Comboboxes
|
||||
|
||||
xemu_settings->EnhanceComboBox(ui->padHandlerBox, emu_settings::PadHandler);
|
||||
ui->padHandlerBox->setToolTip(json_input["padHandlerBox"].toString());
|
||||
|
||||
xemu_settings->EnhanceComboBox(ui->keyboardHandlerBox, emu_settings::KeyboardHandler);
|
||||
ui->keyboardHandlerBox->setToolTip(json_input["keyboardHandlerBox"].toString());
|
||||
|
||||
|
@ -655,13 +655,13 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_52">
|
||||
<widget class="QGroupBox" name="groupBox_51">
|
||||
<property name="title">
|
||||
<string>Controller Handler</string>
|
||||
<string>Keyboard Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_29">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_27">
|
||||
<item>
|
||||
<widget class="QComboBox" name="padHandlerBox"/>
|
||||
<widget class="QComboBox" name="keyboardHandlerBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -683,13 +683,13 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_51">
|
||||
<widget class="QGroupBox" name="groupBox_53">
|
||||
<property name="title">
|
||||
<string>Keyboard Handler</string>
|
||||
<string>Mouse Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_27">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_28">
|
||||
<item>
|
||||
<widget class="QComboBox" name="keyboardHandlerBox"/>
|
||||
<widget class="QComboBox" name="mouseHandlerBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -710,18 +710,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_53">
|
||||
<property name="title">
|
||||
<string>Mouse Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_28">
|
||||
<item>
|
||||
<widget class="QComboBox" name="mouseHandlerBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_3" native="true"/>
|
||||
</item>
|
||||
|
@ -34,7 +34,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
xinput_pad_handler::xinput_pad_handler() : active(false), thread(nullptr), library(nullptr), xinputGetState(nullptr), xinputEnable(nullptr), xinputSetState(nullptr)
|
||||
xinput_pad_handler::xinput_pad_handler() : library(nullptr), xinputGetState(nullptr), xinputEnable(nullptr), xinputSetState(nullptr), is_init(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -43,8 +43,10 @@ xinput_pad_handler::~xinput_pad_handler()
|
||||
Close();
|
||||
}
|
||||
|
||||
void xinput_pad_handler::Init(const u32 max_connect)
|
||||
bool xinput_pad_handler::Init()
|
||||
{
|
||||
if (is_init) return true;
|
||||
|
||||
for (auto it : LIBRARY_FILENAMES)
|
||||
{
|
||||
library = LoadLibrary(it);
|
||||
@ -61,6 +63,7 @@ void xinput_pad_handler::Init(const u32 max_connect)
|
||||
|
||||
if (xinputEnable && xinputGetState && xinputSetState)
|
||||
{
|
||||
is_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -71,88 +74,27 @@ void xinput_pad_handler::Init(const u32 max_connect)
|
||||
}
|
||||
}
|
||||
|
||||
if (library)
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof m_info);
|
||||
m_info.max_connect = max_connect;
|
||||
if (!is_init) return false;
|
||||
|
||||
for (u32 i = 0, max = std::min(max_connect, u32(MAX_GAMEPADS)); i != max; ++i)
|
||||
{
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
auto & pad = m_pads.back();
|
||||
xinput_cfg.load();
|
||||
if (!xinput_cfg.exist()) xinput_cfg.save();
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_UP, CELL_PAD_CTRL_UP);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_DOWN, CELL_PAD_CTRL_DOWN);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_LEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_RIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_START, CELL_PAD_CTRL_START);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_BACK, CELL_PAD_CTRL_SELECT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_LEFT_THUMB, CELL_PAD_CTRL_L3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_RIGHT_THUMB, CELL_PAD_CTRL_R3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_LEFT_SHOULDER, CELL_PAD_CTRL_L1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_RIGHT_SHOULDER, CELL_PAD_CTRL_R1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_GUIDE, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_A, CELL_PAD_CTRL_CROSS);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_B, CELL_PAD_CTRL_CIRCLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_X, CELL_PAD_CTRL_SQUARE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_Y, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2);
|
||||
squircle_factor = xinput_cfg.padsquircling / 1000.f;
|
||||
left_stick_deadzone = xinput_cfg.lstickdeadzone;
|
||||
right_stick_deadzone = xinput_cfg.rstickdeadzone;
|
||||
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
pad.m_vibrateMotors.emplace_back(true, 0);
|
||||
pad.m_vibrateMotors.emplace_back(false, 0);
|
||||
}
|
||||
|
||||
xinput_cfg.load();
|
||||
if (!xinput_cfg.exist()) xinput_cfg.save();
|
||||
|
||||
squircle_factor = xinput_cfg.padsquircling / 1000.f;
|
||||
left_stick_deadzone = xinput_cfg.lstickdeadzone;
|
||||
right_stick_deadzone = xinput_cfg.rstickdeadzone;
|
||||
|
||||
active = true;
|
||||
thread = CreateThread(NULL, 0, &xinput_pad_handler::ThreadProcProxy, this, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void xinput_pad_handler::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) {
|
||||
if (pad > m_pads.size())
|
||||
return;
|
||||
|
||||
m_pads[pad].m_vibrateMotors[0].m_value = largeMotor;
|
||||
m_pads[pad].m_vibrateMotors[1].m_value = smallMotor ? 255 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void xinput_pad_handler::Close()
|
||||
{
|
||||
if (library)
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
active = false;
|
||||
if (WaitForSingleObject(thread, THREAD_TIMEOUT) != WAIT_OBJECT_0)
|
||||
LOG_ERROR(HLE, "XInput thread could not stop within %d milliseconds", (u32)THREAD_TIMEOUT);
|
||||
thread = nullptr;
|
||||
}
|
||||
|
||||
FreeLibrary(library);
|
||||
library = nullptr;
|
||||
xinputGetState = nullptr;
|
||||
xinputEnable = nullptr;
|
||||
}
|
||||
|
||||
m_pads.clear();
|
||||
}
|
||||
|
||||
std::tuple<u16, u16> xinput_pad_handler::ConvertToSquirclePoint(u16 inX, u16 inY)
|
||||
@ -169,131 +111,177 @@ std::tuple<u16, u16> xinput_pad_handler::ConvertToSquirclePoint(u16 inX, u16 inY
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / squircle_factor) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
// we now have len and angle, convert to cartisian
|
||||
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
|
||||
return std::tuple<u16, u16>(newX, newY);
|
||||
}
|
||||
|
||||
DWORD xinput_pad_handler::ThreadProcedure()
|
||||
void xinput_pad_handler::ThreadProc()
|
||||
{
|
||||
// holds internal controller state change
|
||||
std::array<bool, MAX_GAMEPADS> last_connection_status = {};
|
||||
|
||||
while (active)
|
||||
for (u32 index = 0; index != bindings.size(); index++)
|
||||
{
|
||||
XINPUT_STATE state;
|
||||
DWORD result;
|
||||
DWORD online = 0;
|
||||
auto padnum = bindings[index].first;
|
||||
auto pad = bindings[index].second;
|
||||
|
||||
for (DWORD i = 0; i != m_pads.size(); ++i)
|
||||
result = (*xinputGetState)(padnum, &state);
|
||||
switch (result)
|
||||
{
|
||||
auto & pad = m_pads[i];
|
||||
case ERROR_DEVICE_NOT_CONNECTED:
|
||||
if (last_connection_status[padnum] == true)
|
||||
pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[padnum] = false;
|
||||
pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
break;
|
||||
|
||||
result = (*xinputGetState)(i, &state);
|
||||
switch (result)
|
||||
case ERROR_SUCCESS:
|
||||
++online;
|
||||
if (last_connection_status[padnum] == false)
|
||||
pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[padnum] = true;
|
||||
pad->m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
|
||||
for (DWORD j = 0; j != XINPUT_GAMEPAD_BUTTONS; ++j)
|
||||
{
|
||||
case ERROR_DEVICE_NOT_CONNECTED:
|
||||
if (last_connection_status[i] == true)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = false;
|
||||
pad.m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
break;
|
||||
|
||||
case ERROR_SUCCESS:
|
||||
++online;
|
||||
if (last_connection_status[i] == false)
|
||||
pad.m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = true;
|
||||
pad.m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
|
||||
for (DWORD j = 0; j != XINPUT_GAMEPAD_BUTTONS; ++j)
|
||||
{
|
||||
bool pressed = state.Gamepad.wButtons & (1 << j);
|
||||
pad.m_buttons[j].m_pressed = pressed;
|
||||
pad.m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
|
||||
for (int i = 6; i < 16; i++)
|
||||
{
|
||||
if (pad.m_buttons[i].m_pressed)
|
||||
{
|
||||
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS].m_pressed = state.Gamepad.bLeftTrigger > 0;
|
||||
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS].m_value = state.Gamepad.bLeftTrigger;
|
||||
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0;
|
||||
pad.m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger;
|
||||
|
||||
float LX, LY, RX, RY;
|
||||
|
||||
LX = state.Gamepad.sThumbLX;
|
||||
LY = state.Gamepad.sThumbLY;
|
||||
RX = state.Gamepad.sThumbRX;
|
||||
RY = state.Gamepad.sThumbRY;
|
||||
|
||||
auto normalize_input = [](float& X, float& Y, float deadzone)
|
||||
{
|
||||
X /= 32767.0f;
|
||||
Y /= 32767.0f;
|
||||
deadzone /= 32767.0f;
|
||||
|
||||
float mag = sqrtf(X*X + Y*Y);
|
||||
|
||||
if (mag > deadzone)
|
||||
{
|
||||
float legalRange = 1.0f - deadzone;
|
||||
float normalizedMag = std::min(1.0f, (mag - deadzone) / legalRange);
|
||||
float scale = normalizedMag / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
}
|
||||
};
|
||||
|
||||
normalize_input(LX, LY, left_stick_deadzone);
|
||||
normalize_input(RX, RY, right_stick_deadzone);
|
||||
|
||||
pad.m_sticks[0].m_value = ConvertAxis(LX);
|
||||
pad.m_sticks[1].m_value = 255 - ConvertAxis(LY);
|
||||
pad.m_sticks[2].m_value = ConvertAxis(RX);
|
||||
pad.m_sticks[3].m_value = 255 - ConvertAxis(RY);
|
||||
|
||||
if (squircle_factor != 0.f)
|
||||
{
|
||||
std::tie(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value) = ConvertToSquirclePoint(pad.m_sticks[0].m_value, pad.m_sticks[1].m_value);
|
||||
std::tie(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value) = ConvertToSquirclePoint(pad.m_sticks[2].m_value, pad.m_sticks[3].m_value);
|
||||
}
|
||||
|
||||
XINPUT_VIBRATION vibrate;
|
||||
|
||||
vibrate.wLeftMotorSpeed = pad.m_vibrateMotors[0].m_value * 257;
|
||||
vibrate.wRightMotorSpeed = pad.m_vibrateMotors[1].m_value * 257;
|
||||
|
||||
(*xinputSetState)(i, &vibrate);
|
||||
|
||||
break;
|
||||
bool pressed = state.Gamepad.wButtons & (1 << j);
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
}
|
||||
|
||||
for (int i = 6; i < 16; i++)
|
||||
{
|
||||
if (pad->m_buttons[i].m_pressed)
|
||||
{
|
||||
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS].m_pressed = state.Gamepad.bLeftTrigger > 0;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS].m_value = state.Gamepad.bLeftTrigger;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger;
|
||||
|
||||
float LX, LY, RX, RY;
|
||||
|
||||
LX = state.Gamepad.sThumbLX;
|
||||
LY = state.Gamepad.sThumbLY;
|
||||
RX = state.Gamepad.sThumbRX;
|
||||
RY = state.Gamepad.sThumbRY;
|
||||
|
||||
auto normalize_input = [](float& X, float& Y, float deadzone)
|
||||
{
|
||||
X /= 32767.0f;
|
||||
Y /= 32767.0f;
|
||||
deadzone /= 32767.0f;
|
||||
|
||||
float mag = sqrtf(X*X + Y*Y);
|
||||
|
||||
if (mag > deadzone)
|
||||
{
|
||||
float legalRange = 1.0f - deadzone;
|
||||
float normalizedMag = std::min(1.0f, (mag - deadzone) / legalRange);
|
||||
float scale = normalizedMag / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
}
|
||||
};
|
||||
|
||||
normalize_input(LX, LY, left_stick_deadzone);
|
||||
normalize_input(RX, RY, right_stick_deadzone);
|
||||
|
||||
pad->m_sticks[0].m_value = ConvertAxis(LX);
|
||||
pad->m_sticks[1].m_value = 255 - ConvertAxis(LY);
|
||||
pad->m_sticks[2].m_value = ConvertAxis(RX);
|
||||
pad->m_sticks[3].m_value = 255 - ConvertAxis(RY);
|
||||
|
||||
if (squircle_factor != 0.f)
|
||||
{
|
||||
std::tie(pad->m_sticks[0].m_value, pad->m_sticks[1].m_value) = ConvertToSquirclePoint(pad->m_sticks[0].m_value, pad->m_sticks[1].m_value);
|
||||
std::tie(pad->m_sticks[2].m_value, pad->m_sticks[3].m_value) = ConvertToSquirclePoint(pad->m_sticks[2].m_value, pad->m_sticks[3].m_value);
|
||||
}
|
||||
|
||||
XINPUT_VIBRATION vibrate;
|
||||
|
||||
vibrate.wLeftMotorSpeed = pad->m_vibrateMotors[0].m_value * 257;
|
||||
vibrate.wRightMotorSpeed = pad->m_vibrateMotors[1].m_value * 257;
|
||||
|
||||
(*xinputSetState)(padnum, &vibrate);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_info.now_connect = online;
|
||||
Sleep((online > 0) ? THREAD_SLEEP : THREAD_SLEEP_INACTIVE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI xinput_pad_handler::ThreadProcProxy(LPVOID parameter)
|
||||
std::vector<std::string> xinput_pad_handler::ListDevices()
|
||||
{
|
||||
return reinterpret_cast<xinput_pad_handler *>(parameter)->ThreadProcedure();
|
||||
std::vector<std::string> xinput_pads_list;
|
||||
|
||||
if (!Init()) return xinput_pads_list;
|
||||
|
||||
for (DWORD i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
XINPUT_STATE state;
|
||||
DWORD result = (*xinputGetState)(i, &state);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
xinput_pads_list.push_back(fmt::format("Xinput Pad #%d", i));
|
||||
}
|
||||
}
|
||||
return xinput_pads_list;
|
||||
}
|
||||
|
||||
bool xinput_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
//Convert device string to u32 representing xinput device number
|
||||
u32 device_number = 0;
|
||||
size_t pos = device.find("Xinput Pad #");
|
||||
|
||||
if (pos != std::string::npos) device_number = std::stoul(device.substr(pos + 12));
|
||||
|
||||
if (pos == std::string::npos || device_number >= MAX_GAMEPADS) return false;
|
||||
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_UP, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_DOWN, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_LEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_RIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_START, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_BACK, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_LEFT_THUMB, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_RIGHT_THUMB, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_LEFT_SHOULDER, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_RIGHT_SHOULDER, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_GUIDE, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_A, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_B, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_X, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_Y, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
|
||||
bindings.emplace_back(device_number, pad);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <Xinput.h>
|
||||
|
||||
@ -41,10 +42,13 @@ public:
|
||||
xinput_pad_handler();
|
||||
~xinput_pad_handler();
|
||||
|
||||
void Init(const u32 max_connect) override;
|
||||
void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) override;
|
||||
bool Init() override;
|
||||
void Close();
|
||||
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
|
||||
private:
|
||||
typedef void (WINAPI * PFN_XINPUTENABLE)(BOOL);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE *);
|
||||
@ -52,19 +56,23 @@ private:
|
||||
|
||||
private:
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
|
||||
DWORD ThreadProcedure();
|
||||
static DWORD WINAPI ThreadProcProxy(LPVOID parameter);
|
||||
|
||||
private:
|
||||
mutable bool active;
|
||||
bool is_init;
|
||||
float squircle_factor;
|
||||
u32 left_stick_deadzone, right_stick_deadzone;
|
||||
HANDLE thread;
|
||||
HMODULE library;
|
||||
PFN_XINPUTGETSTATE xinputGetState;
|
||||
PFN_XINPUTSETSTATE xinputSetState;
|
||||
PFN_XINPUTENABLE xinputEnable;
|
||||
|
||||
std::vector<std::pair<u32, std::shared_ptr<Pad>>> bindings;
|
||||
std::array<bool, 7> last_connection_status = {};
|
||||
|
||||
// holds internal controller state change
|
||||
XINPUT_STATE state;
|
||||
DWORD result;
|
||||
DWORD online = 0;
|
||||
};
|
||||
|
||||
extern xinput_config xinput_cfg;
|
||||
|
Loading…
x
Reference in New Issue
Block a user