mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-11-03 08:16:40 +08:00 
			
		
		
		
	yuzu: Implement basic controller navigation
This commit is contained in:
		
							parent
							
								
									46e3ed5a48
								
							
						
					
					
						commit
						5ba7b11ba4
					
				@ -152,6 +152,8 @@ add_executable(yuzu
 | 
				
			|||||||
    main.ui
 | 
					    main.ui
 | 
				
			||||||
    uisettings.cpp
 | 
					    uisettings.cpp
 | 
				
			||||||
    uisettings.h
 | 
					    uisettings.h
 | 
				
			||||||
 | 
					    util/controller_navigation.cpp
 | 
				
			||||||
 | 
					    util/controller_navigation.h
 | 
				
			||||||
    util/limitable_input_dialog.cpp
 | 
					    util/limitable_input_dialog.cpp
 | 
				
			||||||
    util/limitable_input_dialog.h
 | 
					    util/limitable_input_dialog.h
 | 
				
			||||||
    util/overlay_dialog.cpp
 | 
					    util/overlay_dialog.cpp
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,6 @@
 | 
				
			|||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <QDialog>
 | 
					#include <QDialog>
 | 
				
			||||||
#include "core/core.h"
 | 
					 | 
				
			||||||
#include "core/frontend/applets/controller.h"
 | 
					#include "core/frontend/applets/controller.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GMainWindow;
 | 
					class GMainWindow;
 | 
				
			||||||
@ -32,8 +31,9 @@ class System;
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Core::HID {
 | 
					namespace Core::HID {
 | 
				
			||||||
 | 
					class HIDCore;
 | 
				
			||||||
enum class NpadStyleIndex : u8;
 | 
					enum class NpadStyleIndex : u8;
 | 
				
			||||||
}
 | 
					} // namespace Core::HID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QtControllerSelectorDialog final : public QDialog {
 | 
					class QtControllerSelectorDialog final : public QDialog {
 | 
				
			||||||
    Q_OBJECT
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@
 | 
				
			|||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <QApplication>
 | 
				
			||||||
#include <QDialogButtonBox>
 | 
					#include <QDialogButtonBox>
 | 
				
			||||||
#include <QHeaderView>
 | 
					#include <QHeaderView>
 | 
				
			||||||
#include <QLabel>
 | 
					#include <QLabel>
 | 
				
			||||||
@ -16,6 +17,7 @@
 | 
				
			|||||||
#include "core/hle/lock.h"
 | 
					#include "core/hle/lock.h"
 | 
				
			||||||
#include "yuzu/applets/qt_profile_select.h"
 | 
					#include "yuzu/applets/qt_profile_select.h"
 | 
				
			||||||
#include "yuzu/main.h"
 | 
					#include "yuzu/main.h"
 | 
				
			||||||
 | 
					#include "yuzu/util/controller_navigation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
 | 
					QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
 | 
				
			||||||
@ -45,7 +47,7 @@ QPixmap GetIcon(Common::UUID uuid) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // 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>()) {
 | 
					    : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
 | 
				
			||||||
    outer_layout = new QVBoxLayout;
 | 
					    outer_layout = new QVBoxLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -65,6 +67,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
 | 
				
			|||||||
    tree_view = new QTreeView;
 | 
					    tree_view = new QTreeView;
 | 
				
			||||||
    item_model = new QStandardItemModel(tree_view);
 | 
					    item_model = new QStandardItemModel(tree_view);
 | 
				
			||||||
    tree_view->setModel(item_model);
 | 
					    tree_view->setModel(item_model);
 | 
				
			||||||
 | 
					    controller_navigation = new ControllerNavigation(hid_core, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tree_view->setAlternatingRowColors(true);
 | 
					    tree_view->setAlternatingRowColors(true);
 | 
				
			||||||
    tree_view->setSelectionMode(QHeaderView::SingleSelection);
 | 
					    tree_view->setSelectionMode(QHeaderView::SingleSelection);
 | 
				
			||||||
@ -91,6 +94,14 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
 | 
				
			|||||||
    scroll_area->setLayout(layout);
 | 
					    scroll_area->setLayout(layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
 | 
					    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();
 | 
					    const auto& profiles = profile_manager->GetAllUsers();
 | 
				
			||||||
    for (const auto& user : profiles) {
 | 
					    for (const auto& user : profiles) {
 | 
				
			||||||
@ -113,7 +124,9 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
 | 
				
			|||||||
    resize(550, 400);
 | 
					    resize(550, 400);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
 | 
					QtProfileSelectionDialog::~QtProfileSelectionDialog() {
 | 
				
			||||||
 | 
					    controller_navigation->UnloadController();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int QtProfileSelectionDialog::exec() {
 | 
					int QtProfileSelectionDialog::exec() {
 | 
				
			||||||
    // Skip profile selection when there's only one.
 | 
					    // Skip profile selection when there's only one.
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@
 | 
				
			|||||||
#include "core/frontend/applets/profile_select.h"
 | 
					#include "core/frontend/applets/profile_select.h"
 | 
				
			||||||
#include "core/hle/service/acc/profile_manager.h"
 | 
					#include "core/hle/service/acc/profile_manager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ControllerNavigation;
 | 
				
			||||||
class GMainWindow;
 | 
					class GMainWindow;
 | 
				
			||||||
class QDialogButtonBox;
 | 
					class QDialogButtonBox;
 | 
				
			||||||
class QGraphicsScene;
 | 
					class QGraphicsScene;
 | 
				
			||||||
@ -20,11 +21,15 @@ class QStandardItem;
 | 
				
			|||||||
class QStandardItemModel;
 | 
					class QStandardItemModel;
 | 
				
			||||||
class QVBoxLayout;
 | 
					class QVBoxLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Core::HID {
 | 
				
			||||||
 | 
					class HIDCore;
 | 
				
			||||||
 | 
					} // namespace Core::HID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QtProfileSelectionDialog final : public QDialog {
 | 
					class QtProfileSelectionDialog final : public QDialog {
 | 
				
			||||||
    Q_OBJECT
 | 
					    Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit QtProfileSelectionDialog(QWidget* parent);
 | 
					    explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent);
 | 
				
			||||||
    ~QtProfileSelectionDialog() override;
 | 
					    ~QtProfileSelectionDialog() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int exec() override;
 | 
					    int exec() override;
 | 
				
			||||||
@ -51,6 +56,7 @@ private:
 | 
				
			|||||||
    QDialogButtonBox* buttons;
 | 
					    QDialogButtonBox* buttons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::unique_ptr<Service::Account::ProfileManager> profile_manager;
 | 
					    std::unique_ptr<Service::Account::ProfileManager> profile_manager;
 | 
				
			||||||
 | 
					    ControllerNavigation* controller_navigation = nullptr;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
 | 
					class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@
 | 
				
			|||||||
#include <fmt/format.h>
 | 
					#include <fmt/format.h>
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
 | 
					#include "core/core.h"
 | 
				
			||||||
#include "core/file_sys/patch_manager.h"
 | 
					#include "core/file_sys/patch_manager.h"
 | 
				
			||||||
#include "core/file_sys/registered_cache.h"
 | 
					#include "core/file_sys/registered_cache.h"
 | 
				
			||||||
#include "yuzu/compatibility_list.h"
 | 
					#include "yuzu/compatibility_list.h"
 | 
				
			||||||
@ -25,6 +26,7 @@
 | 
				
			|||||||
#include "yuzu/game_list_worker.h"
 | 
					#include "yuzu/game_list_worker.h"
 | 
				
			||||||
#include "yuzu/main.h"
 | 
					#include "yuzu/main.h"
 | 
				
			||||||
#include "yuzu/uisettings.h"
 | 
					#include "yuzu/uisettings.h"
 | 
				
			||||||
 | 
					#include "yuzu/util/controller_navigation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
 | 
					GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
 | 
				
			||||||
    : QObject(parent), gamelist{gamelist} {}
 | 
					    : QObject(parent), gamelist{gamelist} {}
 | 
				
			||||||
@ -312,6 +314,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
 | 
				
			|||||||
    this->main_window = parent;
 | 
					    this->main_window = parent;
 | 
				
			||||||
    layout = new QVBoxLayout;
 | 
					    layout = new QVBoxLayout;
 | 
				
			||||||
    tree_view = new QTreeView;
 | 
					    tree_view = new QTreeView;
 | 
				
			||||||
 | 
					    controller_navigation = new ControllerNavigation(system.HIDCore(), this);
 | 
				
			||||||
    search_field = new GameListSearchField(this);
 | 
					    search_field = new GameListSearchField(this);
 | 
				
			||||||
    item_model = new QStandardItemModel(tree_view);
 | 
					    item_model = new QStandardItemModel(tree_view);
 | 
				
			||||||
    tree_view->setModel(item_model);
 | 
					    tree_view->setModel(item_model);
 | 
				
			||||||
@ -341,6 +344,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
 | 
				
			|||||||
    connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
 | 
					    connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
 | 
				
			||||||
    connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
 | 
					    connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
 | 
				
			||||||
    connect(tree_view, &QTreeView::collapsed, 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
 | 
					    // 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.
 | 
					    // it with signals/slots. In this case, QList falls under the umbrells of custom types.
 | 
				
			||||||
@ -353,7 +368,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
 | 
				
			|||||||
    setLayout(layout);
 | 
					    setLayout(layout);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GameList::UnloadController() {
 | 
				
			||||||
 | 
					    controller_navigation->UnloadController();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameList::~GameList() {
 | 
					GameList::~GameList() {
 | 
				
			||||||
 | 
					    UnloadController();
 | 
				
			||||||
    emit ShouldCancelWorker();
 | 
					    emit ShouldCancelWorker();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@
 | 
				
			|||||||
#include "uisettings.h"
 | 
					#include "uisettings.h"
 | 
				
			||||||
#include "yuzu/compatibility_list.h"
 | 
					#include "yuzu/compatibility_list.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ControllerNavigation;
 | 
				
			||||||
class GameListWorker;
 | 
					class GameListWorker;
 | 
				
			||||||
class GameListSearchField;
 | 
					class GameListSearchField;
 | 
				
			||||||
class GameListDir;
 | 
					class GameListDir;
 | 
				
			||||||
@ -88,6 +89,9 @@ public:
 | 
				
			|||||||
    void SaveInterfaceLayout();
 | 
					    void SaveInterfaceLayout();
 | 
				
			||||||
    void LoadInterfaceLayout();
 | 
					    void LoadInterfaceLayout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Disables events from the emulated controller
 | 
				
			||||||
 | 
					    void UnloadController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static const QStringList supported_file_extensions;
 | 
					    static const QStringList supported_file_extensions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
@ -143,6 +147,7 @@ private:
 | 
				
			|||||||
    QStandardItemModel* item_model = nullptr;
 | 
					    QStandardItemModel* item_model = nullptr;
 | 
				
			||||||
    GameListWorker* current_worker = nullptr;
 | 
					    GameListWorker* current_worker = nullptr;
 | 
				
			||||||
    QFileSystemWatcher* watcher = nullptr;
 | 
					    QFileSystemWatcher* watcher = nullptr;
 | 
				
			||||||
 | 
					    ControllerNavigation* controller_navigation = nullptr;
 | 
				
			||||||
    CompatibilityList compatibility_list;
 | 
					    CompatibilityList compatibility_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    friend class GameListSearchField;
 | 
					    friend class GameListSearchField;
 | 
				
			||||||
 | 
				
			|||||||
@ -449,7 +449,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GMainWindow::ProfileSelectorSelectProfile() {
 | 
					void GMainWindow::ProfileSelectorSelectProfile() {
 | 
				
			||||||
    QtProfileSelectionDialog dialog(this);
 | 
					    QtProfileSelectionDialog dialog(system->HIDCore(), this);
 | 
				
			||||||
    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
 | 
					    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
 | 
				
			||||||
                          Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
 | 
					                          Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
 | 
				
			||||||
                          Qt::WindowCloseButtonHint);
 | 
					                          Qt::WindowCloseButtonHint);
 | 
				
			||||||
@ -1346,7 +1346,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GMainWindow::SelectAndSetCurrentUser() {
 | 
					void GMainWindow::SelectAndSetCurrentUser() {
 | 
				
			||||||
    QtProfileSelectionDialog dialog(this);
 | 
					    QtProfileSelectionDialog dialog(system->HIDCore(), this);
 | 
				
			||||||
    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
 | 
					    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
 | 
				
			||||||
                          Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
 | 
					                          Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
 | 
				
			||||||
    dialog.setWindowModality(Qt::WindowModal);
 | 
					    dialog.setWindowModality(Qt::WindowModal);
 | 
				
			||||||
@ -1608,7 +1608,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
 | 
				
			|||||||
        if (has_user_save) {
 | 
					        if (has_user_save) {
 | 
				
			||||||
            // User save data
 | 
					            // User save data
 | 
				
			||||||
            const auto select_profile = [this] {
 | 
					            const auto select_profile = [this] {
 | 
				
			||||||
                QtProfileSelectionDialog dialog(this);
 | 
					                QtProfileSelectionDialog dialog(system->HIDCore(), this);
 | 
				
			||||||
                dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
 | 
					                dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
 | 
				
			||||||
                                      Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
 | 
					                                      Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
 | 
				
			||||||
                dialog.setWindowModality(Qt::WindowModal);
 | 
					                dialog.setWindowModality(Qt::WindowModal);
 | 
				
			||||||
@ -3376,7 +3376,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
 | 
				
			|||||||
    UpdateUISettings();
 | 
					    UpdateUISettings();
 | 
				
			||||||
    game_list->SaveInterfaceLayout();
 | 
					    game_list->SaveInterfaceLayout();
 | 
				
			||||||
    hotkey_registry.SaveHotkeys();
 | 
					    hotkey_registry.SaveHotkeys();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Unload controllers early
 | 
				
			||||||
    controller_dialog->UnloadController();
 | 
					    controller_dialog->UnloadController();
 | 
				
			||||||
 | 
					    game_list->UnloadController();
 | 
				
			||||||
    system->HIDCore().UnloadInputDevices();
 | 
					    system->HIDCore().UnloadInputDevices();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Shutdown session if the emu thread is active...
 | 
					    // Shutdown session if the emu thread is active...
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										177
									
								
								src/yuzu/util/controller_navigation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/yuzu/util/controller_navigation.cpp
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/yuzu/util/controller_navigation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/yuzu/util/controller_navigation.h
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user