2021-02-11 22:13:21 +01:00
|
|
|
#include "hid_pad_handler.h"
|
2021-02-12 01:07:10 +01:00
|
|
|
#include "ds3_pad_handler.h"
|
2021-02-11 22:13:21 +01:00
|
|
|
#include "ds4_pad_handler.h"
|
2021-02-11 23:27:33 +01:00
|
|
|
#include "dualsense_pad_handler.h"
|
2023-08-26 15:47:52 +02:00
|
|
|
#include "skateboard_pad_handler.h"
|
2024-07-08 20:17:21 +02:00
|
|
|
#include "ps_move_handler.h"
|
2021-02-11 22:13:21 +01:00
|
|
|
#include "util/logs.hpp"
|
2022-01-27 23:57:39 +01:00
|
|
|
#include "Utilities/Timer.h"
|
|
|
|
|
#include "Emu/System.h"
|
|
|
|
|
#include "pad_thread.h"
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2022-08-11 07:14:37 +02:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
#include "3rdparty/hidapi/hidapi/mac/hidapi_darwin.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-02-11 22:13:21 +01:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
LOG_CHANNEL(hid_log, "HID");
|
|
|
|
|
|
2024-09-22 19:50:42 +02:00
|
|
|
struct hid_instance
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
hid_instance() = default;
|
|
|
|
|
~hid_instance()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_hid_mutex);
|
|
|
|
|
|
|
|
|
|
// Only exit HIDAPI once on exit. HIDAPI uses a global state internally...
|
|
|
|
|
if (m_initialized)
|
|
|
|
|
{
|
|
|
|
|
hid_log.notice("Exiting HIDAPI...");
|
|
|
|
|
|
|
|
|
|
if (hid_exit() != 0)
|
|
|
|
|
{
|
|
|
|
|
hid_log.error("hid_exit failed!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static hid_instance& get_instance()
|
|
|
|
|
{
|
|
|
|
|
static hid_instance instance {};
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool initialize()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_hid_mutex);
|
|
|
|
|
|
|
|
|
|
// Only init HIDAPI once. HIDAPI uses a global state internally...
|
|
|
|
|
if (m_initialized)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hid_log.notice("Initializing HIDAPI ...");
|
|
|
|
|
|
|
|
|
|
if (hid_init() != 0)
|
|
|
|
|
{
|
|
|
|
|
hid_log.fatal("hid_init error");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_initialized = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool m_initialized = false;
|
|
|
|
|
std::mutex m_hid_mutex;
|
|
|
|
|
};
|
2021-02-12 22:42:00 +01:00
|
|
|
|
2024-06-30 11:32:44 +02:00
|
|
|
void HidDevice::close()
|
|
|
|
|
{
|
|
|
|
|
if (hidDevice)
|
|
|
|
|
{
|
|
|
|
|
hid_close(hidDevice);
|
|
|
|
|
hidDevice = nullptr;
|
|
|
|
|
}
|
2024-07-08 20:17:21 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
if (bt_device)
|
|
|
|
|
{
|
|
|
|
|
hid_close(bt_device);
|
|
|
|
|
bt_device = nullptr;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-06-30 11:32:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-02-11 22:13:21 +01:00
|
|
|
template <class Device>
|
2024-06-11 22:30:16 +02:00
|
|
|
hid_pad_handler<Device>::hid_pad_handler(pad_handler type, std::vector<id_pair> ids)
|
|
|
|
|
: PadHandlerBase(type), m_ids(std::move(ids))
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
hid_pad_handler<Device>::~hid_pad_handler()
|
|
|
|
|
{
|
2022-01-27 23:57:39 +01:00
|
|
|
if (m_enumeration_thread)
|
|
|
|
|
{
|
|
|
|
|
auto& enumeration_thread = *m_enumeration_thread;
|
|
|
|
|
enumeration_thread = thread_state::aborting;
|
|
|
|
|
enumeration_thread();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 22:13:21 +01:00
|
|
|
for (auto& controller : m_controllers)
|
|
|
|
|
{
|
2024-06-30 11:32:44 +02:00
|
|
|
if (controller.second)
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2024-06-30 11:32:44 +02:00
|
|
|
controller.second->close();
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
bool hid_pad_handler<Device>::Init()
|
|
|
|
|
{
|
|
|
|
|
if (m_is_init)
|
|
|
|
|
return true;
|
|
|
|
|
|
2024-09-22 19:50:42 +02:00
|
|
|
if (!hid_instance::get_instance().initialize())
|
|
|
|
|
return false;
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2022-08-11 07:14:37 +02:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
hid_darwin_set_open_exclusive(0);
|
|
|
|
|
#endif
|
2023-02-13 11:33:06 +01:00
|
|
|
|
2021-05-19 21:14:49 +02:00
|
|
|
for (usz i = 1; i <= MAX_GAMEPADS; i++) // Controllers 1-n in GUI
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
|
|
|
|
m_controllers.emplace(m_name_string + std::to_string(i), std::make_shared<Device>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enumerate_devices();
|
2022-01-27 23:57:39 +01:00
|
|
|
update_devices();
|
2021-02-11 22:13:21 +01:00
|
|
|
|
|
|
|
|
m_is_init = true;
|
2022-01-27 23:57:39 +01:00
|
|
|
|
|
|
|
|
m_enumeration_thread = std::make_unique<named_thread<std::function<void()>>>(fmt::format("%s Enumerator", m_type), [this]()
|
|
|
|
|
{
|
|
|
|
|
while (thread_ctrl::state() != thread_state::aborting)
|
|
|
|
|
{
|
|
|
|
|
if (pad::g_enabled && Emu.IsRunning())
|
|
|
|
|
{
|
|
|
|
|
enumerate_devices();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_ctrl::wait_for(2'000'000);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2021-02-11 22:13:21 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
2022-10-21 22:48:38 +02:00
|
|
|
void hid_pad_handler<Device>::process()
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2022-01-27 23:57:39 +01:00
|
|
|
update_devices();
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2022-10-21 22:48:38 +02:00
|
|
|
PadHandlerBase::process();
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
2022-08-13 09:56:04 +02:00
|
|
|
std::vector<pad_list_entry> hid_pad_handler<Device>::list_devices()
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2022-08-13 09:56:04 +02:00
|
|
|
std::vector<pad_list_entry> pads_list;
|
2021-02-11 22:13:21 +01:00
|
|
|
|
|
|
|
|
if (!Init())
|
|
|
|
|
return pads_list;
|
|
|
|
|
|
|
|
|
|
for (const auto& controller : m_controllers) // Controllers 1-n in GUI
|
|
|
|
|
{
|
2022-08-13 09:56:04 +02:00
|
|
|
pads_list.emplace_back(controller.first, false);
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pads_list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
void hid_pad_handler<Device>::enumerate_devices()
|
|
|
|
|
{
|
2022-01-27 23:57:39 +01:00
|
|
|
Timer timer;
|
2021-02-11 22:13:21 +01:00
|
|
|
std::set<std::string> device_paths;
|
2023-03-10 14:05:08 +01:00
|
|
|
std::map<std::string, std::wstring> serials;
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2021-03-30 10:58:11 +02:00
|
|
|
for (const auto& [vid, pid] : m_ids)
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2021-03-30 10:58:11 +02:00
|
|
|
hid_device_info* dev_info = hid_enumerate(vid, pid);
|
2021-02-11 22:13:21 +01:00
|
|
|
hid_device_info* head = dev_info;
|
|
|
|
|
while (dev_info)
|
|
|
|
|
{
|
2023-03-04 18:37:14 +01:00
|
|
|
if (!dev_info->path)
|
|
|
|
|
{
|
|
|
|
|
hid_log.error("Skipping enumeration of device with empty path.");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-07-08 20:17:21 +02:00
|
|
|
|
|
|
|
|
const std::string path = dev_info->path;
|
|
|
|
|
device_paths.insert(path);
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
// Only add serials for col01 ps move device
|
|
|
|
|
if (m_type == pad_handler::move && path.find("&Col01#") != umax)
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
serials[path] = dev_info->serial_number ? std::wstring(dev_info->serial_number) : std::wstring();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev_info = dev_info->next;
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
hid_free_enumeration(head);
|
|
|
|
|
}
|
2022-01-27 23:57:39 +01:00
|
|
|
hid_log.notice("%s enumeration found %d devices (%f ms)", m_type, device_paths.size(), timer.GetElapsedTimeInMilliSec());
|
|
|
|
|
|
|
|
|
|
std::lock_guard lock(m_enumeration_mutex);
|
|
|
|
|
m_new_enumerated_devices = device_paths;
|
2023-03-10 14:05:08 +01:00
|
|
|
m_enumerated_serials = std::move(serials);
|
2024-07-08 20:17:21 +02:00
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
if (m_type == pad_handler::move)
|
|
|
|
|
{
|
|
|
|
|
// Windows enumerates 3 ps move devices: Col01, Col02, and Col03.
|
|
|
|
|
// We use Col01 for data and Col02 for bluetooth.
|
|
|
|
|
|
|
|
|
|
// Filter paths. We only want the Col01 paths.
|
|
|
|
|
std::set<std::string> col01_paths;
|
|
|
|
|
|
|
|
|
|
for (const std::string& path : m_new_enumerated_devices)
|
|
|
|
|
{
|
|
|
|
|
hid_log.trace("Found ps move device: %s", path);
|
|
|
|
|
|
|
|
|
|
if (path.find("&Col01#") != umax)
|
|
|
|
|
{
|
|
|
|
|
col01_paths.insert(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_new_enumerated_devices = std::move(col01_paths);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-01-27 23:57:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
void hid_pad_handler<Device>::update_devices()
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(m_enumeration_mutex);
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2022-01-27 23:57:39 +01:00
|
|
|
if (m_last_enumerated_devices == m_new_enumerated_devices)
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 23:57:39 +01:00
|
|
|
m_last_enumerated_devices = m_new_enumerated_devices;
|
2021-02-11 22:13:21 +01:00
|
|
|
|
|
|
|
|
// Scrap devices that are not in the new list
|
|
|
|
|
for (auto& controller : m_controllers)
|
|
|
|
|
{
|
2022-01-27 23:57:39 +01:00
|
|
|
if (controller.second && !controller.second->path.empty() && !m_new_enumerated_devices.contains(controller.second->path))
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2024-06-30 11:32:44 +02:00
|
|
|
controller.second->close();
|
2021-08-10 21:45:26 +02:00
|
|
|
cfg_pad* config = controller.second->config;
|
2021-02-11 22:13:21 +01:00
|
|
|
controller.second.reset(new Device());
|
|
|
|
|
controller.second->config = config;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool warn_about_drivers = false;
|
|
|
|
|
|
|
|
|
|
// Find and add new devices
|
2022-01-27 23:57:39 +01:00
|
|
|
for (const auto& path : m_new_enumerated_devices)
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2021-02-12 22:42:00 +01:00
|
|
|
// Check if we have at least one virtual controller left
|
|
|
|
|
if (std::none_of(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) { return !c.second || !c.second->hidDevice; }))
|
|
|
|
|
break;
|
|
|
|
|
|
2021-02-11 22:13:21 +01:00
|
|
|
// Check if we already have this controller
|
2021-02-12 22:42:00 +01:00
|
|
|
if (std::any_of(m_controllers.cbegin(), m_controllers.cend(), [&path](const auto& c) { return c.second && c.second->path == path; }))
|
|
|
|
|
continue;
|
2021-02-11 22:13:21 +01:00
|
|
|
|
2024-07-08 20:17:21 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
if (m_type == pad_handler::move)
|
|
|
|
|
{
|
|
|
|
|
check_add_device(nullptr, path, m_enumerated_serials[path]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (hid_device* dev = hid_open_path(path.c_str()))
|
2021-02-12 22:42:00 +01:00
|
|
|
{
|
2023-03-04 18:37:14 +01:00
|
|
|
if (const hid_device_info* info = hid_get_device_info(dev))
|
|
|
|
|
{
|
2023-03-05 20:38:01 +01:00
|
|
|
hid_log.notice("%s adding device: vid=0x%x, pid=0x%x, path='%s'", m_type, info->vendor_id, info->product_id, path);
|
2023-03-04 18:37:14 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-06-12 01:05:31 +02:00
|
|
|
hid_log.warning("%s adding device: vid=N/A, pid=N/A, path='%s', error='%s'", m_type, path, hid_error(dev));
|
2023-03-04 18:37:14 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-27 23:57:39 +01:00
|
|
|
check_add_device(dev, path, m_enumerated_serials[path]);
|
2021-02-12 22:42:00 +01:00
|
|
|
}
|
|
|
|
|
else
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2021-11-22 09:13:52 +01:00
|
|
|
hid_log.error("%s hid_open_path failed! error='%s', path='%s'", m_type, hid_error(dev), path);
|
2021-02-12 22:42:00 +01:00
|
|
|
warn_about_drivers = true;
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (warn_about_drivers)
|
|
|
|
|
{
|
2021-02-12 02:44:26 +01:00
|
|
|
hid_log.error("One or more %s pads were detected but couldn't be interacted with directly", m_type);
|
2021-02-11 22:13:21 +01:00
|
|
|
#if defined(_WIN32) || defined(__linux__)
|
2023-07-28 12:09:06 +02:00
|
|
|
hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for instructions on how to solve this issue");
|
2021-02-11 22:13:21 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-05-19 21:14:49 +02:00
|
|
|
const usz count = std::count_if(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) { return c.second && c.second->hidDevice; });
|
2021-02-11 22:13:21 +01:00
|
|
|
if (count > 0)
|
|
|
|
|
{
|
2021-02-12 02:44:26 +01:00
|
|
|
hid_log.success("%s Controllers found: %d", m_type, count);
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-02-12 02:44:26 +01:00
|
|
|
hid_log.warning("No %s controllers found!", m_type);
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
std::shared_ptr<Device> hid_pad_handler<Device>::get_hid_device(const std::string& padId)
|
|
|
|
|
{
|
|
|
|
|
if (!Init())
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
// Controllers 1-n in GUI
|
2022-06-05 15:58:05 +02:00
|
|
|
if (auto it = m_controllers.find(padId); it != m_controllers.end())
|
2021-02-11 22:13:21 +01:00
|
|
|
{
|
2022-06-05 15:58:05 +02:00
|
|
|
return it->second;
|
2021-02-11 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Device>
|
|
|
|
|
std::shared_ptr<PadDevice> hid_pad_handler<Device>::get_device(const std::string& device)
|
|
|
|
|
{
|
|
|
|
|
return get_hid_device(device);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-03 00:33:10 +01:00
|
|
|
template <class Device>
|
2022-10-21 22:50:08 +02:00
|
|
|
u32 hid_pad_handler<Device>::get_battery_color(u8 battery_level, u32 brightness)
|
2021-03-03 00:33:10 +01:00
|
|
|
{
|
2022-10-21 22:50:08 +02:00
|
|
|
static constexpr std::array<u32, 12> battery_level_clr = {0xff00, 0xff33, 0xff66, 0xff99, 0xffcc, 0xffff, 0xccff, 0x99ff, 0x66ff, 0x33ff, 0x00ff, 0x00ff};
|
2021-03-05 20:32:58 +01:00
|
|
|
|
|
|
|
|
const u32 combined_color = battery_level_clr[battery_level < battery_level_clr.size() ? battery_level : 0];
|
|
|
|
|
|
2021-03-03 00:33:10 +01:00
|
|
|
const u32 red = (combined_color >> 8) * brightness / 100;
|
|
|
|
|
const u32 green = (combined_color & 0xff) * brightness / 100;
|
|
|
|
|
return ((red << 8) | green);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-12 01:07:10 +01:00
|
|
|
template class hid_pad_handler<ds3_device>;
|
2021-02-11 22:13:21 +01:00
|
|
|
template class hid_pad_handler<DS4Device>;
|
2021-02-11 23:27:33 +01:00
|
|
|
template class hid_pad_handler<DualSenseDevice>;
|
2023-08-26 15:47:52 +02:00
|
|
|
template class hid_pad_handler<skateboard_device>;
|
2024-07-08 20:17:21 +02:00
|
|
|
template class hid_pad_handler<ps_move_device>;
|