early-access version 2251

This commit is contained in:
pineappleEA
2021-11-28 11:31:20 +01:00
parent a657cf53a1
commit faf5b13e48
58 changed files with 171227 additions and 121316 deletions

View File

@@ -605,6 +605,7 @@ struct Values {
BasicSetting<bool> extended_logging{false, "extended_logging"};
BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"};
// Miscellaneous
BasicSetting<std::string> log_filter{"*:Info", "log_filter"};

View File

@@ -370,6 +370,11 @@ enum class ControllerType {
RightJoycon,
Handheld,
GameCube,
Pokeball,
NES,
SNES,
N64,
SegaGenesis,
};
struct PlayerInput {

View File

@@ -479,6 +479,8 @@ add_library(core STATIC
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pdm_qry.cpp
hle/service/ns/pdm_qry.h
hle/service/ns/pl_u.cpp
hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h

View File

@@ -27,6 +27,16 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
return NpadStyleIndex::Handheld;
case Settings::ControllerType::GameCube:
return NpadStyleIndex::GameCube;
case Settings::ControllerType::Pokeball:
return NpadStyleIndex::Pokeball;
case Settings::ControllerType::NES:
return NpadStyleIndex::NES;
case Settings::ControllerType::SNES:
return NpadStyleIndex::SNES;
case Settings::ControllerType::N64:
return NpadStyleIndex::N64;
case Settings::ControllerType::SegaGenesis:
return NpadStyleIndex::SegaGenesis;
default:
return NpadStyleIndex::ProController;
}
@@ -46,6 +56,16 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
return Settings::ControllerType::Handheld;
case NpadStyleIndex::GameCube:
return Settings::ControllerType::GameCube;
case NpadStyleIndex::Pokeball:
return Settings::ControllerType::Pokeball;
case NpadStyleIndex::NES:
return Settings::ControllerType::NES;
case NpadStyleIndex::SNES:
return Settings::ControllerType::SNES;
case NpadStyleIndex::N64:
return Settings::ControllerType::N64;
case NpadStyleIndex::SegaGenesis:
return Settings::ControllerType::SegaGenesis;
default:
return Settings::ControllerType::ProController;
}

View File

@@ -263,6 +263,10 @@ void Controller_NPad::OnInit() {
style.fullkey.Assign(1);
style.gamecube.Assign(1);
style.palma.Assign(1);
style.lark.Assign(1);
style.lucia.Assign(1);
style.lagoon.Assign(1);
style.lager.Assign(1);
hid_core.SetSupportedStyleTag(style);
}

View File

@@ -1883,7 +1883,7 @@ public:
{317, nullptr, "GetNpadLeftRightInterfaceType"},
{318, nullptr, "HasBattery"},
{319, nullptr, "HasLeftRightBattery"},
{321, nullptr, "GetUniquePadsFromNpad"},
{321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
{322, nullptr, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{324, nullptr, "GetUniquePadButtonSet"},
@@ -2054,6 +2054,18 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
const s64 total_entries = 0;
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(total_entries);
}
};
class HidTmp final : public ServiceFramework<HidTmp> {

View File

@@ -12,6 +12,7 @@
#include "core/hle/service/ns/errors.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pdm_qry.h"
#include "core/hle/service/ns/pl_u.h"
#include "core/hle/service/set/set.h"
@@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
IFactoryResetInterface::~IFactoryResetInterface() = default;
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
Core::System& system_)
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetApplicationControlData"},
{1, nullptr, "GetApplicationDesiredLanguage"},
{2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"},
};
// clang-format on
RegisterHandlers(functions);
}
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
// clang-format off
static const FunctionInfo functions[] = {
{7988, nullptr, "GetDynamicRightsInterface"},
{7989, nullptr, "GetReadOnlyApplicationControlDataInterface"},
{7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
{7991, nullptr, "GetReadOnlyApplicationRecordInterface"},
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
@@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<NS_SU>(system)->InstallAsService(service_manager);
std::make_shared<NS_VM>(system)->InstallAsService(service_manager);
std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
std::make_shared<PL_U>(system)->InstallAsService(service_manager);
}

View File

@@ -74,6 +74,13 @@ public:
~IFactoryResetInterface() override;
};
class IReadOnlyApplicationControlDataInterface final
: public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
public:
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
~IReadOnlyApplicationControlDataInterface() override;
};
class NS final : public ServiceFramework<NS> {
public:
explicit NS(const char* name, Core::System& system_);

View File

@@ -0,0 +1,69 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#include <memory>
#include "common/logging/log.h"
#include "common/uuid.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/pdm_qry.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::NS {
PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "QueryAppletEvent"},
{1, nullptr, "QueryPlayStatistics"},
{2, nullptr, "QueryPlayStatisticsByUserAccountId"},
{3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
{4, nullptr, "QueryPlayStatisticsByApplicationId"},
{5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
{6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
{7, nullptr, "QueryLastPlayTimeV0"},
{8, nullptr, "QueryPlayEvent"},
{9, nullptr, "GetAvailablePlayEventRange"},
{10, nullptr, "QueryAccountEvent"},
{11, nullptr, "QueryAccountPlayEvent"},
{12, nullptr, "GetAvailableAccountPlayEventRange"},
{13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
{14, nullptr, "QueryRecentlyPlayedApplication"},
{15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
{16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
{17, nullptr, "QueryLastPlayTime"},
{18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
{19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
};
// clang-format on
RegisterHandlers(functions);
}
PDM_QRY::~PDM_QRY() = default;
void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto unknown = rp.Pop<bool>();
rp.Pop<u8>(); // Padding
const auto application_id = rp.Pop<u64>();
const auto user_account_uid = rp.PopRaw<Common::UUID>();
// TODO(German77): Read statistics of the game
PlayStatistics statistics{
.application_id = application_id,
.total_launches = 1,
};
LOG_WARNING(Service_NS,
"(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
unknown, application_id, user_account_uid.Format());
IPC::ResponseBuilder rb{ctx, 12};
rb.Push(ResultSuccess);
rb.PushRaw(statistics);
}
} // namespace Service::NS

View File

@@ -0,0 +1,33 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#pragma once
#include "core/hle/service/service.h"
namespace Service::NS {
struct PlayStatistics {
u64 application_id{};
u32 first_entry_index{};
u32 first_timestamp_user{};
u32 first_timestamp_network{};
u32 last_entry_index{};
u32 last_timestamp_user{};
u32 last_timestamp_network{};
u32 play_time_in_minutes{};
u32 total_launches{};
};
static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
class PDM_QRY final : public ServiceFramework<PDM_QRY> {
public:
explicit PDM_QRY(Core::System& system_);
~PDM_QRY() override;
private:
void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx);
};
} // namespace Service::NS

View File

@@ -430,7 +430,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
}
void EmitSetFragDepth(EmitContext& ctx, Id value) {
if (!ctx.runtime_info.convert_depth_mode) {
if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) {
ctx.OpStore(ctx.frag_depth, value);
return;
}

View File

@@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) {
}
void EmitEpilogue(EmitContext& ctx) {
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode &&
!ctx.profile.support_native_ndc) {
ConvertDepthMode(ctx);
}
if (ctx.stage == Stage::Fragment) {
@@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) {
}
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
if (ctx.runtime_info.convert_depth_mode) {
if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
ConvertDepthMode(ctx);
}
if (stream.IsImmediate()) {

View File

@@ -36,6 +36,7 @@ struct Profile {
bool support_int64_atomics{};
bool support_derivative_control{};
bool support_geometry_shader_passthrough{};
bool support_native_ndc{};
bool support_gl_nv_gpu_shader_5{};
bool support_gl_amd_gpu_shader_half_float{};
bool support_gl_texture_shadow_lod{};

View File

@@ -194,6 +194,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.support_int64_atomics = false,
.support_derivative_control = device.HasDerivativeControl(),
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
.support_native_ndc = true,
.support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
.support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
.support_gl_texture_shadow_lod = device.HasTextureShadowLod(),

View File

@@ -522,9 +522,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
vertex_attributes.push_back({
.location = static_cast<u32>(index),
.binding = 0,
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
: type == 2 ? VK_FORMAT_R32_SINT
: VK_FORMAT_R32_UINT,
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
: type == 2 ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT,
.offset = 0,
});
}
@@ -614,25 +613,36 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
};
void* viewport_next = nullptr;
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
.pNext = nullptr,
.pNext = viewport_next,
.flags = 0,
.viewportCount = Maxwell::NumViewports,
.pViewportSwizzles = swizzles.data(),
};
if (device.IsNvViewportSwizzleSupported()) {
viewport_next = &swizzle_ci;
}
VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT,
.pNext = viewport_next,
.negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
};
if (device.IsExtDepthClipControlSupported()) {
viewport_next = &ndc_info;
}
const VkPipelineViewportStateCreateInfo viewport_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr,
.pNext = viewport_next,
.flags = 0,
.viewportCount = Maxwell::NumViewports,
.pViewports = nullptr,
.scissorCount = Maxwell::NumViewports,
.pScissors = nullptr,
};
VkPipelineRasterizationStateCreateInfo rasterization_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = nullptr,

View File

@@ -311,6 +311,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
.support_derivative_control = true,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),

View File

@@ -556,6 +556,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
if (ext_depth_clip_control) {
depth_clip_control_features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
.pNext = nullptr,
.depthClipControl = VK_TRUE,
};
SetNext(next, depth_clip_control_features);
}
VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) {
nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
@@ -890,6 +900,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_ext_provoking_vertex{};
bool has_ext_vertex_input_dynamic_state{};
bool has_ext_line_rasterization{};
bool has_ext_depth_clip_control{};
for (const std::string& extension : supported_extensions) {
const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
bool push) {
@@ -921,6 +932,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
true);
test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
@@ -1083,6 +1095,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
ext_line_rasterization = true;
}
}
if (has_ext_depth_clip_control) {
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
depth_clip_control_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
depth_clip_control_features.pNext = nullptr;
features.pNext = &depth_clip_control_features;
physical.GetFeatures2KHR(features);
if (depth_clip_control_features.depthClipControl) {
extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
ext_depth_clip_control = true;
}
}
if (has_khr_workgroup_memory_explicit_layout) {
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout;
layout.sType =

View File

@@ -253,6 +253,11 @@ public:
return ext_depth_range_unrestricted;
}
/// Returns true if the device supports VK_EXT_depth_clip_control.
bool IsExtDepthClipControlSupported() const {
return ext_depth_clip_control;
}
/// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
bool IsExtShaderViewportIndexLayerSupported() const {
return ext_shader_viewport_index_layer;
@@ -412,6 +417,7 @@ private:
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.

View File

@@ -126,6 +126,8 @@ add_executable(yuzu
configuration/configure_web.ui
configuration/input_profiles.cpp
configuration/input_profiles.h
controller_navigation.cpp
controller_navigation.h
debugger/console.cpp
debugger/console.h
debugger/controller.cpp

View File

@@ -201,6 +201,16 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&QtControllerSelectorDialog::ApplyConfiguration);
controller_navigation = new ControllerNavigation(system.HIDCore(), this);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
[this](Qt::Key key) {
if (!this->isActiveWindow()) {
return;
}
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(this, event);
});
// Enhancement: Check if the parameters have already been met before disconnecting controllers.
// If all the parameters are met AND only allows a single player,
// stop the constructor here as we do not need to continue.
@@ -218,7 +228,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
resize(0, 0);
}
QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
QtControllerSelectorDialog::~QtControllerSelectorDialog() {
controller_navigation->UnloadController();
};
int QtControllerSelectorDialog::exec() {
if (parameters_met && parameters.enable_single_mode) {

View File

@@ -9,6 +9,7 @@
#include <QDialog>
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "yuzu/controller_navigation.h"
class GMainWindow;
class QCheckBox;
@@ -148,6 +149,9 @@ private:
// Checkboxes representing the "Connected Controllers".
std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
// QObject for navigating the UI with a controller
ControllerNavigation* controller_navigation = nullptr;
};
class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <mutex>
#include <QApplication>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
@@ -45,7 +46,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 +66,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 +93,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 +123,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.

View File

@@ -10,6 +10,7 @@
#include <QTreeView>
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
#include "yuzu/controller_navigation.h"
class GMainWindow;
class QDialogButtonBox;
@@ -24,7 +25,7 @@ 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 +52,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 {

View File

@@ -508,6 +508,7 @@ void Config::ReadDebuggingValues() {
ReadBasicSetting(Settings::values.extended_logging);
ReadBasicSetting(Settings::values.use_debug_asserts);
ReadBasicSetting(Settings::values.use_auto_stub);
ReadBasicSetting(Settings::values.enable_all_controllers);
qt_config->endGroup();
}
@@ -1051,6 +1052,7 @@ void Config::SaveDebuggingValues() {
WriteBasicSetting(Settings::values.quest_flag);
WriteBasicSetting(Settings::values.use_debug_asserts);
WriteBasicSetting(Settings::values.disable_macro_jit);
WriteBasicSetting(Settings::values.enable_all_controllers);
qt_config->endGroup();
}

View File

@@ -42,6 +42,7 @@ void ConfigureDebug::SetConfiguration() {
ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -69,6 +70,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();

View File

@@ -211,6 +211,13 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enable_all_controllers">
<property name="text">
<string>Enable all Controller Types</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -947,6 +947,40 @@ void ConfigureInputPlayer::SetConnectableControllers() {
Core::HID::NpadStyleIndex::GameCube);
ui->comboControllerType->addItem(tr("GameCube Controller"));
}
// 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 (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 (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"));
}
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 (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 (!is_powered_on) {

View File

@@ -0,0 +1,160 @@
// 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/controller_navigation.h"
ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
.is_npad_service = false,
};
callback_key = controller->SetCallback(engine_callback);
is_controller_set = true;
}
ControllerNavigation::~ControllerNavigation() {
UnloadController();
}
void ControllerNavigation::UnloadController() {
if (is_controller_set) {
controller->DeleteCallback(callback_key);
is_controller_set = false;
}
}
void ControllerNavigation::TriggerButton(const Core::HID::ButtonValues& buttons,
Settings::NativeButton::Values native_button,
Qt::Key key) {
if (buttons[native_button].value && !button_values[native_button].locked) {
emit TriggerKeyboardEvent(key);
}
}
void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
if (type == Core::HID::ControllerTriggerType::Button) {
const auto controller_type = controller->GetNpadStyleIndex();
const auto& buttons = controller->GetButtonsValues();
for (std::size_t i = 0; i < buttons.size(); ++i) {
// Trigger only once
if (buttons[i].value == button_values[i].value) {
button_values[i].locked = true;
} else {
button_values[i].value = buttons[i].value;
button_values[i].locked = false;
}
}
switch (controller_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::GameCube:
TriggerButton(buttons, Settings::NativeButton::A, Qt::Key_Enter);
TriggerButton(buttons, Settings::NativeButton::B, Qt::Key_Escape);
TriggerButton(buttons, Settings::NativeButton::DDown, Qt::Key_Down);
TriggerButton(buttons, Settings::NativeButton::DLeft, Qt::Key_Left);
TriggerButton(buttons, Settings::NativeButton::DRight, Qt::Key_Right);
TriggerButton(buttons, Settings::NativeButton::DUp, Qt::Key_Up);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
TriggerButton(buttons, Settings::NativeButton::DDown, Qt::Key_Enter);
TriggerButton(buttons, Settings::NativeButton::DLeft, Qt::Key_Escape);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
TriggerButton(buttons, Settings::NativeButton::X, Qt::Key_Enter);
TriggerButton(buttons, Settings::NativeButton::A, Qt::Key_Escape);
break;
default:
break;
}
return;
}
if (type == Core::HID::ControllerTriggerType::Stick) {
const auto controller_type = controller->GetNpadStyleIndex();
const auto& sticks = controller->GetSticksValues();
bool update = false;
for (std::size_t i = 0; i < sticks.size(); ++i) {
// Trigger only once
if (sticks[i].down != stick_values[i].down || sticks[i].left != stick_values[i].left ||
sticks[i].right != stick_values[i].right || sticks[i].up != stick_values[i].up) {
update = true;
}
stick_values[i] = sticks[i];
}
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 (sticks[Settings::NativeAnalog::LStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (sticks[Settings::NativeAnalog::LStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (sticks[Settings::NativeAnalog::LStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (sticks[Settings::NativeAnalog::LStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
if (sticks[Settings::NativeAnalog::LStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (sticks[Settings::NativeAnalog::LStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (sticks[Settings::NativeAnalog::LStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (sticks[Settings::NativeAnalog::LStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
case Core::HID::NpadStyleIndex::JoyconRight:
if (sticks[Settings::NativeAnalog::RStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (sticks[Settings::NativeAnalog::RStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (sticks[Settings::NativeAnalog::RStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (sticks[Settings::NativeAnalog::RStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
default:
break;
}
return;
}
}

View File

@@ -0,0 +1,42 @@
// 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.h"
#include "core/hid/emulated_controller.h"
namespace Core::HID {
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(const Core::HID::ButtonValues& buttons,
Settings::NativeButton::Values native_button, Qt::Key key);
void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);
Core::HID::ButtonValues button_values{};
Core::HID::SticksValues stick_values{};
int callback_key;
bool is_controller_set{};
Core::HID::EmulatedController* controller;
};

View File

@@ -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"
@@ -312,6 +313,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 +343,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 +367,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
setLayout(layout);
}
void GameList::UnloadController() {
controller_navigation->UnloadController();
}
GameList::~GameList() {
UnloadController();
emit ShouldCancelWorker();
}

View File

@@ -23,6 +23,7 @@
#include "common/common_types.h"
#include "uisettings.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/controller_navigation.h"
class GameListWorker;
class GameListSearchField;
@@ -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;

View File

@@ -449,7 +449,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);
@@ -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);
@@ -1608,7 +1608,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 +3376,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...