@@ -30,8 +30,6 @@

#include "SFML/Network.hpp"

std::array<std::atomic<u32>, MAX_BBMOTES> g_wiimote_sources;

namespace WiimoteReal
{
using namespace WiimoteCommon;
@@ -49,9 +47,49 @@ static std::mutex s_known_ids_mutex;

std::mutex g_wiimotes_mutex;

// Real wii remotes assigned to a particular slot.
std::unique_ptr<Wiimote> g_wiimotes[MAX_BBMOTES];

struct WiimotePoolEntry
{
using Clock = std::chrono::steady_clock;

std::unique_ptr<Wiimote> wiimote;
Clock::time_point entry_time = Clock::now();

bool IsExpired() const
{
// Keep wii remotes in the pool for a bit before disconnecting them.
constexpr auto POOL_TIME = std::chrono::seconds{5};

return (Clock::now() - entry_time) > POOL_TIME;
}
};

// Connected wii remotes are placed here when no open slot is set to "Real".
// They are then automatically disconnected after some time.
std::vector<WiimotePoolEntry> g_wiimote_pool;

WiimoteScanner g_wiimote_scanner;

static void ProcessWiimotePool()
{
std::lock_guard<std::mutex> wm_lk(g_wiimotes_mutex);

for (auto it = g_wiimote_pool.begin(); it != g_wiimote_pool.end();)
{
if (it->IsExpired())
{
INFO_LOG(WIIMOTE, "Removing expired wiimote pool entry.");
it = g_wiimote_pool.erase(it);
}
else
{
++it;
}
}
}

Wiimote::Wiimote() = default;

void Wiimote::Shutdown()
@@ -478,7 +516,7 @@ static unsigned int CalculateWantedWiimotes()
// 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])
if (WiimoteCommon::GetSource(i) == WiimoteSource::Real && !g_wiimotes[i])
++wanted_wiimotes;

return wanted_wiimotes;
@@ -488,7 +526,7 @@ static unsigned int CalculateWantedBB()
{
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
unsigned int wanted_bb = 0;
if (WIIMOTE_SRC_REAL & g_wiimote_sources[WIIMOTE_BALANCE_BOARD] &&
if (WiimoteCommon::GetSource(WIIMOTE_BALANCE_BOARD) == WiimoteSource::Real &&
!g_wiimotes[WIIMOTE_BALANCE_BOARD])
++wanted_bb;
return wanted_bb;
@@ -556,6 +594,8 @@ void WiimoteScanner::ThreadFunc()
{
m_scan_mode_changed_event.WaitFor(std::chrono::milliseconds(500));

ProcessWiimotePool();

CheckForDisconnectedWiimotes();

if (m_scan_mode.load() == WiimoteScanMode::DO_NOT_SCAN)
@@ -619,9 +659,15 @@ bool Wiimote::Connect(int index)

if (!m_run_thread.IsSet())
{
m_run_thread.Set();
StartThread();
m_thread_ready_event.Wait();
}
else
{
IOWakeup();
}

return IsConnected();
}

@@ -652,7 +698,6 @@ void Wiimote::ThreadFunc()
}

m_thread_ready_event.Set();
m_run_thread.Set();

if (!ok)
{
@@ -698,16 +743,16 @@ void LoadSettings()
IniFile::Section& sec = *inifile.GetOrCreateSection(secname);

unsigned int source = 0;
sec.Get("Source", &source, i ? WIIMOTE_SRC_NONE : WIIMOTE_SRC_EMU);
g_wiimote_sources[i] = source;
sec.Get("Source", &source, i ? int(WiimoteSource::None) : int(WiimoteSource::Emulated));
WiimoteCommon::SetSource(i, WiimoteSource(source));
}

std::string secname("BalanceBoard");
IniFile::Section& sec = *inifile.GetOrCreateSection(secname);

unsigned int bb_source = 0;
sec.Get("Source", &bb_source, WIIMOTE_SRC_NONE);
g_wiimote_sources[WIIMOTE_BALANCE_BOARD] = bb_source;
sec.Get("Source", &bb_source, int(WiimoteSource::None));
WiimoteCommon::SetSource(WIIMOTE_BALANCE_BOARD, WiimoteSource(bb_source));
}

// config dialog calls this when some settings change
@@ -779,43 +824,10 @@ void Pause()
wiimote->EmuPause();
}

void ChangeWiimoteSource(unsigned int index, int source)
{
const int previous_source = g_wiimote_sources[index];

if (previous_source == source)
{
// No change. Do nothing.
return;
}

g_wiimote_sources[index] = source;

{
// Kill real wiimote connection (if any) (or swap to different slot)
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
if (auto removed_wiimote = std::move(g_wiimotes[index]))
{
// See if we can use this real Wiimote in another slot.
// Otherwise it will be disconnected.
TryToConnectWiimote(std::move(removed_wiimote));
}
}

// Reconnect to the emulator.
Core::RunAsCPUThread([index, previous_source, source] {
if (previous_source != WIIMOTE_SRC_NONE)
::Wiimote::Connect(index, false);

if (source == WIIMOTE_SRC_EMU)
::Wiimote::Connect(index, true);
});
}

// Called from the Wiimote scanner thread (or UI thread on source change)
static bool TryToConnectWiimoteToSlot(std::unique_ptr<Wiimote>& wm, unsigned int i)
{
if (WIIMOTE_SRC_REAL != g_wiimote_sources[i] || g_wiimotes[i])
if (WiimoteCommon::GetSource(i) != WiimoteSource::Real || g_wiimotes[i])
return false;

if (!wm->Connect(i))
@@ -840,7 +852,12 @@ static void TryToConnectWiimote(std::unique_ptr<Wiimote> wm)
return;
}

NOTICE_LOG(WIIMOTE, "No open slot for real wiimote.");
INFO_LOG(WIIMOTE, "No open slot for real wiimote, adding it to the pool.");
wm->Connect(0);
// Turn on LED 1 and 4 to make it apparant this remote is in the pool.
const u8 led_value = u8(LED::LED_1) | u8(LED::LED_4);
wm->QueueReport(OutputReportID::LED, &led_value, 1);
g_wiimote_pool.emplace_back(WiimotePoolEntry{std::move(wm)});
}

static void TryToConnectBalanceBoard(std::unique_ptr<Wiimote> wm)
@@ -927,4 +944,27 @@ bool IsNewWiimote(const std::string& identifier)
return s_known_ids.count(identifier) == 0;
}

void HandleWiimoteSourceChange(unsigned int index)
{
std::lock_guard<std::mutex> wm_lk(g_wiimotes_mutex);

if (WiimoteCommon::GetSource(index) != WiimoteSource::Real)
{
if (auto removed_wiimote = std::move(g_wiimotes[index]))
{
removed_wiimote->EmuStop();
// Try to use this removed wiimote in another slot.
TryToConnectWiimote(std::move(removed_wiimote));
}
}
else if (WiimoteCommon::GetSource(index) == WiimoteSource::Real)
{
// Try to fill this slot from the pool.
if (!g_wiimote_pool.empty())
{
if (TryToConnectWiimoteToSlot(g_wiimote_pool.front().wiimote, index))
g_wiimote_pool.erase(g_wiimote_pool.begin());
}
}
}
}; // namespace WiimoteReal
@@ -192,12 +192,12 @@ void ControlChannel(int wiimote_number, u16 channel_id, const void* data, u32 si
void Update(int wiimote_number);
bool CheckForButtonPress(int wiimote_number);

void ChangeWiimoteSource(unsigned int index, int source);

bool IsValidDeviceName(const std::string& name);
bool IsBalanceBoardName(const std::string& name);
bool IsNewWiimote(const std::string& identifier);

void HandleWiimoteSourceChange(unsigned int wiimote_number);

#ifdef ANDROID
void InitAdapterClass();
#endif
@@ -67,7 +67,7 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)

DEBUG_LOG(IOS_WIIMOTE, "Wii Remote %d BT ID %x,%x,%x,%x,%x,%x", i, tmp_bd[0], tmp_bd[1],
tmp_bd[2], tmp_bd[3], tmp_bd[4], tmp_bd[5]);
m_wiimotes.emplace_back(this, i, tmp_bd, g_wiimote_sources[i] != WIIMOTE_SRC_NONE);
m_wiimotes.emplace_back(this, i, tmp_bd, WiimoteCommon::GetSource(i) != WiimoteSource::None);
i++;
}

@@ -160,7 +160,7 @@ std::string GetInputDisplay()
{
if (SerialInterface::GetDeviceType(i) != SerialInterface::SIDEVICE_NONE)
s_controllers |= (1 << i);
if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
if (WiimoteCommon::GetSource(i) != WiimoteSource::None)
s_controllers |= (1 << (i + 4));
}
}
@@ -463,7 +463,7 @@ void ChangeWiiPads(bool instantly)
int controllers = 0;

for (int i = 0; i < MAX_WIIMOTES; ++i)
if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
if (WiimoteCommon::GetSource(i) != WiimoteSource::None)
controllers |= (1 << i);

// This is important for Wiimotes, because they can desync easily if they get re-activated
@@ -478,7 +478,7 @@ void ChangeWiiPads(bool instantly)
{
const bool is_using_wiimote = IsUsingWiimote(i);

g_wiimote_sources[i] = is_using_wiimote ? WIIMOTE_SRC_EMU : WIIMOTE_SRC_NONE;
WiimoteCommon::SetSource(i, is_using_wiimote ? WiimoteSource::Emulated : WiimoteSource::None);
if (!SConfig::GetInstance().m_bt_passthrough_enabled && bt)
bt->AccessWiimoteByIndex(i)->Activate(is_using_wiimote);
}
@@ -1510,7 +1510,10 @@ bool NetPlayClient::StartGame(const std::string& path)
}

for (unsigned int i = 0; i < 4; ++i)
WiimoteReal::ChangeWiimoteSource(i, m_wiimote_map[i] > 0 ? WIIMOTE_SRC_EMU : WIIMOTE_SRC_NONE);
{
WiimoteCommon::SetSource(i,
m_wiimote_map[i] > 0 ? WiimoteSource::Emulated : WiimoteSource::None);
}

// boot game
m_dialog->BootGame(path);
@@ -489,11 +489,13 @@ void ControllersWindow::LoadSettings()
m_gc_controller_boxes[i]->setCurrentIndex(*gc_index);
m_gc_buttons[i]->setEnabled(*gc_index != 0 && *gc_index != 6);
}
m_wiimote_boxes[i]->setCurrentIndex(g_wiimote_sources[i]);
m_wiimote_buttons[i]->setEnabled(g_wiimote_sources[i] != 0 && g_wiimote_sources[i] != 2);

const WiimoteSource source = WiimoteCommon::GetSource(int(i));
m_wiimote_boxes[i]->setCurrentIndex(int(source));
m_wiimote_buttons[i]->setEnabled(source == WiimoteSource::Emulated);
}
m_wiimote_real_balance_board->setChecked(g_wiimote_sources[WIIMOTE_BALANCE_BOARD] ==
WIIMOTE_SRC_REAL);
m_wiimote_real_balance_board->setChecked(WiimoteCommon::GetSource(WIIMOTE_BALANCE_BOARD) ==
WiimoteSource::Real);
m_wiimote_speaker_data->setChecked(SConfig::GetInstance().m_WiimoteEnableSpeaker);
m_wiimote_continuous_scanning->setChecked(SConfig::GetInstance().m_WiimoteContinuousScanning);

@@ -514,15 +516,15 @@ void ControllersWindow::SaveSettings()
SConfig::GetInstance().m_bt_passthrough_enabled = m_wiimote_passthrough->isChecked();
SConfig::GetInstance().m_BackgroundInput = m_common_bg_input->isChecked();

WiimoteReal::ChangeWiimoteSource(WIIMOTE_BALANCE_BOARD,
m_wiimote_real_balance_board->isChecked() ? WIIMOTE_SRC_REAL :
WIIMOTE_SRC_NONE);
WiimoteCommon::SetSource(WIIMOTE_BALANCE_BOARD, m_wiimote_real_balance_board->isChecked() ?
WiimoteSource::Real :
WiimoteSource::None);

for (size_t i = 0; i < m_wiimote_groups.size(); i++)
{
const int index = m_wiimote_boxes[i]->currentIndex();
m_wiimote_buttons[i]->setEnabled(index != 0 && index != 2);
WiimoteReal::ChangeWiimoteSource(static_cast<u32>(i), index);
const auto source = WiimoteSource(m_wiimote_boxes[i]->currentIndex());
m_wiimote_buttons[i]->setEnabled(source == WiimoteSource::Emulated);
WiimoteCommon::SetSource(static_cast<u32>(i), source);
}

UICommon::SaveWiimoteSources();
@@ -115,6 +115,8 @@

#if defined(HAVE_XRANDR) && HAVE_XRANDR
#include "UICommon/X11Utils.h"
// This #define within X11/X.h conflicts with our WiimoteSource enum.
#undef None
#endif

#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
@@ -1593,7 +1595,7 @@ void MainWindow::OnStartRecording()
if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[i]))
controllers |= (1 << i);

if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
if (WiimoteCommon::GetSource(i) != WiimoteSource::None)
controllers |= (1 << (i + 4));
}

@@ -1660,7 +1662,7 @@ void MainWindow::ShowTASInput()

for (int i = 0; i < num_wii_controllers; i++)
{
if (g_wiimote_sources[i] == WIIMOTE_SRC_EMU &&
if (WiimoteCommon::GetSource(i) == WiimoteSource::Emulated &&
(!Core::IsRunning() || SConfig::GetInstance().bWii))
{
m_wii_tas_input_windows[i]->show();
@@ -367,12 +367,12 @@ void SaveWiimoteSources()
secname += (char)('1' + i);
IniFile::Section& sec = *inifile.GetOrCreateSection(secname);

sec.Set("Source", (int)g_wiimote_sources[i]);
sec.Set("Source", int(WiimoteCommon::GetSource(i)));
}

std::string secname("BalanceBoard");
IniFile::Section& sec = *inifile.GetOrCreateSection(secname);
sec.Set("Source", (int)g_wiimote_sources[WIIMOTE_BALANCE_BOARD]);
sec.Set("Source", int(WiimoteCommon::GetSource(WIIMOTE_BALANCE_BOARD)));

inifile.Save(ini_filename);
}