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:
RipleyTom 2017-08-15 14:03:07 +02:00 committed by Ani
parent a6ba7ed21c
commit 0457f23b13
28 changed files with 1776 additions and 1416 deletions

View File

@ -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;

View File

@ -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
{
}
};

View File

@ -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;
};

View File

@ -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;

View File

@ -1,5 +1,7 @@
#pragma once
#include <string>
namespace vfs
{
// VFS type

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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

View File

@ -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
View 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
View 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;
};

View File

@ -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" />

View File

@ -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">

View File

@ -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>

View 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]);
}

View 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;
};

View File

@ -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(); });

View File

@ -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">

View File

@ -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?

View File

@ -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);
};

View File

@ -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());

View File

@ -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>

View File

@ -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

View File

@ -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;