Merge pull request #11193 from jordan-woyak/ciface-input-backend-interface
ControllerInterface: Add InputBackend interface.
This commit is contained in:
commit
1d07332657
|
@ -487,6 +487,7 @@
|
||||||
<ClInclude Include="InputCommon\ControllerEmu\StickGate.h" />
|
<ClInclude Include="InputCommon\ControllerEmu\StickGate.h" />
|
||||||
<ClInclude Include="InputCommon\ControllerInterface\ControllerInterface.h" />
|
<ClInclude Include="InputCommon\ControllerInterface\ControllerInterface.h" />
|
||||||
<ClInclude Include="InputCommon\ControllerInterface\CoreDevice.h" />
|
<ClInclude Include="InputCommon\ControllerInterface\CoreDevice.h" />
|
||||||
|
<ClInclude Include="InputCommon\ControllerInterface\InputBackend.h" />
|
||||||
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInput.h" />
|
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInput.h" />
|
||||||
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInput8.h" />
|
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInput8.h" />
|
||||||
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInputJoystick.h" />
|
<ClInclude Include="InputCommon\ControllerInterface\DInput\DInputJoystick.h" />
|
||||||
|
@ -1111,6 +1112,7 @@
|
||||||
<ClCompile Include="InputCommon\ControllerEmu\StickGate.cpp" />
|
<ClCompile Include="InputCommon\ControllerEmu\StickGate.cpp" />
|
||||||
<ClCompile Include="InputCommon\ControllerInterface\ControllerInterface.cpp" />
|
<ClCompile Include="InputCommon\ControllerInterface\ControllerInterface.cpp" />
|
||||||
<ClCompile Include="InputCommon\ControllerInterface\CoreDevice.cpp" />
|
<ClCompile Include="InputCommon\ControllerInterface\CoreDevice.cpp" />
|
||||||
|
<ClCompile Include="InputCommon\ControllerInterface\InputBackend.cpp" />
|
||||||
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInput.cpp" />
|
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInput.cpp" />
|
||||||
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInputJoystick.cpp" />
|
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInputJoystick.cpp" />
|
||||||
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
<ClCompile Include="InputCommon\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
||||||
|
|
|
@ -56,6 +56,8 @@ add_library(inputcommon
|
||||||
ControllerInterface/ControllerInterface.h
|
ControllerInterface/ControllerInterface.h
|
||||||
ControllerInterface/CoreDevice.cpp
|
ControllerInterface/CoreDevice.cpp
|
||||||
ControllerInterface/CoreDevice.h
|
ControllerInterface/CoreDevice.h
|
||||||
|
ControllerInterface/InputBackend.cpp
|
||||||
|
ControllerInterface/InputBackend.h
|
||||||
ControllerInterface/MappingCommon.cpp
|
ControllerInterface/MappingCommon.cpp
|
||||||
ControllerInterface/MappingCommon.h
|
ControllerInterface/MappingCommon.h
|
||||||
ControllerInterface/Wiimote/WiimoteController.cpp
|
ControllerInterface/Wiimote/WiimoteController.cpp
|
||||||
|
|
|
@ -64,19 +64,19 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
||||||
// nothing needed for OSX and Quartz
|
// nothing needed for OSX and Quartz
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_SDL
|
#ifdef CIFACE_USE_SDL
|
||||||
ciface::SDL::Init();
|
m_input_backends.emplace_back(ciface::SDL::CreateInputBackend(this));
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_ANDROID
|
#ifdef CIFACE_USE_ANDROID
|
||||||
// nothing needed
|
// nothing needed
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_EVDEV
|
#ifdef CIFACE_USE_EVDEV
|
||||||
ciface::evdev::Init();
|
m_input_backends.emplace_back(ciface::evdev::CreateInputBackend(this));
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_PIPES
|
#ifdef CIFACE_USE_PIPES
|
||||||
// nothing needed
|
// nothing needed
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||||
ciface::DualShockUDPClient::Init();
|
m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
|
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
|
||||||
|
@ -181,21 +181,15 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
|
||||||
ciface::Quartz::PopulateDevices(m_wsi.render_window);
|
ciface::Quartz::PopulateDevices(m_wsi.render_window);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_SDL
|
|
||||||
ciface::SDL::PopulateDevices();
|
|
||||||
#endif
|
|
||||||
#ifdef CIFACE_USE_ANDROID
|
#ifdef CIFACE_USE_ANDROID
|
||||||
ciface::Android::PopulateDevices();
|
ciface::Android::PopulateDevices();
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_EVDEV
|
|
||||||
ciface::evdev::PopulateDevices();
|
|
||||||
#endif
|
|
||||||
#ifdef CIFACE_USE_PIPES
|
#ifdef CIFACE_USE_PIPES
|
||||||
ciface::Pipes::PopulateDevices();
|
ciface::Pipes::PopulateDevices();
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
|
||||||
ciface::DualShockUDPClient::PopulateDevices();
|
for (auto& backend : m_input_backends)
|
||||||
#endif
|
backend->PopulateDevices();
|
||||||
|
|
||||||
WiimoteReal::PopulateDevices();
|
WiimoteReal::PopulateDevices();
|
||||||
|
|
||||||
|
@ -242,18 +236,12 @@ void ControllerInterface::Shutdown()
|
||||||
ciface::OSX::DeInit();
|
ciface::OSX::DeInit();
|
||||||
ciface::Quartz::DeInit();
|
ciface::Quartz::DeInit();
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_SDL
|
|
||||||
ciface::SDL::DeInit();
|
|
||||||
#endif
|
|
||||||
#ifdef CIFACE_USE_ANDROID
|
#ifdef CIFACE_USE_ANDROID
|
||||||
// nothing needed
|
// nothing needed
|
||||||
#endif
|
#endif
|
||||||
#ifdef CIFACE_USE_EVDEV
|
|
||||||
ciface::evdev::Shutdown();
|
// Empty the container of input backends to deconstruct and deinitialize them.
|
||||||
#endif
|
m_input_backends.clear();
|
||||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
|
||||||
ciface::DualShockUDPClient::DeInit();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Make sure no devices had been added within Shutdown() in the time
|
// Make sure no devices had been added within Shutdown() in the time
|
||||||
// between checking they checked atomic m_is_init bool and we changed it.
|
// between checking they checked atomic m_is_init bool and we changed it.
|
||||||
|
@ -384,15 +372,19 @@ void ControllerInterface::UpdateInput()
|
||||||
|
|
||||||
// TODO: if we are an emulation input channel, we should probably always lock
|
// TODO: if we are an emulation input channel, we should probably always lock
|
||||||
// Prefer outdated values over blocking UI or CPU thread (avoids short but noticeable frame drop)
|
// Prefer outdated values over blocking UI or CPU thread (avoids short but noticeable frame drop)
|
||||||
if (m_devices_mutex.try_lock())
|
if (!m_devices_mutex.try_lock())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard lk(m_devices_mutex, std::adopt_lock);
|
||||||
|
|
||||||
|
for (auto& backend : m_input_backends)
|
||||||
|
backend->UpdateInput();
|
||||||
|
|
||||||
|
for (const auto& d : m_devices)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(m_devices_mutex, std::adopt_lock);
|
// Theoretically we could avoid updating input on devices that don't have any references to
|
||||||
for (const auto& d : m_devices)
|
// them, but in practice a few devices types could break in different ways, so we don't
|
||||||
{
|
d->UpdateInput();
|
||||||
// Theoretically we could avoid updating input on devices that don't have any references to
|
|
||||||
// them, but in practice a few devices types could break in different ways, so we don't
|
|
||||||
d->UpdateInput();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "Common/Matrix.h"
|
#include "Common/Matrix.h"
|
||||||
#include "Common/WindowSystemInfo.h"
|
#include "Common/WindowSystemInfo.h"
|
||||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||||
|
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||||
|
|
||||||
// enable disable sources
|
// enable disable sources
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -133,6 +134,8 @@ private:
|
||||||
WindowSystemInfo m_wsi;
|
WindowSystemInfo m_wsi;
|
||||||
std::atomic<float> m_aspect_ratio_adjustment = 1;
|
std::atomic<float> m_aspect_ratio_adjustment = 1;
|
||||||
std::atomic<bool> m_requested_mouse_centering = false;
|
std::atomic<bool> m_requested_mouse_centering = false;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<ciface::InputBackend>> m_input_backends;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ciface
|
namespace ciface
|
||||||
|
|
|
@ -130,7 +130,7 @@ private:
|
||||||
public:
|
public:
|
||||||
void UpdateInput() override;
|
void UpdateInput() override;
|
||||||
|
|
||||||
Device(std::string name, int index, std::string server_address, u16 server_port);
|
Device(std::string name, int index, std::string server_address, u16 server_port, u32 client_uid);
|
||||||
|
|
||||||
std::string GetName() const final override;
|
std::string GetName() const final override;
|
||||||
std::string GetSource() const final override;
|
std::string GetSource() const final override;
|
||||||
|
@ -150,13 +150,15 @@ private:
|
||||||
bool m_prev_touch_valid = false;
|
bool m_prev_touch_valid = false;
|
||||||
int m_touch_x = 0;
|
int m_touch_x = 0;
|
||||||
int m_touch_y = 0;
|
int m_touch_y = 0;
|
||||||
std::string m_server_address;
|
const std::string m_server_address;
|
||||||
u16 m_server_port;
|
const u16 m_server_port;
|
||||||
|
|
||||||
s16 m_touch_x_min;
|
s16 m_touch_x_min;
|
||||||
s16 m_touch_y_min;
|
s16 m_touch_y_min;
|
||||||
s16 m_touch_x_max;
|
s16 m_touch_x_max;
|
||||||
s16 m_touch_y_max;
|
s16 m_touch_y_max;
|
||||||
|
|
||||||
|
const u32 m_client_uid;
|
||||||
};
|
};
|
||||||
|
|
||||||
using MathUtil::GRAVITY_ACCELERATION;
|
using MathUtil::GRAVITY_ACCELERATION;
|
||||||
|
@ -196,13 +198,34 @@ struct Server
|
||||||
SteadyClock::time_point m_disconnect_time = SteadyClock::now();
|
SteadyClock::time_point m_disconnect_time = SteadyClock::now();
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool s_has_init;
|
class InputBackend final : public ciface::InputBackend
|
||||||
static bool s_servers_enabled;
|
{
|
||||||
static std::vector<Server> s_servers;
|
public:
|
||||||
static u32 s_client_uid;
|
InputBackend(ControllerInterface* controller_interface);
|
||||||
static SteadyClock::time_point s_next_listports_time;
|
~InputBackend();
|
||||||
static std::thread s_hotplug_thread;
|
void PopulateDevices() override;
|
||||||
static Common::Flag s_hotplug_thread_running;
|
|
||||||
|
private:
|
||||||
|
void ConfigChanged();
|
||||||
|
void Restart();
|
||||||
|
|
||||||
|
void HotplugThreadFunc();
|
||||||
|
void StartHotplugThread();
|
||||||
|
void StopHotplugThread();
|
||||||
|
|
||||||
|
bool m_servers_enabled;
|
||||||
|
std::vector<Server> m_servers;
|
||||||
|
u32 m_client_uid;
|
||||||
|
SteadyClock::time_point m_next_listports_time;
|
||||||
|
std::thread m_hotplug_thread;
|
||||||
|
Common::Flag m_hotplug_thread_running;
|
||||||
|
std::size_t m_config_change_callback_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||||
|
{
|
||||||
|
return std::make_unique<InputBackend>(controller_interface);
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsSameController(const Proto::MessageType::PortInfo& a,
|
static bool IsSameController(const Proto::MessageType::PortInfo& a,
|
||||||
const Proto::MessageType::PortInfo& b)
|
const Proto::MessageType::PortInfo& b)
|
||||||
|
@ -212,29 +235,29 @@ static bool IsSameController(const Proto::MessageType::PortInfo& a,
|
||||||
std::tie(b.pad_id, b.pad_state, b.model, b.connection_type, b.pad_mac_address);
|
std::tie(b.pad_id, b.pad_state, b.model, b.connection_type, b.pad_mac_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HotplugThreadFunc()
|
void InputBackend::HotplugThreadFunc()
|
||||||
{
|
{
|
||||||
Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread");
|
Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread");
|
||||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread started");
|
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread started");
|
||||||
Common::ScopeGuard thread_stop_guard{
|
Common::ScopeGuard thread_stop_guard{
|
||||||
[] { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread stopped"); }};
|
[] { INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient hotplug thread stopped"); }};
|
||||||
|
|
||||||
std::vector<bool> timed_out_servers(s_servers.size(), false);
|
std::vector<bool> timed_out_servers(m_servers.size(), false);
|
||||||
|
|
||||||
while (s_hotplug_thread_running.IsSet())
|
while (m_hotplug_thread_running.IsSet())
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
const auto now = SteadyClock::now();
|
const auto now = SteadyClock::now();
|
||||||
if (now >= s_next_listports_time)
|
if (now >= m_next_listports_time)
|
||||||
{
|
{
|
||||||
s_next_listports_time = now + SERVER_LISTPORTS_INTERVAL;
|
m_next_listports_time = now + SERVER_LISTPORTS_INTERVAL;
|
||||||
|
|
||||||
for (size_t i = 0; i < s_servers.size(); ++i)
|
for (size_t i = 0; i < m_servers.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& server = s_servers[i];
|
auto& server = m_servers[i];
|
||||||
Proto::Message<Proto::MessageType::ListPorts> msg(s_client_uid);
|
Proto::Message<Proto::MessageType::ListPorts> msg(m_client_uid);
|
||||||
auto& list_ports = msg.m_message;
|
auto& list_ports = msg.m_message;
|
||||||
// We ask for x possible devices. We will receive a message for every connected device.
|
// We ask for x possible devices. We will receive a message for every connected device.
|
||||||
list_ports.pad_request_count = SERVER_ASKED_PADS;
|
list_ports.pad_request_count = SERVER_ASKED_PADS;
|
||||||
|
@ -250,12 +273,12 @@ static void HotplugThreadFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::SocketSelector selector;
|
sf::SocketSelector selector;
|
||||||
for (auto& server : s_servers)
|
for (auto& server : m_servers)
|
||||||
{
|
{
|
||||||
selector.add(server.m_socket);
|
selector.add(server.m_socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto timeout = duration_cast<milliseconds>(s_next_listports_time - SteadyClock::now());
|
auto timeout = duration_cast<milliseconds>(m_next_listports_time - SteadyClock::now());
|
||||||
|
|
||||||
// Receive controller port info within a time from our request.
|
// Receive controller port info within a time from our request.
|
||||||
// Run this even if we sent no new requests, to disconnect devices,
|
// Run this even if we sent no new requests, to disconnect devices,
|
||||||
|
@ -271,9 +294,9 @@ static void HotplugThreadFunc()
|
||||||
if (selector.wait(sf::milliseconds(current_timeout.count())))
|
if (selector.wait(sf::milliseconds(current_timeout.count())))
|
||||||
{
|
{
|
||||||
// Now check all the servers because we don't know which one(s) sent a reply
|
// Now check all the servers because we don't know which one(s) sent a reply
|
||||||
for (size_t i = 0; i < s_servers.size(); ++i)
|
for (size_t i = 0; i < m_servers.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& server = s_servers[i];
|
auto& server = m_servers[i];
|
||||||
if (!selector.isReady(server.m_socket))
|
if (!selector.isReady(server.m_socket))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -302,20 +325,20 @@ static void HotplugThreadFunc()
|
||||||
{
|
{
|
||||||
server.m_port_info[port_info->pad_id] = *port_info;
|
server.m_port_info[port_info->pad_id] = *port_info;
|
||||||
// Just remove and re-add all the devices for simplicity
|
// Just remove and re-add all the devices for simplicity
|
||||||
g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
|
GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!s_hotplug_thread_running.IsSet()) // Avoid hanging the thread for too long
|
if (!m_hotplug_thread_running.IsSet()) // Avoid hanging the thread for too long
|
||||||
return;
|
return;
|
||||||
} while (timeout > 0ms);
|
} while (timeout > 0ms);
|
||||||
|
|
||||||
// If we have failed to receive any information from the server (or even send it),
|
// If we have failed to receive any information from the server (or even send it),
|
||||||
// disconnect all devices from it (after enough time has elapsed, to avoid false positives).
|
// disconnect all devices from it (after enough time has elapsed, to avoid false positives).
|
||||||
for (size_t i = 0; i < s_servers.size(); ++i)
|
for (size_t i = 0; i < m_servers.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& server = s_servers[i];
|
auto& server = m_servers[i];
|
||||||
if (timed_out_servers[i] && SteadyClock::now() >= server.m_disconnect_time)
|
if (timed_out_servers[i] && SteadyClock::now() >= server.m_disconnect_time)
|
||||||
{
|
{
|
||||||
bool any_connected = false;
|
bool any_connected = false;
|
||||||
|
@ -328,49 +351,49 @@ static void HotplugThreadFunc()
|
||||||
}
|
}
|
||||||
// We can't only remove devices added by this server as we wouldn't know which they are
|
// We can't only remove devices added by this server as we wouldn't know which they are
|
||||||
if (any_connected)
|
if (any_connected)
|
||||||
g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
|
GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StartHotplugThread()
|
void InputBackend::StartHotplugThread()
|
||||||
{
|
{
|
||||||
// Mark the thread as running.
|
// Mark the thread as running.
|
||||||
if (!s_hotplug_thread_running.TestAndSet())
|
if (!m_hotplug_thread_running.TestAndSet())
|
||||||
{
|
{
|
||||||
// It was already running.
|
// It was already running.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_hotplug_thread = std::thread(HotplugThreadFunc);
|
m_hotplug_thread = std::thread(&InputBackend::HotplugThreadFunc, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StopHotplugThread()
|
void InputBackend::StopHotplugThread()
|
||||||
{
|
{
|
||||||
// Tell the hotplug thread to stop.
|
// Tell the hotplug thread to stop.
|
||||||
if (!s_hotplug_thread_running.TestAndClear())
|
if (!m_hotplug_thread_running.TestAndClear())
|
||||||
{
|
{
|
||||||
// It wasn't running, we're done.
|
// It wasn't running, we're done.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_hotplug_thread.join();
|
m_hotplug_thread.join();
|
||||||
|
|
||||||
for (auto& server : s_servers)
|
for (auto& server : m_servers)
|
||||||
{
|
{
|
||||||
server.m_socket.unbind(); // interrupt blocking socket
|
server.m_socket.unbind(); // interrupt blocking socket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also just start
|
// Also just start
|
||||||
static void Restart()
|
void InputBackend::Restart()
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient Restart");
|
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient Restart");
|
||||||
|
|
||||||
StopHotplugThread();
|
StopHotplugThread();
|
||||||
|
|
||||||
for (auto& server : s_servers)
|
for (auto& server : m_servers)
|
||||||
{
|
{
|
||||||
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
|
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
|
||||||
{
|
{
|
||||||
|
@ -380,37 +403,34 @@ static void Restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only removes devices as servers have been cleaned
|
// Only removes devices as servers have been cleaned
|
||||||
g_controller_interface.PlatformPopulateDevices([] { PopulateDevices(); });
|
GetControllerInterface().PlatformPopulateDevices([this] { PopulateDevices(); });
|
||||||
|
|
||||||
s_client_uid = Common::Random::GenerateValue<u32>();
|
m_client_uid = Common::Random::GenerateValue<u32>();
|
||||||
s_next_listports_time = SteadyClock::now();
|
m_next_listports_time = SteadyClock::now();
|
||||||
|
|
||||||
if (s_servers_enabled && !s_servers.empty())
|
if (m_servers_enabled && !m_servers.empty())
|
||||||
StartHotplugThread();
|
StartHotplugThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConfigChanged()
|
void InputBackend::ConfigChanged()
|
||||||
{
|
{
|
||||||
if (!s_has_init)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const bool servers_enabled = Config::Get(Settings::SERVERS_ENABLED);
|
const bool servers_enabled = Config::Get(Settings::SERVERS_ENABLED);
|
||||||
const std::string servers_setting = Config::Get(Settings::SERVERS);
|
const std::string servers_setting = Config::Get(Settings::SERVERS);
|
||||||
|
|
||||||
std::string new_servers_setting;
|
std::string new_servers_setting;
|
||||||
for (const auto& server : s_servers)
|
for (const auto& server : m_servers)
|
||||||
{
|
{
|
||||||
new_servers_setting +=
|
new_servers_setting +=
|
||||||
fmt::format("{}:{}:{};", server.m_description, server.m_address, server.m_port);
|
fmt::format("{}:{}:{};", server.m_description, server.m_address, server.m_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (servers_enabled != s_servers_enabled || servers_setting != new_servers_setting)
|
if (servers_enabled != m_servers_enabled || servers_setting != new_servers_setting)
|
||||||
{
|
{
|
||||||
// Stop the thread before writing to s_servers
|
// Stop the thread before writing to m_servers
|
||||||
StopHotplugThread();
|
StopHotplugThread();
|
||||||
|
|
||||||
s_servers_enabled = servers_enabled;
|
m_servers_enabled = servers_enabled;
|
||||||
s_servers.clear();
|
m_servers.clear();
|
||||||
|
|
||||||
const auto server_details = SplitString(servers_setting, ';');
|
const auto server_details = SplitString(servers_setting, ';');
|
||||||
for (const auto& server_detail : server_details)
|
for (const auto& server_detail : server_details)
|
||||||
|
@ -428,17 +448,15 @@ static void ConfigChanged()
|
||||||
}
|
}
|
||||||
u16 server_port = static_cast<u16>(port);
|
u16 server_port = static_cast<u16>(port);
|
||||||
|
|
||||||
s_servers.emplace_back(description, server_address, server_port);
|
m_servers.emplace_back(description, server_address, server_port);
|
||||||
}
|
}
|
||||||
Restart();
|
Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||||
|
: ciface::InputBackend(controller_interface)
|
||||||
{
|
{
|
||||||
// Does not support multiple init calls
|
|
||||||
s_has_init = true;
|
|
||||||
|
|
||||||
// The following is added for backwards compatibility
|
// The following is added for backwards compatibility
|
||||||
const auto server_address_setting = Config::Get(Settings::SERVER_ADDRESS);
|
const auto server_address_setting = Config::Get(Settings::SERVER_ADDRESS);
|
||||||
const auto server_port_setting = Config::Get(Settings::SERVER_PORT);
|
const auto server_port_setting = Config::Get(Settings::SERVER_PORT);
|
||||||
|
@ -454,29 +472,30 @@ void Init()
|
||||||
Config::SetBase(Settings::SERVER_PORT, 0);
|
Config::SetBase(Settings::SERVER_PORT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It would be much better to unbind from this callback on DeInit but it's not possible as of now
|
m_config_change_callback_id =
|
||||||
Config::AddConfigChangedCallback(ConfigChanged);
|
Config::AddConfigChangedCallback(std::bind(&InputBackend::ConfigChanged, this));
|
||||||
ConfigChanged(); // Call it immediately to load settings
|
// Call it immediately to load settings
|
||||||
|
ConfigChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can be called by the host thread as well as the hotplug thread, concurrently.
|
// This can be called by the host thread as well as the hotplug thread, concurrently.
|
||||||
// So use PlatformPopulateDevices().
|
// So use PlatformPopulateDevices().
|
||||||
// s_servers is already safe because it can only be modified when the DSU thread is not running,
|
// m_servers is already safe because it can only be modified when the DSU thread is not running,
|
||||||
// from the main thread
|
// from the main thread
|
||||||
void PopulateDevices()
|
void InputBackend::PopulateDevices()
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient PopulateDevices");
|
INFO_LOG_FMT(CONTROLLERINTERFACE, "DualShockUDPClient PopulateDevices");
|
||||||
|
|
||||||
// s_servers has already been updated so we can't use it to know which devices we removed,
|
// m_servers has already been updated so we can't use it to know which devices we removed,
|
||||||
// also it's good to remove all of them before adding new ones so that their id will be set
|
// also it's good to remove all of them before adding new ones so that their id will be set
|
||||||
// correctly if they have the same name
|
// correctly if they have the same name
|
||||||
g_controller_interface.RemoveDevice(
|
GetControllerInterface().RemoveDevice(
|
||||||
[](const auto* dev) { return dev->GetSource() == DUALSHOCKUDP_SOURCE_NAME; });
|
[](const auto* dev) { return dev->GetSource() == DUALSHOCKUDP_SOURCE_NAME; });
|
||||||
|
|
||||||
// Users might have created more than one server on the same IP/Port.
|
// Users might have created more than one server on the same IP/Port.
|
||||||
// Devices might end up being duplicated (if the server responds two all requests)
|
// Devices might end up being duplicated (if the server responds two all requests)
|
||||||
// but they won't conflict.
|
// but they won't conflict.
|
||||||
for (const auto& server : s_servers)
|
for (const auto& server : m_servers)
|
||||||
{
|
{
|
||||||
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
|
for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
|
||||||
{
|
{
|
||||||
|
@ -484,24 +503,23 @@ void PopulateDevices()
|
||||||
if (port_info.pad_state != Proto::DsState::Connected)
|
if (port_info.pad_state != Proto::DsState::Connected)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
g_controller_interface.AddDevice(std::make_shared<Device>(
|
GetControllerInterface().AddDevice(
|
||||||
server.m_description, static_cast<int>(port_index), server.m_address, server.m_port));
|
std::make_shared<Device>(server.m_description, static_cast<int>(port_index),
|
||||||
|
server.m_address, server.m_port, m_client_uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeInit()
|
InputBackend::~InputBackend()
|
||||||
{
|
{
|
||||||
|
Config::RemoveConfigChangedCallback(m_config_change_callback_id);
|
||||||
StopHotplugThread();
|
StopHotplugThread();
|
||||||
|
|
||||||
s_has_init = false;
|
|
||||||
s_servers_enabled = false;
|
|
||||||
s_servers.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::Device(std::string name, int index, std::string server_address, u16 server_port)
|
Device::Device(std::string name, int index, std::string server_address, u16 server_port,
|
||||||
|
u32 client_uid)
|
||||||
: m_name{std::move(name)}, m_index{index}, m_server_address{std::move(server_address)},
|
: m_name{std::move(name)}, m_index{index}, m_server_address{std::move(server_address)},
|
||||||
m_server_port{server_port}
|
m_server_port{server_port}, m_client_uid(client_uid)
|
||||||
{
|
{
|
||||||
m_socket.setBlocking(false);
|
m_socket.setBlocking(false);
|
||||||
|
|
||||||
|
@ -604,7 +622,7 @@ void Device::UpdateInput()
|
||||||
{
|
{
|
||||||
m_next_reregister = now + SERVER_REREGISTER_INTERVAL;
|
m_next_reregister = now + SERVER_REREGISTER_INTERVAL;
|
||||||
|
|
||||||
Proto::Message<Proto::MessageType::PadDataRequest> msg(s_client_uid);
|
Proto::Message<Proto::MessageType::PadDataRequest> msg(m_client_uid);
|
||||||
auto& data_req = msg.m_message;
|
auto& data_req = msg.m_message;
|
||||||
data_req.register_flags = Proto::RegisterFlags::PadID;
|
data_req.register_flags = Proto::RegisterFlags::PadID;
|
||||||
data_req.pad_id_to_register = m_index;
|
data_req.pad_id_to_register = m_index;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/Config/Config.h"
|
#include "Common/Config/Config.h"
|
||||||
|
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||||
|
|
||||||
namespace ciface::DualShockUDPClient
|
namespace ciface::DualShockUDPClient
|
||||||
{
|
{
|
||||||
|
@ -20,7 +21,6 @@ extern const Config::Info<std::string> SERVERS;
|
||||||
extern const Config::Info<bool> SERVERS_ENABLED;
|
extern const Config::Info<bool> SERVERS_ENABLED;
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
||||||
void Init();
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||||
void PopulateDevices();
|
|
||||||
void DeInit();
|
|
||||||
} // namespace ciface::DualShockUDPClient
|
} // namespace ciface::DualShockUDPClient
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||||
|
|
||||||
|
namespace ciface
|
||||||
|
{
|
||||||
|
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||||
|
: m_controller_interface(*controller_interface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InputBackend::~InputBackend() = default;
|
||||||
|
|
||||||
|
void InputBackend::UpdateInput()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerInterface& InputBackend::GetControllerInterface()
|
||||||
|
{
|
||||||
|
return m_controller_interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ciface
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class ControllerInterface;
|
||||||
|
|
||||||
|
namespace ciface
|
||||||
|
{
|
||||||
|
class InputBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputBackend(ControllerInterface* controller_interface);
|
||||||
|
|
||||||
|
virtual ~InputBackend();
|
||||||
|
|
||||||
|
virtual void PopulateDevices() = 0;
|
||||||
|
virtual void UpdateInput();
|
||||||
|
|
||||||
|
ControllerInterface& GetControllerInterface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ControllerInterface& m_controller_interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ciface
|
|
@ -29,7 +29,33 @@ static std::string GetJoystickName(int index)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OpenAndAddDevice(int index)
|
class InputBackend final : public ciface::InputBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputBackend(ControllerInterface* controller_interface);
|
||||||
|
~InputBackend();
|
||||||
|
void PopulateDevices() override;
|
||||||
|
void UpdateInput() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OpenAndAddDevice(int index);
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
|
bool HandleEventAndContinue(const SDL_Event& e);
|
||||||
|
|
||||||
|
Common::Event m_init_event;
|
||||||
|
Uint32 m_stop_event_type;
|
||||||
|
Uint32 m_populate_event_type;
|
||||||
|
std::thread m_hotplug_thread;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||||
|
{
|
||||||
|
return std::make_unique<InputBackend>(controller_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputBackend::OpenAndAddDevice(int index)
|
||||||
{
|
{
|
||||||
SDL_Joystick* const dev = SDL_JoystickOpen(index);
|
SDL_Joystick* const dev = SDL_JoystickOpen(index);
|
||||||
if (dev)
|
if (dev)
|
||||||
|
@ -37,17 +63,13 @@ static void OpenAndAddDevice(int index)
|
||||||
auto js = std::make_shared<Joystick>(dev, index);
|
auto js = std::make_shared<Joystick>(dev, index);
|
||||||
// only add if it has some inputs/outputs
|
// only add if it has some inputs/outputs
|
||||||
if (!js->Inputs().empty() || !js->Outputs().empty())
|
if (!js->Inputs().empty() || !js->Outputs().empty())
|
||||||
g_controller_interface.AddDevice(std::move(js));
|
GetControllerInterface().AddDevice(std::move(js));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
static Common::Event s_init_event;
|
|
||||||
static Uint32 s_stop_event_type;
|
|
||||||
static Uint32 s_populate_event_type;
|
|
||||||
static std::thread s_hotplug_thread;
|
|
||||||
|
|
||||||
static bool HandleEventAndContinue(const SDL_Event& e)
|
bool InputBackend::HandleEventAndContinue(const SDL_Event& e)
|
||||||
{
|
{
|
||||||
if (e.type == SDL_JOYDEVICEADDED)
|
if (e.type == SDL_JOYDEVICEADDED)
|
||||||
{
|
{
|
||||||
|
@ -55,20 +77,20 @@ static bool HandleEventAndContinue(const SDL_Event& e)
|
||||||
}
|
}
|
||||||
else if (e.type == SDL_JOYDEVICEREMOVED)
|
else if (e.type == SDL_JOYDEVICEREMOVED)
|
||||||
{
|
{
|
||||||
g_controller_interface.RemoveDevice([&e](const auto* device) {
|
GetControllerInterface().RemoveDevice([&e](const auto* device) {
|
||||||
return device->GetSource() == "SDL" &&
|
return device->GetSource() == "SDL" &&
|
||||||
SDL_JoystickInstanceID(static_cast<const Joystick*>(device)->GetSDLJoystick()) ==
|
SDL_JoystickInstanceID(static_cast<const Joystick*>(device)->GetSDLJoystick()) ==
|
||||||
e.jdevice.which;
|
e.jdevice.which;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (e.type == s_populate_event_type)
|
else if (e.type == m_populate_event_type)
|
||||||
{
|
{
|
||||||
g_controller_interface.PlatformPopulateDevices([] {
|
GetControllerInterface().PlatformPopulateDevices([this] {
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||||
OpenAndAddDevice(i);
|
OpenAndAddDevice(i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (e.type == s_stop_event_type)
|
else if (e.type == m_stop_event_type)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +166,8 @@ static void EnableSDLLogging()
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||||
|
: ciface::InputBackend(controller_interface)
|
||||||
{
|
{
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
|
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
|
||||||
|
@ -168,13 +191,13 @@ void Init()
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s_hotplug_thread = std::thread([] {
|
m_hotplug_thread = std::thread([this] {
|
||||||
Common::ScopeGuard quit_guard([] {
|
Common::ScopeGuard quit_guard([] {
|
||||||
// TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up
|
// TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
Common::ScopeGuard init_guard([] { s_init_event.Set(); });
|
Common::ScopeGuard init_guard([this] { m_init_event.Set(); });
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER) != 0)
|
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER) != 0)
|
||||||
{
|
{
|
||||||
|
@ -188,8 +211,8 @@ void Init()
|
||||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to register custom events");
|
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to register custom events");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s_stop_event_type = custom_events_start;
|
m_stop_event_type = custom_events_start;
|
||||||
s_populate_event_type = custom_events_start + 1;
|
m_populate_event_type = custom_events_start + 1;
|
||||||
|
|
||||||
// Drain all of the events and add the initial joysticks before returning. Otherwise, the
|
// Drain all of the events and add the initial joysticks before returning. Otherwise, the
|
||||||
// individual joystick events as well as the custom populate event will be handled _after_
|
// individual joystick events as well as the custom populate event will be handled _after_
|
||||||
|
@ -235,26 +258,26 @@ void Init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
s_init_event.Wait();
|
m_init_event.Wait();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeInit()
|
InputBackend::~InputBackend()
|
||||||
{
|
{
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
#else
|
#else
|
||||||
if (!s_hotplug_thread.joinable())
|
if (!m_hotplug_thread.joinable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SDL_Event stop_event{s_stop_event_type};
|
SDL_Event stop_event{m_stop_event_type};
|
||||||
SDL_PushEvent(&stop_event);
|
SDL_PushEvent(&stop_event);
|
||||||
|
|
||||||
s_hotplug_thread.join();
|
m_hotplug_thread.join();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopulateDevices()
|
void InputBackend::PopulateDevices()
|
||||||
{
|
{
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
if (!SDL_WasInit(SDL_INIT_JOYSTICK))
|
if (!SDL_WasInit(SDL_INIT_JOYSTICK))
|
||||||
|
@ -263,10 +286,10 @@ void PopulateDevices()
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||||
OpenAndAddDevice(i);
|
OpenAndAddDevice(i);
|
||||||
#else
|
#else
|
||||||
if (!s_hotplug_thread.joinable())
|
if (!m_hotplug_thread.joinable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SDL_Event populate_event{s_populate_event_type};
|
SDL_Event populate_event{m_populate_event_type};
|
||||||
SDL_PushEvent(&populate_event);
|
SDL_PushEvent(&populate_event);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -617,9 +640,8 @@ void Joystick::Motor::SetState(ControlState state)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Joystick::UpdateInput()
|
void InputBackend::UpdateInput()
|
||||||
{
|
{
|
||||||
// TODO: Don't call this for every Joystick, only once per ControllerInterface::UpdateInput()
|
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,11 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||||
|
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||||
|
|
||||||
namespace ciface::SDL
|
namespace ciface::SDL
|
||||||
{
|
{
|
||||||
void Init();
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||||
void DeInit();
|
|
||||||
void PopulateDevices();
|
|
||||||
|
|
||||||
class Joystick : public Core::Device
|
class Joystick : public Core::Device
|
||||||
{
|
{
|
||||||
|
@ -182,8 +181,6 @@ private:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void UpdateInput() override;
|
|
||||||
|
|
||||||
Joystick(SDL_Joystick* const joystick, const int sdl_index);
|
Joystick(SDL_Joystick* const joystick, const int sdl_index);
|
||||||
~Joystick();
|
~Joystick();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,43 @@
|
||||||
|
|
||||||
namespace ciface::evdev
|
namespace ciface::evdev
|
||||||
{
|
{
|
||||||
|
class InputBackend final : public ciface::InputBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputBackend(ControllerInterface* controller_interface);
|
||||||
|
~InputBackend();
|
||||||
|
void PopulateDevices() override;
|
||||||
|
|
||||||
|
void RemoveDevnodeObject(const std::string&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<evdevDevice>
|
||||||
|
FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location);
|
||||||
|
|
||||||
|
void AddDeviceNode(const char* devnode);
|
||||||
|
|
||||||
|
void StartHotplugThread();
|
||||||
|
void StopHotplugThread();
|
||||||
|
void HotplugThreadFunc();
|
||||||
|
|
||||||
|
std::thread m_hotplug_thread;
|
||||||
|
Common::Flag m_hotplug_thread_running;
|
||||||
|
int m_wakeup_eventfd;
|
||||||
|
|
||||||
|
// There is no easy way to get the device name from only a dev node
|
||||||
|
// during a device removed event, since libevdev can't work on removed devices;
|
||||||
|
// sysfs is not stable, so this is probably the easiest way to get a name for a node.
|
||||||
|
// This can, and will be modified by different thread, possibly concurrently,
|
||||||
|
// as devices can be destroyed by any thread at any time. As of now it's protected
|
||||||
|
// by ControllerInterface::m_devices_population_mutex.
|
||||||
|
std::map<std::string, std::weak_ptr<evdevDevice>> m_devnode_objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||||
|
{
|
||||||
|
return std::make_unique<InputBackend>(controller_interface);
|
||||||
|
}
|
||||||
|
|
||||||
class Input : public Core::Device::Input
|
class Input : public Core::Device::Input
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -195,25 +232,14 @@ public:
|
||||||
bool IsDetectable() const override { return false; }
|
bool IsDetectable() const override { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::thread s_hotplug_thread;
|
std::shared_ptr<evdevDevice>
|
||||||
static Common::Flag s_hotplug_thread_running;
|
InputBackend::FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id,
|
||||||
static int s_wakeup_eventfd;
|
const char* physical_location)
|
||||||
|
|
||||||
// There is no easy way to get the device name from only a dev node
|
|
||||||
// during a device removed event, since libevdev can't work on removed devices;
|
|
||||||
// sysfs is not stable, so this is probably the easiest way to get a name for a node.
|
|
||||||
// This can, and will be modified by different thread, possibly concurrently,
|
|
||||||
// as devices can be destroyed by any thread at any time. As of now it's protected
|
|
||||||
// by ControllerInterface::m_devices_population_mutex.
|
|
||||||
static std::map<std::string, std::weak_ptr<evdevDevice>> s_devnode_objects;
|
|
||||||
|
|
||||||
static std::shared_ptr<evdevDevice>
|
|
||||||
FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* physical_location)
|
|
||||||
{
|
{
|
||||||
if (!unique_id || !physical_location)
|
if (!unique_id || !physical_location)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
for (auto& [node, dev] : s_devnode_objects)
|
for (auto& [node, dev] : m_devnode_objects)
|
||||||
{
|
{
|
||||||
if (const auto device = dev.lock())
|
if (const auto device = dev.lock())
|
||||||
{
|
{
|
||||||
|
@ -231,7 +257,7 @@ FindDeviceWithUniqueIDAndPhysicalLocation(const char* unique_id, const char* phy
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddDeviceNode(const char* devnode)
|
void InputBackend::AddDeviceNode(const char* devnode)
|
||||||
{
|
{
|
||||||
// Unfortunately udev gives us no way to filter out the non event device interfaces.
|
// Unfortunately udev gives us no way to filter out the non event device interfaces.
|
||||||
// So we open it and see if it works with evdev ioctls or not.
|
// So we open it and see if it works with evdev ioctls or not.
|
||||||
|
@ -265,30 +291,30 @@ static void AddDeviceNode(const char* devnode)
|
||||||
// This will also give it the correct index and invoke device change callbacks.
|
// This will also give it the correct index and invoke device change callbacks.
|
||||||
// Make sure to force the device removal immediately (as they are shared ptrs and
|
// Make sure to force the device removal immediately (as they are shared ptrs and
|
||||||
// they could be kept alive, preventing us from re-creating the device)
|
// they could be kept alive, preventing us from re-creating the device)
|
||||||
g_controller_interface.RemoveDevice(
|
GetControllerInterface().RemoveDevice(
|
||||||
[&evdev_device](const auto* device) {
|
[&evdev_device](const auto* device) {
|
||||||
return static_cast<const evdevDevice*>(device) == evdev_device.get();
|
return static_cast<const evdevDevice*>(device) == evdev_device.get();
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
|
|
||||||
g_controller_interface.AddDevice(evdev_device);
|
GetControllerInterface().AddDevice(evdev_device);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
evdev_device = std::make_shared<evdevDevice>();
|
evdev_device = std::make_shared<evdevDevice>(this);
|
||||||
|
|
||||||
const bool was_interesting = evdev_device->AddNode(devnode, fd, dev);
|
const bool was_interesting = evdev_device->AddNode(devnode, fd, dev);
|
||||||
|
|
||||||
if (was_interesting)
|
if (was_interesting)
|
||||||
g_controller_interface.AddDevice(evdev_device);
|
GetControllerInterface().AddDevice(evdev_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the devices failed to be added to g_controller_interface, it will be added here but then
|
// If the devices failed to be added to ControllerInterface, it will be added here but then
|
||||||
// immediately removed in its destructor due to the shared ptr not having any references left
|
// immediately removed in its destructor due to the shared ptr not having any references left
|
||||||
s_devnode_objects.emplace(devnode, std::move(evdev_device));
|
m_devnode_objects.emplace(devnode, std::move(evdev_device));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HotplugThreadFunc()
|
void InputBackend::HotplugThreadFunc()
|
||||||
{
|
{
|
||||||
Common::SetCurrentThreadName("evdev Hotplug Thread");
|
Common::SetCurrentThreadName("evdev Hotplug Thread");
|
||||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread started");
|
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread started");
|
||||||
|
@ -306,16 +332,16 @@ static void HotplugThreadFunc()
|
||||||
udev_monitor_enable_receiving(monitor);
|
udev_monitor_enable_receiving(monitor);
|
||||||
const int monitor_fd = udev_monitor_get_fd(monitor);
|
const int monitor_fd = udev_monitor_get_fd(monitor);
|
||||||
|
|
||||||
while (s_hotplug_thread_running.IsSet())
|
while (m_hotplug_thread_running.IsSet())
|
||||||
{
|
{
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(monitor_fd, &fds);
|
FD_SET(monitor_fd, &fds);
|
||||||
FD_SET(s_wakeup_eventfd, &fds);
|
FD_SET(m_wakeup_eventfd, &fds);
|
||||||
|
|
||||||
const int ret =
|
const int ret =
|
||||||
select(std::max(monitor_fd, s_wakeup_eventfd) + 1, &fds, nullptr, nullptr, nullptr);
|
select(std::max(monitor_fd, m_wakeup_eventfd) + 1, &fds, nullptr, nullptr, nullptr);
|
||||||
if (ret < 1 || !FD_ISSET(monitor_fd, &fds))
|
if (ret < 1 || !FD_ISSET(monitor_fd, &fds))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -327,53 +353,54 @@ static void HotplugThreadFunc()
|
||||||
if (!devnode)
|
if (!devnode)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Use g_controller_interface.PlatformPopulateDevices() to protect access around
|
// Use GetControllerInterface().PlatformPopulateDevices() to protect access around
|
||||||
// s_devnode_objects. Note that even if we get these events at the same time as a
|
// m_devnode_objects. Note that even if we get these events at the same time as a
|
||||||
// a PopulateDevices() request (e.g. on start up, we might get all the add events
|
// a PopulateDevices() request (e.g. on start up, we might get all the add events
|
||||||
// for connected devices), this won't ever cause duplicate devices as AddDeviceNode()
|
// for connected devices), this won't ever cause duplicate devices as AddDeviceNode()
|
||||||
// automatically removes the old one if it already existed
|
// automatically removes the old one if it already existed
|
||||||
if (strcmp(action, "remove") == 0)
|
if (strcmp(action, "remove") == 0)
|
||||||
{
|
{
|
||||||
g_controller_interface.PlatformPopulateDevices([&devnode] {
|
GetControllerInterface().PlatformPopulateDevices([&devnode, this] {
|
||||||
std::shared_ptr<evdevDevice> ptr;
|
std::shared_ptr<evdevDevice> ptr;
|
||||||
|
|
||||||
const auto it = s_devnode_objects.find(devnode);
|
const auto it = m_devnode_objects.find(devnode);
|
||||||
if (it != s_devnode_objects.end())
|
if (it != m_devnode_objects.end())
|
||||||
ptr = it->second.lock();
|
ptr = it->second.lock();
|
||||||
|
|
||||||
// If we don't recognize this device, ptr will be null and no device will be removed.
|
// If we don't recognize this device, ptr will be null and no device will be removed.
|
||||||
|
|
||||||
g_controller_interface.RemoveDevice([&ptr](const auto* device) {
|
GetControllerInterface().RemoveDevice([&ptr](const auto* device) {
|
||||||
return static_cast<const evdevDevice*>(device) == ptr.get();
|
return static_cast<const evdevDevice*>(device) == ptr.get();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (strcmp(action, "add") == 0)
|
else if (strcmp(action, "add") == 0)
|
||||||
{
|
{
|
||||||
g_controller_interface.PlatformPopulateDevices([&devnode] { AddDeviceNode(devnode); });
|
GetControllerInterface().PlatformPopulateDevices(
|
||||||
|
[&devnode, this] { AddDeviceNode(devnode); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread stopped");
|
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "evdev hotplug thread stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StartHotplugThread()
|
void InputBackend::StartHotplugThread()
|
||||||
{
|
{
|
||||||
// Mark the thread as running.
|
// Mark the thread as running.
|
||||||
if (!s_hotplug_thread_running.TestAndSet())
|
if (!m_hotplug_thread_running.TestAndSet())
|
||||||
{
|
{
|
||||||
// It was already running.
|
// It was already running.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_wakeup_eventfd = eventfd(0, 0);
|
m_wakeup_eventfd = eventfd(0, 0);
|
||||||
ASSERT_MSG(CONTROLLERINTERFACE, s_wakeup_eventfd != -1, "Couldn't create eventfd.");
|
ASSERT_MSG(CONTROLLERINTERFACE, m_wakeup_eventfd != -1, "Couldn't create eventfd.");
|
||||||
s_hotplug_thread = std::thread(HotplugThreadFunc);
|
m_hotplug_thread = std::thread(&InputBackend::HotplugThreadFunc, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StopHotplugThread()
|
void InputBackend::StopHotplugThread()
|
||||||
{
|
{
|
||||||
// Tell the hotplug thread to stop.
|
// Tell the hotplug thread to stop.
|
||||||
if (!s_hotplug_thread_running.TestAndClear())
|
if (!m_hotplug_thread_running.TestAndClear())
|
||||||
{
|
{
|
||||||
// It wasn't running, we're done.
|
// It wasn't running, we're done.
|
||||||
return;
|
return;
|
||||||
|
@ -381,22 +408,23 @@ static void StopHotplugThread()
|
||||||
|
|
||||||
// Write something to efd so that select() stops blocking.
|
// Write something to efd so that select() stops blocking.
|
||||||
const uint64_t value = 1;
|
const uint64_t value = 1;
|
||||||
static_cast<void>(!write(s_wakeup_eventfd, &value, sizeof(uint64_t)));
|
static_cast<void>(!write(m_wakeup_eventfd, &value, sizeof(uint64_t)));
|
||||||
|
|
||||||
s_hotplug_thread.join();
|
m_hotplug_thread.join();
|
||||||
close(s_wakeup_eventfd);
|
close(m_wakeup_eventfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||||
|
: ciface::InputBackend(controller_interface)
|
||||||
{
|
{
|
||||||
StartHotplugThread();
|
StartHotplugThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only call this when ControllerInterface::m_devices_population_mutex is locked
|
// Only call this when ControllerInterface::m_devices_population_mutex is locked
|
||||||
void PopulateDevices()
|
void InputBackend::PopulateDevices()
|
||||||
{
|
{
|
||||||
// Don't run if we are not initialized
|
// Don't run if we are not initialized
|
||||||
if (!s_hotplug_thread_running.IsSet())
|
if (!m_hotplug_thread_running.IsSet())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -431,7 +459,7 @@ void PopulateDevices()
|
||||||
udev_unref(udev);
|
udev_unref(udev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
InputBackend::~InputBackend()
|
||||||
{
|
{
|
||||||
StopHotplugThread();
|
StopHotplugThread();
|
||||||
}
|
}
|
||||||
|
@ -627,16 +655,25 @@ const char* evdevDevice::GetPhysicalLocation() const
|
||||||
return libevdev_get_phys(m_nodes.front().device);
|
return libevdev_get_phys(m_nodes.front().device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evdevDevice::evdevDevice(InputBackend* input_backend) : m_input_backend(*input_backend)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
evdevDevice::~evdevDevice()
|
evdevDevice::~evdevDevice()
|
||||||
{
|
{
|
||||||
for (auto& node : m_nodes)
|
for (auto& node : m_nodes)
|
||||||
{
|
{
|
||||||
s_devnode_objects.erase(node.devnode);
|
m_input_backend.RemoveDevnodeObject(node.devnode);
|
||||||
libevdev_free(node.device);
|
libevdev_free(node.device);
|
||||||
close(node.fd);
|
close(node.fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputBackend::RemoveDevnodeObject(const std::string& node)
|
||||||
|
{
|
||||||
|
m_devnode_objects.erase(node);
|
||||||
|
}
|
||||||
|
|
||||||
void evdevDevice::UpdateInput()
|
void evdevDevice::UpdateInput()
|
||||||
{
|
{
|
||||||
// Run through all evdev events
|
// Run through all evdev events
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
namespace ciface::evdev
|
namespace ciface::evdev
|
||||||
{
|
{
|
||||||
void Init();
|
class InputBackend;
|
||||||
void PopulateDevices();
|
|
||||||
void Shutdown();
|
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||||
|
|
||||||
class evdevDevice : public Core::Device
|
class evdevDevice : public Core::Device
|
||||||
{
|
{
|
||||||
|
@ -75,6 +75,7 @@ public:
|
||||||
void UpdateInput() override;
|
void UpdateInput() override;
|
||||||
bool IsValid() const override;
|
bool IsValid() const override;
|
||||||
|
|
||||||
|
evdevDevice(InputBackend* input_backend);
|
||||||
~evdevDevice();
|
~evdevDevice();
|
||||||
|
|
||||||
// Return true if node was "interesting".
|
// Return true if node was "interesting".
|
||||||
|
@ -97,5 +98,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Node> m_nodes;
|
std::vector<Node> m_nodes;
|
||||||
|
|
||||||
|
InputBackend& m_input_backend;
|
||||||
};
|
};
|
||||||
} // namespace ciface::evdev
|
} // namespace ciface::evdev
|
||||||
|
|
Loading…
Reference in New Issue