Compare commits

...

26 Commits

Author SHA1 Message Date
ameerj 41aec2773f general: Add missing copyright notices 2021-12-05 16:18:53 -05:00
Morph fae07919af Merge pull request #7518 from german77/is_npad_valid
core/hid: Add missing controller type
2021-12-05 15:11:14 -05:00
german77 7347cdb651 core/hid: Add missing controller type 2021-12-05 13:57:59 -06:00
Fernando S f1f91ad468 Merge pull request #7494 from Morph1984/no-time-to-wait
native_clock: Wait for less time in EstimateRDTSCFrequency
2021-12-05 18:56:38 +01:00
bunnei 60e923046e Merge pull request #7503 from german77/is_npad_valid
core/hid: Ensure only valid npad are connected
2021-12-04 23:08:43 -08:00
german77 7fe455e42e core/hid: Ensure only valid npad are connected 2021-12-04 23:20:18 -06:00
bunnei e482dd82b9 Merge pull request #7467 from liushuyu/fix-linux-decoding
video_core/codecs: more robust ffmpeg hwdecoder selection logic
2021-12-03 17:11:12 -08:00
Morph f919498f8f native_clock: Wait for less time in EstimateRDTSCFrequency
In my testing, waiting for 200ms provided the same level of precision as the previous implementation when estimating the RDTSC frequency.
This significantly improves the yuzu executable launch times since we reduced the wait time from 3 seconds to 200 milliseconds.
2021-12-03 19:55:59 -05:00
bunnei a2fb5a13b2 Merge pull request #7489 from Morph1984/steady-clock
general: Replace high_resolution_clock with steady_clock
2021-12-03 16:08:20 -08:00
liushuyu e7f10de11a video_core/cmake: link against libva explicitly ...
... to fix build on Flatpak (and self-builds)
2021-12-02 22:35:30 -07:00
liushuyu a578df4c6b video_core/codecs: more fixes for VAAPI detection ...
* skip impersonated VAAPI implementaions ("imposter detection")
* place VAAPI priority below CUDA/NVDEC/CUVID
2021-12-02 21:31:51 -07:00
liushuyu 20a46790d7 video_core/codec: address comments 2021-12-02 21:01:34 -07:00
liushuyu cd27f211c8 video_core/codecs: more robust ffmpeg hwdecoder selection logic 2021-12-02 21:01:34 -07:00
bunnei fdcc161323 Merge pull request #7490 from Morph1984/stub-album-save-screenshot
service: am: ISelfController: Stub SaveCurrentScreenshot
2021-12-02 18:02:26 -08:00
Morph f138731e2f service: am: ISelfController: Stub SaveCurrentScreenshot
- Used by Disney Magical World 2: Enchanted Edition
2021-12-02 20:12:24 -05:00
Morph 55d6b095e5 Merge pull request #7452 from german77/controller_navigation
yuzu: Implement basic controller UI navigation
2021-12-02 19:55:08 -05:00
german77 5ba7b11ba4 yuzu: Implement basic controller navigation 2021-12-02 15:17:44 -06:00
Morph 762b8ad448 general: Replace high_resolution_clock with steady_clock
On some OSes, high_resolution_clock is an alias to system_clock and is not monotonic in nature. Replace this with steady_clock.
2021-12-02 14:20:43 -05:00
Mai M 8a613f6c8f Merge pull request #7483 from zhaobot/tx-update-20211201022129
Update translations (2021-12-01)
2021-11-30 21:31:28 -05:00
The yuzu Community 40317eccba Update translations (2021-12-01) 2021-12-01 02:21:55 +00:00
bunnei ff63080cd9 Merge pull request #7482 from Morph1984/friend
service: friend: Implement GetCompletionEvent
2021-11-30 11:09:22 -08:00
Morph 505ae5ea1b service: friend: Implement GetCompletionEvent
- Used by Super Bomberman R Online
2021-11-30 12:19:21 -05:00
Narr the Reg 46e3ed5a48 Merge pull request #7472 from Morph1984/post-kraken-cleanup
core: hid: Post kraken cleanup
2021-11-29 21:10:02 -06:00
Narr the Reg e4492a9a82 input_common: Fix error with thread name 2021-11-29 22:03:47 -05:00
bunnei 051e63c9a1 Merge pull request #7466 from vonchenplus/add_miss_pixel_format_mapping
Add missing pixel format mapping
2021-11-29 15:19:15 -08:00
Feng Chen 524a9baa7e Add missing pixel format mapping 2021-11-29 12:39:37 +08:00
56 changed files with 25355 additions and 14472 deletions
+940 -706
View File
File diff suppressed because it is too large Load Diff
+949 -715
View File
File diff suppressed because it is too large Load Diff
+938 -704
View File
File diff suppressed because it is too large Load Diff
+950 -716
View File
File diff suppressed because it is too large Load Diff
+1431 -1196
View File
File diff suppressed because it is too large Load Diff
+967 -733
View File
File diff suppressed because it is too large Load Diff
+956 -722
View File
File diff suppressed because it is too large Load Diff
+955 -721
View File
File diff suppressed because it is too large Load Diff
+958 -724
View File
File diff suppressed because it is too large Load Diff
+934 -700
View File
File diff suppressed because it is too large Load Diff
+947 -707
View File
File diff suppressed because it is too large Load Diff
+942 -708
View File
File diff suppressed because it is too large Load Diff
+953 -719
View File
File diff suppressed because it is too large Load Diff
+941 -707
View File
File diff suppressed because it is too large Load Diff
+1012 -778
View File
File diff suppressed because it is too large Load Diff
+940 -706
View File
File diff suppressed because it is too large Load Diff
+1017 -780
View File
File diff suppressed because it is too large Load Diff
+6044
View File
File diff suppressed because it is too large Load Diff
+954 -720
View File
File diff suppressed because it is too large Load Diff
+1047 -805
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -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"
+4
View File
@@ -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"
+4
View File
@@ -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>
+18 -18
View File
@@ -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;
}
+45
View File
@@ -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) {
+14
View File
@@ -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};
+10
View File
@@ -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 {
+1 -1
View File
@@ -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
+2
View File
@@ -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");
+13 -1
View File
@@ -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");
+1
View File
@@ -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,
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -24,7 +24,7 @@ enum class AlbumImageOrientation {
Orientation3 = 3,
};
enum class AlbumReportOption {
enum class AlbumReportOption : s32 {
Disable = 0,
Enable = 1,
};
+21 -2
View File
@@ -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> {
+13 -17
View File
@@ -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;
}
+4
View File
@@ -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>
+2 -2
View File
@@ -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);
+1 -2
View File
@@ -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();
+4
View File
@@ -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>
+1
View File
@@ -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
+49 -66
View File
@@ -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) {
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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):
+2
View File
@@ -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
+2 -2
View File
@@ -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
+15 -2
View File
@@ -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.
+7 -1
View File
@@ -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 {
+20
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"
@@ -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();
}
+5
View File
@@ -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;
+2 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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
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);
@@ -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...
+177
View File
@@ -0,0 +1,177 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#include "common/settings_input.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "yuzu/util/controller_navigation.h"
ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) {
player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
.is_npad_service = false,
};
player1_callback_key = player1_controller->SetCallback(engine_callback);
handheld_callback_key = handheld_controller->SetCallback(engine_callback);
is_controller_set = true;
}
ControllerNavigation::~ControllerNavigation() {
UnloadController();
}
void ControllerNavigation::UnloadController() {
if (is_controller_set) {
player1_controller->DeleteCallback(player1_callback_key);
handheld_controller->DeleteCallback(handheld_callback_key);
is_controller_set = false;
}
}
void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button,
Qt::Key key) {
if (button_values[native_button].value && !button_values[native_button].locked) {
emit TriggerKeyboardEvent(key);
}
}
void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
std::lock_guard lock{mutex};
if (type == Core::HID::ControllerTriggerType::Button) {
ControllerUpdateButton();
return;
}
if (type == Core::HID::ControllerTriggerType::Stick) {
ControllerUpdateStick();
return;
}
}
void ControllerNavigation::ControllerUpdateButton() {
const auto controller_type = player1_controller->GetNpadStyleIndex();
const auto& player1_buttons = player1_controller->GetButtonsValues();
const auto& handheld_buttons = handheld_controller->GetButtonsValues();
for (std::size_t i = 0; i < player1_buttons.size(); ++i) {
const bool button = player1_buttons[i].value || handheld_buttons[i].value;
// Trigger only once
button_values[i].locked = button == button_values[i].value;
button_values[i].value = button;
}
switch (controller_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::GameCube:
TriggerButton(Settings::NativeButton::A, Qt::Key_Enter);
TriggerButton(Settings::NativeButton::B, Qt::Key_Escape);
TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down);
TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left);
TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right);
TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter);
TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
TriggerButton(Settings::NativeButton::X, Qt::Key_Enter);
TriggerButton(Settings::NativeButton::A, Qt::Key_Escape);
break;
default:
break;
}
}
void ControllerNavigation::ControllerUpdateStick() {
const auto controller_type = player1_controller->GetNpadStyleIndex();
const auto& player1_sticks = player1_controller->GetSticksValues();
const auto& handheld_sticks = player1_controller->GetSticksValues();
bool update = false;
for (std::size_t i = 0; i < player1_sticks.size(); ++i) {
const Common::Input::StickStatus stick{
.left = player1_sticks[i].left || handheld_sticks[i].left,
.right = player1_sticks[i].right || handheld_sticks[i].right,
.up = player1_sticks[i].up || handheld_sticks[i].up,
.down = player1_sticks[i].down || handheld_sticks[i].down,
};
// Trigger only once
if (stick.down != stick_values[i].down || stick.left != stick_values[i].left ||
stick.right != stick_values[i].right || stick.up != stick_values[i].up) {
update = true;
}
stick_values[i] = stick;
}
if (!update) {
return;
}
switch (controller_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::GameCube:
if (stick_values[Settings::NativeAnalog::LStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
if (stick_values[Settings::NativeAnalog::LStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (stick_values[Settings::NativeAnalog::LStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
case Core::HID::NpadStyleIndex::JoyconRight:
if (stick_values[Settings::NativeAnalog::RStick].right) {
emit TriggerKeyboardEvent(Qt::Key_Down);
return;
}
if (stick_values[Settings::NativeAnalog::RStick].down) {
emit TriggerKeyboardEvent(Qt::Key_Left);
return;
}
if (stick_values[Settings::NativeAnalog::RStick].up) {
emit TriggerKeyboardEvent(Qt::Key_Right);
return;
}
if (stick_values[Settings::NativeAnalog::RStick].left) {
emit TriggerKeyboardEvent(Qt::Key_Up);
return;
}
break;
default:
break;
}
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included
#pragma once
#include <QKeyEvent>
#include <QObject>
#include "common/input.h"
#include "common/settings_input.h"
namespace Core::HID {
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
enum class ControllerTriggerType;
class EmulatedController;
class HIDCore;
} // namespace Core::HID
class ControllerNavigation : public QObject {
Q_OBJECT
public:
explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr);
~ControllerNavigation();
/// Disables events from the emulated controller
void UnloadController();
signals:
void TriggerKeyboardEvent(Qt::Key key);
private:
void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key);
void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);
void ControllerUpdateButton();
void ControllerUpdateStick();
Core::HID::ButtonValues button_values{};
Core::HID::SticksValues stick_values{};
int player1_callback_key{};
int handheld_callback_key{};
bool is_controller_set{};
mutable std::mutex mutex;
Core::HID::EmulatedController* player1_controller;
Core::HID::EmulatedController* handheld_controller;
};