diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_controller.cpp | 10 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_controller.h | 4 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_profile_select.cpp | 17 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_profile_select.h | 8 | ||||
| -rw-r--r-- | src/yuzu/applets/qt_software_keyboard.cpp | 23 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 163 | ||||
| -rw-r--r-- | src/yuzu/debugger/profiler.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 39 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 5 | ||||
| -rw-r--r-- | src/yuzu/loading_screen.cpp | 4 | ||||
| -rw-r--r-- | src/yuzu/loading_screen.h | 4 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 52 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 3 | ||||
| -rw-r--r-- | src/yuzu/util/controller_navigation.cpp | 177 | ||||
| -rw-r--r-- | src/yuzu/util/controller_navigation.h | 51 |
17 files changed, 431 insertions, 135 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index a44815e71..732e8c276 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -152,6 +152,8 @@ add_executable(yuzu main.ui uisettings.cpp uisettings.h + util/controller_navigation.cpp + util/controller_navigation.h util/limitable_input_dialog.cpp util/limitable_input_dialog.h util/overlay_dialog.cpp diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 589e0577a..c5685db2e 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -139,7 +139,6 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( DisableUnsupportedPlayers(); for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) { - system.HIDCore().GetEmulatedControllerByIndex(player_index)->EnableConfiguration(); SetEmulatedControllers(player_index); } @@ -218,7 +217,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( resize(0, 0); } -QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; +QtControllerSelectorDialog::~QtControllerSelectorDialog() { + system.HIDCore().DisableAllControllerConfiguration(); +} int QtControllerSelectorDialog::exec() { if (parameters_met && parameters.enable_single_mode) { @@ -234,12 +235,11 @@ void QtControllerSelectorDialog::ApplyConfiguration() { Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); - for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) { - system.HIDCore().GetEmulatedControllerByIndex(player_index)->DisableConfiguration(); - } } void QtControllerSelectorDialog::LoadConfiguration() { + system.HIDCore().EnableAllControllerConfiguration(); + const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index cc343e5ae..7ab9ced3d 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -7,7 +7,6 @@ #include <array> #include <memory> #include <QDialog> -#include "core/core.h" #include "core/frontend/applets/controller.h" class GMainWindow; @@ -32,8 +31,9 @@ class System; } namespace Core::HID { +class HIDCore; enum class NpadStyleIndex : u8; -} +} // namespace Core::HID class QtControllerSelectorDialog final : public QDialog { Q_OBJECT diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index a56638e21..7b19f1f8d 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <mutex> +#include <QApplication> #include <QDialogButtonBox> #include <QHeaderView> #include <QLabel> @@ -16,6 +17,7 @@ #include "core/hle/lock.h" #include "yuzu/applets/qt_profile_select.h" #include "yuzu/main.h" +#include "yuzu/util/controller_navigation.h" namespace { QString FormatUserEntryText(const QString& username, Common::UUID uuid) { @@ -45,7 +47,7 @@ QPixmap GetIcon(Common::UUID uuid) { } } // Anonymous namespace -QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) +QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent) : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { outer_layout = new QVBoxLayout; @@ -65,6 +67,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) tree_view = new QTreeView; item_model = new QStandardItemModel(tree_view); tree_view->setModel(item_model); + controller_navigation = new ControllerNavigation(hid_core, this); tree_view->setAlternatingRowColors(true); tree_view->setSelectionMode(QHeaderView::SingleSelection); @@ -91,6 +94,14 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) scroll_area->setLayout(layout); connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); + connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, + [this](Qt::Key key) { + if (!this->isActiveWindow()) { + return; + } + QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); + QCoreApplication::postEvent(tree_view, event); + }); const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { @@ -113,7 +124,9 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) resize(550, 400); } -QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; +QtProfileSelectionDialog::~QtProfileSelectionDialog() { + controller_navigation->UnloadController(); +}; int QtProfileSelectionDialog::exec() { // Skip profile selection when there's only one. diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h index 4e9037488..56496ed31 100644 --- a/src/yuzu/applets/qt_profile_select.h +++ b/src/yuzu/applets/qt_profile_select.h @@ -11,6 +11,7 @@ #include "core/frontend/applets/profile_select.h" #include "core/hle/service/acc/profile_manager.h" +class ControllerNavigation; class GMainWindow; class QDialogButtonBox; class QGraphicsScene; @@ -20,11 +21,15 @@ class QStandardItem; class QStandardItemModel; class QVBoxLayout; +namespace Core::HID { +class HIDCore; +} // namespace Core::HID + class QtProfileSelectionDialog final : public QDialog { Q_OBJECT public: - explicit QtProfileSelectionDialog(QWidget* parent); + explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent); ~QtProfileSelectionDialog() override; int exec() override; @@ -51,6 +56,7 @@ private: QDialogButtonBox* buttons; std::unique_ptr<Service::Account::ProfileManager> profile_manager; + ControllerNavigation* controller_navigation = nullptr; }; class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index de7f98c4f..c3857fc98 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -475,11 +475,26 @@ void QtSoftwareKeyboardDialog::open() { row = 0; column = 0; - const auto* const curr_button = - keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column]; + switch (bottom_osk_index) { + case BottomOSKIndex::LowerCase: + case BottomOSKIndex::UpperCase: { + const auto* const curr_button = + keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column]; + + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + case BottomOSKIndex::NumberPad: { + const auto* const curr_button = numberpad_buttons[row][column]; - // This is a workaround for setFocus() randomly not showing focus in the UI - QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + // This is a workaround for setFocus() randomly not showing focus in the UI + QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); + break; + } + default: + break; + } StartInputThread(); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 463d500c2..0f679c37e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -776,6 +776,7 @@ void Config::ReadUIGamelistValues() { ReadBasicSetting(UISettings::values.row_1_text_id); ReadBasicSetting(UISettings::values.row_2_text_id); ReadBasicSetting(UISettings::values.cache_game_list); + ReadBasicSetting(UISettings::values.favorites_expanded); const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); for (int i = 0; i < favorites_size; i++) { qt_config->setArrayIndex(i); @@ -1300,6 +1301,7 @@ void Config::SaveUIGamelistValues() { WriteBasicSetting(UISettings::values.row_1_text_id); WriteBasicSetting(UISettings::values.row_2_text_id); WriteBasicSetting(UISettings::values.cache_game_list); + WriteBasicSetting(UISettings::values.favorites_expanded); qt_config->beginWriteArray(QStringLiteral("favorites")); for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { qt_config->setArrayIndex(i); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 16284d5a6..8a8be8e40 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -246,15 +246,15 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* emulated_controller_hanheld = + auto* emulated_controller_handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); emulated_controller_p1->SaveCurrentConfig(); emulated_controller_p1->EnableConfiguration(); - emulated_controller_hanheld->SaveCurrentConfig(); - emulated_controller_hanheld->EnableConfiguration(); - if (emulated_controller_hanheld->IsConnected(true)) { + emulated_controller_handheld->SaveCurrentConfig(); + emulated_controller_handheld->EnableConfiguration(); + if (emulated_controller_handheld->IsConnected(true)) { emulated_controller_p1->Disconnect(); - emulated_controller = emulated_controller_hanheld; + emulated_controller = emulated_controller_handheld; } else { emulated_controller = emulated_controller_p1; } @@ -590,19 +590,19 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* emulated_controller_hanheld = + auto* emulated_controller_handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); bool is_connected = emulated_controller->IsConnected(true); emulated_controller_p1->SetNpadStyleIndex(type); - emulated_controller_hanheld->SetNpadStyleIndex(type); + emulated_controller_handheld->SetNpadStyleIndex(type); if (is_connected) { if (type == Core::HID::NpadStyleIndex::Handheld) { emulated_controller_p1->Disconnect(); - emulated_controller_hanheld->Connect(); - emulated_controller = emulated_controller_hanheld; + emulated_controller_handheld->Connect(); + emulated_controller = emulated_controller_handheld; } else { - emulated_controller_hanheld->Disconnect(); + emulated_controller_handheld->Disconnect(); emulated_controller_p1->Connect(); emulated_controller = emulated_controller_p1; } @@ -650,10 +650,10 @@ ConfigureInputPlayer::~ConfigureInputPlayer() { if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* emulated_controller_hanheld = + auto* emulated_controller_handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); emulated_controller_p1->DisableConfiguration(); - emulated_controller_hanheld->DisableConfiguration(); + emulated_controller_handheld->DisableConfiguration(); } else { emulated_controller->DisableConfiguration(); } @@ -663,14 +663,14 @@ void ConfigureInputPlayer::ApplyConfiguration() { if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* emulated_controller_hanheld = + auto* emulated_controller_handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); emulated_controller_p1->DisableConfiguration(); emulated_controller_p1->SaveCurrentConfig(); emulated_controller_p1->EnableConfiguration(); - emulated_controller_hanheld->DisableConfiguration(); - emulated_controller_hanheld->SaveCurrentConfig(); - emulated_controller_hanheld->EnableConfiguration(); + emulated_controller_handheld->DisableConfiguration(); + emulated_controller_handheld->SaveCurrentConfig(); + emulated_controller_handheld->EnableConfiguration(); return; } emulated_controller->DisableConfiguration(); @@ -907,88 +907,79 @@ void ConfigureInputPlayer::UpdateUI() { } void ConfigureInputPlayer::SetConnectableControllers() { - const auto add_controllers = [this](bool enable_all, - Core::HID::NpadStyleTag npad_style_set = {}) { - index_controller_type_pairs.clear(); - ui->comboControllerType->clear(); - - if (enable_all || npad_style_set.fullkey == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::ProController); - ui->comboControllerType->addItem(tr("Pro Controller")); - } - - if (enable_all || npad_style_set.joycon_dual == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconDual); - ui->comboControllerType->addItem(tr("Dual Joycons")); - } + Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag(); + index_controller_type_pairs.clear(); + ui->comboControllerType->clear(); - if (enable_all || npad_style_set.joycon_left == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconLeft); - ui->comboControllerType->addItem(tr("Left Joycon")); - } + if (npad_style_set.fullkey == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::ProController); + ui->comboControllerType->addItem(tr("Pro Controller")); + } - if (enable_all || npad_style_set.joycon_right == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::JoyconRight); - ui->comboControllerType->addItem(tr("Right Joycon")); - } + if (npad_style_set.joycon_dual == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::JoyconDual); + ui->comboControllerType->addItem(tr("Dual Joycons")); + } - if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::Handheld); - ui->comboControllerType->addItem(tr("Handheld")); - } + if (npad_style_set.joycon_left == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::JoyconLeft); + ui->comboControllerType->addItem(tr("Left Joycon")); + } - if (enable_all || npad_style_set.gamecube == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::GameCube); - ui->comboControllerType->addItem(tr("GameCube Controller")); - } + if (npad_style_set.joycon_right == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::JoyconRight); + ui->comboControllerType->addItem(tr("Right Joycon")); + } - // Disable all unsupported controllers - if (!Settings::values.enable_all_controllers) { - return; - } - if (enable_all || npad_style_set.palma == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::Pokeball); - ui->comboControllerType->addItem(tr("Poke Ball Plus")); - } + if (player_index == 0 && npad_style_set.handheld == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::Handheld); + ui->comboControllerType->addItem(tr("Handheld")); + } - if (enable_all || npad_style_set.lark == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::NES); - ui->comboControllerType->addItem(tr("NES Controller")); - } + if (npad_style_set.gamecube == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::GameCube); + ui->comboControllerType->addItem(tr("GameCube Controller")); + } - if (enable_all || npad_style_set.lucia == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::SNES); - ui->comboControllerType->addItem(tr("SNES Controller")); - } + // Disable all unsupported controllers + if (!Settings::values.enable_all_controllers) { + return; + } + if (npad_style_set.palma == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::Pokeball); + ui->comboControllerType->addItem(tr("Poke Ball Plus")); + } - if (enable_all || npad_style_set.lagoon == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::N64); - ui->comboControllerType->addItem(tr("N64 Controller")); - } + if (npad_style_set.lark == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::NES); + ui->comboControllerType->addItem(tr("NES Controller")); + } - if (enable_all || npad_style_set.lager == 1) { - index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), - Core::HID::NpadStyleIndex::SegaGenesis); - ui->comboControllerType->addItem(tr("Sega Genesis")); - } - }; + if (npad_style_set.lucia == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::SNES); + ui->comboControllerType->addItem(tr("SNES Controller")); + } - if (!is_powered_on) { - add_controllers(true); - return; + if (npad_style_set.lagoon == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::N64); + ui->comboControllerType->addItem(tr("N64 Controller")); } - add_controllers(false, hid_core.GetSupportedStyleTag()); + if (npad_style_set.lager == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Core::HID::NpadStyleIndex::SegaGenesis); + ui->comboControllerType->addItem(tr("Sega Genesis")); + } } Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index a8b254199..33110685a 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp @@ -163,7 +163,7 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { } void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { - const auto wheel_position = ev->pos(); + const auto wheel_position = ev->position().toPoint(); MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale, ev->angleDelta().y() / 120); ev->accept(); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2af95dbe5..8b5c4a10a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -17,6 +17,7 @@ #include <fmt/format.h> #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "yuzu/compatibility_list.h" @@ -25,6 +26,7 @@ #include "yuzu/game_list_worker.h" #include "yuzu/main.h" #include "yuzu/uisettings.h" +#include "yuzu/util/controller_navigation.h" GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) : QObject(parent), gamelist{gamelist} {} @@ -171,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) { const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir; - - if (!is_dir) { + const bool is_fave = type == GameListItemType::Favorites; + if (!is_dir && !is_fave) { return; } - - UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = - tree_view->isExpanded(item); + const bool is_expanded = tree_view->isExpanded(item); + if (is_fave) { + UISettings::values.favorites_expanded = is_expanded; + return; + } + const int item_dir_index = item.data(GameListDir::GameDirRole).toInt(); + UISettings::values.game_dirs[item_dir_index].expanded = is_expanded; } // Event in order to filter the gamelist after editing the searchfield @@ -312,6 +318,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide this->main_window = parent; layout = new QVBoxLayout; tree_view = new QTreeView; + controller_navigation = new ControllerNavigation(system.HIDCore(), this); search_field = new GameListSearchField(this); item_model = new QStandardItemModel(tree_view); tree_view->setModel(item_model); @@ -341,6 +348,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); + connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, + [this](Qt::Key key) { + // Avoid pressing buttons while playing + if (system.IsPoweredOn()) { + return; + } + if (!this->isActiveWindow()) { + return; + } + QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); + QCoreApplication::postEvent(tree_view, event); + }); // We must register all custom types with the Qt Automoc system so that we are able to use // it with signals/slots. In this case, QList falls under the umbrells of custom types. @@ -353,7 +372,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide setLayout(layout); } +void GameList::UnloadController() { + controller_navigation->UnloadController(); +} + GameList::~GameList() { + UnloadController(); emit ShouldCancelWorker(); } @@ -438,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) { emit ShowList(!IsEmpty()); item_model->invisibleRootItem()->appendRow(new GameListAddDir()); + + // Add favorites row item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), UISettings::values.favorited_ids.size() == 0); - tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); + tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), + UISettings::values.favorites_expanded.GetValue()); for (const auto id : UISettings::values.favorited_ids) { AddFavorite(id); } diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 675469e66..a94ea1477 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -24,6 +24,7 @@ #include "uisettings.h" #include "yuzu/compatibility_list.h" +class ControllerNavigation; class GameListWorker; class GameListSearchField; class GameListDir; @@ -88,6 +89,9 @@ public: void SaveInterfaceLayout(); void LoadInterfaceLayout(); + /// Disables events from the emulated controller + void UnloadController(); + static const QStringList supported_file_extensions; signals: @@ -143,6 +147,7 @@ private: QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; + ControllerNavigation* controller_navigation = nullptr; CompatibilityList compatibility_list; friend class GameListSearchField; diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index ae842306c..b001b8c23 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -136,7 +136,7 @@ void LoadingScreen::OnLoadComplete() { void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { using namespace std::chrono; - const auto now = high_resolution_clock::now(); + const auto now = steady_clock::now(); // reset the timer if the stage changes if (stage != previous_stage) { ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); @@ -160,7 +160,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size // If theres a drastic slowdown in the rate, then display an estimate if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { if (!slow_shader_compile_start) { - slow_shader_start = high_resolution_clock::now(); + slow_shader_start = steady_clock::now(); slow_shader_compile_start = true; slow_shader_first_value = value; } diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 801d08e1a..29155a77c 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h @@ -84,8 +84,8 @@ private: // shaders, it will start quickly but end slow if new shaders were added since previous launch. // These variables are used to detect the change in speed so we can generate an ETA bool slow_shader_compile_start = false; - std::chrono::high_resolution_clock::time_point slow_shader_start; - std::chrono::high_resolution_clock::time_point previous_time; + std::chrono::steady_clock::time_point slow_shader_start; + std::chrono::steady_clock::time_point previous_time; std::size_t slow_shader_first_value = 0; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9bd0db10a..b7bb43348 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -77,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/fs/fs.h" #include "common/fs/fs_paths.h" #include "common/fs/path_util.h" +#include "common/literals.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -134,6 +135,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/main.h" #include "yuzu/uisettings.h" +using namespace Common::Literals; + #ifdef USE_DISCORD_PRESENCE #include "yuzu/discord_impl.h" #endif @@ -259,10 +262,9 @@ GMainWindow::GMainWindow() LOG_INFO(Frontend, "Host CPU: {}", cpu_string); #endif LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); - LOG_INFO(Frontend, "Host RAM: {:.2f} GB", - Common::GetMemInfo().TotalPhysicalMemory / 1024.0f / 1024 / 1024); - LOG_INFO(Frontend, "Host Swap: {:.2f} GB", - Common::GetMemInfo().TotalSwapMemory / 1024.0f / 1024 / 1024); + LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", + Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); + LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); UpdateWindowTitle(); show(); @@ -449,7 +451,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( } void GMainWindow::ProfileSelectorSelectProfile() { - QtProfileSelectionDialog dialog(this); + QtProfileSelectionDialog dialog(system->HIDCore(), this); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); @@ -1082,14 +1084,15 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { state != Qt::ApplicationActive) { LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); } - if (ui->action_Pause->isEnabled() && - (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { - auto_paused = true; - OnPauseGame(); - } else if (emulation_running && !emu_thread->IsRunning() && auto_paused && - state == Qt::ApplicationActive) { - auto_paused = false; - OnStartGame(); + if (emulation_running) { + if (emu_thread->IsRunning() && + (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { + auto_paused = true; + OnPauseGame(); + } else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) { + auto_paused = false; + OnStartGame(); + } } } @@ -1301,16 +1304,13 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p case Core::SystemResultStatus::ErrorVideoCore: QMessageBox::critical( this, tr("An error occurred initializing the video core."), - tr("yuzu has encountered an error while running the video core, please see the " - "log for more details." + tr("yuzu has encountered an error while running the video core. " + "This is usually caused by outdated GPU drivers, including integrated ones. " + "Please see the log for more details. " "For more information on accessing the log, please see the following page: " - "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " - "to " - "Upload the Log File</a>." - "Ensure that you have the latest graphics drivers for your GPU.")); - + "<a href='https://yuzu-emu.org/help/reference/log-files/'>" + "How to Upload the Log File</a>. ")); break; - default: if (result > Core::SystemResultStatus::ErrorLoader) { const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader); @@ -1346,7 +1346,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p } void GMainWindow::SelectAndSetCurrentUser() { - QtProfileSelectionDialog dialog(this); + QtProfileSelectionDialog dialog(system->HIDCore(), this); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); @@ -1516,6 +1516,9 @@ void GMainWindow::ShutdownGame() { input_subsystem->GetTas()->Stop(); OnTasStateChanged(); + // Enable all controllers + system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); + render_window->removeEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, false); @@ -1608,7 +1611,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target if (has_user_save) { // User save data const auto select_profile = [this] { - QtProfileSelectionDialog dialog(this); + QtProfileSelectionDialog dialog(system->HIDCore(), this); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); @@ -3376,7 +3379,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) { UpdateUISettings(); game_list->SaveInterfaceLayout(); hotkey_registry.SaveHotkeys(); + + // Unload controllers early controller_dialog->UnloadController(); + game_list->UnloadController(); system->HIDCore().UnloadInputDevices(); // Shutdown session if the emu thread is active... diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 936914ef3..a610e7e25 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -74,7 +74,6 @@ struct Values { QString game_dir_deprecated; bool game_dir_deprecated_deepscan; QVector<UISettings::GameDir> game_dirs; - QVector<u64> favorited_ids; QStringList recent_files; QString language; @@ -96,6 +95,8 @@ struct Values { Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; std::atomic_bool is_game_list_reload_pending{false}; Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; + Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"}; + QVector<u64> favorited_ids; bool configuration_applied; bool reset_to_defaults; diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp new file mode 100644 index 000000000..86fb28b9f --- /dev/null +++ b/src/yuzu/util/controller_navigation.cpp @@ -0,0 +1,177 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included + +#include "common/settings_input.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "yuzu/util/controller_navigation.h" + +ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) { + player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); }, + .is_npad_service = false, + }; + player1_callback_key = player1_controller->SetCallback(engine_callback); + handheld_callback_key = handheld_controller->SetCallback(engine_callback); + is_controller_set = true; +} + +ControllerNavigation::~ControllerNavigation() { + UnloadController(); +} + +void ControllerNavigation::UnloadController() { + if (is_controller_set) { + player1_controller->DeleteCallback(player1_callback_key); + handheld_controller->DeleteCallback(handheld_callback_key); + is_controller_set = false; + } +} + +void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button, + Qt::Key key) { + if (button_values[native_button].value && !button_values[native_button].locked) { + emit TriggerKeyboardEvent(key); + } +} + +void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { + std::lock_guard lock{mutex}; + if (type == Core::HID::ControllerTriggerType::Button) { + ControllerUpdateButton(); + return; + } + + if (type == Core::HID::ControllerTriggerType::Stick) { + ControllerUpdateStick(); + return; + } +} + +void ControllerNavigation::ControllerUpdateButton() { + const auto controller_type = player1_controller->GetNpadStyleIndex(); + const auto& player1_buttons = player1_controller->GetButtonsValues(); + const auto& handheld_buttons = handheld_controller->GetButtonsValues(); + + for (std::size_t i = 0; i < player1_buttons.size(); ++i) { + const bool button = player1_buttons[i].value || handheld_buttons[i].value; + // Trigger only once + button_values[i].locked = button == button_values[i].value; + button_values[i].value = button; + } + + switch (controller_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::Handheld: + case Core::HID::NpadStyleIndex::GameCube: + TriggerButton(Settings::NativeButton::A, Qt::Key_Enter); + TriggerButton(Settings::NativeButton::B, Qt::Key_Escape); + TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down); + TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left); + TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right); + TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up); + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter); + TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape); + break; + case Core::HID::NpadStyleIndex::JoyconRight: + TriggerButton(Settings::NativeButton::X, Qt::Key_Enter); + TriggerButton(Settings::NativeButton::A, Qt::Key_Escape); + break; + default: + break; + } +} + +void ControllerNavigation::ControllerUpdateStick() { + const auto controller_type = player1_controller->GetNpadStyleIndex(); + const auto& player1_sticks = player1_controller->GetSticksValues(); + const auto& handheld_sticks = player1_controller->GetSticksValues(); + bool update = false; + + for (std::size_t i = 0; i < player1_sticks.size(); ++i) { + const Common::Input::StickStatus stick{ + .left = player1_sticks[i].left || handheld_sticks[i].left, + .right = player1_sticks[i].right || handheld_sticks[i].right, + .up = player1_sticks[i].up || handheld_sticks[i].up, + .down = player1_sticks[i].down || handheld_sticks[i].down, + }; + // Trigger only once + if (stick.down != stick_values[i].down || stick.left != stick_values[i].left || + stick.right != stick_values[i].right || stick.up != stick_values[i].up) { + update = true; + } + stick_values[i] = stick; + } + + if (!update) { + return; + } + + switch (controller_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::Handheld: + case Core::HID::NpadStyleIndex::GameCube: + if (stick_values[Settings::NativeAnalog::LStick].down) { + emit TriggerKeyboardEvent(Qt::Key_Down); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].left) { + emit TriggerKeyboardEvent(Qt::Key_Left); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].right) { + emit TriggerKeyboardEvent(Qt::Key_Right); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].up) { + emit TriggerKeyboardEvent(Qt::Key_Up); + return; + } + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + if (stick_values[Settings::NativeAnalog::LStick].left) { + emit TriggerKeyboardEvent(Qt::Key_Down); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].up) { + emit TriggerKeyboardEvent(Qt::Key_Left); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].down) { + emit TriggerKeyboardEvent(Qt::Key_Right); + return; + } + if (stick_values[Settings::NativeAnalog::LStick].right) { + emit TriggerKeyboardEvent(Qt::Key_Up); + return; + } + break; + case Core::HID::NpadStyleIndex::JoyconRight: + if (stick_values[Settings::NativeAnalog::RStick].right) { + emit TriggerKeyboardEvent(Qt::Key_Down); + return; + } + if (stick_values[Settings::NativeAnalog::RStick].down) { + emit TriggerKeyboardEvent(Qt::Key_Left); + return; + } + if (stick_values[Settings::NativeAnalog::RStick].up) { + emit TriggerKeyboardEvent(Qt::Key_Right); + return; + } + if (stick_values[Settings::NativeAnalog::RStick].left) { + emit TriggerKeyboardEvent(Qt::Key_Up); + return; + } + break; + default: + break; + } +} diff --git a/src/yuzu/util/controller_navigation.h b/src/yuzu/util/controller_navigation.h new file mode 100644 index 000000000..7c616a088 --- /dev/null +++ b/src/yuzu/util/controller_navigation.h @@ -0,0 +1,51 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included + +#pragma once + +#include <QKeyEvent> +#include <QObject> + +#include "common/input.h" +#include "common/settings_input.h" + +namespace Core::HID { +using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; +using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; +enum class ControllerTriggerType; +class EmulatedController; +class HIDCore; +} // namespace Core::HID + +class ControllerNavigation : public QObject { + Q_OBJECT + +public: + explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr); + ~ControllerNavigation(); + + /// Disables events from the emulated controller + void UnloadController(); + +signals: + void TriggerKeyboardEvent(Qt::Key key); + +private: + void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key); + void ControllerUpdateEvent(Core::HID::ControllerTriggerType type); + + void ControllerUpdateButton(); + + void ControllerUpdateStick(); + + Core::HID::ButtonValues button_values{}; + Core::HID::SticksValues stick_values{}; + + int player1_callback_key{}; + int handheld_callback_key{}; + bool is_controller_set{}; + mutable std::mutex mutex; + Core::HID::EmulatedController* player1_controller; + Core::HID::EmulatedController* handheld_controller; +}; |
