Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 41aec2773f | |||
| fae07919af | |||
| 7347cdb651 | |||
| f1f91ad468 | |||
| 60e923046e | |||
| 7fe455e42e | |||
| e482dd82b9 | |||
| f919498f8f | |||
| a2fb5a13b2 | |||
| e7f10de11a | |||
| a578df4c6b | |||
| 20a46790d7 | |||
| cd27f211c8 | |||
| fdcc161323 | |||
| f138731e2f | |||
| 55d6b095e5 | |||
| 5ba7b11ba4 | |||
| 762b8ad448 | |||
| 8a613f6c8f | |||
| 40317eccba | |||
| ff63080cd9 | |||
| 505ae5ea1b | |||
| 46e3ed5a48 | |||
| e4492a9a82 | |||
| 051e63c9a1 | |||
| 524a9baa7e |
Vendored
+940
-706
File diff suppressed because it is too large
Load Diff
Vendored
+949
-715
File diff suppressed because it is too large
Load Diff
Vendored
+938
-704
File diff suppressed because it is too large
Load Diff
Vendored
+950
-716
File diff suppressed because it is too large
Load Diff
Vendored
+1431
-1196
File diff suppressed because it is too large
Load Diff
Vendored
+967
-733
File diff suppressed because it is too large
Load Diff
Vendored
+956
-722
File diff suppressed because it is too large
Load Diff
Vendored
+955
-721
File diff suppressed because it is too large
Load Diff
Vendored
+958
-724
File diff suppressed because it is too large
Load Diff
Vendored
+934
-700
File diff suppressed because it is too large
Load Diff
Vendored
+947
-707
File diff suppressed because it is too large
Load Diff
Vendored
+942
-708
File diff suppressed because it is too large
Load Diff
Vendored
+953
-719
File diff suppressed because it is too large
Load Diff
Vendored
+941
-707
File diff suppressed because it is too large
Load Diff
Vendored
+1012
-778
File diff suppressed because it is too large
Load Diff
Vendored
+940
-706
File diff suppressed because it is too large
Load Diff
Vendored
+1017
-780
File diff suppressed because it is too large
Load Diff
Vendored
+6044
File diff suppressed because it is too large
Load Diff
Vendored
+954
-720
File diff suppressed because it is too large
Load Diff
Vendored
+1047
-805
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "audio_core/delay_line.h"
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <iterator>
|
||||
|
||||
@@ -15,26 +15,26 @@
|
||||
namespace Common {
|
||||
|
||||
u64 EstimateRDTSCFrequency() {
|
||||
const auto milli_10 = std::chrono::milliseconds{10};
|
||||
// get current time
|
||||
// Discard the first result measuring the rdtsc.
|
||||
_mm_mfence();
|
||||
const u64 tscStart = __rdtsc();
|
||||
const auto startTime = std::chrono::high_resolution_clock::now();
|
||||
// wait roughly 3 seconds
|
||||
while (true) {
|
||||
auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now() - startTime);
|
||||
if (milli.count() >= 3000)
|
||||
break;
|
||||
std::this_thread::sleep_for(milli_10);
|
||||
}
|
||||
const auto endTime = std::chrono::high_resolution_clock::now();
|
||||
__rdtsc();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
||||
_mm_mfence();
|
||||
const u64 tscEnd = __rdtsc();
|
||||
// calculate difference
|
||||
const u64 timer_diff =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
|
||||
const u64 tsc_diff = tscEnd - tscStart;
|
||||
__rdtsc();
|
||||
|
||||
// Get the current time.
|
||||
const auto start_time = std::chrono::steady_clock::now();
|
||||
_mm_mfence();
|
||||
const u64 tsc_start = __rdtsc();
|
||||
// Wait for 200 milliseconds.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{200});
|
||||
const auto end_time = std::chrono::steady_clock::now();
|
||||
_mm_mfence();
|
||||
const u64 tsc_end = __rdtsc();
|
||||
// Calculate differences.
|
||||
const u64 timer_diff = static_cast<u64>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
|
||||
const u64 tsc_diff = tsc_end - tsc_start;
|
||||
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
|
||||
return tsc_freq;
|
||||
}
|
||||
|
||||
@@ -866,7 +866,52 @@ void EmulatedController::SetLedPattern() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
|
||||
supported_style_tag = supported_styles;
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported() const {
|
||||
switch (npad_type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
case NpadStyleIndex::Handheld:
|
||||
return supported_style_tag.handheld;
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
return supported_style_tag.joycon_dual;
|
||||
case NpadStyleIndex::JoyconLeft:
|
||||
return supported_style_tag.joycon_left;
|
||||
case NpadStyleIndex::JoyconRight:
|
||||
return supported_style_tag.joycon_right;
|
||||
case NpadStyleIndex::GameCube:
|
||||
return supported_style_tag.gamecube;
|
||||
case NpadStyleIndex::Pokeball:
|
||||
return supported_style_tag.palma;
|
||||
case NpadStyleIndex::NES:
|
||||
return supported_style_tag.lark;
|
||||
case NpadStyleIndex::SNES:
|
||||
return supported_style_tag.lucia;
|
||||
case NpadStyleIndex::N64:
|
||||
return supported_style_tag.lagoon;
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return supported_style_tag.lager;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect() {
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (is_configuring) {
|
||||
|
||||
@@ -160,6 +160,13 @@ public:
|
||||
*/
|
||||
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Sets the supported controller types. Disconnects the controller if current type is not
|
||||
* supported
|
||||
* @param supported_styles bitflag with supported types
|
||||
*/
|
||||
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
|
||||
|
||||
/// Sets the connected status to true
|
||||
void Connect();
|
||||
|
||||
@@ -310,6 +317,12 @@ private:
|
||||
/// Set the params for TAS devices
|
||||
void LoadTASParams();
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @return true if the controller is supported
|
||||
*/
|
||||
bool IsControllerSupported() const;
|
||||
|
||||
/**
|
||||
* Updates the button status of the controller
|
||||
* @param callback A CallbackStatus containing the button status
|
||||
@@ -354,6 +367,7 @@ private:
|
||||
|
||||
NpadIdType npad_id_type;
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
f32 motion_sensitivity{0.01f};
|
||||
|
||||
@@ -108,6 +108,16 @@ const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t inde
|
||||
|
||||
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
|
||||
supported_style_tag.raw = style_tag.raw;
|
||||
player_1->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_2->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_3->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_4->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_5->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_6->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_7->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
player_8->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
other->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
handheld->SetSupportedNpadStyleTag(supported_style_tag);
|
||||
}
|
||||
|
||||
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
|
||||
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
std::unique_ptr<EmulatedController> handheld;
|
||||
std::unique_ptr<EmulatedConsole> console;
|
||||
std::unique_ptr<EmulatedDevices> devices;
|
||||
NpadStyleTag supported_style_tag;
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
};
|
||||
|
||||
} // namespace Core::HID
|
||||
|
||||
@@ -256,6 +256,8 @@ enum class NpadStyleSet : u32 {
|
||||
Lager = 1U << 11,
|
||||
SystemExt = 1U << 29,
|
||||
System = 1U << 30,
|
||||
|
||||
All = 0xFFFFFFFFU,
|
||||
};
|
||||
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
#include "core/hle/service/apm/apm_interface.h"
|
||||
#include "core/hle/service/bcat/backend/backend.h"
|
||||
#include "core/hle/service/caps/caps.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
|
||||
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
|
||||
{110, nullptr, "SetApplicationAlbumUserData"},
|
||||
{120, nullptr, "SaveCurrentScreenshot"},
|
||||
{120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
|
||||
{130, nullptr, "SetRecordVolumeMuted"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
@@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>();
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue(Core::System& system)
|
||||
: service_context{system, "AppletMessageQueue"} {
|
||||
on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
|
||||
|
||||
@@ -151,6 +151,7 @@ private:
|
||||
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
|
||||
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class ScreenshotPermission : u32 {
|
||||
Inherit = 0,
|
||||
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
|
||||
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
|
||||
std::vector<opus_int16>& output, u64* out_performance_time) const {
|
||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
||||
const auto start_time = std::chrono::steady_clock::now();
|
||||
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
|
||||
if (sizeof(OpusPacketHeader) > input.size()) {
|
||||
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
|
||||
const auto end_time = std::chrono::steady_clock::now() - start_time;
|
||||
sample_count = out_sample_count;
|
||||
consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
|
||||
if (out_performance_time != nullptr) {
|
||||
|
||||
@@ -24,7 +24,7 @@ enum class AlbumImageOrientation {
|
||||
Orientation3 = 3,
|
||||
};
|
||||
|
||||
enum class AlbumReportOption {
|
||||
enum class AlbumReportOption : s32 {
|
||||
Disable = 0,
|
||||
Enable = 1,
|
||||
};
|
||||
|
||||
@@ -17,10 +17,11 @@ namespace Service::Friend {
|
||||
|
||||
class IFriendService final : public ServiceFramework<IFriendService> {
|
||||
public:
|
||||
explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} {
|
||||
explicit IFriendService(Core::System& system_)
|
||||
: ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetCompletionEvent"},
|
||||
{0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
|
||||
{1, nullptr, "Cancel"},
|
||||
{10100, nullptr, "GetFriendListIds"},
|
||||
{10101, &IFriendService::GetFriendList, "GetFriendList"},
|
||||
@@ -109,6 +110,12 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
|
||||
}
|
||||
|
||||
~IFriendService() override {
|
||||
service_context.CloseEvent(completion_event);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -129,6 +136,14 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
|
||||
|
||||
void GetCompletionEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(completion_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
|
||||
// This is safe to stub, as there should be no adverse consequences from reporting no
|
||||
// blocked users.
|
||||
@@ -179,6 +194,10 @@ private:
|
||||
rb.Push<u32>(0); // Friend count
|
||||
// TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* completion_event;
|
||||
};
|
||||
|
||||
class INotificationService final : public ServiceFramework<INotificationService> {
|
||||
|
||||
@@ -126,8 +126,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
|
||||
}
|
||||
|
||||
void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
|
||||
const auto controller_type = controller.device->GetNpadStyleIndex();
|
||||
auto& shared_memory = controller.shared_memory_entry;
|
||||
if (controller_type == Core::HID::NpadStyleIndex::None) {
|
||||
@@ -255,19 +258,7 @@ void Controller_NPad::OnInit() {
|
||||
|
||||
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
|
||||
// We want to support all controllers
|
||||
Core::HID::NpadStyleTag style{};
|
||||
style.handheld.Assign(1);
|
||||
style.joycon_left.Assign(1);
|
||||
style.joycon_right.Assign(1);
|
||||
style.joycon_dual.Assign(1);
|
||||
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);
|
||||
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||
}
|
||||
|
||||
supported_npad_id_types.resize(npad_id_list.size());
|
||||
@@ -1072,13 +1063,18 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
|
||||
const auto type_index_1 = controller_1->GetNpadStyleIndex();
|
||||
const auto type_index_2 = controller_2->GetNpadStyleIndex();
|
||||
const auto is_connected_1 = controller_1->IsConnected();
|
||||
const auto is_connected_2 = controller_2->IsConnected();
|
||||
|
||||
if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_2)) {
|
||||
if (!IsControllerSupported(type_index_1) && is_connected_1) {
|
||||
return false;
|
||||
}
|
||||
if (!IsControllerSupported(type_index_2) && is_connected_2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AddNewControllerAt(type_index_2, npad_id_1);
|
||||
AddNewControllerAt(type_index_1, npad_id_2);
|
||||
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
|
||||
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
explicit PerfStats(u64 title_id_);
|
||||
~PerfStats();
|
||||
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
void BeginSystemFrame();
|
||||
void EndSystemFrame();
|
||||
@@ -87,7 +87,7 @@ private:
|
||||
|
||||
class SpeedLimiter {
|
||||
public:
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
|
||||
|
||||
|
||||
@@ -388,8 +388,6 @@ void SDLDriver::CloseJoysticks() {
|
||||
}
|
||||
|
||||
SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
Common::SetCurrentThreadName("yuzu:input:SDL");
|
||||
|
||||
if (!Settings::values.enable_raw_input) {
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
@@ -422,6 +420,7 @@ SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engin
|
||||
initialized = true;
|
||||
if (start_thread) {
|
||||
poll_thread = std::thread([this] {
|
||||
Common::SetCurrentThreadName("yuzu:input:SDL");
|
||||
using namespace std::chrono_literals;
|
||||
while (initialized) {
|
||||
SDL_PumpEvents();
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -3,6 +3,7 @@ add_subdirectory(host_shaders)
|
||||
if(LIBVA_FOUND)
|
||||
set_source_files_properties(command_classes/codecs/codec.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
|
||||
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_library(video_core STATIC
|
||||
|
||||
@@ -17,12 +17,28 @@
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/opt.h>
|
||||
#ifdef LIBVA_FOUND
|
||||
// for querying VAAPI driver information
|
||||
#include <libavutil/hwcontext_vaapi.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
namespace {
|
||||
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
|
||||
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
|
||||
constexpr std::array PREFERRED_GPU_DECODERS = {
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
#ifdef _WIN32
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
AV_HWDEVICE_TYPE_DXVA2,
|
||||
#elif defined(__linux__)
|
||||
AV_HWDEVICE_TYPE_VAAPI,
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
#endif
|
||||
// last resort for Linux Flatpak (w/ NVIDIA)
|
||||
AV_HWDEVICE_TYPE_VULKAN,
|
||||
};
|
||||
|
||||
void AVPacketDeleter(AVPacket* ptr) {
|
||||
av_packet_free(&ptr);
|
||||
@@ -61,83 +77,50 @@ Codec::~Codec() {
|
||||
av_buffer_unref(&av_gpu_decoder);
|
||||
}
|
||||
|
||||
#ifdef LIBVA_FOUND
|
||||
// List all the currently loaded Linux modules
|
||||
static std::vector<std::string> ListLinuxKernelModules() {
|
||||
using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
|
||||
auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose};
|
||||
std::vector<std::string> modules{};
|
||||
if (!module_listing) {
|
||||
LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules");
|
||||
return modules;
|
||||
}
|
||||
char* buffer = nullptr;
|
||||
size_t buf_len = 0;
|
||||
while (getline(&buffer, &buf_len, module_listing.get()) != -1) {
|
||||
// format for the module listing file (sysfs)
|
||||
// <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
|
||||
auto line = std::string(buffer);
|
||||
// we are only interested in module names
|
||||
auto name_pos = line.find_first_of(" ");
|
||||
if (name_pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
modules.push_back(line.erase(name_pos));
|
||||
}
|
||||
free(buffer);
|
||||
return modules;
|
||||
// List all the currently available hwcontext in ffmpeg
|
||||
static std::vector<AVHWDeviceType> ListSupportedContexts() {
|
||||
std::vector<AVHWDeviceType> contexts{};
|
||||
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
|
||||
do {
|
||||
current_device_type = av_hwdevice_iterate_types(current_device_type);
|
||||
contexts.push_back(current_device_type);
|
||||
} while (current_device_type != AV_HWDEVICE_TYPE_NONE);
|
||||
return contexts;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Codec::CreateGpuAvDevice() {
|
||||
#if defined(LIBVA_FOUND)
|
||||
static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
|
||||
"i915",
|
||||
"iHD",
|
||||
"amdgpu",
|
||||
};
|
||||
AVDictionary* hwdevice_options = nullptr;
|
||||
const auto loaded_modules = ListLinuxKernelModules();
|
||||
av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
|
||||
for (const auto& driver : VAAPI_DRIVERS) {
|
||||
// first check if the target driver is loaded in the kernel
|
||||
bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(),
|
||||
[&driver](const auto& module) { return module == driver; });
|
||||
if (!found) {
|
||||
LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver);
|
||||
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
|
||||
static const auto supported_contexts = ListSupportedContexts();
|
||||
for (const auto& type : PREFERRED_GPU_DECODERS) {
|
||||
if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
|
||||
[&type](const auto& context) { return context == type; })) {
|
||||
LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
|
||||
continue;
|
||||
}
|
||||
av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
|
||||
const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
|
||||
nullptr, hwdevice_options, 0);
|
||||
if (hwdevice_error >= 0) {
|
||||
LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
|
||||
av_dict_free(&hwdevice_options);
|
||||
av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
|
||||
return true;
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
|
||||
av_dict_free(&hwdevice_options);
|
||||
#endif
|
||||
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
|
||||
static constexpr std::array GPU_DECODER_TYPES{
|
||||
#ifdef linux
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
#endif
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
#ifdef _WIN32
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
#endif
|
||||
};
|
||||
for (const auto& type : GPU_DECODER_TYPES) {
|
||||
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
||||
if (hwdevice_res < 0) {
|
||||
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
|
||||
av_hwdevice_get_type_name(type), hwdevice_res);
|
||||
continue;
|
||||
}
|
||||
#ifdef LIBVA_FOUND
|
||||
if (type == AV_HWDEVICE_TYPE_VAAPI) {
|
||||
// we need to determine if this is an impersonated VAAPI driver
|
||||
AVHWDeviceContext* hwctx =
|
||||
static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
|
||||
AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
|
||||
const char* vendor_name = vaQueryVendorString(vactx->display);
|
||||
if (strstr(vendor_name, "VDPAU backend")) {
|
||||
// VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
|
||||
LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
|
||||
continue;
|
||||
} else {
|
||||
// according to some user testing, certain vaapi driver (Intel?) could be buggy
|
||||
// so let's log the driver name which may help the developers/supporters
|
||||
LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
||||
if (!config) {
|
||||
|
||||
@@ -18,7 +18,7 @@ int ShaderNotify::ShadersBuilding() noexcept {
|
||||
const int now_complete = num_complete.load(std::memory_order::relaxed);
|
||||
const int now_building = num_building.load(std::memory_order::relaxed);
|
||||
if (now_complete == now_building) {
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (completed && num_complete == num_when_completed) {
|
||||
if (now - complete_time > TIME_TO_STOP_REPORTING) {
|
||||
report_base = now_complete;
|
||||
|
||||
@@ -28,6 +28,6 @@ private:
|
||||
|
||||
bool completed{};
|
||||
int num_when_completed{};
|
||||
std::chrono::high_resolution_clock::time_point complete_time;
|
||||
std::chrono::steady_clock::time_point complete_time;
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -139,6 +139,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
|
||||
return PixelFormat::D16_UNORM;
|
||||
case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR):
|
||||
return PixelFormat::S8_UINT_D24_UNORM;
|
||||
case Hash(TextureFormat::S8D24, UINT, UNORM, UINT, UINT, LINEAR):
|
||||
return PixelFormat::S8_UINT_D24_UNORM;
|
||||
case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
|
||||
return PixelFormat::S8_UINT_D24_UNORM;
|
||||
case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag();
|
||||
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"));
|
||||
}
|
||||
|
||||
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 (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 (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 (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"));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
add_controllers(true);
|
||||
return;
|
||||
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"));
|
||||
}
|
||||
|
||||
add_controllers(false, hid_core.GetSupportedStyleTag());
|
||||
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 (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.joycon_right == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Core::HID::NpadStyleIndex::JoyconRight);
|
||||
ui->comboControllerType->addItem(tr("Right Joycon"));
|
||||
}
|
||||
|
||||
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 (npad_style_set.gamecube == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Core::HID::NpadStyleIndex::GameCube);
|
||||
ui->comboControllerType->addItem(tr("GameCube 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 (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.lucia == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Core::HID::NpadStyleIndex::SNES);
|
||||
ui->comboControllerType->addItem(tr("SNES Controller"));
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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} {}
|
||||
@@ -312,6 +314,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 +344,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 +368,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void GameList::UnloadController() {
|
||||
controller_navigation->UnloadController();
|
||||
}
|
||||
|
||||
GameList::~GameList() {
|
||||
UnloadController();
|
||||
emit ShouldCancelWorker();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
+9
-3
@@ -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);
|
||||
@@ -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...
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user