Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a pairing utility for Wiimotes to Cemu #941

Merged
merged 3 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ add_library(CemuGui
MemorySearcherTool.h
PadViewFrame.cpp
PadViewFrame.h
PairingDialog.cpp
PairingDialog.h
TitleManager.cpp
TitleManager.h
windows/PPCThreadsViewer
Expand Down Expand Up @@ -170,3 +172,7 @@ if (ENABLE_WXWIDGETS)
# PUBLIC because wx/app.h is included in CemuApp.h
target_link_libraries(CemuGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc)
endif()

if(WIN32)
target_link_libraries(CemuGui PRIVATE bthprops)
endif()
1 change: 1 addition & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2160,6 +2160,7 @@ void MainWindow::RecreateMenu()
m_memorySearcherMenuItem->Enable(false);
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager"));

m_menuBar->Append(toolsMenu, _("&Tools"));

// cpu timer speed menu
Expand Down
236 changes: 236 additions & 0 deletions src/gui/PairingDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#include "gui/wxgui.h"
#include "gui/PairingDialog.h"

#if BOOST_OS_WINDOWS
#include <bluetoothapis.h>
#endif

wxDECLARE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent);

PairingDialog::PairingDialog(wxWindow* parent)
: wxDialog(parent, wxID_ANY, _("Pairing..."), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX)
{
auto* sizer = new wxBoxSizer(wxVERTICAL);
m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(350, 20), wxGA_HORIZONTAL);
m_gauge->SetValue(0);
sizer->Add(m_gauge, 0, wxALL | wxEXPAND, 5);

auto* rows = new wxFlexGridSizer(0, 2, 0, 0);
rows->AddGrowableCol(1);

m_text = new wxStaticText(this, wxID_ANY, _("Searching for controllers..."));
rows->Add(m_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);

{
auto* right_side = new wxBoxSizer(wxHORIZONTAL);

m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel"));
m_cancelButton->Bind(wxEVT_BUTTON, &PairingDialog::OnCancelButton, this);
right_side->Add(m_cancelButton, 0, wxALL, 5);

rows->Add(right_side, 1, wxALIGN_RIGHT, 5);
}

sizer->Add(rows, 0, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer);
Centre(wxBOTH);

Bind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this);
Bind(wxEVT_PROGRESS_PAIR, &PairingDialog::OnGaugeUpdate, this);

m_thread = std::thread(&PairingDialog::WorkerThread, this);
}

PairingDialog::~PairingDialog()
{
Unbind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this);
}

void PairingDialog::OnClose(wxCloseEvent& event)
{
event.Skip();

m_threadShouldQuit = true;
if (m_thread.joinable())
m_thread.join();
}

void PairingDialog::OnCancelButton(const wxCommandEvent& event)
{
Close();
}

void PairingDialog::OnGaugeUpdate(wxCommandEvent& event)
{
PairingState state = (PairingState)event.GetInt();

switch (state)
{
case PairingState::Pairing:
{
m_text->SetLabel(_("Found controller. Pairing..."));
m_gauge->SetValue(50);
break;
}

case PairingState::Finished:
{
m_text->SetLabel(_("Successfully paired the controller."));
m_gauge->SetValue(100);
m_cancelButton->SetLabel(_("Close"));
break;
}

case PairingState::NoBluetoothAvailable:
{
m_text->SetLabel(_("Failed to find a suitable Bluetooth radio."));
m_gauge->SetValue(0);
m_cancelButton->SetLabel(_("Close"));
break;
}

case PairingState::BluetoothFailed:
{
m_text->SetLabel(_("Failed to search for controllers."));
m_gauge->SetValue(0);
m_cancelButton->SetLabel(_("Close"));
break;
}

case PairingState::PairingFailed:
{
m_text->SetLabel(_("Failed to pair with the found controller."));
m_gauge->SetValue(0);
m_cancelButton->SetLabel(_("Close"));
break;
}

case PairingState::BluetoothUnusable:
{
m_text->SetLabel(_("Please use your system's Bluetooth manager instead."));
m_gauge->SetValue(0);
m_cancelButton->SetLabel(_("Close"));
break;
}


default:
{
break;
}
}
}

void PairingDialog::WorkerThread()
{
const std::wstring wiimoteName = L"Nintendo RVL-CNT-01";
const std::wstring wiiUProControllerName = L"Nintendo RVL-CNT-01-UC";

#if BOOST_OS_WINDOWS
const GUID bthHidGuid = {0x00001124,0x0000,0x1000,{0x80,0x00,0x00,0x80,0x5F,0x9B,0x34,0xFB}};

const BLUETOOTH_FIND_RADIO_PARAMS radioFindParams =
{
.dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS)
};

HANDLE radio = INVALID_HANDLE_VALUE;
HBLUETOOTH_RADIO_FIND radioFind = BluetoothFindFirstRadio(&radioFindParams, &radio);
if (radioFind == nullptr)
{
UpdateCallback(PairingState::NoBluetoothAvailable);
return;
}

BluetoothFindRadioClose(radioFind);

BLUETOOTH_RADIO_INFO radioInfo =
{
.dwSize = sizeof(BLUETOOTH_RADIO_INFO)
};

DWORD result = BluetoothGetRadioInfo(radio, &radioInfo);
if (result != ERROR_SUCCESS)
{
UpdateCallback(PairingState::NoBluetoothAvailable);
return;
}

const BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams =
{
.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS),

.fReturnAuthenticated = FALSE,
.fReturnRemembered = FALSE,
.fReturnUnknown = TRUE,
.fReturnConnected = FALSE,

.fIssueInquiry = TRUE,
.cTimeoutMultiplier = 5,

.hRadio = radio
};

BLUETOOTH_DEVICE_INFO info =
{
.dwSize = sizeof(BLUETOOTH_DEVICE_INFO)
};

while (!m_threadShouldQuit)
{
HBLUETOOTH_DEVICE_FIND deviceFind = BluetoothFindFirstDevice(&searchParams, &info);
if (deviceFind == nullptr)
{
UpdateCallback(PairingState::BluetoothFailed);
return;
}

while (!m_threadShouldQuit)
{
if (info.szName == wiimoteName || info.szName == wiiUProControllerName)
{
BluetoothFindDeviceClose(deviceFind);

UpdateCallback(PairingState::Pairing);

wchar_t passwd[6] = { radioInfo.address.rgBytes[0], radioInfo.address.rgBytes[1], radioInfo.address.rgBytes[2], radioInfo.address.rgBytes[3], radioInfo.address.rgBytes[4], radioInfo.address.rgBytes[5] };
DWORD bthResult = BluetoothAuthenticateDevice(nullptr, radio, &info, passwd, 6);
if (bthResult != ERROR_SUCCESS)
{
UpdateCallback(PairingState::PairingFailed);
return;
}

bthResult = BluetoothSetServiceState(radio, &info, &bthHidGuid, BLUETOOTH_SERVICE_ENABLE);
if (bthResult != ERROR_SUCCESS)
{
UpdateCallback(PairingState::PairingFailed);
return;
}

UpdateCallback(PairingState::Finished);
return;
}

BOOL nextDevResult = BluetoothFindNextDevice(deviceFind, &info);
if (nextDevResult == FALSE)
{
break;
}
}

BluetoothFindDeviceClose(deviceFind);
}
#else
UpdateCallback(PairingState::BluetoothUnusable);
#endif
}

void PairingDialog::UpdateCallback(PairingState state)
{
auto* event = new wxCommandEvent(wxEVT_PROGRESS_PAIR);
event->SetInt((int)state);
wxQueueEvent(this, event);
}
38 changes: 38 additions & 0 deletions src/gui/PairingDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include <wx/button.h>
#include <wx/dialog.h>
#include <wx/gauge.h>
#include <wx/stattext.h>

class PairingDialog : public wxDialog
{
public:
PairingDialog(wxWindow* parent);
~PairingDialog();

private:
enum class PairingState
{
Pairing,
Finished,
NoBluetoothAvailable,
BluetoothFailed,
PairingFailed,
BluetoothUnusable
};

void OnClose(wxCloseEvent& event);
void OnCancelButton(const wxCommandEvent& event);
void OnGaugeUpdate(wxCommandEvent& event);

void WorkerThread();
void UpdateCallback(PairingState state);

wxStaticText* m_text;
wxGauge* m_gauge;
wxButton* m_cancelButton;

std::thread m_thread;
bool m_threadShouldQuit = false;
};
24 changes: 20 additions & 4 deletions src/gui/input/panels/WiimoteInputPanel.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "gui/input/panels/WiimoteInputPanel.h"

#include <wx/button.h>
#include <wx/gbsizer.h>
#include <wx/stattext.h>
#include <wx/statline.h>
Expand All @@ -11,6 +12,7 @@
#include "input/emulated/WiimoteController.h"
#include "gui/helpers/wxHelpers.h"
#include "gui/components/wxInputDraw.h"
#include "gui/PairingDialog.h"

constexpr WiimoteController::ButtonId g_kFirstColumnItems[] =
{
Expand All @@ -36,10 +38,18 @@ WiimoteInputPanel::WiimoteInputPanel(wxWindow* parent)
bold_font.MakeBold();

auto* main_sizer = new wxBoxSizer(wxVERTICAL);
auto* horiz_main_sizer = new wxBoxSizer(wxHORIZONTAL);

auto* extensions_sizer = new wxBoxSizer(wxHORIZONTAL);
extensions_sizer->Add(new wxStaticText(this, wxID_ANY, _("Extensions:")));
extensions_sizer->AddSpacer(10);
auto* pair_button = new wxButton(this, wxID_ANY, _("Pair a Wii or Wii U controller"));
pair_button->Bind(wxEVT_BUTTON, &WiimoteInputPanel::on_pair_button, this);
horiz_main_sizer->Add(pair_button);
horiz_main_sizer->AddSpacer(10);

auto* extensions_sizer = new wxBoxSizer(wxHORIZONTAL);
horiz_main_sizer->Add(extensions_sizer, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));

extensions_sizer->Add(new wxStaticText(this, wxID_ANY, _("Extensions:")));
extensions_sizer->AddSpacer(10);

m_motion_plus = new wxCheckBox(this, wxID_ANY, _("MotionPlus"));
m_motion_plus->Bind(wxEVT_CHECKBOX, &WiimoteInputPanel::on_extension_change, this);
Expand All @@ -54,7 +64,7 @@ WiimoteInputPanel::WiimoteInputPanel(wxWindow* parent)
m_classic->Hide();
extensions_sizer->Add(m_classic);

main_sizer->Add(extensions_sizer, 0, wxEXPAND | wxALL, 5);
main_sizer->Add(horiz_main_sizer, 0, wxEXPAND | wxALL, 5);
main_sizer->Add(new wxStaticLine(this), 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 5);

m_item_sizer = new wxGridBagSizer();
Expand Down Expand Up @@ -254,3 +264,9 @@ void WiimoteInputPanel::load_controller(const EmulatedControllerPtr& emulated_co
set_active_device_type(wiimote->get_device_type());
}
}

void WiimoteInputPanel::on_pair_button(wxCommandEvent& event)
{
PairingDialog pairing_dialog(this);
pairing_dialog.ShowModal();
}
1 change: 1 addition & 0 deletions src/gui/input/panels/WiimoteInputPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class WiimoteInputPanel : public InputPanel

void on_volume_change(wxCommandEvent& event);
void on_extension_change(wxCommandEvent& event);
void on_pair_button(wxCommandEvent& event);

wxGridBagSizer* m_item_sizer;

Expand Down