Merge branch 'real-wiimote-scanning'
This commit is contained in:
commit
46adbfa9ed
|
@ -155,7 +155,6 @@ enum HOST_COMM
|
|||
WM_USER_CREATE,
|
||||
WM_USER_SETCURSOR,
|
||||
WM_USER_KEYDOWN,
|
||||
WIIMOTE_DISCONNECT // Disconnect Wiimote
|
||||
};
|
||||
|
||||
// Used for notification on emulation state
|
||||
|
|
|
@ -100,9 +100,6 @@ SConfig::SConfig()
|
|||
{
|
||||
// Make sure we have log manager
|
||||
LoadSettings();
|
||||
//Make sure we load any extra settings
|
||||
LoadSettingsWii();
|
||||
|
||||
}
|
||||
|
||||
void SConfig::Init()
|
||||
|
@ -244,6 +241,8 @@ void SConfig::SaveSettings()
|
|||
ini.Set("Core", "WiiSDCard", m_WiiSDCard);
|
||||
ini.Set("Core", "WiiKeyboard", m_WiiKeyboard);
|
||||
ini.Set("Core", "WiimoteReconnectOnLoad", m_WiimoteReconnectOnLoad);
|
||||
ini.Set("Core", "WiimoteContinuousScanning", m_WiimoteContinuousScanning);
|
||||
ini.Set("Core", "WiimoteEnableSpeaker", m_WiimoteEnableSpeaker);
|
||||
ini.Set("Core", "RunCompareServer", m_LocalCoreStartupParameter.bRunCompareServer);
|
||||
ini.Set("Core", "RunCompareClient", m_LocalCoreStartupParameter.bRunCompareClient);
|
||||
ini.Set("Core", "FrameLimit", m_Framelimit);
|
||||
|
@ -393,7 +392,9 @@ void SConfig::LoadSettings()
|
|||
|
||||
ini.Get("Core", "WiiSDCard", &m_WiiSDCard, false);
|
||||
ini.Get("Core", "WiiKeyboard", &m_WiiKeyboard, false);
|
||||
ini.Get("Core", "WiimoteReconnectOnLoad", &m_WiimoteReconnectOnLoad, true);
|
||||
ini.Get("Core", "WiimoteReconnectOnLoad", &m_WiimoteReconnectOnLoad, true);
|
||||
ini.Get("Core", "WiimoteContinuousScanning", &m_WiimoteContinuousScanning, false);
|
||||
ini.Get("Core", "WiimoteEnableSpeaker", &m_WiimoteEnableSpeaker, true);
|
||||
ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false);
|
||||
ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false);
|
||||
ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false);
|
||||
|
@ -428,18 +429,3 @@ void SConfig::LoadSettings()
|
|||
|
||||
m_SYSCONF = new SysConf();
|
||||
}
|
||||
void SConfig::LoadSettingsWii()
|
||||
{
|
||||
IniFile ini;
|
||||
//Wiimote configs
|
||||
ini.Load((File::GetUserPath(D_CONFIG_IDX) + "Dolphin.ini"));
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char SectionName[32];
|
||||
sprintf(SectionName, "Wiimote%i", i + 1);
|
||||
ini.Get(SectionName, "AutoReconnectRealWiimote", &m_WiiAutoReconnect[i], false);
|
||||
}
|
||||
ini.Load((File::GetUserPath(D_CONFIG_IDX) + "wiimote.ini"));
|
||||
ini.Get("Real", "Unpair", &m_WiiAutoUnpair, false);
|
||||
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ struct SConfig : NonCopyable
|
|||
// Wii Devices
|
||||
bool m_WiiSDCard;
|
||||
bool m_WiiKeyboard;
|
||||
bool m_WiiAutoReconnect[4];
|
||||
bool m_WiiAutoUnpair;
|
||||
bool m_WiimoteReconnectOnLoad;
|
||||
bool m_WiimoteContinuousScanning;
|
||||
bool m_WiimoteEnableSpeaker;
|
||||
|
||||
// name of the last used filename
|
||||
std::string m_LastFilename;
|
||||
|
@ -107,9 +107,6 @@ struct SConfig : NonCopyable
|
|||
// load settings
|
||||
void LoadSettings();
|
||||
|
||||
//Special load settings
|
||||
void LoadSettingsWii();
|
||||
|
||||
// Return the permanent and somewhat globally used instance of this struct
|
||||
static SConfig& GetInstance() {return(*m_Instance);}
|
||||
|
||||
|
|
|
@ -38,17 +38,12 @@ void Update(int _number);
|
|||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
unsigned int Initialize();
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Refresh();
|
||||
|
||||
void LoadSettings();
|
||||
|
||||
#ifdef _WIN32
|
||||
int PairUp(bool unpair = false);
|
||||
int UnPair();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -248,13 +248,12 @@ void Wiimote::RequestStatus(const wm_request_status* const rs)
|
|||
{
|
||||
using namespace WiimoteReal;
|
||||
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
g_wiimotes[m_index]->SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
wm_request_status rpt = {};
|
||||
g_wiimotes[m_index]->QueueReport(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -704,7 +704,7 @@ void Wiimote::Update()
|
|||
{
|
||||
using namespace WiimoteReal;
|
||||
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
Report rpt = g_wiimotes[m_index]->ProcessReadQueue();
|
||||
|
|
|
@ -201,6 +201,7 @@ struct wm_report
|
|||
#define WM_LEDS 0x11
|
||||
struct wm_leds {
|
||||
u8 rumble : 1;
|
||||
// real wii also sets bit 0x2 (unknown purpose)
|
||||
u8 : 3;
|
||||
u8 leds : 4;
|
||||
};
|
||||
|
@ -208,8 +209,9 @@ struct wm_leds {
|
|||
#define WM_REPORT_MODE 0x12
|
||||
struct wm_report_mode {
|
||||
u8 rumble : 1;
|
||||
u8 continuous : 1; // these 2 seem to be named wrong
|
||||
// unsure what "all_the_time" actually is, the real wii does set it (bit 0x2)
|
||||
u8 all_the_time : 1;
|
||||
u8 continuous : 1;
|
||||
u8 : 5;
|
||||
u8 mode;
|
||||
};
|
||||
|
|
|
@ -21,9 +21,25 @@
|
|||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
{
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
return std::vector<Wiimote*>();
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Wiimote::Connect()
|
||||
|
@ -31,22 +47,22 @@ bool Wiimote::Connect()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return false;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char* buf)
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(const u8* buf, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,16 +15,6 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
|
@ -32,114 +22,116 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "WiimoteReal.h"
|
||||
#include "Host.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
// Find wiimotes.
|
||||
// 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.
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
, device_id(-1)
|
||||
, device_sock(-1)
|
||||
{
|
||||
int device_id;
|
||||
int device_sock;
|
||||
int found_devices;
|
||||
int found_wiimotes = 0;
|
||||
int i;
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (i = 0; i < MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
}
|
||||
|
||||
// Get the id of the first bluetooth device.
|
||||
if ((device_id = hci_get_route(NULL)) < 0)
|
||||
device_id = hci_get_route(NULL);
|
||||
if (device_id < 0)
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "Bluetooth not found.");
|
||||
return found_wiimotes;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a socket to the device
|
||||
if ((device_sock = hci_open_dev(device_id)) < 0)
|
||||
device_sock = hci_open_dev(device_id);
|
||||
if (device_sock < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to open bluetooth.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
return device_sock > 0;
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{
|
||||
if (IsReady())
|
||||
close(device_sock);
|
||||
}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
// supposedly 1.28 seconds
|
||||
int const wait_len = 1;
|
||||
|
||||
int const max_infos = 255;
|
||||
inquiry_info scan_infos[max_infos] = {};
|
||||
auto* scan_infos_ptr = scan_infos;
|
||||
|
||||
// Scan for bluetooth devices
|
||||
int const found_devices = hci_inquiry(device_id, wait_len, max_infos, NULL, &scan_infos_ptr, IREQ_CACHE_FLUSH);
|
||||
if (found_devices < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Error searching for bluetooth devices.");
|
||||
return found_wiimotes;
|
||||
}
|
||||
|
||||
int try_num = 0;
|
||||
while ((try_num < 5) && (found_wiimotes < max_wiimotes))
|
||||
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
||||
|
||||
// Display discovered devices
|
||||
for (int i = 0; i < found_devices; ++i)
|
||||
{
|
||||
inquiry_info scan_info_arr[128];
|
||||
inquiry_info* scan_info = scan_info_arr;
|
||||
memset(&scan_info_arr, 0, sizeof(scan_info_arr));
|
||||
|
||||
// Scan for bluetooth devices for approximately one second
|
||||
found_devices = hci_inquiry(device_id, 1, 128, NULL, &scan_info, IREQ_CACHE_FLUSH);
|
||||
if (found_devices < 0)
|
||||
ERROR_LOG(WIIMOTE, "found a device...");
|
||||
|
||||
// BT names are a maximum of 248 bytes apparently
|
||||
char name[255] = {};
|
||||
if (hci_read_remote_name(device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 0) < 0)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Error searching for bluetooth devices.");
|
||||
return found_wiimotes;
|
||||
ERROR_LOG(WIIMOTE, "name request failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices);
|
||||
|
||||
// Display discovered devices
|
||||
for (i = 0; (i < found_devices) && (found_wiimotes < max_wiimotes); ++i)
|
||||
ERROR_LOG(WIIMOTE, "device name %s", name);
|
||||
if (IsValidBluetoothName(name))
|
||||
{
|
||||
char name[1000];
|
||||
memset(name, 0, sizeof(name));
|
||||
ERROR_LOG(WIIMOTE, "found a device...");
|
||||
if (hci_read_remote_name(device_sock, &scan_info[i].bdaddr, sizeof(name), name, 0) < 0) {
|
||||
ERROR_LOG(WIIMOTE, "name request failed");
|
||||
continue;
|
||||
}
|
||||
ERROR_LOG(WIIMOTE, "device name %s", name);
|
||||
if (IsValidBluetoothName(name))
|
||||
bool new_wiimote = true;
|
||||
|
||||
// TODO: do this
|
||||
|
||||
// Determine if this wiimote has already been found.
|
||||
//for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j)
|
||||
//{
|
||||
// if (wm[j] && bacmp(&scan_infos[i].bdaddr,&wm[j]->bdaddr) == 0)
|
||||
// new_wiimote = false;
|
||||
//}
|
||||
|
||||
if (new_wiimote)
|
||||
{
|
||||
bool new_wiimote = true;
|
||||
// Determine if this wiimote has already been found.
|
||||
for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j)
|
||||
{
|
||||
if (wm[j] && bacmp(&scan_info[i].bdaddr,&wm[j]->bdaddr) == 0)
|
||||
new_wiimote = false;
|
||||
}
|
||||
// Found a new device
|
||||
char bdaddr_str[18] = {};
|
||||
ba2str(&scan_infos[i].bdaddr, bdaddr_str);
|
||||
|
||||
if (new_wiimote)
|
||||
{
|
||||
// Find an unused slot
|
||||
unsigned int k = 0;
|
||||
for (; k < MAX_WIIMOTES && !(WIIMOTE_SRC_REAL & g_wiimote_sources[k] && !wm[k]); ++k);
|
||||
wm[k] = new Wiimote(k);
|
||||
|
||||
// Found a new device
|
||||
char bdaddr_str[18];
|
||||
ba2str(&scan_info[i].bdaddr, bdaddr_str);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found wiimote %i, (%s).",
|
||||
wm[k]->index + 1, bdaddr_str);
|
||||
|
||||
wm[k]->bdaddr = scan_info[i].bdaddr;
|
||||
++found_wiimotes;
|
||||
}
|
||||
auto* const wm = new Wiimote;
|
||||
wm->bdaddr = scan_infos[i].bdaddr;
|
||||
found_wiimotes.push_back(wm);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found wiimote (%s).", bdaddr_str);
|
||||
}
|
||||
}
|
||||
try_num++;
|
||||
}
|
||||
|
||||
close(device_sock);
|
||||
return found_wiimotes;
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known address.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
sockaddr_l2 addr;
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_bdaddr = bdaddr;
|
||||
|
@ -148,7 +140,7 @@ bool Wiimote::Connect()
|
|||
// Output channel
|
||||
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
|
||||
if ((cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(cmd_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DEBUG_LOG(WIIMOTE, "Unable to open output socket to wiimote.");
|
||||
close(cmd_sock);
|
||||
|
@ -159,7 +151,7 @@ bool Wiimote::Connect()
|
|||
// Input channel
|
||||
addr.l2_psm = htobs(WM_INPUT_CHANNEL);
|
||||
if ((int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(int_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DEBUG_LOG(WIIMOTE, "Unable to open input socket from wiimote.");
|
||||
close(int_sock);
|
||||
|
@ -168,57 +160,27 @@ bool Wiimote::Connect()
|
|||
return false;
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", index + 1);
|
||||
|
||||
m_connected = true;
|
||||
|
||||
// Do the handshake
|
||||
Handshake();
|
||||
|
||||
// Set LEDs
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect a wiimote.
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
close(cmd_sock);
|
||||
close(int_sock);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i.", index + 1);
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
Close();
|
||||
cmd_sock = -1;
|
||||
int_sock = -1;
|
||||
}
|
||||
|
||||
void Wiimote::Close()
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
if (IsOpen())
|
||||
{
|
||||
Host_ConnectWiimote(index, false);
|
||||
|
||||
close(cmd_sock);
|
||||
close(int_sock);
|
||||
|
||||
cmd_sock = -1;
|
||||
int_sock = -1;
|
||||
}
|
||||
return cmd_sock != -1;// && int_sock != -1;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
{
|
||||
return IsConnected() && cmd_sock != -1 && int_sock != -1;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char *buf)
|
||||
// positive = read packet
|
||||
// negative = didn't read packet
|
||||
// zero = error
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
// Block select for 1/2000th of a second
|
||||
timeval tv;
|
||||
|
@ -232,11 +194,11 @@ int Wiimote::IORead(unsigned char *buf)
|
|||
if (select(int_sock + 1, &fds, NULL, NULL, &tv) == -1)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", index + 1);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!FD_ISSET(int_sock, &fds))
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
// Read the pending message into the buffer
|
||||
int r = read(int_sock, buf, MAX_PAYLOAD);
|
||||
|
@ -250,21 +212,15 @@ int Wiimote::IORead(unsigned char *buf)
|
|||
// This can happen if the bluetooth dongle is disconnected
|
||||
ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. "
|
||||
"Wiimote %i will be disconnected.", index + 1);
|
||||
Close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (!r)
|
||||
{
|
||||
// Disconnect
|
||||
Close();
|
||||
r = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(u8 const* buf, int len)
|
||||
{
|
||||
return write(int_sock, buf, len);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <regex>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <ctime>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbt.h>
|
||||
|
@ -73,6 +76,8 @@ HINSTANCE bthprops_lib = NULL;
|
|||
|
||||
static int initialized = 0;
|
||||
|
||||
std::unordered_map<BTH_ADDR, std::time_t> g_connect_times;
|
||||
|
||||
inline void init_lib()
|
||||
{
|
||||
if (!initialized)
|
||||
|
@ -127,311 +132,332 @@ inline void init_lib()
|
|||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
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()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
{
|
||||
init_lib();
|
||||
}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{
|
||||
// TODO: what do we want here?
|
||||
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.
|
||||
// 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 and connected wiimotes.
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes)
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
GUID device_id;
|
||||
HANDLE dev;
|
||||
HDEVINFO device_info;
|
||||
int found_wiimotes = 0;
|
||||
DWORD len;
|
||||
SP_DEVICE_INTERFACE_DATA device_data;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
||||
HIDD_ATTRIBUTES attr;
|
||||
|
||||
init_lib();
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
ProcessWiimotes(true, [](HANDLE hRadio, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
|
||||
{
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
}
|
||||
|
||||
device_data.cbSize = sizeof(device_data);
|
||||
ForgetWiimote(hRadio, btdi);
|
||||
AttachWiimote(hRadio, btdi);
|
||||
});
|
||||
|
||||
// Get the device id
|
||||
GUID device_id;
|
||||
HidD_GetHidGuid(&device_id);
|
||||
|
||||
// Get all hid devices connected
|
||||
device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
HDEVINFO const device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
|
||||
|
||||
for (int index = 0; found_wiimotes < max_wiimotes; ++index)
|
||||
std::vector<Wiimote*> wiimotes;
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA device_data;
|
||||
device_data.cbSize = sizeof(device_data);
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
|
||||
|
||||
for (int index = 0; SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data); ++index)
|
||||
{
|
||||
if (detail_data)
|
||||
{
|
||||
free(detail_data);
|
||||
detail_data = NULL;
|
||||
}
|
||||
|
||||
// Query the next hid device info
|
||||
if (!SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data))
|
||||
break;
|
||||
|
||||
// Get the size of the data block required
|
||||
DWORD len;
|
||||
SetupDiGetDeviceInterfaceDetail(device_info, &device_data, NULL, 0, &len, NULL);
|
||||
detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(len);
|
||||
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Query the data for this device
|
||||
if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
||||
continue;
|
||||
|
||||
// Determine if this wiimote has already been found.
|
||||
bool found = false;
|
||||
for(int i = 0; i < MAX_WIIMOTES; i++)
|
||||
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
||||
{
|
||||
if(wm[i] && (wm[i]->devicepath == detail_data->DevicePath))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
auto const wm = new Wiimote;
|
||||
wm->devicepath = detail_data->DevicePath;
|
||||
wiimotes.push_back(wm);
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
// Open new device
|
||||
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);
|
||||
|
||||
// Find an unused slot
|
||||
unsigned int k = 0;
|
||||
for (; k < MAX_WIIMOTES && !(WIIMOTE_SRC_REAL & g_wiimote_sources[k] && !wm[k]); ++k);
|
||||
wm[k] = new Wiimote(k);
|
||||
wm[k]->dev_handle = dev;
|
||||
wm[k]->devicepath = detail_data->DevicePath;
|
||||
|
||||
if (!wm[k]->Connect())
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to connect to wiimote %i.", wm[k]->index + 1);
|
||||
delete wm[k];
|
||||
wm[k] = NULL;
|
||||
CloseHandle(dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
++found_wiimotes;
|
||||
}
|
||||
}
|
||||
|
||||
if (detail_data)
|
||||
free(detail_data);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(device_info);
|
||||
|
||||
return found_wiimotes;
|
||||
// Don't mind me, just a random sleep to fix stuff on Windows
|
||||
//if (!wiimotes.empty())
|
||||
// SLEEP(2000);
|
||||
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
// TODO: don't search for a radio each time
|
||||
|
||||
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
||||
radioParam.dwSize = sizeof(radioParam);
|
||||
|
||||
HANDLE hRadio;
|
||||
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
||||
|
||||
if (NULL != hFindRadio)
|
||||
{
|
||||
Bth_BluetoothFindRadioClose(hFindRadio);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known device path.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
if (IsConnected()) return false;
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
if (!dev_handle)
|
||||
dev_handle = CreateFile(devicepath.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
// Having no FILE_SHARE_WRITE disallows us from connecting to the same wiimote twice.
|
||||
// This is what "WiiYourself" does.
|
||||
FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
dev_handle = CreateFile(devicepath.c_str(),
|
||||
(GENERIC_READ | GENERIC_WRITE),
|
||||
(FILE_SHARE_READ | FILE_SHARE_WRITE),
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
|
||||
hid_overlap.hEvent = CreateEvent(NULL, 1, 1, _T(""));
|
||||
hid_overlap.Offset = 0;
|
||||
hid_overlap.OffsetHigh = 0;
|
||||
|
||||
m_connected = true;
|
||||
|
||||
// Try a handshake to see if the device is actually connected
|
||||
if (!Handshake())
|
||||
{
|
||||
m_connected = false;
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set LEDs
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
#if 0
|
||||
HIDD_ATTRIBUTES attr;
|
||||
attr.Size = sizeof(attr);
|
||||
if (!HidD_GetAttributes(dev_handle, &attr))
|
||||
{
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
hid_overlap_read = OVERLAPPED();
|
||||
hid_overlap_read.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
hid_overlap_write = OVERLAPPED();
|
||||
hid_overlap_write.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
// 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
|
||||
// reside is normal priority. Needed for keeping audio reports at a decent rate
|
||||
/*
|
||||
if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL))
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Failed to set wiimote thread priority");
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", index + 1);
|
||||
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
CloseHandle(dev_handle);
|
||||
dev_handle = 0;
|
||||
|
||||
ResetEvent(&hid_overlap);
|
||||
CloseHandle(hid_overlap_read.hEvent);
|
||||
CloseHandle(hid_overlap_write.hEvent);
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return dev_handle != 0;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char* buf)
|
||||
// positive = read packet
|
||||
// negative = didn't read packet
|
||||
// zero = error
|
||||
int Wiimote::IORead(u8* buf)
|
||||
{
|
||||
DWORD b, r;
|
||||
|
||||
init_lib();
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
// used below for a warning
|
||||
*buf = 0;
|
||||
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD, &b, &hid_overlap))
|
||||
|
||||
DWORD bytes;
|
||||
ResetEvent(hid_overlap_read.hEvent);
|
||||
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read))
|
||||
{
|
||||
// Partial read
|
||||
b = GetLastError();
|
||||
auto const err = GetLastError();
|
||||
|
||||
if ((b == ERROR_HANDLE_EOF) || (b == ERROR_DEVICE_NOT_CONNECTED))
|
||||
if (ERROR_IO_PENDING == err)
|
||||
{
|
||||
auto const r = WaitForSingleObject(hid_overlap_read.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_read, &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
|
||||
RealDisconnect();
|
||||
return 0;
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
||||
if (r == WAIT_TIMEOUT)
|
||||
else if (ERROR_DEVICE_NOT_CONNECTED == err)
|
||||
{
|
||||
// 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);
|
||||
ResetEvent(hid_overlap.hEvent);
|
||||
return 0;
|
||||
// Remote disconnect
|
||||
bytes = 0;
|
||||
}
|
||||
else if (r == WAIT_FAILED)
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(dev_handle, &hid_overlap, &b, 0))
|
||||
{
|
||||
return 0;
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be done even if ReadFile fails, essential during init
|
||||
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
||||
memmove(buf + 1, buf, MAX_PAYLOAD - 1);
|
||||
buf[0] = 0xa1;
|
||||
if (bytes > 0)
|
||||
{
|
||||
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
|
||||
std::copy_n(buf, MAX_PAYLOAD - 1, buf + 1);
|
||||
buf[0] = 0xa1;
|
||||
|
||||
ResetEvent(hid_overlap.hEvent);
|
||||
return MAX_PAYLOAD; // XXX
|
||||
// TODO: is this really needed?
|
||||
bytes = MAX_PAYLOAD;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char* buf, int len)
|
||||
int Wiimote::IOWrite(const u8* buf, int len)
|
||||
{
|
||||
DWORD bytes, dw;
|
||||
int i;
|
||||
|
||||
init_lib();
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
switch (stack)
|
||||
{
|
||||
case MSBT_STACK_UNKNOWN:
|
||||
{
|
||||
// Try to auto-detect the stack type
|
||||
if (i = WriteFile(dev_handle, buf + 1, 22, &bytes, &hid_overlap))
|
||||
{
|
||||
// Bluesoleil will always return 1 here, even if it's not connected
|
||||
stack = MSBT_STACK_BLUESOLEIL;
|
||||
return i;
|
||||
}
|
||||
case MSBT_STACK_UNKNOWN:
|
||||
{
|
||||
// Try to auto-detect the stack type
|
||||
stack = MSBT_STACK_BLUESOLEIL;
|
||||
if (IOWrite(buf, len))
|
||||
return 1;
|
||||
|
||||
if (i = HidD_SetOutputReport(dev_handle, buf + 1, len - 1))
|
||||
{
|
||||
stack = MSBT_STACK_MS;
|
||||
return i;
|
||||
}
|
||||
stack = MSBT_STACK_MS;
|
||||
if (IOWrite(buf, len))
|
||||
return 1;
|
||||
|
||||
dw = GetLastError();
|
||||
// Checking for 121 = timeout on semaphore/device off/disconnected to
|
||||
// avoid trouble with other stacks toshiba/widcomm
|
||||
if (dw == 121)
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "IOWrite[MSBT_STACK_UNKNOWN]: Timeout");
|
||||
RealDisconnect();
|
||||
}
|
||||
else ERROR_LOG(WIIMOTE,
|
||||
"IOWrite[MSBT_STACK_UNKNOWN]: ERROR: %08x", dw);
|
||||
return 0;
|
||||
}
|
||||
stack = MSBT_STACK_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
case MSBT_STACK_MS:
|
||||
{
|
||||
auto result = HidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, len - 1);
|
||||
//FlushFileBuffers(dev_handle);
|
||||
|
||||
case MSBT_STACK_MS:
|
||||
i = HidD_SetOutputReport(dev_handle, buf + 1, len - 1);
|
||||
dw = GetLastError();
|
||||
|
||||
if (dw == 121)
|
||||
if (!result)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
if (err == 121)
|
||||
{
|
||||
// Semaphore timeout
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to wiimote");
|
||||
RealDisconnect();
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
case MSBT_STACK_BLUESOLEIL:
|
||||
return WriteFile(dev_handle, buf + 1, 22, &bytes, &hid_overlap);
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
case MSBT_STACK_BLUESOLEIL:
|
||||
{
|
||||
u8 big_buf[MAX_PAYLOAD];
|
||||
if (len < MAX_PAYLOAD)
|
||||
{
|
||||
std::copy(buf, buf + len, big_buf);
|
||||
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
|
||||
buf = big_buf;
|
||||
}
|
||||
|
||||
ResetEvent(hid_overlap_write.hEvent);
|
||||
DWORD bytes = 0;
|
||||
if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write))
|
||||
{
|
||||
// WriteFile always returns true with bluesoleil.
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const err = GetLastError();
|
||||
if (ERROR_IO_PENDING == err)
|
||||
{
|
||||
CancelIo(dev_handle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UnPair()
|
||||
// invokes callback for each found wiimote bluetooth device
|
||||
template <typename T>
|
||||
void ProcessWiimotes(bool new_scan, T& callback)
|
||||
{
|
||||
// TODO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// WiiMote Pair-Up, function will return amount of either new paired or unpaired devices
|
||||
// negative number on failure
|
||||
int PairUp(bool unpair)
|
||||
{
|
||||
init_lib();
|
||||
|
||||
// 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})?");
|
||||
|
||||
int nPaired = 0;
|
||||
const std::wregex wiimote_device_name(L"Nintendo RVL-.*");
|
||||
|
||||
BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
|
||||
srch.dwSize = sizeof(srch);
|
||||
|
@ -441,20 +467,19 @@ int PairUp(bool unpair)
|
|||
// fConnected BT Devices
|
||||
srch.fReturnConnected = true;
|
||||
srch.fReturnUnknown = true;
|
||||
srch.fIssueInquiry = true;
|
||||
srch.cTimeoutMultiplier = 2; // == (2 * 1.28) seconds
|
||||
srch.fIssueInquiry = new_scan;
|
||||
// multiple of 1.28 seconds
|
||||
srch.cTimeoutMultiplier = 2;
|
||||
|
||||
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
||||
radioParam.dwSize = sizeof(radioParam);
|
||||
|
||||
HANDLE hRadio;
|
||||
|
||||
// TODO: save radio(s) in the WiimoteScanner constructor?
|
||||
|
||||
// Enumerate BT radios
|
||||
HBLUETOOTH_RADIO_FIND hFindRadio = Bth_BluetoothFindFirstRadio(&radioParam, &hRadio);
|
||||
|
||||
if (NULL == hFindRadio)
|
||||
return -1;
|
||||
|
||||
while (hFindRadio)
|
||||
{
|
||||
BLUETOOTH_RADIO_INFO radioInfo;
|
||||
|
@ -478,40 +503,7 @@ int PairUp(bool unpair)
|
|||
|
||||
if (std::regex_match(btdi.szName, wiimote_device_name))
|
||||
{
|
||||
if (unpair)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
callback(hRadio, btdi);
|
||||
}
|
||||
|
||||
if (false == Bth_BluetoothFindNextDevice(hFindDevice, &btdi))
|
||||
|
@ -527,8 +519,64 @@ int PairUp(bool unpair)
|
|||
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)
|
||||
{
|
||||
// We don't want "remembered" devices.
|
||||
// SetServiceState will just fail with them..
|
||||
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);
|
||||
|
||||
g_connect_times[btdi.Address.ullLong] = std::time(nullptr);
|
||||
|
||||
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)
|
||||
{
|
||||
// Time to avoid RemoveDevice after SetServiceState.
|
||||
// Sometimes SetServiceState takes a while..
|
||||
auto const avoid_forget_seconds = 5.0;
|
||||
|
||||
auto pair_time = g_connect_times.find(btdi.Address.ullLong);
|
||||
if (pair_time == g_connect_times.end()
|
||||
|| std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
|
||||
{
|
||||
// Make Windows forget about device so it will re-find it if visible.
|
||||
// This is also required to detect a disconnect for some reason..
|
||||
NOTICE_LOG(WIIMOTE, "Removing remembered wiimote.");
|
||||
Bth_BluetoothRemoveDevice(&btdi.Address);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -40,8 +40,11 @@
|
|||
{
|
||||
IOBluetoothDevice *device = [l2capChannel getDevice];
|
||||
WiimoteReal::Wiimote *wm = NULL;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++) {
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
if (WiimoteReal::g_wiimotes[i] == NULL)
|
||||
continue;
|
||||
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
|
||||
|
@ -77,8 +80,11 @@
|
|||
{
|
||||
IOBluetoothDevice *device = [l2capChannel getDevice];
|
||||
WiimoteReal::Wiimote *wm = NULL;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++) {
|
||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||||
{
|
||||
if (WiimoteReal::g_wiimotes[i] == NULL)
|
||||
continue;
|
||||
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
|
||||
|
@ -92,41 +98,47 @@
|
|||
|
||||
WARN_LOG(WIIMOTE, "Lost channel to wiimote %i", wm->index + 1);
|
||||
|
||||
wm->RealDisconnect();
|
||||
wm->Disconnect();
|
||||
}
|
||||
@end
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
// Find wiimotes.
|
||||
// wm is an array of max_wiimotes wiimotes
|
||||
// Returns the total number of found wiimotes.
|
||||
int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
||||
WiimoteScanner::WiimoteScanner()
|
||||
: m_run_thread()
|
||||
, m_want_wiimotes()
|
||||
{}
|
||||
|
||||
WiimoteScanner::~WiimoteScanner()
|
||||
{}
|
||||
|
||||
void WiimoteScanner::Update()
|
||||
{}
|
||||
|
||||
std::vector<Wiimote*> WiimoteScanner::FindWiimotes()
|
||||
{
|
||||
// TODO: find the device in the constructor and save it for later
|
||||
|
||||
std::vector<Wiimote*> wiimotes;
|
||||
IOBluetoothHostController *bth;
|
||||
IOBluetoothDeviceInquiry *bti;
|
||||
SearchBT *sbt;
|
||||
NSEnumerator *en;
|
||||
int found_devices = 0, found_wiimotes = 0;
|
||||
|
||||
// Count the number of already found wiimotes
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (wm[i])
|
||||
found_wiimotes++;
|
||||
|
||||
bth = [[IOBluetoothHostController alloc] init];
|
||||
if ([bth addressAsString] == nil) {
|
||||
if ([bth addressAsString] == nil)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "No bluetooth host controller");
|
||||
[bth release];
|
||||
return found_wiimotes;
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
sbt = [[SearchBT alloc] init];
|
||||
sbt->maxDevices = max_wiimotes - found_wiimotes;
|
||||
sbt->maxDevices = 32;
|
||||
bti = [[IOBluetoothDeviceInquiry alloc] init];
|
||||
[bti setDelegate: sbt];
|
||||
[bti setInquiryLength: 5];
|
||||
[bti setInquiryLength: 2];
|
||||
|
||||
if ([bti start] == kIOReturnSuccess)
|
||||
[bti retain];
|
||||
|
@ -136,10 +148,10 @@ int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
|||
CFRunLoopRun();
|
||||
|
||||
[bti stop];
|
||||
found_devices = [[bti foundDevices] count];
|
||||
int found_devices = [[bti foundDevices] count];
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found %i bluetooth device%c", found_devices,
|
||||
found_devices == 1 ? '\0' : 's');
|
||||
if (found_devices)
|
||||
NOTICE_LOG(WIIMOTE, "Found %i bluetooth devices", found_devices);
|
||||
|
||||
en = [[bti foundDevices] objectEnumerator];
|
||||
for (int i = 0; i < found_devices; i++)
|
||||
|
@ -147,42 +159,44 @@ int FindWiimotes(Wiimote **wm, int max_wiimotes)
|
|||
IOBluetoothDevice *dev = [en nextObject];
|
||||
if (!IsValidBluetoothName([[dev name] UTF8String]))
|
||||
continue;
|
||||
// Find an unused slot
|
||||
for (int k = 0; k < MAX_WIIMOTES; k++) {
|
||||
if (wm[k] != NULL ||
|
||||
!(g_wiimote_sources[k] & WIIMOTE_SRC_REAL))
|
||||
continue;
|
||||
|
||||
wm[k] = new Wiimote(k);
|
||||
wm[k]->btd = dev;
|
||||
found_wiimotes++;
|
||||
break;
|
||||
}
|
||||
Wiimote *wm = new Wiimote();
|
||||
wm->btd = dev;
|
||||
wiimotes.push_back(wm);
|
||||
}
|
||||
|
||||
[bth release];
|
||||
[bti release];
|
||||
[sbt release];
|
||||
|
||||
return found_wiimotes;
|
||||
return wiimotes;
|
||||
}
|
||||
|
||||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
// TODO: only return true when a BT device is present
|
||||
return true;
|
||||
}
|
||||
|
||||
// Connect to a wiimote with a known address.
|
||||
bool Wiimote::Connect()
|
||||
{
|
||||
ConnectBT *cbt = [[ConnectBT alloc] init];
|
||||
|
||||
if (IsConnected())
|
||||
return false;
|
||||
|
||||
ConnectBT *cbt = [[ConnectBT alloc] init];
|
||||
|
||||
[btd openL2CAPChannelSync: &cchan
|
||||
withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt];
|
||||
[btd openL2CAPChannelSync: &ichan
|
||||
withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt];
|
||||
if (ichan == NULL || cchan == NULL) {
|
||||
if (ichan == NULL || cchan == NULL)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Unable to open L2CAP channels "
|
||||
"for wiimote %i", index + 1);
|
||||
RealDisconnect();
|
||||
Disconnect();
|
||||
|
||||
[cbt release];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -196,40 +210,37 @@ bool Wiimote::Connect()
|
|||
|
||||
m_connected = true;
|
||||
|
||||
Handshake();
|
||||
SetLEDs(WIIMOTE_LED_1 << index);
|
||||
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
|
||||
[cbt release];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect a wiimote.
|
||||
void Wiimote::RealDisconnect()
|
||||
void Wiimote::Disconnect()
|
||||
{
|
||||
if (btd != NULL)
|
||||
[btd closeConnection];
|
||||
|
||||
if (ichan != NULL)
|
||||
[ichan release];
|
||||
|
||||
if (cchan != NULL)
|
||||
[cchan release];
|
||||
|
||||
btd = NULL;
|
||||
cchan = NULL;
|
||||
ichan = NULL;
|
||||
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", index + 1);
|
||||
|
||||
m_connected = false;
|
||||
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
|
||||
[btd closeConnection];
|
||||
[ichan release];
|
||||
[cchan release];
|
||||
btd = NULL;
|
||||
cchan = NULL;
|
||||
ichan = NULL;
|
||||
}
|
||||
|
||||
bool Wiimote::IsOpen() const
|
||||
bool Wiimote::IsConnected() const
|
||||
{
|
||||
return IsConnected();
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
int Wiimote::IORead(unsigned char *buf)
|
||||
|
@ -246,14 +257,14 @@ int Wiimote::IORead(unsigned char *buf)
|
|||
return bytes;
|
||||
}
|
||||
|
||||
int Wiimote::IOWrite(unsigned char *buf, int len)
|
||||
int Wiimote::IOWrite(const unsigned char *buf, int len)
|
||||
{
|
||||
IOReturn ret;
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
ret = [ichan writeAsync: buf length: len refcon: nil];
|
||||
ret = [ichan writeAsync: const_cast<void*>((void *)buf) length: len refcon: nil];
|
||||
|
||||
if (ret == kIOReturnSuccess)
|
||||
return len;
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "IniFile.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Timer.h"
|
||||
#include "Host.h"
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include "WiimoteReal.h"
|
||||
|
||||
|
@ -33,36 +35,43 @@ unsigned int g_wiimote_sources[MAX_WIIMOTES];
|
|||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
void HandleFoundWiimotes(const std::vector<Wiimote*>&);
|
||||
void TryToConnectWiimote(Wiimote*);
|
||||
void HandleWiimoteDisconnect(int index);
|
||||
void DoneWithWiimote(int index);
|
||||
|
||||
bool g_real_wiimotes_initialized = false;
|
||||
unsigned int g_wiimotes_found = 0;
|
||||
|
||||
std::mutex g_refresh_lock;
|
||||
std::recursive_mutex g_refresh_lock;
|
||||
|
||||
Wiimote *g_wiimotes[MAX_WIIMOTES];
|
||||
Wiimote* g_wiimotes[MAX_WIIMOTES];
|
||||
|
||||
Wiimote::Wiimote(const unsigned int _index)
|
||||
: index(_index)
|
||||
WiimoteScanner g_wiimote_scanner;
|
||||
|
||||
Wiimote::Wiimote()
|
||||
: index()
|
||||
#ifdef __APPLE__
|
||||
, inputlen(0)
|
||||
, btd(), ichan(), cchan(), inputlen(), m_connected()
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
, cmd_sock(-1), int_sock(-1)
|
||||
#elif defined(_WIN32)
|
||||
, dev_handle(0), stack(MSBT_STACK_UNKNOWN)
|
||||
#endif
|
||||
, leds(0), m_last_data_report(Report((u8 *)NULL, 0))
|
||||
, m_channel(0), m_connected(false)
|
||||
, m_last_data_report(Report((u8 *)NULL, 0))
|
||||
, m_channel(0), m_run_thread(false)
|
||||
{
|
||||
#if defined(__linux__) && HAVE_BLUEZ
|
||||
bdaddr = (bdaddr_t){{0, 0, 0, 0, 0, 0}};
|
||||
#endif
|
||||
|
||||
DisableDataReporting();
|
||||
}
|
||||
|
||||
Wiimote::~Wiimote()
|
||||
{
|
||||
RealDisconnect();
|
||||
StopThread();
|
||||
|
||||
if (IsConnected())
|
||||
Disconnect();
|
||||
|
||||
ClearReadQueue();
|
||||
|
||||
// clear write queue
|
||||
|
@ -71,26 +80,28 @@ Wiimote::~Wiimote()
|
|||
delete[] rpt.first;
|
||||
}
|
||||
|
||||
// Silly, copying data n stuff, o well, don't use this too often
|
||||
void Wiimote::SendPacket(const u8 rpt_id, const void* const data, const unsigned int size)
|
||||
// to be called from CPU thread
|
||||
void Wiimote::QueueReport(u8 rpt_id, const void* _data, unsigned int size)
|
||||
{
|
||||
auto const data = static_cast<const u8*>(_data);
|
||||
|
||||
Report rpt;
|
||||
rpt.second = size + 2;
|
||||
rpt.first = new u8[rpt.second];
|
||||
rpt.first[0] = 0xA1;
|
||||
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT;
|
||||
rpt.first[1] = rpt_id;
|
||||
memcpy(rpt.first + 2, data, size);
|
||||
std::copy(data, data + size, rpt.first + 2);
|
||||
m_write_reports.Push(rpt);
|
||||
}
|
||||
|
||||
void Wiimote::DisableDataReporting()
|
||||
{
|
||||
wm_report_mode rpt;
|
||||
wm_report_mode rpt = {};
|
||||
rpt.mode = WM_REPORT_CORE;
|
||||
rpt.all_the_time = 0;
|
||||
rpt.continuous = 0;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REPORT_MODE, &rpt, sizeof(rpt));
|
||||
QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
void Wiimote::ClearReadQueue()
|
||||
|
@ -111,7 +122,7 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
|
|||
{
|
||||
// Check for custom communication
|
||||
if (99 == channel)
|
||||
Disconnect();
|
||||
EmuStop();
|
||||
else
|
||||
{
|
||||
InterruptChannel(channel, data, size);
|
||||
|
@ -124,24 +135,24 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
|
|||
}
|
||||
}
|
||||
|
||||
void Wiimote::InterruptChannel(const u16 channel, const void* const data, const u32 size)
|
||||
void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const u32 size)
|
||||
{
|
||||
if (0 == m_channel) // first interrupt/control channel sent
|
||||
// first interrupt/control channel sent
|
||||
if (channel != m_channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
|
||||
ClearReadQueue();
|
||||
|
||||
// request status
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
EmuStart();
|
||||
}
|
||||
|
||||
m_channel = channel; // this right?
|
||||
|
||||
auto const data = static_cast<const u8*>(_data);
|
||||
|
||||
Report rpt;
|
||||
rpt.first = new u8[size];
|
||||
rpt.second = (u8)size;
|
||||
memcpy(rpt.first, (u8*)data, size);
|
||||
std::copy(data, data + size, rpt.first);
|
||||
|
||||
// Convert output DATA packets to SET_REPORT packets.
|
||||
// Nintendo Wiimotes work without this translation, but 3rd
|
||||
|
@ -150,13 +161,27 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
|
|||
{
|
||||
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT;
|
||||
}
|
||||
|
||||
if (rpt.first[0] == (WM_SET_REPORT | WM_BT_OUTPUT) &&
|
||||
rpt.first[1] == 0x18 && rpt.second == 23)
|
||||
|
||||
// Disallow games from turning off all of the LEDs.
|
||||
// It makes wiimote connection status confusing.
|
||||
if (rpt.first[1] == WM_LEDS)
|
||||
{
|
||||
m_audio_reports.Push(rpt);
|
||||
return;
|
||||
}
|
||||
auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt.first[2]);
|
||||
if (0 == leds_rpt.leds)
|
||||
{
|
||||
// Turn on ALL of the LEDs.
|
||||
leds_rpt.leds = 0xf;
|
||||
}
|
||||
}
|
||||
else if (rpt.first[1] == WM_WRITE_SPEAKER_DATA
|
||||
&& !SConfig::GetInstance().m_WiimoteEnableSpeaker)
|
||||
{
|
||||
// Translate speaker data reports into rumble reports.
|
||||
rpt.first[1] = WM_CMD_RUMBLE;
|
||||
// Keep only the rumble bit.
|
||||
rpt.first[2] &= 0x1;
|
||||
rpt.second = 3;
|
||||
}
|
||||
|
||||
m_write_reports.Push(rpt);
|
||||
}
|
||||
|
@ -168,7 +193,14 @@ bool Wiimote::Read()
|
|||
rpt.first = new unsigned char[MAX_PAYLOAD];
|
||||
rpt.second = IORead(rpt.first);
|
||||
|
||||
if (rpt.second > 0 && m_channel > 0) {
|
||||
if (0 == rpt.second)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting wiimote %d.", index + 1);
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
if (rpt.second > 0 && m_channel > 0)
|
||||
{
|
||||
// Add it to queue
|
||||
m_read_reports.Push(rpt);
|
||||
return true;
|
||||
|
@ -180,23 +212,25 @@ bool Wiimote::Read()
|
|||
|
||||
bool Wiimote::Write()
|
||||
{
|
||||
Report rpt;
|
||||
|
||||
if (last_audio_report.GetTimeDifference() > 5 && m_audio_reports.Pop(rpt))
|
||||
if (!m_write_reports.Empty())
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
last_audio_report.Update();
|
||||
Report const& rpt = m_write_reports.Front();
|
||||
|
||||
delete[] rpt.first;
|
||||
return true;
|
||||
bool const is_speaker_data = rpt.first[1] == WM_WRITE_SPEAKER_DATA;
|
||||
|
||||
if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
|
||||
if (is_speaker_data)
|
||||
m_last_audio_report.Update();
|
||||
|
||||
delete[] rpt.first;
|
||||
m_write_reports.Pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_write_reports.Pop(rpt))
|
||||
{
|
||||
IOWrite(rpt.first, rpt.second);
|
||||
delete[] rpt.first;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -221,6 +255,12 @@ Report Wiimote::ProcessReadQueue()
|
|||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
if (!IsConnected())
|
||||
{
|
||||
HandleWiimoteDisconnect(index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pop through the queued reports
|
||||
Report const rpt = ProcessReadQueue();
|
||||
|
||||
|
@ -234,123 +274,151 @@ void Wiimote::Update()
|
|||
delete[] rpt.first;
|
||||
}
|
||||
|
||||
void Wiimote::Disconnect()
|
||||
bool Wiimote::Prepare(int _index)
|
||||
{
|
||||
index = _index;
|
||||
|
||||
// core buttons, no continuous reporting
|
||||
u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_REPORT_TYPE, 0, 0x30};
|
||||
|
||||
// Set the active LEDs and turn on rumble.
|
||||
u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_LED, u8(WIIMOTE_LED_1 << index | 0x1)};
|
||||
|
||||
// Turn off rumble
|
||||
u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_RUMBLE, 0};
|
||||
|
||||
// Request status report
|
||||
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
// TODO: check for sane response?
|
||||
|
||||
return (IOWrite(mode_report, sizeof(mode_report))
|
||||
&& IOWrite(led_report, sizeof(led_report))
|
||||
&& (SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report)))
|
||||
&& IOWrite(req_status_report, sizeof(req_status_report)));
|
||||
}
|
||||
|
||||
void Wiimote::EmuStart()
|
||||
{
|
||||
DisableDataReporting();
|
||||
}
|
||||
|
||||
void Wiimote::EmuStop()
|
||||
{
|
||||
m_channel = 0;
|
||||
|
||||
DisableDataReporting();
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Stopping wiimote data reporting");
|
||||
}
|
||||
|
||||
bool Wiimote::IsConnected() const
|
||||
unsigned int CalculateWantedWiimotes()
|
||||
{
|
||||
return m_connected;
|
||||
// Figure out how many real wiimotes are required
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && !g_wiimotes[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
return wanted_wiimotes;
|
||||
}
|
||||
|
||||
// Rumble briefly
|
||||
void Wiimote::Rumble()
|
||||
void WiimoteScanner::WantWiimotes(bool do_want)
|
||||
{
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
unsigned char buffer = 0x01;
|
||||
DEBUG_LOG(WIIMOTE, "Starting rumble...");
|
||||
SendRequest(WM_CMD_RUMBLE, &buffer, 1);
|
||||
|
||||
SLEEP(200);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Stopping rumble...");
|
||||
buffer = 0x00;
|
||||
SendRequest(WM_CMD_RUMBLE, &buffer, 1);
|
||||
m_want_wiimotes = do_want;
|
||||
}
|
||||
|
||||
// Set the active LEDs.
|
||||
// leds is a bitwise or of WIIMOTE_LED_1 through WIIMOTE_LED_4.
|
||||
void Wiimote::SetLEDs(int new_leds)
|
||||
void WiimoteScanner::StartScanning()
|
||||
{
|
||||
unsigned char buffer;
|
||||
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
// Remove the lower 4 bits because they control rumble
|
||||
buffer = leds = (new_leds & 0xF0);
|
||||
|
||||
SendRequest(WM_CMD_LED, &buffer, 1);
|
||||
if (!m_run_thread && IsReady())
|
||||
{
|
||||
m_run_thread = true;
|
||||
m_scan_thread = std::thread(std::mem_fun(&WiimoteScanner::ThreadFunc), this);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a handshake
|
||||
bool Wiimote::Handshake()
|
||||
void WiimoteScanner::StopScanning()
|
||||
{
|
||||
// Set buffer[0] to 0x04 for continuous reporting
|
||||
unsigned char buffer[2] = {0x04, 0x30};
|
||||
|
||||
if (!IsConnected())
|
||||
return 0;
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Sending handshake to wiimote");
|
||||
|
||||
return SendRequest(WM_CMD_REPORT_TYPE, buffer, 2);
|
||||
m_run_thread = false;
|
||||
if (m_scan_thread.joinable())
|
||||
{
|
||||
m_scan_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Send a packet to the wiimote.
|
||||
// report_type should be one of WIIMOTE_CMD_LED, WIIMOTE_CMD_RUMBLE, etc.
|
||||
bool Wiimote::SendRequest(unsigned char report_type, unsigned char* data, int length)
|
||||
void CheckForDisconnectedWiimotes()
|
||||
{
|
||||
unsigned char buffer[32] = {WM_SET_REPORT | WM_BT_OUTPUT, report_type};
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
memcpy(buffer + 2, data, length);
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i] && !g_wiimotes[i]->IsConnected())
|
||||
HandleWiimoteDisconnect(i);
|
||||
}
|
||||
|
||||
return (IOWrite(buffer, length + 2) != 0);
|
||||
void WiimoteScanner::ThreadFunc()
|
||||
{
|
||||
Common::SetCurrentThreadName("Wiimote Scanning Thread");
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Wiimote scanning has started");
|
||||
|
||||
while (m_run_thread)
|
||||
{
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
//NOTICE_LOG(WIIMOTE, "in loop");
|
||||
|
||||
if (m_want_wiimotes)
|
||||
found_wiimotes = FindWiimotes();
|
||||
else
|
||||
{
|
||||
// Does stuff needed to detect disconnects on Windows
|
||||
Update();
|
||||
}
|
||||
|
||||
//NOTICE_LOG(WIIMOTE, "after update");
|
||||
|
||||
// TODO: this is a fairly lame place for this
|
||||
CheckForDisconnectedWiimotes();
|
||||
|
||||
HandleFoundWiimotes(found_wiimotes);
|
||||
|
||||
//std::this_thread::yield();
|
||||
Common::SleepCurrentThread(500);
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Wiimote scanning has stopped");
|
||||
}
|
||||
|
||||
void Wiimote::StartThread()
|
||||
{
|
||||
m_run_thread = true;
|
||||
m_wiimote_thread = std::thread(std::mem_fun(&Wiimote::ThreadFunc), this);
|
||||
}
|
||||
|
||||
void Wiimote::StopThread()
|
||||
{
|
||||
m_run_thread = false;
|
||||
if (m_wiimote_thread.joinable())
|
||||
m_wiimote_thread.join();
|
||||
}
|
||||
|
||||
void Wiimote::ThreadFunc()
|
||||
{
|
||||
char thname[] = "Wiimote # Thread";
|
||||
thname[8] = (char)('1' + index);
|
||||
Common::SetCurrentThreadName(thname);
|
||||
|
||||
// rumble briefly
|
||||
Rumble();
|
||||
Common::SetCurrentThreadName("Wiimote Device Thread");
|
||||
|
||||
// main loop
|
||||
while (IsOpen())
|
||||
while (m_run_thread && IsConnected())
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
while (Write()) {}
|
||||
Common::SleepCurrentThread(1);
|
||||
// Reading happens elsewhere on OSX
|
||||
bool const did_something = Write();
|
||||
#else
|
||||
bool const did_something = Write() || Read();
|
||||
#endif
|
||||
if (!did_something)
|
||||
Common::SleepCurrentThread(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Connect all discovered wiimotes
|
||||
// Return the number of wiimotes that successfully connected.
|
||||
static int ConnectWiimotes(Wiimote** wm)
|
||||
{
|
||||
int connected = 0;
|
||||
|
||||
for (int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (!wm[i] || wm[i]->IsConnected())
|
||||
continue;
|
||||
|
||||
if (wm[i]->Connect())
|
||||
++connected;
|
||||
else
|
||||
{
|
||||
delete wm[i];
|
||||
wm[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LoadSettings()
|
||||
{
|
||||
std::string ini_filename = File::GetUserPath(D_CONFIG_IDX) + WIIMOTE_INI_NAME ".ini";
|
||||
|
@ -368,116 +436,181 @@ void LoadSettings()
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int Initialize()
|
||||
// config dialog calls this when some settings change
|
||||
void Initialize()
|
||||
{
|
||||
// Return if already initialized
|
||||
if (SConfig::GetInstance().m_WiimoteContinuousScanning)
|
||||
g_wiimote_scanner.StartScanning();
|
||||
else
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
if (g_real_wiimotes_initialized)
|
||||
return g_wiimotes_found;
|
||||
return;
|
||||
|
||||
memset(g_wiimotes, 0, sizeof(g_wiimotes));
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteReal::Initialize");
|
||||
|
||||
// Only call FindWiimotes with the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
// Don't bother initializing if we don't want any real wiimotes
|
||||
if (0 == wanted_wiimotes)
|
||||
{
|
||||
g_wiimotes_found = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialized
|
||||
g_real_wiimotes_initialized = true;
|
||||
|
||||
g_wiimotes_found = FindWiimotes(g_wiimotes, wanted_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i Real Wiimotes, %i wanted",
|
||||
g_wiimotes_found, wanted_wiimotes);
|
||||
|
||||
#ifndef _WIN32
|
||||
atexit(WiimoteReal::Shutdown);
|
||||
g_wiimotes_found = ConnectWiimotes(g_wiimotes);
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Connected to %i Real Wiimotes", g_wiimotes_found);
|
||||
|
||||
return g_wiimotes_found;
|
||||
}
|
||||
|
||||
void Shutdown(void)
|
||||
{
|
||||
if (false == g_real_wiimotes_initialized)
|
||||
{
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (!g_real_wiimotes_initialized)
|
||||
return;
|
||||
|
||||
// Uninitialized
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown");
|
||||
|
||||
g_real_wiimotes_initialized = false;
|
||||
|
||||
// Delete wiimotes
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i])
|
||||
HandleWiimoteDisconnect(i);
|
||||
}
|
||||
|
||||
void ChangeWiimoteSource(unsigned int index, int source)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
g_wiimote_sources[index] = source;
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
// kill real connection (or swap to different slot)
|
||||
DoneWithWiimote(index);
|
||||
}
|
||||
|
||||
// reconnect to emu
|
||||
Host_ConnectWiimote(index, false);
|
||||
if (WIIMOTE_SRC_EMU & source)
|
||||
Host_ConnectWiimote(index, true);
|
||||
}
|
||||
|
||||
void TryToConnectWiimote(Wiimote* wm)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i]
|
||||
&& !g_wiimotes[i])
|
||||
{
|
||||
delete g_wiimotes[i];
|
||||
g_wiimotes[i] = NULL;
|
||||
if (wm->Connect() && wm->Prepare(i))
|
||||
{
|
||||
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", i + 1);
|
||||
|
||||
std::swap(g_wiimotes[i], wm);
|
||||
g_wiimotes[i]->StartThread();
|
||||
|
||||
Host_ConnectWiimote(i, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
|
||||
lk.unlock();
|
||||
|
||||
delete wm;
|
||||
}
|
||||
|
||||
void DoneWithWiimote(int index)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[index])
|
||||
{
|
||||
g_wiimotes[index]->StopThread();
|
||||
|
||||
// First see if we can use this real wiimote in another slot.
|
||||
for (unsigned int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i]
|
||||
&& !g_wiimotes[i])
|
||||
{
|
||||
if (g_wiimotes[index]->Prepare(i))
|
||||
{
|
||||
std::swap(g_wiimotes[i], g_wiimotes[index]);
|
||||
g_wiimotes[i]->StartThread();
|
||||
|
||||
Host_ConnectWiimote(i, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// else, just disconnect the wiimote
|
||||
HandleWiimoteDisconnect(index);
|
||||
}
|
||||
|
||||
void HandleWiimoteDisconnect(int index)
|
||||
{
|
||||
Wiimote* wm = NULL;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
std::swap(wm, g_wiimotes[index]);
|
||||
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
|
||||
}
|
||||
|
||||
if (wm)
|
||||
{
|
||||
delete wm;
|
||||
NOTICE_LOG(WIIMOTE, "Disconnected wiimote %i.", index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleFoundWiimotes(const std::vector<Wiimote*>& wiimotes)
|
||||
{
|
||||
std::for_each(wiimotes.begin(), wiimotes.end(), TryToConnectWiimote);
|
||||
}
|
||||
|
||||
// This is called from the GUI thread
|
||||
void Refresh()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
|
||||
#ifdef _WIN32
|
||||
Shutdown();
|
||||
Initialize();
|
||||
#else
|
||||
// Make sure real wiimotes have been initialized
|
||||
if (!g_real_wiimotes_initialized)
|
||||
g_wiimote_scanner.StopScanning();
|
||||
|
||||
{
|
||||
Initialize();
|
||||
return;
|
||||
std::unique_lock<std::recursive_mutex> lk(g_refresh_lock);
|
||||
std::vector<Wiimote*> found_wiimotes;
|
||||
|
||||
if (0 != CalculateWantedWiimotes())
|
||||
{
|
||||
// Don't hang dolphin when searching
|
||||
lk.unlock();
|
||||
found_wiimotes = g_wiimote_scanner.FindWiimotes();
|
||||
lk.lock();
|
||||
}
|
||||
|
||||
// Find the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
CheckForDisconnectedWiimotes();
|
||||
|
||||
// Remove wiimotes that are paired with slots no longer configured for a
|
||||
// real wiimote or that are disconnected
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (g_wiimotes[i] && (!(WIIMOTE_SRC_REAL & g_wiimote_sources[i]) ||
|
||||
!g_wiimotes[i]->IsConnected()))
|
||||
// Brief rumble for already connected wiimotes.
|
||||
for (int i = 0; i != MAX_WIIMOTES; ++i)
|
||||
{
|
||||
if (g_wiimotes[i])
|
||||
{
|
||||
delete g_wiimotes[i];
|
||||
g_wiimotes[i] = NULL;
|
||||
--g_wiimotes_found;
|
||||
g_wiimotes[i]->StopThread();
|
||||
g_wiimotes[i]->Prepare(i);
|
||||
g_wiimotes[i]->StartThread();
|
||||
}
|
||||
|
||||
// Scan for wiimotes if we want more
|
||||
if (wanted_wiimotes > g_wiimotes_found)
|
||||
{
|
||||
// Scan for wiimotes
|
||||
unsigned int num_wiimotes = FindWiimotes(g_wiimotes, wanted_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Found %i Real Wiimotes, %i wanted", num_wiimotes, wanted_wiimotes);
|
||||
|
||||
// Connect newly found wiimotes.
|
||||
int num_new_wiimotes = ConnectWiimotes(g_wiimotes);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Connected to %i additional Real Wiimotes", num_new_wiimotes);
|
||||
|
||||
g_wiimotes_found = num_wiimotes;
|
||||
}
|
||||
#endif
|
||||
|
||||
HandleFoundWiimotes(found_wiimotes);
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, _pData, _Size);
|
||||
|
@ -485,7 +618,7 @@ void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u3
|
|||
|
||||
void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->ControlChannel(_channelID, _pData, _Size);
|
||||
|
@ -495,15 +628,21 @@ void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32
|
|||
// Read the Wiimote once
|
||||
void Update(int _WiimoteNumber)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->Update();
|
||||
|
||||
// Wiimote::Update() may remove the wiimote if it was disconnected.
|
||||
if (!g_wiimotes[_WiimoteNumber])
|
||||
{
|
||||
Host_ConnectWiimote(_WiimoteNumber, false);
|
||||
}
|
||||
}
|
||||
|
||||
void StateChange(EMUSTATE_CHANGE newState)
|
||||
{
|
||||
//std::lock_guard<std::mutex> lk(g_refresh_lock);
|
||||
//std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
|
||||
|
||||
// TODO: disable/enable auto reporting, maybe
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define WIIMOTE_REAL_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "WiimoteRealBase.h"
|
||||
#include "ChunkFile.h"
|
||||
|
@ -42,7 +43,7 @@ class Wiimote : NonCopyable
|
|||
{
|
||||
friend class WiimoteEmu::Wiimote;
|
||||
public:
|
||||
Wiimote(const unsigned int _index);
|
||||
Wiimote();
|
||||
~Wiimote();
|
||||
|
||||
void ControlChannel(const u16 channel, const void* const data, const u32 size);
|
||||
|
@ -53,16 +54,29 @@ public:
|
|||
|
||||
bool Read();
|
||||
bool Write();
|
||||
bool Connect();
|
||||
bool IsConnected() const;
|
||||
bool IsOpen() const;
|
||||
void Disconnect();
|
||||
void DisableDataReporting();
|
||||
void Rumble();
|
||||
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
|
||||
void RealDisconnect();
|
||||
|
||||
const unsigned int index;
|
||||
void StartThread();
|
||||
void StopThread();
|
||||
|
||||
// "handshake" / stop packets
|
||||
void EmuStart();
|
||||
void EmuStop();
|
||||
|
||||
// connecting and disconnecting from physical devices
|
||||
// (using address inserted by FindWiimotes)
|
||||
bool Connect();
|
||||
void Disconnect();
|
||||
|
||||
// TODO: change to something like IsRelevant
|
||||
bool IsConnected() const;
|
||||
|
||||
bool Prepare(int index);
|
||||
|
||||
void DisableDataReporting();
|
||||
|
||||
void QueueReport(u8 rpt_id, const void* data, unsigned int size);
|
||||
|
||||
int index;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
IOBluetoothDevice *btd;
|
||||
|
@ -70,21 +84,19 @@ public:
|
|||
IOBluetoothL2CAPChannel *cchan;
|
||||
char input[MAX_PAYLOAD];
|
||||
int inputlen;
|
||||
bool m_connected;
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
bdaddr_t bdaddr; // Bluetooth address
|
||||
int cmd_sock; // Command socket
|
||||
int int_sock; // Interrupt socket
|
||||
|
||||
void Close();
|
||||
|
||||
#elif defined(_WIN32)
|
||||
std::string devicepath; // Unique wiimote reference
|
||||
//ULONGLONG btaddr; // Bluetooth address
|
||||
HANDLE dev_handle; // HID handle
|
||||
OVERLAPPED hid_overlap; // Overlap handle
|
||||
OVERLAPPED hid_overlap_read, hid_overlap_write; // Overlap handle
|
||||
enum win_bt_stack_t stack; // Type of bluetooth stack to use
|
||||
#endif
|
||||
unsigned char leds; // Currently lit leds
|
||||
|
||||
protected:
|
||||
Report m_last_data_report;
|
||||
|
@ -92,23 +104,58 @@ protected:
|
|||
|
||||
private:
|
||||
void ClearReadQueue();
|
||||
bool SendRequest(unsigned char report_type, unsigned char* data, int length);
|
||||
bool Handshake();
|
||||
void SetLEDs(int leds);
|
||||
int IORead(unsigned char* buf);
|
||||
int IOWrite(unsigned char* buf, int len);
|
||||
|
||||
int IORead(u8* buf);
|
||||
int IOWrite(u8 const* buf, int len);
|
||||
|
||||
void ThreadFunc();
|
||||
|
||||
bool m_connected;
|
||||
bool m_run_thread;
|
||||
std::thread m_wiimote_thread;
|
||||
|
||||
Common::FifoQueue<Report> m_read_reports;
|
||||
Common::FifoQueue<Report> m_write_reports;
|
||||
Common::FifoQueue<Report> m_audio_reports;
|
||||
|
||||
Common::Timer last_audio_report;
|
||||
Common::Timer m_last_audio_report;
|
||||
};
|
||||
|
||||
extern std::mutex g_refresh_lock;
|
||||
class WiimoteScanner
|
||||
{
|
||||
public:
|
||||
WiimoteScanner();
|
||||
~WiimoteScanner();
|
||||
|
||||
bool IsReady() const;
|
||||
|
||||
void WantWiimotes(bool do_want);
|
||||
|
||||
void StartScanning();
|
||||
void StopScanning();
|
||||
|
||||
std::vector<Wiimote*> FindWiimotes();
|
||||
|
||||
// function called when not looking for more wiimotes
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
std::thread m_scan_thread;
|
||||
|
||||
volatile bool m_run_thread;
|
||||
volatile bool m_want_wiimotes;
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
|
||||
#elif defined(__linux__) && HAVE_BLUEZ
|
||||
int device_id;
|
||||
int device_sock;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern std::recursive_mutex g_refresh_lock;
|
||||
extern WiimoteScanner g_wiimote_scanner;
|
||||
extern Wiimote *g_wiimotes[4];
|
||||
|
||||
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size);
|
||||
|
@ -119,6 +166,7 @@ void DoState(PointerWrap &p);
|
|||
void StateChange(EMUSTATE_CHANGE newState);
|
||||
|
||||
int FindWiimotes(Wiimote** wm, int max_wiimotes);
|
||||
void ChangeWiimoteSource(unsigned int index, int source);
|
||||
|
||||
bool IsValidBluetoothName(const std::string& name);
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#define WM_SET_REPORT 0xA0
|
||||
#endif
|
||||
|
||||
// TODO: duplicated in WiimoteHid.h
|
||||
// Commands
|
||||
#define WM_CMD_RUMBLE 0x10
|
||||
#define WM_CMD_LED 0x11
|
||||
|
@ -66,8 +67,9 @@
|
|||
|
||||
// End Wiimote internal codes
|
||||
|
||||
#define MAX_PAYLOAD 32
|
||||
#define WIIMOTE_DEFAULT_TIMEOUT 30
|
||||
// It's 23. NOT 32!
|
||||
#define MAX_PAYLOAD 23
|
||||
#define WIIMOTE_DEFAULT_TIMEOUT 1000
|
||||
|
||||
#ifdef _WIN32
|
||||
// Available bluetooth stacks for Windows.
|
||||
|
|
|
@ -211,8 +211,7 @@ void CWII_IPC_HLE_WiiMote::EventConnectionAccepted()
|
|||
void CWII_IPC_HLE_WiiMote::EventDisconnect()
|
||||
{
|
||||
// Send disconnect message to plugin
|
||||
u8 Message = WIIMOTE_DISCONNECT;
|
||||
Wiimote::ControlChannel(m_ConnectionHandle & 0xFF, 99, &Message, 0);
|
||||
Wiimote::ControlChannel(m_ConnectionHandle & 0xFF, 99, NULL, 0);
|
||||
|
||||
m_ConnectionState = CONN_INACTIVE;
|
||||
// Clear channel flags
|
||||
|
|
|
@ -100,37 +100,6 @@ CPanel::CPanel(
|
|||
else
|
||||
SetCursor(wxNullCursor);
|
||||
break;
|
||||
|
||||
case WIIMOTE_DISCONNECT:
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
|
||||
{
|
||||
const int wiimote_idx = lParam;
|
||||
const int wiimote_num = wiimote_idx + 1;
|
||||
|
||||
//Auto reconnect if option is turned on.
|
||||
//TODO: Make this only auto reconnect wiimotes that have the option activated.
|
||||
SConfig::GetInstance().LoadSettingsWii();//Make sure we are using the newest settings.
|
||||
if (SConfig::GetInstance().m_WiiAutoReconnect[wiimote_idx])
|
||||
{
|
||||
GetUsbPointer()->AccessWiiMote(wiimote_idx | 0x100)->Activate(true);
|
||||
NOTICE_LOG(WIIMOTE, "Wiimote %i has been auto-reconnected...", wiimote_num);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The Wiimote has been disconnected, we offer reconnect here.
|
||||
wxMessageDialog *dlg = new wxMessageDialog(
|
||||
this,
|
||||
wxString::Format(_("Wiimote %i has been disconnected by system.\nMaybe this game doesn't support multi-wiimote,\nor maybe it is due to idle time out or other reason.\nDo you want to reconnect immediately?"), wiimote_num),
|
||||
_("Reconnect Wiimote Confirm"),
|
||||
wxYES_NO | wxSTAY_ON_TOP | wxICON_INFORMATION, //wxICON_QUESTION,
|
||||
wxDefaultPosition);
|
||||
|
||||
if (dlg->ShowModal() == wxID_YES)
|
||||
GetUsbPointer()->AccessWiiMote(wiimote_idx | 0x100)->Activate(true);
|
||||
|
||||
dlg->Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -1355,10 +1355,6 @@ void CFrame::ConnectWiimote(int wm_idx, bool connect)
|
|||
wxString msg(wxString::Format(wxT("Wiimote %i %s"), wm_idx + 1,
|
||||
connect ? wxT("Connected") : wxT("Disconnected")));
|
||||
Core::DisplayMessage(msg.ToAscii(), 3000);
|
||||
|
||||
// Wait for the wiimote to connect
|
||||
while (GetUsbPointer()->AccessWiiMote(wm_idx | 0x100)->IsConnected() != connect)
|
||||
{}
|
||||
Host_UpdateMainFrame();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,10 +390,6 @@ void DolphinApp::InitLanguageSupport()
|
|||
int DolphinApp::OnExit()
|
||||
{
|
||||
WiimoteReal::Shutdown();
|
||||
#ifdef _WIN32
|
||||
if (SConfig::GetInstance().m_WiiAutoUnpair)
|
||||
WiimoteReal::UnPair();
|
||||
#endif
|
||||
VideoBackend::ClearList();
|
||||
SConfig::Shutdown();
|
||||
LogManager::Shutdown();
|
||||
|
|
|
@ -4,13 +4,6 @@
|
|||
#include "HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "Frame.h"
|
||||
|
||||
const wxString& ConnectedWiimotesString()
|
||||
{
|
||||
static wxString str;
|
||||
str.Printf(_("%i connected"), WiimoteReal::Initialize());
|
||||
return str;
|
||||
}
|
||||
|
||||
WiimoteConfigDiag::WiimoteConfigDiag(wxWindow* const parent, InputPlugin& plugin)
|
||||
: wxDialog(parent, -1, _("Dolphin Wiimote Configuration"), wxDefaultPosition, wxDefaultSize)
|
||||
, m_plugin(plugin)
|
||||
|
@ -64,27 +57,31 @@ WiimoteConfigDiag::WiimoteConfigDiag(wxWindow* const parent, InputPlugin& plugin
|
|||
|
||||
|
||||
// "Real wiimotes" controls
|
||||
connected_wiimotes_txt = new wxStaticText(this, -1, ConnectedWiimotesString());
|
||||
|
||||
wxButton* const refresh_btn = new wxButton(this, -1, _("Refresh"), wxDefaultPosition);
|
||||
refresh_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &WiimoteConfigDiag::RefreshRealWiimotes, this);
|
||||
|
||||
#ifdef _WIN32
|
||||
wxButton* const pairup_btn = new wxButton(this, -1, _("Pair Up"), wxDefaultPosition);
|
||||
pairup_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &WiimoteConfigDiag::PairUpRealWiimotes, this);
|
||||
#endif
|
||||
wxStaticBoxSizer* const real_wiimotes_group = new wxStaticBoxSizer(wxVERTICAL, this, _("Real Wiimotes"));
|
||||
|
||||
wxBoxSizer* const real_wiimotes_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
if (!WiimoteReal::g_wiimote_scanner.IsReady())
|
||||
real_wiimotes_group->Add(new wxStaticText(this, -1, _("A supported bluetooth device could not be found.\n"
|
||||
"You must manually connect your wiimotes.")), 0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
wxCheckBox* const continuous_scanning = new wxCheckBox(this, wxID_ANY, _("Continuous Scanning"));
|
||||
continuous_scanning->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &WiimoteConfigDiag::OnContinuousScanning, this);
|
||||
continuous_scanning->SetValue(SConfig::GetInstance().m_WiimoteContinuousScanning);
|
||||
|
||||
auto wiimote_speaker = new wxCheckBox(this, wxID_ANY, _("Enable Speaker Data"));
|
||||
wiimote_speaker->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &WiimoteConfigDiag::OnEnableSpeaker, this);
|
||||
wiimote_speaker->SetValue(SConfig::GetInstance().m_WiimoteEnableSpeaker);
|
||||
|
||||
real_wiimotes_sizer->Add(continuous_scanning, 0, wxALIGN_CENTER_VERTICAL);
|
||||
real_wiimotes_sizer->AddStretchSpacer(1);
|
||||
real_wiimotes_sizer->Add(refresh_btn, 0, wxALL | wxALIGN_CENTER, 5);
|
||||
|
||||
// "Real wiimotes" layout
|
||||
wxStaticBoxSizer* const real_wiimotes_group = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Real Wiimotes"));
|
||||
wxFlexGridSizer* const real_wiimotes_sizer = new wxFlexGridSizer(3, 5, 5);
|
||||
real_wiimotes_sizer->Add(connected_wiimotes_txt, 0, wxALIGN_CENTER_VERTICAL);
|
||||
#ifdef _WIN32
|
||||
real_wiimotes_sizer->Add(pairup_btn);
|
||||
#endif
|
||||
real_wiimotes_sizer->Add(refresh_btn);
|
||||
real_wiimotes_group->Add(real_wiimotes_sizer, 1, wxALL, 5);
|
||||
|
||||
real_wiimotes_group->Add(wiimote_speaker, 0);
|
||||
real_wiimotes_group->Add(real_wiimotes_sizer, 0, wxEXPAND);
|
||||
|
||||
// "General Settings" controls
|
||||
const wxString str[] = { _("Bottom"), _("Top") };
|
||||
|
@ -188,33 +185,9 @@ void WiimoteConfigDiag::ConfigEmulatedWiimote(wxCommandEvent& ev)
|
|||
m_emu_config_diag->Destroy();
|
||||
}
|
||||
|
||||
void WiimoteConfigDiag::UpdateGUI()
|
||||
{
|
||||
connected_wiimotes_txt->SetLabel(ConnectedWiimotesString());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void WiimoteConfigDiag::PairUpRealWiimotes(wxCommandEvent&)
|
||||
{
|
||||
const int paired = WiimoteReal::PairUp();
|
||||
|
||||
if (paired > 0)
|
||||
{
|
||||
// TODO: Maybe add a label of newly paired up wiimotes?
|
||||
WiimoteReal::Refresh();
|
||||
UpdateGUI();
|
||||
}
|
||||
else if (paired < 0)
|
||||
PanicAlertT("A supported bluetooth device could not be found!\n"
|
||||
"If you are not using Microsoft's bluetooth stack "
|
||||
"you must manually pair your wiimotes and use only the \"Refresh\" button.");
|
||||
}
|
||||
#endif
|
||||
|
||||
void WiimoteConfigDiag::RefreshRealWiimotes(wxCommandEvent&)
|
||||
{
|
||||
WiimoteReal::Refresh();
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
void WiimoteConfigDiag::SelectSource(wxCommandEvent& event)
|
||||
|
@ -222,30 +195,15 @@ void WiimoteConfigDiag::SelectSource(wxCommandEvent& event)
|
|||
// This needs to be changed now in order for refresh to work right.
|
||||
// Revert if the dialog is canceled.
|
||||
int index = m_wiimote_index_from_ctrl_id[event.GetId()];
|
||||
g_wiimote_sources[index] = event.GetInt();
|
||||
|
||||
WiimoteReal::ChangeWiimoteSource(index, event.GetInt());
|
||||
|
||||
if (g_wiimote_sources[index] != WIIMOTE_SRC_EMU && g_wiimote_sources[index] != WIIMOTE_SRC_HYBRID)
|
||||
wiimote_configure_bt[index]->Disable();
|
||||
else
|
||||
wiimote_configure_bt[index]->Enable();
|
||||
}
|
||||
|
||||
void WiimoteConfigDiag::UpdateWiimoteStatus()
|
||||
{
|
||||
for (int index = 0; index < 4; ++index)
|
||||
{
|
||||
if (m_orig_wiimote_sources[index] != g_wiimote_sources[index])
|
||||
{
|
||||
// Disconnect first, otherwise the new source doesn't seem to work
|
||||
CFrame::ConnectWiimote(index, false);
|
||||
// Connect wiimotes
|
||||
if (WIIMOTE_SRC_EMU & g_wiimote_sources[index])
|
||||
CFrame::ConnectWiimote(index, true);
|
||||
else if (WIIMOTE_SRC_REAL & g_wiimote_sources[index] && WiimoteReal::g_wiimotes[index])
|
||||
CFrame::ConnectWiimote(index, WiimoteReal::g_wiimotes[index]->IsConnected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiimoteConfigDiag::RevertSource()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
|
@ -267,7 +225,6 @@ void WiimoteConfigDiag::Save(wxCommandEvent& event)
|
|||
|
||||
sec.Set("Source", (int)g_wiimote_sources[i]);
|
||||
}
|
||||
UpdateWiimoteStatus();
|
||||
|
||||
inifile.Save(ini_filename);
|
||||
|
||||
|
|
|
@ -24,20 +24,13 @@ class WiimoteConfigDiag : public wxDialog
|
|||
public:
|
||||
WiimoteConfigDiag(wxWindow* const parent, InputPlugin& plugin);
|
||||
|
||||
#ifdef _WIN32
|
||||
void PairUpRealWiimotes(wxCommandEvent& event);
|
||||
#endif
|
||||
void RefreshRealWiimotes(wxCommandEvent& event);
|
||||
|
||||
|
||||
void SelectSource(wxCommandEvent& event);
|
||||
void UpdateWiimoteStatus();
|
||||
void RevertSource();
|
||||
|
||||
|
||||
void ConfigEmulatedWiimote(wxCommandEvent& event);
|
||||
void Save(wxCommandEvent& event);
|
||||
void UpdateGUI();
|
||||
|
||||
void OnSensorBarPos(wxCommandEvent& event)
|
||||
{
|
||||
|
@ -64,6 +57,17 @@ public:
|
|||
SConfig::GetInstance().m_WiimoteReconnectOnLoad = event.IsChecked();
|
||||
event.Skip();
|
||||
}
|
||||
void OnContinuousScanning(wxCommandEvent& event)
|
||||
{
|
||||
SConfig::GetInstance().m_WiimoteContinuousScanning = event.IsChecked();
|
||||
WiimoteReal::Initialize();
|
||||
event.Skip();
|
||||
}
|
||||
void OnEnableSpeaker(wxCommandEvent& event)
|
||||
{
|
||||
SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked();
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
private:
|
||||
void Cancel(wxCommandEvent& event);
|
||||
|
@ -76,8 +80,6 @@ private:
|
|||
|
||||
wxButton* wiimote_configure_bt[4];
|
||||
std::map<wxWindowID, unsigned int> m_wiimote_index_from_conf_bt_id;
|
||||
|
||||
wxStaticText* connected_wiimotes_txt;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -205,10 +205,6 @@ LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
|
|||
OnKeyDown(lParam);
|
||||
FreeLookInput((u32)wParam, lParam);
|
||||
}
|
||||
else if (wParam == WIIMOTE_DISCONNECT)
|
||||
{
|
||||
PostMessage(m_hParent, WM_USER, wParam, lParam);
|
||||
}
|
||||
break;
|
||||
|
||||
// Called when a screensaver wants to show up while this window is active
|
||||
|
|
Loading…
Reference in New Issue