mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 14:19:46 -06:00
GCAdapter: Add a background thread to detect the adapter
This scanning thread either polls libusb or checks every 500ms for a change depending on host capabilities. The GC Adapter can now be plugged and unplugged at any time when dolphin is open, it will be used if the direct connect option is set.
This commit is contained in:
@ -19,6 +19,9 @@ enum ControllerTypes
|
||||
CONTROLLER_WIRELESS = 2
|
||||
};
|
||||
|
||||
static bool CheckDeviceAccess(libusb_device* device);
|
||||
static void AddGCAdapter(libusb_device* device);
|
||||
|
||||
static bool s_detected = false;
|
||||
static libusb_device_handle* s_handle = nullptr;
|
||||
static u8 s_controller_type[MAX_SI_CHANNELS] = { CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE };
|
||||
@ -33,8 +36,15 @@ static int s_controller_payload_size = 0;
|
||||
static std::thread s_adapter_thread;
|
||||
static Common::Flag s_adapter_thread_running;
|
||||
|
||||
static std::thread s_adapter_detect_thread;
|
||||
static Common::Flag s_adapter_detect_thread_running;
|
||||
|
||||
static std::function<void(void)> s_detect_callback;
|
||||
|
||||
static bool s_libusb_driver_not_supported = false;
|
||||
static libusb_context* s_libusb_context = nullptr;
|
||||
static bool s_libusb_hotplug_enabled = false;
|
||||
static libusb_hotplug_callback_handle s_hotplug_handle;
|
||||
|
||||
static u8 s_endpoint_in = 0;
|
||||
static u8 s_endpoint_out = 0;
|
||||
@ -56,6 +66,61 @@ static void Read()
|
||||
}
|
||||
}
|
||||
|
||||
static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotplug_event event, void* user_data)
|
||||
{
|
||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
||||
{
|
||||
if (s_handle == nullptr && CheckDeviceAccess(dev))
|
||||
AddGCAdapter(dev);
|
||||
}
|
||||
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
{
|
||||
if (s_handle != nullptr && libusb_get_device(s_handle) == dev)
|
||||
Reset();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ScanThreadFunc()
|
||||
{
|
||||
Common::SetCurrentThreadName("GC Adapter Scanning Thread");
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started");
|
||||
|
||||
s_libusb_hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0;
|
||||
if (s_libusb_hotplug_enabled)
|
||||
{
|
||||
if (libusb_hotplug_register_callback(s_libusb_context, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback, NULL, &s_hotplug_handle) != LIBUSB_SUCCESS)
|
||||
s_libusb_hotplug_enabled = false;
|
||||
if (s_libusb_hotplug_enabled)
|
||||
NOTICE_LOG(SERIALINTERFACE, "Using libUSB hotplug detection");
|
||||
}
|
||||
|
||||
while (s_adapter_detect_thread_running.IsSet())
|
||||
{
|
||||
if (s_libusb_hotplug_enabled)
|
||||
{
|
||||
static timeval tv = {0, 500000};
|
||||
libusb_handle_events_timeout(s_libusb_context, &tv);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_handle == nullptr)
|
||||
{
|
||||
Setup();
|
||||
if (s_detected && s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
}
|
||||
Common::SleepCurrentThread(500);
|
||||
}
|
||||
}
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped");
|
||||
}
|
||||
|
||||
void SetAdapterCallback(std::function<void(void)> func)
|
||||
{
|
||||
s_detect_callback = func;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
if (s_handle != nullptr)
|
||||
@ -80,13 +145,26 @@ void Init()
|
||||
}
|
||||
else
|
||||
{
|
||||
Setup();
|
||||
StartScanThread();
|
||||
}
|
||||
}
|
||||
|
||||
void StartScanThread()
|
||||
{
|
||||
s_adapter_detect_thread_running.Set(true);
|
||||
s_adapter_detect_thread = std::thread(ScanThreadFunc);
|
||||
}
|
||||
|
||||
void StopScanThread()
|
||||
{
|
||||
if (s_adapter_detect_thread_running.TestAndClear())
|
||||
{
|
||||
s_adapter_detect_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Setup()
|
||||
{
|
||||
int ret;
|
||||
libusb_device** list;
|
||||
ssize_t cnt = libusb_get_device_list(s_libusb_context, &list);
|
||||
|
||||
@ -96,17 +174,26 @@ void Setup()
|
||||
s_controller_rumble[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
for (int d = 0; d < cnt; d++)
|
||||
{
|
||||
libusb_device* device = list[d];
|
||||
if (CheckDeviceAccess(device))
|
||||
AddGCAdapter(device);
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
}
|
||||
|
||||
static bool CheckDeviceAccess(libusb_device* device)
|
||||
{
|
||||
int ret;
|
||||
libusb_device_descriptor desc;
|
||||
int dRet = libusb_get_device_descriptor(device, &desc);
|
||||
if (dRet)
|
||||
{
|
||||
// could not acquire the descriptor, no point in trying to use it.
|
||||
ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet);
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.idVendor == 0x057e && desc.idProduct == 0x0337)
|
||||
@ -155,7 +242,7 @@ void Setup()
|
||||
// detaching the kernel driver is successful
|
||||
if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
else if ((ret = libusb_claim_interface(s_handle, 0)))
|
||||
{
|
||||
@ -163,20 +250,13 @@ void Setup()
|
||||
}
|
||||
else
|
||||
{
|
||||
AddGCAdapter(device);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
|
||||
if (!s_detected)
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
void AddGCAdapter(libusb_device* device)
|
||||
static void AddGCAdapter(libusb_device* device)
|
||||
{
|
||||
libusb_config_descriptor *config = nullptr;
|
||||
libusb_get_config_descriptor(device, 0, &config);
|
||||
@ -205,10 +285,15 @@ void AddGCAdapter(libusb_device* device)
|
||||
s_adapter_thread = std::thread(Read);
|
||||
|
||||
s_detected = true;
|
||||
if (s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
StopScanThread();
|
||||
if (s_libusb_hotplug_enabled)
|
||||
libusb_hotplug_deregister_callback(s_libusb_context, s_hotplug_handle);
|
||||
Reset();
|
||||
|
||||
if (s_libusb_context)
|
||||
@ -230,36 +315,29 @@ void Reset()
|
||||
s_adapter_thread.join();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||
s_controller_type[i] = CONTROLLER_NONE;
|
||||
|
||||
s_detected = false;
|
||||
|
||||
if (s_handle)
|
||||
{
|
||||
libusb_release_interface(s_handle, 0);
|
||||
libusb_close(s_handle);
|
||||
s_handle = nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||
s_controller_type[i] = CONTROLLER_NONE;
|
||||
if (s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached");
|
||||
}
|
||||
|
||||
|
||||
void Input(int chan, GCPadStatus* pad)
|
||||
{
|
||||
if (!SConfig::GetInstance().m_GameCubeAdapter)
|
||||
return;
|
||||
|
||||
if (s_handle == nullptr)
|
||||
{
|
||||
if (s_detected)
|
||||
{
|
||||
Init();
|
||||
if (s_handle == nullptr)
|
||||
if (s_handle == nullptr || !s_detected)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
u8 controller_payload_copy[37];
|
||||
|
||||
@ -271,7 +349,7 @@ void Input(int chan, GCPadStatus* pad)
|
||||
if (s_controller_payload_size != sizeof(controller_payload_copy) || controller_payload_copy[0] != LIBUSB_DT_HID)
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]);
|
||||
Shutdown();
|
||||
Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -330,7 +408,7 @@ void Output(int chan, u8 rumble_command)
|
||||
if (size != 0x05 && size != 0x00)
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size);
|
||||
Shutdown();
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct libusb_device;
|
||||
#include <functional>
|
||||
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/HW/SI.h"
|
||||
@ -17,7 +17,9 @@ void Init();
|
||||
void Reset();
|
||||
void Setup();
|
||||
void Shutdown();
|
||||
void AddGCAdapter(libusb_device* device);
|
||||
void SetAdapterCallback(std::function<void(void)> func);
|
||||
void StartScanThread();
|
||||
void StopScanThread();
|
||||
void Input(int chan, GCPadStatus* pad);
|
||||
void Output(int chan, u8 rumble_command);
|
||||
bool IsDetected();
|
||||
|
@ -52,6 +52,8 @@ const std::array<wxString, 8> ControllerConfigDiag::m_gc_pad_type_strs = {{
|
||||
_("AM-Baseboard")
|
||||
}};
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_ADAPTER_UPDATE, wxCommandEvent);
|
||||
|
||||
ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent)
|
||||
: wxDialog(parent, wxID_ANY, _("Dolphin Controller Configuration"))
|
||||
{
|
||||
@ -71,6 +73,7 @@ ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent)
|
||||
SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED);
|
||||
SetSizerAndFit(main_sizer);
|
||||
Center();
|
||||
Bind(wxEVT_ADAPTER_UPDATE, &ControllerConfigDiag::UpdateAdapter, this);
|
||||
}
|
||||
|
||||
wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
||||
@ -147,39 +150,58 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
||||
gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND, 5);
|
||||
gamecube_static_sizer->AddSpacer(5);
|
||||
|
||||
wxStaticBoxSizer* const gamecube_adapter_group = new wxStaticBoxSizer(wxHORIZONTAL, this, _("GameCube Adapter"));
|
||||
wxStaticBoxSizer* const gamecube_adapter_group = new wxStaticBoxSizer(wxVERTICAL, this, _("GameCube Adapter"));
|
||||
wxBoxSizer* const gamecube_adapter_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxCheckBox* const gamecube_adapter = new wxCheckBox(this, wxID_ANY, _("Direct Connect"));
|
||||
gamecube_adapter->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnGameCubeAdapter, this);
|
||||
m_adapter_status = new wxStaticText(this, wxID_ANY, _("Adapter Not Detected"));
|
||||
|
||||
gamecube_adapter_sizer->Add(m_adapter_status, 0, wxEXPAND);
|
||||
gamecube_adapter_sizer->Add(gamecube_adapter, 0, wxEXPAND);
|
||||
gamecube_adapter_group->Add(gamecube_adapter_sizer, 0, wxEXPAND);
|
||||
gamecube_static_sizer->Add(gamecube_adapter_group, 0, wxEXPAND);
|
||||
|
||||
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||
gamecube_adapter->SetValue(SConfig::GetInstance().m_GameCubeAdapter);
|
||||
if (!SI_GCAdapter::IsDetected())
|
||||
{
|
||||
if (!SI_GCAdapter::IsDriverDetected())
|
||||
gamecube_adapter->SetLabelText(_("Driver Not Detected"));
|
||||
else
|
||||
gamecube_adapter->SetLabelText(_("Adapter Not Detected"));
|
||||
gamecube_adapter->SetValue(false);
|
||||
{
|
||||
m_adapter_status->SetLabelText(_("Driver Not Detected"));
|
||||
gamecube_adapter->Disable();
|
||||
gamecube_adapter->SetValue(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gamecube_adapter->SetValue(SConfig::GetInstance().m_GameCubeAdapter);
|
||||
m_adapter_status->SetLabelText(_("Adapter Detected"));
|
||||
}
|
||||
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||
{
|
||||
gamecube_adapter->Disable();
|
||||
}
|
||||
}
|
||||
SI_GCAdapter::SetAdapterCallback(std::bind(&ControllerConfigDiag::ScheduleAdapterUpdate, this));
|
||||
#endif
|
||||
|
||||
return gamecube_static_sizer;
|
||||
}
|
||||
|
||||
void ControllerConfigDiag::ScheduleAdapterUpdate()
|
||||
{
|
||||
wxQueueEvent(this, new wxCommandEvent(wxEVT_ADAPTER_UPDATE));
|
||||
}
|
||||
|
||||
void ControllerConfigDiag::UpdateAdapter(wxCommandEvent& ev)
|
||||
{
|
||||
bool unpause = Core::PauseAndLock(true);
|
||||
if (SI_GCAdapter::IsDetected())
|
||||
m_adapter_status->SetLabelText(_("Adapter Detected"));
|
||||
else
|
||||
m_adapter_status->SetLabelText(_("Adapter Not Detected"));
|
||||
Core::PauseAndLock(false, unpause);
|
||||
}
|
||||
|
||||
wxStaticBoxSizer* ControllerConfigDiag::CreateWiimoteConfigSizer()
|
||||
{
|
||||
wxStaticText* wiimote_label[4];
|
||||
@ -537,3 +559,8 @@ void ControllerConfigDiag::OnGameCubeConfigButton(wxCommandEvent& event)
|
||||
|
||||
HotkeyManagerEmu::Enable(true);
|
||||
}
|
||||
|
||||
ControllerConfigDiag::~ControllerConfigDiag()
|
||||
{
|
||||
SI_GCAdapter::SetAdapterCallback(nullptr);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Common/SysConf.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/HW/SI_GCAdapter.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
|
||||
class InputConfig;
|
||||
@ -19,6 +20,7 @@ class ControllerConfigDiag : public wxDialog
|
||||
{
|
||||
public:
|
||||
ControllerConfigDiag(wxWindow* const parent);
|
||||
~ControllerConfigDiag();
|
||||
|
||||
private:
|
||||
void RefreshRealWiimotes(wxCommandEvent& event);
|
||||
@ -82,6 +84,8 @@ private:
|
||||
void Cancel(wxCommandEvent& event);
|
||||
void OnGameCubePortChanged(wxCommandEvent& event);
|
||||
void OnGameCubeConfigButton(wxCommandEvent& event);
|
||||
void ScheduleAdapterUpdate();
|
||||
void UpdateAdapter(wxCommandEvent& ev);
|
||||
|
||||
std::map<wxWindowID, unsigned int> m_gc_port_choice_ids;
|
||||
std::map<wxWindowID, unsigned int> m_gc_port_config_ids;
|
||||
@ -90,6 +94,7 @@ private:
|
||||
std::map<wxWindowID, unsigned int> m_wiimote_index_from_ctrl_id;
|
||||
unsigned int m_orig_wiimote_sources[MAX_BBMOTES];
|
||||
|
||||
wxStaticText* m_adapter_status;
|
||||
wxButton* wiimote_configure_bt[MAX_WIIMOTES];
|
||||
wxButton* gamecube_configure_bt[4];
|
||||
std::map<wxWindowID, unsigned int> m_wiimote_index_from_conf_bt_id;
|
||||
|
Reference in New Issue
Block a user