diff --git a/Source/Core/Common/Src/Common.h b/Source/Core/Common/Src/Common.h index c3cab37dd8..0132915a5c 100644 --- a/Source/Core/Common/Src/Common.h +++ b/Source/Core/Common/Src/Common.h @@ -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 diff --git a/Source/Core/Core/Src/ConfigManager.cpp b/Source/Core/Core/Src/ConfigManager.cpp index f6194c1bb7..85ecc7143f 100644 --- a/Source/Core/Core/Src/ConfigManager.cpp +++ b/Source/Core/Core/Src/ConfigManager.cpp @@ -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); - -} diff --git a/Source/Core/Core/Src/ConfigManager.h b/Source/Core/Core/Src/ConfigManager.h index e92d29ee65..0c4ceffa9f 100644 --- a/Source/Core/Core/Src/ConfigManager.h +++ b/Source/Core/Core/Src/ConfigManager.h @@ -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);} diff --git a/Source/Core/Core/Src/HW/Wiimote.h b/Source/Core/Core/Src/HW/Wiimote.h index 5e0ab8d05e..dd0e6df389 100644 --- a/Source/Core/Core/Src/HW/Wiimote.h +++ b/Source/Core/Core/Src/HW/Wiimote.h @@ -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 diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp index 5c34fff9ac..5c2a28b57a 100644 --- a/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp @@ -248,13 +248,12 @@ void Wiimote::RequestStatus(const wm_request_status* const rs) { using namespace WiimoteReal; - std::lock_guard lk(g_refresh_lock); + std::lock_guard 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; diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp index 6fe6b81613..92018f7c21 100644 --- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp @@ -704,7 +704,7 @@ void Wiimote::Update() { using namespace WiimoteReal; - std::lock_guard lk(g_refresh_lock); + std::lock_guard lk(g_refresh_lock); if (g_wiimotes[m_index]) { Report rpt = g_wiimotes[m_index]->ProcessReadQueue(); diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h index fd7c549e8d..9d1a2092a0 100644 --- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h +++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h @@ -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; }; diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IODummy.cpp b/Source/Core/Core/Src/HW/WiimoteReal/IODummy.cpp index 49afee8ad1..3a017354eb 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IODummy.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/IODummy.cpp @@ -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 WiimoteScanner::FindWiimotes() +{ + return std::vector(); +} + +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; } diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp b/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp index ec838d9313..87ddc5086b 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp @@ -15,16 +15,6 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#include -#include - -#include -#include -#include -#include -#include -#include - #include #include #include @@ -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 WiimoteScanner::FindWiimotes() +{ + std::vector 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); } diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp index 529a48d15d..61abeffa2d 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include #include #include @@ -73,6 +76,8 @@ HINSTANCE bthprops_lib = NULL; static int initialized = 0; +std::unordered_map g_connect_times; + inline void init_lib() { if (!initialized) @@ -127,311 +132,332 @@ inline void init_lib() namespace WiimoteReal { +template +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 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 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(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 +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; } }; diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm b/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm index 64de880180..cec18ed63a 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm +++ b/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm @@ -40,8 +40,11 @@ { IOBluetoothDevice *device = [l2capChannel getDevice]; WiimoteReal::Wiimote *wm = NULL; + + std::lock_guard 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 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 WiimoteScanner::FindWiimotes() { + // TODO: find the device in the constructor and save it for later + + std::vector 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 *)buf) length: len refcon: nil]; if (ret == kIOReturnSuccess) return len; diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp index c726a836d9..3608c060fc 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp @@ -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&); +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(_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(_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(&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 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 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 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 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 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 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 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 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& wiimotes) +{ + std::for_each(wiimotes.begin(), wiimotes.end(), TryToConnectWiimote); } // This is called from the GUI thread void Refresh() { - std::lock_guard 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 lk(g_refresh_lock); + std::vector 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 lk(g_refresh_lock); + std::lock_guard 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 lk(g_refresh_lock); + std::lock_guard 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 lk(g_refresh_lock); + std::lock_guard 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 lk(g_refresh_lock); + //std::lock_guard lk(g_refresh_lock); // TODO: disable/enable auto reporting, maybe } diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h index edd4b4f71b..793a13ceb0 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h +++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h @@ -20,6 +20,7 @@ #define WIIMOTE_REAL_H #include +#include #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 m_read_reports; Common::FifoQueue m_write_reports; - Common::FifoQueue 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 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); diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteRealBase.h b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteRealBase.h index 75a6d3e6a3..1a1f6676cf 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteRealBase.h +++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteRealBase.h @@ -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. diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_WiiMote.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_WiiMote.cpp index 6e06f60dd6..b3449ec059 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_WiiMote.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_WiiMote.cpp @@ -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 diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index d390b8afcd..46b3a1e8e5 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -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; diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index 56dde8e2ce..cb2750ade4 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -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(); } } diff --git a/Source/Core/DolphinWX/Src/Main.cpp b/Source/Core/DolphinWX/Src/Main.cpp index 338ed59547..d9d1908de3 100644 --- a/Source/Core/DolphinWX/Src/Main.cpp +++ b/Source/Core/DolphinWX/Src/Main.cpp @@ -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(); diff --git a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.cpp b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.cpp index ea23e532e7..475718ed2e 100644 --- a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.cpp +++ b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.cpp @@ -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); diff --git a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h index 2b2e60de4a..4c33661ca2 100644 --- a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h +++ b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h @@ -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 m_wiimote_index_from_conf_bt_id; - - wxStaticText* connected_wiimotes_txt; }; diff --git a/Source/Core/VideoCommon/Src/EmuWindow.cpp b/Source/Core/VideoCommon/Src/EmuWindow.cpp index cbb0bc31ac..81227ba0ca 100644 --- a/Source/Core/VideoCommon/Src/EmuWindow.cpp +++ b/Source/Core/VideoCommon/Src/EmuWindow.cpp @@ -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