mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-05 15:56:49 +00:00
DS4: enumerate devices periodically
This commit is contained in:
parent
b7c2bfbcde
commit
d0cc5c0fc7
@ -246,17 +246,12 @@ std::shared_ptr<ds4_pad_handler::DS4Device> ds4_pad_handler::GetDS4Device(const
|
||||
if (!Init())
|
||||
return nullptr;
|
||||
|
||||
usz pos = padId.find(m_name_string);
|
||||
if (pos == umax)
|
||||
return nullptr;
|
||||
|
||||
std::string pad_serial = padId.substr(pos + 9);
|
||||
std::shared_ptr<DS4Device> device = nullptr;
|
||||
|
||||
int i = 0; // Controllers 1-n in GUI
|
||||
// Controllers 1-n in GUI
|
||||
for (auto& cur_control : controllers)
|
||||
{
|
||||
if (pad_serial == std::to_string(++i) || pad_serial == cur_control.first)
|
||||
if (padId == cur_control.first)
|
||||
{
|
||||
device = cur_control.second;
|
||||
break;
|
||||
@ -524,11 +519,26 @@ bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr<DS4Device>& ds4De
|
||||
return true;
|
||||
}
|
||||
|
||||
void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo)
|
||||
void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view path, std::wstring_view wide_serial)
|
||||
{
|
||||
std::shared_ptr<DS4Device> ds4_dev;
|
||||
|
||||
for (auto& controller : controllers)
|
||||
{
|
||||
if (!controller.second || !controller.second->hidDevice)
|
||||
{
|
||||
ds4_dev = controller.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ds4_dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string serial;
|
||||
std::shared_ptr<DS4Device> ds4Dev = std::make_shared<DS4Device>();
|
||||
ds4Dev->hidDevice = hidDevice;
|
||||
ds4_dev->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{};
|
||||
@ -545,7 +555,7 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, hid_device_info* hid
|
||||
buf[1] = 0;
|
||||
if (hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x12_SIZE) == -1)
|
||||
{
|
||||
ds4_log.error("CheckAddDevice: hid_get_feature_report 0x12 failed! Reason: %s", hid_error(ds4Dev->hidDevice));
|
||||
ds4_log.error("CheckAddDevice: hid_get_feature_report 0x12 failed! Reason: %s", hid_error(hidDevice));
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,13 +563,12 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, hid_device_info* hid
|
||||
}
|
||||
else
|
||||
{
|
||||
ds4Dev->btCon = true;
|
||||
std::wstring_view wideSerial(hidDevInfo->serial_number);
|
||||
for (wchar_t ch : wideSerial)
|
||||
ds4_dev->btCon = true;
|
||||
for (wchar_t ch : wide_serial)
|
||||
serial += static_cast<uchar>(ch);
|
||||
}
|
||||
|
||||
if (!GetCalibrationData(ds4Dev))
|
||||
if (!GetCalibrationData(ds4_dev))
|
||||
{
|
||||
ds4_log.error("CheckAddDevice: GetCalibrationData failed!");
|
||||
hid_close(hidDevice);
|
||||
@ -573,17 +582,17 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, hid_device_info* hid
|
||||
return;
|
||||
}
|
||||
|
||||
ds4Dev->hasCalibData = true;
|
||||
ds4Dev->path = hidDevInfo->path;
|
||||
ds4_dev->hasCalibData = true;
|
||||
ds4_dev->path = path;
|
||||
|
||||
controllers.emplace(serial, ds4Dev);
|
||||
send_output_report(ds4_dev);
|
||||
}
|
||||
|
||||
ds4_pad_handler::~ds4_pad_handler()
|
||||
{
|
||||
for (auto& controller : controllers)
|
||||
{
|
||||
if (controller.second->hidDevice)
|
||||
if (controller.second && controller.second->hidDevice)
|
||||
{
|
||||
// Disable blinking and vibration
|
||||
controller.second->smallVibrate = 0;
|
||||
@ -603,7 +612,7 @@ ds4_pad_handler::~ds4_pad_handler()
|
||||
|
||||
int ds4_pad_handler::send_output_report(const std::shared_ptr<DS4Device>& device)
|
||||
{
|
||||
if (!device)
|
||||
if (!device || !device->hidDevice)
|
||||
return -2;
|
||||
|
||||
auto config = device->config;
|
||||
@ -656,39 +665,72 @@ int ds4_pad_handler::send_output_report(const std::shared_ptr<DS4Device>& device
|
||||
}
|
||||
}
|
||||
|
||||
bool ds4_pad_handler::Init()
|
||||
void ds4_pad_handler::enumerate_devices()
|
||||
{
|
||||
if (is_init)
|
||||
return true;
|
||||
std::set<std::string> device_paths;
|
||||
std::map<std::string, std::wstring_view> serials;
|
||||
|
||||
const int res = hid_init();
|
||||
if (res != 0)
|
||||
fmt::throw_exception("hidapi-init error.threadproc");
|
||||
|
||||
// get all the possible controllers at start
|
||||
bool warn_about_drivers = false;
|
||||
for (auto pid : ds4Pids)
|
||||
{
|
||||
hid_device_info* devInfo = hid_enumerate(DS4_VID, pid);
|
||||
hid_device_info* head = devInfo;
|
||||
while (devInfo)
|
||||
hid_device_info* dev_info = hid_enumerate(DS4_VID, pid);
|
||||
hid_device_info* head = dev_info;
|
||||
while (dev_info)
|
||||
{
|
||||
if (controllers.size() >= MAX_GAMEPADS)
|
||||
break;
|
||||
ensure(dev_info->path != nullptr);
|
||||
device_paths.insert(dev_info->path);
|
||||
serials[dev_info->path] = dev_info->serial_number ? std::wstring_view(dev_info->serial_number) : std::wstring_view{};
|
||||
dev_info = dev_info->next;
|
||||
}
|
||||
hid_free_enumeration(head);
|
||||
}
|
||||
|
||||
hid_device* dev = hid_open_path(devInfo->path);
|
||||
if (m_last_enumerated_devices == device_paths)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_last_enumerated_devices = device_paths;
|
||||
|
||||
// Scrap devices that are not in the new list
|
||||
for (auto it = controllers.begin(); it != controllers.end(); ++it)
|
||||
{
|
||||
if (it->second && !it->second->path.empty() && !device_paths.contains(it->second->path))
|
||||
{
|
||||
hid_close(it->second->hidDevice);
|
||||
pad_config* config = it->second->config;
|
||||
it->second.reset(new DS4Device());
|
||||
it->second->config = config;
|
||||
}
|
||||
}
|
||||
|
||||
bool warn_about_drivers = false;
|
||||
|
||||
// Find and add new devices
|
||||
for (const auto& path : device_paths)
|
||||
{
|
||||
// Check if we already have this controller
|
||||
const auto it_found = std::find_if(controllers.cbegin(), controllers.cend(), [path](const auto& c) { return c.second && c.second->path == path; });
|
||||
|
||||
if (it_found == controllers.cend())
|
||||
{
|
||||
// Check if we have at least one virtual controller left
|
||||
const auto it_free = std::find_if(controllers.cbegin(), controllers.cend(), [](const auto& c) { return !c.second || !c.second->hidDevice; });
|
||||
if (it_free == controllers.cend())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hid_device* dev = hid_open_path(path.c_str());
|
||||
if (dev)
|
||||
{
|
||||
CheckAddDevice(dev, devInfo);
|
||||
CheckAddDevice(dev, path, serials[path]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ds4_log.error("hid_open_path failed! Reason: %s", hid_error(dev));
|
||||
warn_about_drivers = true;
|
||||
}
|
||||
devInfo = devInfo->next;
|
||||
}
|
||||
hid_free_enumeration(head);
|
||||
}
|
||||
|
||||
if (warn_about_drivers)
|
||||
@ -698,19 +740,53 @@ bool ds4_pad_handler::Init()
|
||||
ds4_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for intructions on how to solve this issue");
|
||||
#endif
|
||||
}
|
||||
else if (controllers.empty())
|
||||
{
|
||||
ds4_log.warning("No controllers found!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ds4_log.success("Controllers found: %d", controllers.size());
|
||||
const size_t count = std::count_if(controllers.cbegin(), controllers.cend(), [](const auto& c) { return c.second && c.second->hidDevice; });
|
||||
if (count > 0)
|
||||
{
|
||||
ds4_log.success("Controllers found: %d", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
ds4_log.warning("No controllers found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ds4_pad_handler::Init()
|
||||
{
|
||||
if (is_init)
|
||||
return true;
|
||||
|
||||
const int res = hid_init();
|
||||
if (res != 0)
|
||||
fmt::throw_exception("hidapi-init error.threadproc");
|
||||
|
||||
for (size_t i = 1; i <= MAX_GAMEPADS; i++) // Controllers 1-n in GUI
|
||||
{
|
||||
controllers.emplace(m_name_string + std::to_string(i), std::make_shared<DS4Device>());
|
||||
}
|
||||
|
||||
enumerate_devices();
|
||||
|
||||
is_init = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ds4_pad_handler::ThreadProc()
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_enumeration).count();
|
||||
if (elapsed > 2000)
|
||||
{
|
||||
m_last_enumeration = now;
|
||||
enumerate_devices();
|
||||
}
|
||||
|
||||
PadHandlerBase::ThreadProc();
|
||||
}
|
||||
|
||||
std::vector<std::string> ds4_pad_handler::ListDevices()
|
||||
{
|
||||
std::vector<std::string> ds4_pads_list;
|
||||
@ -718,9 +794,9 @@ std::vector<std::string> ds4_pad_handler::ListDevices()
|
||||
if (!Init())
|
||||
return ds4_pads_list;
|
||||
|
||||
for (usz i = 1; i <= controllers.size(); ++i) // Controllers 1-n in GUI
|
||||
for (const auto& controller : controllers) // Controllers 1-n in GUI
|
||||
{
|
||||
ds4_pads_list.emplace_back(m_name_string + std::to_string(i));
|
||||
ds4_pads_list.emplace_back(controller.first);
|
||||
}
|
||||
|
||||
return ds4_pads_list;
|
||||
@ -809,7 +885,7 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr
|
||||
std::shared_ptr<PadDevice> ds4_pad_handler::get_device(const std::string& device)
|
||||
{
|
||||
std::shared_ptr<DS4Device> ds4device = GetDS4Device(device);
|
||||
if (ds4device == nullptr || ds4device->hidDevice == nullptr)
|
||||
if (ds4device == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return ds4device;
|
||||
@ -870,7 +946,7 @@ u32 ds4_pad_handler::get_battery_color(u8 battery_level, int brightness)
|
||||
PadHandlerBase::connection ds4_pad_handler::update_connection(const std::shared_ptr<PadDevice>& device)
|
||||
{
|
||||
auto ds4_dev = std::static_pointer_cast<DS4Device>(device);
|
||||
if (!ds4_dev)
|
||||
if (!ds4_dev || ds4_dev->path.empty())
|
||||
return connection::disconnected;
|
||||
|
||||
if (ds4_dev->hidDevice == nullptr)
|
||||
|
@ -99,7 +99,7 @@ class ds4_pad_handler final : public PadHandlerBase
|
||||
const std::array<u16, 3> ds4Pids = { { 0xBA0, 0x5C4, 0x09CC } };
|
||||
|
||||
// pseudo 'controller id' to keep track of unique controllers
|
||||
std::unordered_map<std::string, std::shared_ptr<DS4Device>> controllers;
|
||||
std::map<std::string, std::shared_ptr<DS4Device>> controllers;
|
||||
CRCPP::CRC::Table<u32, 32> crcTable{ CRCPP::CRC::CRC_32() };
|
||||
|
||||
public:
|
||||
@ -107,6 +107,7 @@ public:
|
||||
~ds4_pad_handler();
|
||||
|
||||
bool Init() override;
|
||||
void ThreadProc() override;
|
||||
|
||||
std::vector<std::string> ListDevices() override;
|
||||
void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) override;
|
||||
@ -116,6 +117,9 @@ public:
|
||||
private:
|
||||
bool is_init = false;
|
||||
DS4DataStatus status;
|
||||
std::chrono::system_clock::time_point m_last_enumeration;
|
||||
std::set<std::string> m_last_enumerated_devices;
|
||||
void enumerate_devices();
|
||||
u32 get_battery_color(u8 battery_level, int brightness);
|
||||
|
||||
private:
|
||||
@ -124,7 +128,7 @@ private:
|
||||
DS4DataStatus GetRawData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
// This function gets us usuable buffer from the rawbuffer of padData
|
||||
bool GetCalibrationData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
void CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo);
|
||||
void CheckAddDevice(hid_device* hidDevice, std::string_view path, std::wstring_view serial);
|
||||
int send_output_report(const std::shared_ptr<DS4Device>& device);
|
||||
inline s16 ApplyCalibration(s32 rawValue, const DS4CalibData& calibData)
|
||||
{
|
||||
|
@ -104,7 +104,7 @@ void pad_thread::Init()
|
||||
const bool is_ldd_pad = pad_settings[i].ldd_handle == static_cast<s32>(i);
|
||||
const auto handler_type = is_ldd_pad ? pad_handler::null : g_cfg_input.player[i]->handler.get();
|
||||
|
||||
if (handlers.count(handler_type) != 0)
|
||||
if (handlers.contains(handler_type))
|
||||
{
|
||||
cur_pad_handler = handlers[handler_type];
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||
connect(ui->chooseHandler, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeInputType);
|
||||
|
||||
// Combobox: Devices
|
||||
connect(ui->chooseDevice, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index)
|
||||
connect(ui->chooseDevice, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
@ -119,7 +119,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||
});
|
||||
|
||||
// Combobox: Profiles
|
||||
connect(ui->chooseProfile, &QComboBox::currentTextChanged, [this](const QString& prof)
|
||||
connect(ui->chooseProfile, &QComboBox::currentTextChanged, this, [this](const QString& prof)
|
||||
{
|
||||
if (prof.isEmpty())
|
||||
{
|
||||
@ -136,7 +136,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||
});
|
||||
|
||||
// Pushbutton: Add Profile
|
||||
connect(ui->b_addProfile, &QAbstractButton::clicked, [this]()
|
||||
connect(ui->b_addProfile, &QAbstractButton::clicked, this, [this]()
|
||||
{
|
||||
const int i = ui->tabWidget->currentIndex();
|
||||
|
||||
@ -175,7 +175,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Filter Noise"));
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button)
|
||||
{
|
||||
if (button == ui->buttonBox->button(QDialogButtonBox::Save))
|
||||
{
|
||||
@ -304,7 +304,7 @@ void pad_settings_dialog::InitButtons()
|
||||
|
||||
connect(m_pad_buttons, &QButtonGroup::idClicked, this, &pad_settings_dialog::OnPadButtonClicked);
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, [this]()
|
||||
connect(&m_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
if (--m_seconds <= 0)
|
||||
{
|
||||
@ -314,7 +314,7 @@ void pad_settings_dialog::InitButtons()
|
||||
m_pad_buttons->button(m_button_id)->setText(tr("[ Waiting %1 ]").arg(m_seconds));
|
||||
});
|
||||
|
||||
connect(ui->chb_vibration_large, &QCheckBox::clicked, [this](bool checked)
|
||||
connect(ui->chb_vibration_large, &QCheckBox::clicked, this, [this](bool checked)
|
||||
{
|
||||
if (!checked)
|
||||
{
|
||||
@ -330,7 +330,7 @@ void pad_settings_dialog::InitButtons()
|
||||
});
|
||||
});
|
||||
|
||||
connect(ui->chb_vibration_small, &QCheckBox::clicked, [this](bool checked)
|
||||
connect(ui->chb_vibration_small, &QCheckBox::clicked, this, [this](bool checked)
|
||||
{
|
||||
if (!checked)
|
||||
{
|
||||
@ -346,7 +346,7 @@ void pad_settings_dialog::InitButtons()
|
||||
});
|
||||
});
|
||||
|
||||
connect(ui->chb_vibration_switch, &QCheckBox::clicked, [this](bool checked)
|
||||
connect(ui->chb_vibration_switch, &QCheckBox::clicked, this, [this](bool checked)
|
||||
{
|
||||
checked ? SetPadData(m_min_force, m_max_force)
|
||||
: SetPadData(m_max_force, m_min_force);
|
||||
@ -363,18 +363,18 @@ void pad_settings_dialog::InitButtons()
|
||||
});
|
||||
});
|
||||
|
||||
connect(ui->slider_stick_left, &QSlider::valueChanged, [&](int value)
|
||||
connect(ui->slider_stick_left, &QSlider::valueChanged, this, [&](int value)
|
||||
{
|
||||
RepaintPreviewLabel(ui->preview_stick_left, value, ui->slider_stick_left->size().width(), m_lx, m_ly, ui->squircle_left->value(), ui->stick_multi_left->value());
|
||||
});
|
||||
|
||||
connect(ui->slider_stick_right, &QSlider::valueChanged, [&](int value)
|
||||
connect(ui->slider_stick_right, &QSlider::valueChanged, this, [&](int value)
|
||||
{
|
||||
RepaintPreviewLabel(ui->preview_stick_right, value, ui->slider_stick_right->size().width(), m_rx, m_ry, ui->squircle_right->value(), ui->stick_multi_right->value());
|
||||
});
|
||||
|
||||
// Open LED settings
|
||||
connect(ui->b_led_settings, &QPushButton::clicked, [this]()
|
||||
connect(ui->b_led_settings, &QPushButton::clicked, this, [this]()
|
||||
{
|
||||
// Allow LED battery indication while the dialog is open
|
||||
m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, m_handler_cfg.led_battery_indicator.get(), m_handler_cfg.led_battery_indicator_brightness);
|
||||
@ -462,7 +462,7 @@ void pad_settings_dialog::InitButtons()
|
||||
};
|
||||
|
||||
// Use timer to get button input
|
||||
connect(&m_timer_input, &QTimer::timeout, [this, callback, fail_callback]()
|
||||
connect(&m_timer_input, &QTimer::timeout, this, [this, callback, fail_callback]()
|
||||
{
|
||||
const std::vector<std::string> buttons =
|
||||
{
|
||||
@ -475,7 +475,7 @@ void pad_settings_dialog::InitButtons()
|
||||
});
|
||||
|
||||
// Use timer to refresh pad connection status
|
||||
connect(&m_timer_pad_refresh, &QTimer::timeout, [this]()
|
||||
connect(&m_timer_pad_refresh, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
for (int i = 0; i < ui->chooseDevice->count(); i++)
|
||||
{
|
||||
@ -1193,6 +1193,7 @@ void pad_settings_dialog::ChangeInputType()
|
||||
|
||||
// Get this player's current handler and it's currently available devices
|
||||
m_handler = GetHandler(g_cfg_input.player[player]->handler);
|
||||
ensure(m_handler);
|
||||
const auto device_list = m_handler->ListDevices();
|
||||
|
||||
// Localized tooltips
|
||||
|
Loading…
Reference in New Issue
Block a user