mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 23:29:44 -06:00
Make real wiimotes not so crappy on Windows hopefully.
This commit is contained in:
@ -29,7 +29,10 @@ WiimoteScanner::WiimoteScanner()
|
|||||||
WiimoteScanner::~WiimoteScanner()
|
WiimoteScanner::~WiimoteScanner()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
void WiimoteScanner::Update()
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||||
{
|
{
|
||||||
return std::vector<Wiimote*>();
|
return std::vector<Wiimote*>();
|
||||||
}
|
}
|
||||||
|
@ -60,11 +60,10 @@ WiimoteScanner::~WiimoteScanner()
|
|||||||
close(device_sock);
|
close(device_sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find wiimotes.
|
void WiimoteScanner::Update()
|
||||||
// Does not replace already found wiimotes even if they are disconnected.
|
{}
|
||||||
// wm is an array of max_wiimotes wiimotes
|
|
||||||
// Returns the total number of found wiimotes.
|
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|
||||||
{
|
{
|
||||||
std::vector<Wiimote*> found_wiimotes;
|
std::vector<Wiimote*> found_wiimotes;
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|||||||
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
||||||
|
|
||||||
// Display discovered devices
|
// Display discovered devices
|
||||||
for (int i = 0; (i < found_devices) && (found_wiimotes.size() < max_wiimotes); ++i)
|
for (int i = 0; i < found_devices; ++i)
|
||||||
{
|
{
|
||||||
ERROR_LOG(WIIMOTE, "found a device...");
|
ERROR_LOG(WIIMOTE, "found a device...");
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <dbt.h>
|
#include <dbt.h>
|
||||||
@ -73,6 +74,8 @@ HINSTANCE bthprops_lib = NULL;
|
|||||||
|
|
||||||
static int initialized = 0;
|
static int initialized = 0;
|
||||||
|
|
||||||
|
static std::unordered_set<std::string> g_connected_devices;
|
||||||
|
|
||||||
inline void init_lib()
|
inline void init_lib()
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
@ -127,7 +130,12 @@ inline void init_lib()
|
|||||||
namespace WiimoteReal
|
namespace WiimoteReal
|
||||||
{
|
{
|
||||||
|
|
||||||
int PairUp(bool unpair = false);
|
template <typename T>
|
||||||
|
void ProcessWiimotes(bool new_scan, T& callback);
|
||||||
|
|
||||||
|
bool AttachWiimote(HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi);
|
||||||
|
void RemoveWiimote(HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi);
|
||||||
|
bool ForgetWiimote(HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi);
|
||||||
|
|
||||||
WiimoteScanner::WiimoteScanner()
|
WiimoteScanner::WiimoteScanner()
|
||||||
{
|
{
|
||||||
@ -137,24 +145,47 @@ WiimoteScanner::WiimoteScanner()
|
|||||||
WiimoteScanner::~WiimoteScanner()
|
WiimoteScanner::~WiimoteScanner()
|
||||||
{
|
{
|
||||||
// TODO: what do we want here?
|
// TODO: what do we want here?
|
||||||
//PairUp(true);
|
ProcessWiimotes(false, RemoveWiimote);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiimoteScanner::Update()
|
||||||
|
{
|
||||||
|
bool forgot_some = false;
|
||||||
|
|
||||||
|
ProcessWiimotes(false, [&](HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||||
|
{
|
||||||
|
forgot_some |= ForgetWiimote(hRadio, btdi);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Some hacks that allows disconnects to be detected before connections are handled
|
||||||
|
// workaround for wiimote 1 moving to slot 2 on temporary disconnect
|
||||||
|
if (forgot_some)
|
||||||
|
SLEEP(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and connect wiimotes.
|
// Find and connect wiimotes.
|
||||||
// Does not replace already found wiimotes even if they are disconnected.
|
// Does not replace already found wiimotes even if they are disconnected.
|
||||||
// wm is an array of max_wiimotes wiimotes
|
// wm is an array of max_wiimotes wiimotes
|
||||||
// Returns the total number of found and connected wiimotes.
|
// Returns the total number of found and connected wiimotes.
|
||||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||||
{
|
{
|
||||||
PairUp();
|
bool attached_some;
|
||||||
|
|
||||||
|
ProcessWiimotes(true, [&](HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||||
|
{
|
||||||
|
ForgetWiimote(hRadio, btdi);
|
||||||
|
attached_some |= AttachWiimote(hRadio, btdi);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hacks...
|
||||||
|
if (attached_some)
|
||||||
|
SLEEP(1000);
|
||||||
|
|
||||||
GUID device_id;
|
GUID device_id;
|
||||||
HANDLE dev;
|
|
||||||
HDEVINFO device_info;
|
HDEVINFO device_info;
|
||||||
DWORD len;
|
DWORD len;
|
||||||
SP_DEVICE_INTERFACE_DATA device_data;
|
SP_DEVICE_INTERFACE_DATA device_data;
|
||||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
||||||
HIDD_ATTRIBUTES attr;
|
|
||||||
|
|
||||||
device_data.cbSize = sizeof(device_data);
|
device_data.cbSize = sizeof(device_data);
|
||||||
|
|
||||||
@ -165,7 +196,7 @@ std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|||||||
device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||||
|
|
||||||
std::vector<Wiimote*> wiimotes;
|
std::vector<Wiimote*> wiimotes;
|
||||||
for (int index = 0; wiimotes.size() < max_wiimotes; ++index)
|
for (int index = 0; true; ++index)
|
||||||
{
|
{
|
||||||
free(detail_data);
|
free(detail_data);
|
||||||
detail_data = NULL;
|
detail_data = NULL;
|
||||||
@ -184,24 +215,7 @@ std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto const wm = new Wiimote;
|
auto const wm = new Wiimote;
|
||||||
|
|
||||||
// Open new device
|
|
||||||
#if 0
|
|
||||||
dev = CreateFile(detail_data->DevicePath,
|
|
||||||
(GENERIC_READ | GENERIC_WRITE),
|
|
||||||
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
|
||||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
|
||||||
if (dev == INVALID_HANDLE_VALUE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get device attributes
|
|
||||||
attr.Size = sizeof(attr);
|
|
||||||
HidD_GetAttributes(dev, &attr);
|
|
||||||
|
|
||||||
wm->dev_handle = dev;
|
|
||||||
#endif
|
|
||||||
wm->devicepath = detail_data->DevicePath;
|
wm->devicepath = detail_data->DevicePath;
|
||||||
|
|
||||||
wiimotes.push_back(wm);
|
wiimotes.push_back(wm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,9 +250,15 @@ bool WiimoteScanner::IsReady() const
|
|||||||
// Connect to a wiimote with a known device path.
|
// Connect to a wiimote with a known device path.
|
||||||
bool Wiimote::Connect()
|
bool Wiimote::Connect()
|
||||||
{
|
{
|
||||||
|
// This is where we disallow connecting to the same device twice
|
||||||
|
if (g_connected_devices.count(devicepath))
|
||||||
|
return false;
|
||||||
|
|
||||||
dev_handle = CreateFile(devicepath.c_str(),
|
dev_handle = CreateFile(devicepath.c_str(),
|
||||||
(GENERIC_READ | GENERIC_WRITE),
|
(GENERIC_READ | GENERIC_WRITE),
|
||||||
/*(FILE_SHARE_READ | FILE_SHARE_WRITE)*/ 0,
|
// TODO: Just do FILE_SHARE_READ and remove "g_connected_devices"?
|
||||||
|
// That is what "WiiYourself" does.
|
||||||
|
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
||||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||||
|
|
||||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||||
@ -247,11 +267,12 @@ bool Wiimote::Connect()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hid_overlap = OVERLAPPED();
|
||||||
hid_overlap.hEvent = CreateEvent(NULL, 1, 1, _T(""));
|
hid_overlap.hEvent = CreateEvent(NULL, 1, 1, _T(""));
|
||||||
hid_overlap.Offset = 0;
|
hid_overlap.Offset = 0;
|
||||||
hid_overlap.OffsetHigh = 0;
|
hid_overlap.OffsetHigh = 0;
|
||||||
|
|
||||||
// TODO: do this elsewhere
|
// TODO: thread isn't started here now, do this elsewhere
|
||||||
// This isn't as drastic as it sounds, since the process in which the threads
|
// This isn't as drastic as it sounds, since the process in which the threads
|
||||||
// reside is normal priority. Needed for keeping audio reports at a decent rate
|
// reside is normal priority. Needed for keeping audio reports at a decent rate
|
||||||
/*
|
/*
|
||||||
@ -261,15 +282,17 @@ bool Wiimote::Connect()
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
g_connected_devices.insert(devicepath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wiimote::Disconnect()
|
void Wiimote::Disconnect()
|
||||||
{
|
{
|
||||||
|
g_connected_devices.erase(devicepath);
|
||||||
|
|
||||||
CloseHandle(dev_handle);
|
CloseHandle(dev_handle);
|
||||||
dev_handle = 0;
|
dev_handle = 0;
|
||||||
|
|
||||||
//ResetEvent(&hid_overlap);
|
|
||||||
CloseHandle(hid_overlap.hEvent);
|
CloseHandle(hid_overlap.hEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,50 +306,73 @@ bool Wiimote::IsConnected() const
|
|||||||
// zero = error
|
// zero = error
|
||||||
int Wiimote::IORead(unsigned char* buf)
|
int Wiimote::IORead(unsigned char* buf)
|
||||||
{
|
{
|
||||||
|
// used below for a warning
|
||||||
*buf = 0;
|
*buf = 0;
|
||||||
|
|
||||||
DWORD b;
|
DWORD bytes;
|
||||||
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD, &b, &hid_overlap))
|
ResetEvent(hid_overlap.hEvent);
|
||||||
|
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap))
|
||||||
{
|
{
|
||||||
// Partial read
|
auto const err = GetLastError();
|
||||||
b = GetLastError();
|
|
||||||
if ((b == ERROR_HANDLE_EOF) || (b == ERROR_DEVICE_NOT_CONNECTED))
|
if (ERROR_IO_PENDING == err)
|
||||||
|
{
|
||||||
|
auto const r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
||||||
|
if (WAIT_TIMEOUT == r)
|
||||||
|
{
|
||||||
|
// Timeout - cancel and continue
|
||||||
|
if (*buf)
|
||||||
|
WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).",
|
||||||
|
WIIMOTE_DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
CancelIo(dev_handle);
|
||||||
|
bytes = -1;
|
||||||
|
}
|
||||||
|
else if (WAIT_FAILED == r)
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1);
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
else if (WAIT_OBJECT_0 == r)
|
||||||
|
{
|
||||||
|
if (!GetOverlappedResult(dev_handle, &hid_overlap, &bytes, TRUE))
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "GetOverlappedResult failed on wiimote %i.", index + 1);
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ERROR_HANDLE_EOF == err)
|
||||||
{
|
{
|
||||||
// Remote disconnect
|
// Remote disconnect
|
||||||
return 0;
|
bytes = 0;
|
||||||
}
|
}
|
||||||
|
else if (ERROR_DEVICE_NOT_CONNECTED == err)
|
||||||
auto const r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
|
||||||
if (r == WAIT_TIMEOUT)
|
|
||||||
{
|
{
|
||||||
// Timeout - cancel and continue
|
// Remote disconnect
|
||||||
if (*buf)
|
bytes = 0;
|
||||||
WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).",
|
|
||||||
WIIMOTE_DEFAULT_TIMEOUT);
|
|
||||||
|
|
||||||
CancelIo(dev_handle);
|
|
||||||
ResetEvent(hid_overlap.hEvent);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
else if (r == WAIT_FAILED)
|
else
|
||||||
{
|
{
|
||||||
WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1);
|
bytes = 0;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GetOverlappedResult(dev_handle, &hid_overlap, &b, 0))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This needs to be done even if ReadFile fails, essential during init
|
if (bytes > 0)
|
||||||
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
{
|
||||||
memmove(buf + 1, buf, MAX_PAYLOAD - 1);
|
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
||||||
buf[0] = 0xa1;
|
memmove(buf + 1, buf, MAX_PAYLOAD - 1);
|
||||||
|
buf[0] = 0xa1;
|
||||||
|
|
||||||
ResetEvent(hid_overlap.hEvent);
|
// TODO: is this really needed?
|
||||||
return MAX_PAYLOAD; // XXX
|
bytes = MAX_PAYLOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Wiimote::IOWrite(const u8* buf, int len)
|
int Wiimote::IOWrite(const u8* buf, int len)
|
||||||
@ -392,14 +438,12 @@ int Wiimote::IOWrite(const u8* buf, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiiMote Pair-Up, function will return amount of either new paired or unpaired devices
|
// invokes callback for each found wiimote bluetooth device
|
||||||
// negative number on failure
|
template <typename T>
|
||||||
int PairUp(bool unpair)
|
void ProcessWiimotes(bool new_scan, T& callback)
|
||||||
{
|
{
|
||||||
// match strings like "Nintendo RVL-WBC-01", "Nintendo RVL-CNT-01", "Nintendo RVL-CNT-01-TR"
|
// match strings like "Nintendo RVL-WBC-01", "Nintendo RVL-CNT-01", "Nintendo RVL-CNT-01-TR"
|
||||||
const std::wregex wiimote_device_name(L"Nintendo RVL-\\w{3}-\\d{2}(-\\w{2})?");
|
const std::wregex wiimote_device_name(L"Nintendo RVL-.*");
|
||||||
|
|
||||||
int nPaired = 0;
|
|
||||||
|
|
||||||
BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
|
BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
|
||||||
srch.dwSize = sizeof(srch);
|
srch.dwSize = sizeof(srch);
|
||||||
@ -409,7 +453,7 @@ int PairUp(bool unpair)
|
|||||||
// fConnected BT Devices
|
// fConnected BT Devices
|
||||||
srch.fReturnConnected = true;
|
srch.fReturnConnected = true;
|
||||||
srch.fReturnUnknown = true;
|
srch.fReturnUnknown = true;
|
||||||
srch.fIssueInquiry = true;
|
srch.fIssueInquiry = new_scan;
|
||||||
// multiple of 1.28 seconds
|
// multiple of 1.28 seconds
|
||||||
srch.cTimeoutMultiplier = 1;
|
srch.cTimeoutMultiplier = 1;
|
||||||
|
|
||||||
@ -418,14 +462,10 @@ int PairUp(bool unpair)
|
|||||||
|
|
||||||
HANDLE hRadio;
|
HANDLE hRadio;
|
||||||
|
|
||||||
// TODO: save radio(s) in the WiimoteScanner constructor
|
// TODO: save radio(s) in the WiimoteScanner constructor?
|
||||||
|
|
||||||
// Enumerate BT radios
|
// Enumerate BT radios
|
||||||
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
||||||
|
|
||||||
if (NULL == hFindRadio)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
while (hFindRadio)
|
while (hFindRadio)
|
||||||
{
|
{
|
||||||
BLUETOOTH_RADIO_INFO radioInfo;
|
BLUETOOTH_RADIO_INFO radioInfo;
|
||||||
@ -449,40 +489,7 @@ int PairUp(bool unpair)
|
|||||||
|
|
||||||
if (std::regex_match(btdi.szName, wiimote_device_name))
|
if (std::regex_match(btdi.szName, wiimote_device_name))
|
||||||
{
|
{
|
||||||
if (unpair)
|
callback(hRadio, btdi);
|
||||||
{
|
|
||||||
if (SUCCEEDED(Bth_BluetoothRemoveDevice(&btdi.Address)))
|
|
||||||
{
|
|
||||||
NOTICE_LOG(WIIMOTE,
|
|
||||||
"Pair-Up: Automatically removed BT Device on shutdown: %08x",
|
|
||||||
GetLastError());
|
|
||||||
++nPaired;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (false == btdi.fConnected)
|
|
||||||
{
|
|
||||||
// TODO: improve the read of the BT driver, esp. when batteries
|
|
||||||
// of the wiimote are removed while being fConnected
|
|
||||||
if (btdi.fRemembered)
|
|
||||||
{
|
|
||||||
// Make Windows forget old expired pairing. We can pretty
|
|
||||||
// much ignore the return value here. It either worked
|
|
||||||
// (ERROR_SUCCESS), or the device did not exist
|
|
||||||
// (ERROR_NOT_FOUND). In both cases, there is nothing left.
|
|
||||||
Bth_BluetoothRemoveDevice(&btdi.Address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate service
|
|
||||||
const DWORD hr = Bth_BluetoothSetServiceState(hRadio, &btdi,
|
|
||||||
&HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
++nPaired;
|
|
||||||
else
|
|
||||||
ERROR_LOG(WIIMOTE, "Pair-Up: BluetoothSetServiceState() returned %08x", hr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == Bth_BluetoothFindNextDevice(hFindDevice, &btdi))
|
if (false == Bth_BluetoothFindNextDevice(hFindDevice, &btdi))
|
||||||
@ -498,8 +505,53 @@ int PairUp(bool unpair)
|
|||||||
hFindRadio = NULL;
|
hFindRadio = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nPaired;
|
void RemoveWiimote(HANDLE, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||||
|
{
|
||||||
|
//if (btdi.fConnected)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(Bth_BluetoothRemoveDevice(&btdi.Address)))
|
||||||
|
{
|
||||||
|
NOTICE_LOG(WIIMOTE, "Removed BT Device", GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttachWiimote(HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||||
|
{
|
||||||
|
if (!btdi.fConnected && !btdi.fRemembered)
|
||||||
|
{
|
||||||
|
NOTICE_LOG(WIIMOTE, "Found wiimote. Enabling HID service.");
|
||||||
|
|
||||||
|
// Activate service
|
||||||
|
const DWORD hr = Bth_BluetoothSetServiceState(hRadio, &btdi,
|
||||||
|
&HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
ERROR_LOG(WIIMOTE, "Pair-Up: BluetoothSetServiceState() returned %08x", hr);
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes remembered non-connected devices
|
||||||
|
bool ForgetWiimote(HANDLE, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||||
|
{
|
||||||
|
if (!btdi.fConnected && btdi.fRemembered)
|
||||||
|
{
|
||||||
|
// We don't want "remembered" devices.
|
||||||
|
// SetServiceState seems to just fail with them.
|
||||||
|
// Make Windows forget about them.
|
||||||
|
NOTICE_LOG(WIIMOTE, "Removing remembered wiimote.");
|
||||||
|
Bth_BluetoothRemoveDevice(&btdi.Address);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -111,7 +111,10 @@ WiimoteScanner::WiimoteScanner()
|
|||||||
WiimoteScanner::~WiimoteScanner()
|
WiimoteScanner::~WiimoteScanner()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
void WiimoteScanner::Update()
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||||
{
|
{
|
||||||
// TODO: find the device in the constructor and save it for later
|
// TODO: find the device in the constructor and save it for later
|
||||||
|
|
||||||
@ -130,7 +133,7 @@ std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sbt = [[SearchBT alloc] init];
|
sbt = [[SearchBT alloc] init];
|
||||||
sbt->maxDevices = max_wiimotes;
|
sbt->maxDevices = 32;
|
||||||
bti = [[IOBluetoothDeviceInquiry alloc] init];
|
bti = [[IOBluetoothDeviceInquiry alloc] init];
|
||||||
[bti setDelegate: sbt];
|
[bti setDelegate: sbt];
|
||||||
[bti setInquiryLength: 2];
|
[bti setInquiryLength: 2];
|
||||||
@ -157,9 +160,6 @@ std::vector<Wiimote*> WiimoteScanner::FindWiimotes(size_t max_wiimotes)
|
|||||||
Wiimote *wm = new Wiimote();
|
Wiimote *wm = new Wiimote();
|
||||||
wm->btd = dev;
|
wm->btd = dev;
|
||||||
wiimotes.push_back(wm);
|
wiimotes.push_back(wm);
|
||||||
|
|
||||||
if(wiimotes.size() >= max_wiimotes)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[bth release];
|
[bth release];
|
||||||
|
@ -35,7 +35,7 @@ namespace WiimoteReal
|
|||||||
{
|
{
|
||||||
|
|
||||||
void HandleFoundWiimotes(const std::vector<Wiimote*>&);
|
void HandleFoundWiimotes(const std::vector<Wiimote*>&);
|
||||||
void HandleWiimoteConnect(Wiimote*);
|
void TryToConnectWiimote(Wiimote*);
|
||||||
void HandleWiimoteDisconnect(int index);
|
void HandleWiimoteDisconnect(int index);
|
||||||
|
|
||||||
bool g_real_wiimotes_initialized = false;
|
bool g_real_wiimotes_initialized = false;
|
||||||
@ -171,7 +171,10 @@ bool Wiimote::Read()
|
|||||||
rpt.second = IORead(rpt.first);
|
rpt.second = IORead(rpt.first);
|
||||||
|
|
||||||
if (0 == rpt.second)
|
if (0 == rpt.second)
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting wiimote %d.", index + 1);
|
||||||
Disconnect();
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
if (rpt.second > 0 && m_channel > 0)
|
if (rpt.second > 0 && m_channel > 0)
|
||||||
{
|
{
|
||||||
@ -192,12 +195,12 @@ bool Wiimote::Write()
|
|||||||
|
|
||||||
bool const is_speaker_data = rpt.first[1] == WM_WRITE_SPEAKER_DATA;
|
bool const is_speaker_data = rpt.first[1] == WM_WRITE_SPEAKER_DATA;
|
||||||
|
|
||||||
if (!is_speaker_data || last_audio_report.GetTimeDifference() > 5)
|
if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
|
||||||
{
|
{
|
||||||
IOWrite(rpt.first, rpt.second);
|
IOWrite(rpt.first, rpt.second);
|
||||||
|
|
||||||
if (is_speaker_data)
|
if (is_speaker_data)
|
||||||
last_audio_report.Update();
|
m_last_audio_report.Update();
|
||||||
|
|
||||||
delete[] rpt.first;
|
delete[] rpt.first;
|
||||||
m_write_reports.Pop();
|
m_write_reports.Pop();
|
||||||
@ -242,27 +245,24 @@ void Wiimote::Update()
|
|||||||
delete[] rpt.first;
|
delete[] rpt.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rumble briefly
|
bool Wiimote::Prepare(int _index)
|
||||||
void Wiimote::RumbleBriefly()
|
|
||||||
{
|
{
|
||||||
unsigned char buffer = 0x01;
|
index = _index;
|
||||||
DEBUG_LOG(WIIMOTE, "Starting rumble...");
|
|
||||||
QueueReport(WM_CMD_RUMBLE, &buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
SLEEP(200);
|
// Set the active LEDs.
|
||||||
|
u8 const led_report[] = {HID_TYPE_SET_REPORT, WM_CMD_LED, WIIMOTE_LED_1 << index};
|
||||||
|
|
||||||
DEBUG_LOG(WIIMOTE, "Stopping rumble...");
|
// Rumble briefly
|
||||||
buffer = 0x00;
|
u8 rumble_report[] = {HID_TYPE_SET_REPORT, WM_CMD_RUMBLE, 1};
|
||||||
QueueReport(WM_CMD_RUMBLE, &buffer, sizeof(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the active LEDs.
|
// don't really care what is read, just that reading works
|
||||||
// leds is a bitwise OR of WIIMOTE_LED_1 through WIIMOTE_LED_4.
|
u8 input_buf[MAX_PAYLOAD];
|
||||||
void Wiimote::SetLEDs(int new_leds)
|
|
||||||
{
|
// TODO: request status and check for sane response?
|
||||||
// Remove the lower 4 bits because they control rumble
|
|
||||||
u8 const buffer = (new_leds & 0xF0);
|
return (IOWrite(led_report, sizeof(led_report))
|
||||||
QueueReport(WM_CMD_LED, &buffer, sizeof(buffer));
|
&& IOWrite(rumble_report, sizeof(rumble_report))
|
||||||
|
&& (rumble_report[2] = 0, SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wiimote::EmuStart()
|
void Wiimote::EmuStart()
|
||||||
@ -290,48 +290,66 @@ unsigned int CalculateWantedWiimotes()
|
|||||||
return wanted_wiimotes;
|
return wanted_wiimotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiimoteScanner::WantWiimotes(size_t count)
|
void WiimoteScanner::WantWiimotes(bool do_want)
|
||||||
{
|
{
|
||||||
want_wiimotes = count;
|
m_want_wiimotes = do_want;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiimoteScanner::StartScanning()
|
void WiimoteScanner::StartScanning()
|
||||||
{
|
{
|
||||||
run_thread = true;
|
m_run_thread = true;
|
||||||
if (IsReady())
|
if (IsReady())
|
||||||
{
|
{
|
||||||
scan_thread = std::thread(std::mem_fun(&WiimoteScanner::ThreadFunc), this);
|
m_scan_thread = std::thread(std::mem_fun(&WiimoteScanner::ThreadFunc), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiimoteScanner::StopScanning()
|
void WiimoteScanner::StopScanning()
|
||||||
{
|
{
|
||||||
run_thread = false;
|
m_run_thread = false;
|
||||||
if (scan_thread.joinable())
|
if (m_scan_thread.joinable())
|
||||||
{
|
{
|
||||||
scan_thread.join();
|
m_scan_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckForDisconnectedWiimotes()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||||
|
if (g_wiimotes[i] && !g_wiimotes[i]->IsConnected())
|
||||||
|
HandleWiimoteDisconnect(i);
|
||||||
|
}
|
||||||
|
|
||||||
void WiimoteScanner::ThreadFunc()
|
void WiimoteScanner::ThreadFunc()
|
||||||
{
|
{
|
||||||
Common::SetCurrentThreadName("Wiimote Scanning Thread");
|
Common::SetCurrentThreadName("Wiimote Scanning Thread");
|
||||||
|
|
||||||
NOTICE_LOG(WIIMOTE, "Wiimote scanning has started");
|
NOTICE_LOG(WIIMOTE, "Wiimote scanning has started");
|
||||||
|
|
||||||
while (run_thread)
|
while (m_run_thread)
|
||||||
{
|
{
|
||||||
auto const found_wiimotes = FindWiimotes(want_wiimotes);
|
std::vector<Wiimote*> found_wiimotes;
|
||||||
HandleFoundWiimotes(found_wiimotes);
|
|
||||||
#if 1
|
//NOTICE_LOG(WIIMOTE, "in loop");
|
||||||
|
|
||||||
|
if (m_want_wiimotes)
|
||||||
|
found_wiimotes = FindWiimotes();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// TODO: this code here is ugly
|
// Does stuff needed to detect disconnects on Windows
|
||||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
Update();
|
||||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
|
||||||
if (g_wiimotes[i] && !g_wiimotes[i]->IsConnected())
|
|
||||||
HandleWiimoteDisconnect(i);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
//NOTICE_LOG(WIIMOTE, "after update");
|
||||||
|
|
||||||
|
// TODO: this is a fairly lame place for this
|
||||||
|
CheckForDisconnectedWiimotes();
|
||||||
|
|
||||||
|
HandleFoundWiimotes(found_wiimotes);
|
||||||
|
|
||||||
//std::this_thread::yield();
|
//std::this_thread::yield();
|
||||||
Common::SleepCurrentThread(500);
|
Common::SleepCurrentThread(500);
|
||||||
}
|
}
|
||||||
@ -396,8 +414,7 @@ void Initialize()
|
|||||||
if (g_real_wiimotes_initialized)
|
if (g_real_wiimotes_initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto const wanted_wiimotes = CalculateWantedWiimotes();
|
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||||
g_wiimote_scanner.WantWiimotes(wanted_wiimotes);
|
|
||||||
|
|
||||||
g_wiimote_scanner.StartScanning();
|
g_wiimote_scanner.StartScanning();
|
||||||
|
|
||||||
@ -431,26 +448,23 @@ void ChangeWiimoteSource(unsigned int index, int source)
|
|||||||
if (!(WIIMOTE_SRC_REAL & g_wiimote_sources[index]))
|
if (!(WIIMOTE_SRC_REAL & g_wiimote_sources[index]))
|
||||||
HandleWiimoteDisconnect(index);
|
HandleWiimoteDisconnect(index);
|
||||||
|
|
||||||
auto const wanted_wiimotes = CalculateWantedWiimotes();
|
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||||
g_wiimote_scanner.WantWiimotes(wanted_wiimotes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleWiimoteConnect(Wiimote* wm)
|
void TryToConnectWiimote(Wiimote* wm)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||||
|
|
||||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||||
{
|
{
|
||||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && !g_wiimotes[i])
|
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i]
|
||||||
|
&& !g_wiimotes[i]
|
||||||
|
&& wm->Connect()
|
||||||
|
&& wm->Prepare(i))
|
||||||
{
|
{
|
||||||
g_wiimotes[i] = wm;
|
g_wiimotes[i] = wm;
|
||||||
|
|
||||||
wm->index = i;
|
|
||||||
wm->StartThread();
|
wm->StartThread();
|
||||||
|
|
||||||
wm->DisableDataReporting();
|
|
||||||
wm->SetLEDs(WIIMOTE_LED_1 << i);
|
|
||||||
wm->RumbleBriefly();
|
|
||||||
|
|
||||||
Host_ConnectWiimote(i, true);
|
Host_ConnectWiimote(i, true);
|
||||||
|
|
||||||
@ -463,14 +477,12 @@ void HandleWiimoteConnect(Wiimote* wm)
|
|||||||
|
|
||||||
delete wm;
|
delete wm;
|
||||||
|
|
||||||
auto const want_wiimotes = CalculateWantedWiimotes();
|
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||||
g_wiimote_scanner.WantWiimotes(want_wiimotes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleWiimoteDisconnect(int index)
|
void HandleWiimoteDisconnect(int index)
|
||||||
{
|
{
|
||||||
// locked above
|
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||||
//std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
|
||||||
|
|
||||||
Host_ConnectWiimote(index, false);
|
Host_ConnectWiimote(index, false);
|
||||||
|
|
||||||
@ -482,19 +494,12 @@ void HandleWiimoteDisconnect(int index)
|
|||||||
NOTICE_LOG(WIIMOTE, "Disconnected wiimote %i.", index + 1);
|
NOTICE_LOG(WIIMOTE, "Disconnected wiimote %i.", index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const want_wiimotes = CalculateWantedWiimotes();
|
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||||
g_wiimote_scanner.WantWiimotes(want_wiimotes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleFoundWiimotes(const std::vector<Wiimote*>& wiimotes)
|
void HandleFoundWiimotes(const std::vector<Wiimote*>& wiimotes)
|
||||||
{
|
{
|
||||||
std::for_each(wiimotes.begin(), wiimotes.end(), [](Wiimote* const wm)
|
std::for_each(wiimotes.begin(), wiimotes.end(), TryToConnectWiimote);
|
||||||
{
|
|
||||||
if (wm->Connect())
|
|
||||||
HandleWiimoteConnect(wm);
|
|
||||||
else
|
|
||||||
delete wm;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from the GUI thread
|
// This is called from the GUI thread
|
||||||
@ -504,14 +509,13 @@ void Refresh()
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||||
|
|
||||||
|
CheckForDisconnectedWiimotes();
|
||||||
|
|
||||||
auto wanted_wiimotes = CalculateWantedWiimotes();
|
if (0 != CalculateWantedWiimotes())
|
||||||
auto const found_wiimotes = g_wiimote_scanner.FindWiimotes(wanted_wiimotes);
|
{
|
||||||
|
HandleFoundWiimotes(g_wiimote_scanner.FindWiimotes());
|
||||||
HandleFoundWiimotes(found_wiimotes);
|
}
|
||||||
|
|
||||||
wanted_wiimotes = CalculateWantedWiimotes();
|
|
||||||
g_wiimote_scanner.WantWiimotes(wanted_wiimotes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_wiimote_scanner.StartScanning();
|
g_wiimote_scanner.StartScanning();
|
||||||
|
@ -63,14 +63,16 @@ public:
|
|||||||
void EmuStop();
|
void EmuStop();
|
||||||
|
|
||||||
// connecting and disconnecting from physical devices
|
// connecting and disconnecting from physical devices
|
||||||
|
// (using address inserted by FindWiimotes)
|
||||||
|
|
||||||
|
// FYI, Connect/Disconnect are not thread safe even between unique objects (on windows)
|
||||||
bool Connect();
|
bool Connect();
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
// TODO: change to something like IsRelevant
|
// TODO: change to something like IsRelevant
|
||||||
bool IsConnected() const;
|
bool IsConnected() const;
|
||||||
|
|
||||||
void SetLEDs(int leds);
|
bool Prepare(int index);
|
||||||
void RumbleBriefly();
|
|
||||||
|
|
||||||
void DisableDataReporting();
|
void DisableDataReporting();
|
||||||
|
|
||||||
@ -116,7 +118,7 @@ private:
|
|||||||
Common::FifoQueue<Report> m_read_reports;
|
Common::FifoQueue<Report> m_read_reports;
|
||||||
Common::FifoQueue<Report> m_write_reports;
|
Common::FifoQueue<Report> m_write_reports;
|
||||||
|
|
||||||
Common::Timer last_audio_report;
|
Common::Timer m_last_audio_report;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WiimoteScanner
|
class WiimoteScanner
|
||||||
@ -127,22 +129,23 @@ public:
|
|||||||
|
|
||||||
bool IsReady() const;
|
bool IsReady() const;
|
||||||
|
|
||||||
void WantWiimotes(size_t count);
|
void WantWiimotes(bool do_want);
|
||||||
|
|
||||||
void StartScanning();
|
void StartScanning();
|
||||||
void StopScanning();
|
void StopScanning();
|
||||||
|
|
||||||
std::vector<Wiimote*> FindWiimotes(size_t max_wiimotes);
|
std::vector<Wiimote*> FindWiimotes();
|
||||||
|
|
||||||
|
// function called when not looking for more wiimotes
|
||||||
|
void Update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ThreadFunc();
|
void ThreadFunc();
|
||||||
|
|
||||||
std::thread scan_thread;
|
std::thread m_scan_thread;
|
||||||
|
|
||||||
volatile bool run_thread;
|
volatile bool m_run_thread;
|
||||||
|
volatile bool m_want_wiimotes;
|
||||||
// TODO: this should probably be atomic
|
|
||||||
volatile size_t want_wiimotes;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
@ -66,8 +66,9 @@
|
|||||||
|
|
||||||
// End Wiimote internal codes
|
// End Wiimote internal codes
|
||||||
|
|
||||||
#define MAX_PAYLOAD 32
|
// It's 23. NOT 32!
|
||||||
#define WIIMOTE_DEFAULT_TIMEOUT 30
|
#define MAX_PAYLOAD 23
|
||||||
|
#define WIIMOTE_DEFAULT_TIMEOUT 1000
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Available bluetooth stacks for Windows.
|
// Available bluetooth stacks for Windows.
|
||||||
|
Reference in New Issue
Block a user