Browse Source
Merge pull request #7452 from german77/controller_navigation
Merge pull request #7452 from german77/controller_navigation
yuzu: Implement basic controller UI navigationpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 285 additions and 8 deletions
-
2src/yuzu/CMakeLists.txt
-
4src/yuzu/applets/qt_controller.h
-
17src/yuzu/applets/qt_profile_select.cpp
-
8src/yuzu/applets/qt_profile_select.h
-
20src/yuzu/game_list.cpp
-
5src/yuzu/game_list.h
-
9src/yuzu/main.cpp
-
177src/yuzu/util/controller_navigation.cpp
-
51src/yuzu/util/controller_navigation.h
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
}; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue