diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 1d4b958b04..38ccd561e7 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -7,6 +7,7 @@ set(GUI_SRCS Cheats/CheatsWindow.cpp Cheats/CreateCodeDialog.cpp Cheats/GeckoCodeDiag.cpp + Config/AddUSBDeviceDiag.cpp Config/AdvancedConfigPane.cpp Config/AudioConfigPane.cpp Config/ConfigMain.cpp diff --git a/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp new file mode 100644 index 0000000000..c6f1154af5 --- /dev/null +++ b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp @@ -0,0 +1,164 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/StringUtil.h" +#include "Core/ConfigManager.h" +#include "DolphinWX/Config/AddUSBDeviceDiag.h" +#include "DolphinWX/WxUtils.h" +#include "UICommon/USBUtils.h" + +AddUSBDeviceDiag::AddUSBDeviceDiag(wxWindow* const parent) + : wxDialog(parent, wxID_ANY, _("Add New USB Device")) +{ + InitControls(); + + RefreshDeviceList(); + Bind(wxEVT_TIMER, &AddUSBDeviceDiag::OnRefreshDevicesTimer, this, + m_refresh_devices_timer.GetId()); + m_refresh_devices_timer.Start(DEVICE_REFRESH_INTERVAL_MS, wxTIMER_CONTINUOUS); + + auto* const btn_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); + btn_sizer->GetAffirmativeButton()->SetLabel(_("Add")); + Bind(wxEVT_BUTTON, &AddUSBDeviceDiag::OnSave, this, wxID_OK); + + auto* const sizer = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); + sizer->AddSpacer(FromDIP(10)); + sizer->Add(new wxStaticText(this, wxID_ANY, _("Enter USB device ID"), wxDefaultPosition, + wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), + 0, wxEXPAND | wxBOTTOM, FromDIP(10)); + sizer->Add(CreateManualControlsSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer->Add(new wxStaticText(this, wxID_ANY, _("or select a device"), wxDefaultPosition, + wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), + 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10)); + auto* const device_list_sizer = CreateDeviceListSizer(); + sizer->Add(device_list_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, space5); + sizer->SetItemMinSize(device_list_sizer, FromDIP(350), FromDIP(150)); + sizer->Add(btn_sizer, 0, wxEXPAND); + sizer->AddSpacer(space5); + + SetSizerAndFit(sizer); + Center(); +} + +void AddUSBDeviceDiag::InitControls() +{ + m_new_device_vid_ctrl = new wxTextCtrl(this, wxID_ANY); + m_new_device_pid_ctrl = new wxTextCtrl(this, wxID_ANY); + // i18n: VID means Vendor ID (in the context of a USB device) + m_new_device_vid_ctrl->SetHint(_("Device VID (e.g., 057e)")); + // i18n: PID means Product ID (in the context of a USB device), not Process ID + m_new_device_pid_ctrl->SetHint(_("Device PID (e.g., 0305)")); + + m_inserted_devices_listbox = new wxListBox(this, wxID_ANY); + m_inserted_devices_listbox->Bind(wxEVT_LISTBOX, &AddUSBDeviceDiag::OnDeviceSelection, this); + m_inserted_devices_listbox->Bind(wxEVT_LISTBOX_DCLICK, &AddUSBDeviceDiag::OnSave, this); +} + +void AddUSBDeviceDiag::RefreshDeviceList() +{ + const auto& current_devices = USBUtils::GetInsertedDevices(); + if (current_devices == m_shown_devices) + return; + + m_inserted_devices_listbox->Freeze(); + const auto selection_string = m_inserted_devices_listbox->GetStringSelection(); + m_inserted_devices_listbox->Clear(); + for (const auto& device : current_devices) + { + if (SConfig::GetInstance().IsUSBDeviceWhitelisted(device.first)) + continue; + m_inserted_devices_listbox->Append(device.second, new USBPassthroughDeviceEntry(device.first)); + } + if (!selection_string.empty()) + m_inserted_devices_listbox->SetStringSelection(selection_string); + m_inserted_devices_listbox->Thaw(); + + m_shown_devices = current_devices; +} + +wxSizer* AddUSBDeviceDiag::CreateManualControlsSizer() +{ + const int space5 = FromDIP(5); + auto* const sizer = new wxBoxSizer(wxHORIZONTAL); + + sizer->Add(m_new_device_vid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer->Add(m_new_device_pid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + + return sizer; +} + +wxSizer* AddUSBDeviceDiag::CreateDeviceListSizer() +{ + const int space5 = FromDIP(5); + auto* const sizer = new wxBoxSizer(wxVERTICAL); + + sizer->Add(m_inserted_devices_listbox, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + + return sizer; +} + +static bool IsValidUSBIDString(const std::string& string) +{ + if (string.empty() || string.length() > 4) + return false; + return std::all_of(string.begin(), string.end(), + [](const auto character) { return std::isxdigit(character) != 0; }); +} + +void AddUSBDeviceDiag::OnRefreshDevicesTimer(wxTimerEvent&) +{ + RefreshDeviceList(); +} + +void AddUSBDeviceDiag::OnDeviceSelection(wxCommandEvent&) +{ + const int index = m_inserted_devices_listbox->GetSelection(); + if (index == wxNOT_FOUND) + return; + auto* const entry = static_cast( + m_inserted_devices_listbox->GetClientObject(index)); + m_new_device_vid_ctrl->SetValue(StringFromFormat("%04x", entry->m_vid)); + m_new_device_pid_ctrl->SetValue(StringFromFormat("%04x", entry->m_pid)); +} + +void AddUSBDeviceDiag::OnSave(wxCommandEvent&) +{ + const std::string vid_string = StripSpaces(WxStrToStr(m_new_device_vid_ctrl->GetValue())); + const std::string pid_string = StripSpaces(WxStrToStr(m_new_device_pid_ctrl->GetValue())); + if (!IsValidUSBIDString(vid_string)) + { + // i18n: Here, VID means Vendor ID (for a USB device). + WxUtils::ShowErrorDialog(_("The entered VID is invalid.")); + return; + } + if (!IsValidUSBIDString(pid_string)) + { + // i18n: Here, PID means Product ID (for a USB device). + WxUtils::ShowErrorDialog(_("The entered PID is invalid.")); + return; + } + + const u16 vid = static_cast(std::stoul(vid_string, nullptr, 16)); + const u16 pid = static_cast(std::stoul(pid_string, nullptr, 16)); + + if (SConfig::GetInstance().IsUSBDeviceWhitelisted({vid, pid})) + { + WxUtils::ShowErrorDialog(_("This USB device is already whitelisted.")); + return; + } + + SConfig::GetInstance().m_usb_passthrough_devices.emplace(vid, pid); + AcceptAndClose(); +} diff --git a/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h new file mode 100644 index 0000000000..38b65c34db --- /dev/null +++ b/Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h @@ -0,0 +1,56 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include +#include +#include + +#include "Common/CommonTypes.h" + +class wxListBox; +class wxSizer; +class wxTextCtrl; + +class USBPassthroughDeviceEntry final : public wxClientData +{ +public: + explicit USBPassthroughDeviceEntry(const std::pair pair) + : m_vid(pair.first), m_pid(pair.second) + { + } + const u16 m_vid; + const u16 m_pid; +}; + +// This dialog is used to add a new USB device to the USB passthrough whitelist, +// either by selecting a connected USB device or by entering the PID/VID manually. +class AddUSBDeviceDiag final : public wxDialog +{ +public: + explicit AddUSBDeviceDiag(wxWindow* parent); + +private: + static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100; + + void InitControls(); + void RefreshDeviceList(); + wxSizer* CreateManualControlsSizer(); + wxSizer* CreateDeviceListSizer(); + + void OnRefreshDevicesTimer(wxTimerEvent&); + void OnDeviceSelection(wxCommandEvent&); + void OnSave(wxCommandEvent&); + + std::map, std::string> m_shown_devices; + wxTimer m_refresh_devices_timer{this}; + + wxTextCtrl* m_new_device_vid_ctrl; + wxTextCtrl* m_new_device_pid_ctrl; + wxListBox* m_inserted_devices_listbox; +}; diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp index a98778eb2d..0d2ede097d 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "DolphinWX/Config/WiiConfigPane.h" - +#include #include #include #include +#include #include #include #include @@ -14,9 +14,12 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/IOS/IPC.h" +#include "DolphinWX/Config/AddUSBDeviceDiag.h" +#include "DolphinWX/Config/WiiConfigPane.h" #include "DolphinWX/DolphinSlider.h" #include "DolphinWX/WxEventUtils.h" #include "DolphinWX/WxUtils.h" +#include "UICommon/USBUtils.h" WiiConfigPane::WiiConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) { @@ -52,6 +55,10 @@ void WiiConfigPane::InitializeGUI() new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_system_language_strings); m_sd_card_checkbox = new wxCheckBox(this, wxID_ANY, _("Insert SD Card")); m_connect_keyboard_checkbox = new wxCheckBox(this, wxID_ANY, _("Connect USB Keyboard")); + m_usb_passthrough_devices_listbox = + new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100)); + m_usb_passthrough_add_device_btn = new wxButton(this, wxID_ANY, _("Add...")); + m_usb_passthrough_rem_device_btn = new wxButton(this, wxID_ANY, _("Remove")); m_bt_sensor_bar_pos = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bt_sensor_bar_pos_strings); m_bt_sensor_bar_sens = new DolphinSlider(this, wxID_ANY, 0, 0, 4); @@ -79,6 +86,12 @@ void WiiConfigPane::InitializeGUI() misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + auto* const usb_passthrough_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + usb_passthrough_btn_sizer->AddStretchSpacer(); + usb_passthrough_btn_sizer->Add(m_usb_passthrough_add_device_btn, 0, + wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, space5); + usb_passthrough_btn_sizer->Add(m_usb_passthrough_rem_device_btn, 0, wxALIGN_CENTER_VERTICAL); + auto* const bt_sensor_bar_pos_sizer = new wxBoxSizer(wxHORIZONTAL); bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0, wxALIGN_CENTER_VERTICAL); @@ -123,6 +136,15 @@ void WiiConfigPane::InitializeGUI() device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxLEFT | wxRIGHT, space5); device_settings_sizer->AddSpacer(space5); + auto* const usb_passthrough_sizer = + new wxStaticBoxSizer(wxVERTICAL, this, _("Whitelisted USB Passthrough Devices")); + usb_passthrough_sizer->AddSpacer(space5); + usb_passthrough_sizer->Add(m_usb_passthrough_devices_listbox, 0, wxEXPAND | wxLEFT | wxRIGHT, + space5); + usb_passthrough_sizer->AddSpacer(space5); + usb_passthrough_sizer->Add(usb_passthrough_btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + usb_passthrough_sizer->AddSpacer(space5); + auto* const bt_settings_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings")); bt_settings_static_sizer->AddSpacer(space5); @@ -135,6 +157,8 @@ void WiiConfigPane::InitializeGUI() main_sizer->AddSpacer(space5); main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); main_sizer->AddSpacer(space5); + main_sizer->Add(usb_passthrough_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); main_sizer->AddSpacer(space5); @@ -151,12 +175,26 @@ void WiiConfigPane::LoadGUIValues() m_sd_card_checkbox->SetValue(SConfig::GetInstance().m_WiiSDCard); m_connect_keyboard_checkbox->SetValue(SConfig::GetInstance().m_WiiKeyboard); + PopulateUSBPassthroughListbox(); + m_bt_sensor_bar_pos->SetSelection(SConfig::GetInstance().m_sensor_bar_position); m_bt_sensor_bar_sens->SetValue(SConfig::GetInstance().m_sensor_bar_sensitivity); m_bt_speaker_volume->SetValue(SConfig::GetInstance().m_speaker_volume); m_bt_wiimote_motor->SetValue(SConfig::GetInstance().m_wiimote_motor); } +void WiiConfigPane::PopulateUSBPassthroughListbox() +{ + m_usb_passthrough_devices_listbox->Freeze(); + m_usb_passthrough_devices_listbox->Clear(); + for (const auto& device : SConfig::GetInstance().m_usb_passthrough_devices) + { + m_usb_passthrough_devices_listbox->Append(USBUtils::GetDeviceName(device), + new USBPassthroughDeviceEntry(device)); + } + m_usb_passthrough_devices_listbox->Thaw(); +} + void WiiConfigPane::BindEvents() { m_screensaver_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnScreenSaverCheckBoxChanged, this); @@ -186,6 +224,38 @@ void WiiConfigPane::BindEvents() m_bt_wiimote_motor->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnWiimoteMotorChanged, this); m_bt_wiimote_motor->Bind(wxEVT_UPDATE_UI, &WxEventUtils::OnEnableIfCoreNotRunning); + + m_usb_passthrough_add_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistAddButton, + this); + + m_usb_passthrough_rem_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistRemoveButton, + this); + m_usb_passthrough_rem_device_btn->Bind(wxEVT_UPDATE_UI, + &WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate, this); +} + +void WiiConfigPane::OnUSBWhitelistAddButton(wxCommandEvent&) +{ + AddUSBDeviceDiag add_dialog{this}; + // Reload the USB device whitelist + if (add_dialog.ShowModal() == wxID_OK) + PopulateUSBPassthroughListbox(); +} + +void WiiConfigPane::OnUSBWhitelistRemoveButton(wxCommandEvent&) +{ + const int index = m_usb_passthrough_devices_listbox->GetSelection(); + if (index == wxNOT_FOUND) + return; + auto* const entry = static_cast( + m_usb_passthrough_devices_listbox->GetClientObject(index)); + SConfig::GetInstance().m_usb_passthrough_devices.erase({entry->m_vid, entry->m_pid}); + m_usb_passthrough_devices_listbox->Delete(index); +} + +void WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent& event) +{ + event.Enable(m_usb_passthrough_devices_listbox->GetSelection() != wxNOT_FOUND); } void WiiConfigPane::OnScreenSaverCheckBoxChanged(wxCommandEvent& event) diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.h b/Source/Core/DolphinWX/Config/WiiConfigPane.h index bb68f89e4e..557ab624f1 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.h +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.h @@ -6,11 +6,12 @@ #include #include -#include "Common/CommonTypes.h" class DolphinSlider; +class wxButton; class wxCheckBox; class wxChoice; +class wxListBox; class wxSlider; class WiiConfigPane final : public wxPanel @@ -23,6 +24,8 @@ private: void LoadGUIValues(); void BindEvents(); + void PopulateUSBPassthroughListbox(); + void OnScreenSaverCheckBoxChanged(wxCommandEvent&); void OnPAL60CheckBoxChanged(wxCommandEvent&); void OnSDCardCheckBoxChanged(wxCommandEvent&); @@ -30,6 +33,10 @@ private: void OnSystemLanguageChoiceChanged(wxCommandEvent&); void OnAspectRatioChoiceChanged(wxCommandEvent&); + void OnUSBWhitelistAddButton(wxCommandEvent&); + void OnUSBWhitelistRemoveButton(wxCommandEvent&); + void OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent&); + void OnSensorBarPosChanged(wxCommandEvent&); void OnSensorBarSensChanged(wxCommandEvent&); void OnSpeakerVolumeChanged(wxCommandEvent&); @@ -46,6 +53,10 @@ private: wxChoice* m_system_language_choice; wxChoice* m_aspect_ratio_choice; + wxListBox* m_usb_passthrough_devices_listbox; + wxButton* m_usb_passthrough_add_device_btn; + wxButton* m_usb_passthrough_rem_device_btn; + wxChoice* m_bt_sensor_bar_pos; DolphinSlider* m_bt_sensor_bar_sens; DolphinSlider* m_bt_speaker_volume; diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 81412afe65..bae3476422 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -62,6 +62,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index 5cc92cb65a..90842a6629 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -224,6 +224,9 @@ GUI\Config + + GUI\Config + GUI\Config @@ -443,6 +446,9 @@ GUI\Config + + GUI\Config + GUI\Config