Compare commits
39 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb4b3c127f | ||
|
|
f6c47df671 | ||
|
|
be351c2b3d | ||
|
|
f3d3528754 | ||
|
|
ce961cb4c6 | ||
|
|
1c8b509441 | ||
|
|
cc289a8ed0 | ||
|
|
8a858c2623 | ||
|
|
6e12eb80a0 | ||
|
|
d879741ec6 | ||
|
|
439e621674 | ||
|
|
a1f2610522 | ||
|
|
240f59a4c8 | ||
|
|
c889a5805e | ||
|
|
762a30d0db | ||
|
|
390d49c5f1 | ||
|
|
3cf15af31e | ||
|
|
7aa1d10655 | ||
|
|
74d1b9a254 | ||
|
|
b01541f4e4 | ||
|
|
0e5a6676c3 | ||
|
|
a9beb4746e | ||
|
|
79352ee9d5 | ||
|
|
7f53cd1e96 | ||
|
|
4118aad491 | ||
|
|
bf948b5790 | ||
|
|
c82806f9cb | ||
|
|
4eb7f6c044 | ||
|
|
775e29efb6 | ||
|
|
5035df27c3 | ||
|
|
8f3098fc1e | ||
|
|
3ca3254b9f | ||
|
|
f7908eebb9 | ||
|
|
ffd3afcf2f | ||
|
|
36d44bf52f | ||
|
|
370d3fa1e3 | ||
|
|
b3f8c7ce58 | ||
|
|
eea68eae18 | ||
|
|
535bc61b4c |
@@ -218,8 +218,8 @@ if (Boost_FOUND)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
message(STATUS "Boost 1.79.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
|
||||
5
dist/org.yuzu_emu.yuzu.metainfo.xml
vendored
5
dist/org.yuzu_emu.yuzu.metainfo.xml
vendored
@@ -32,6 +32,11 @@
|
||||
<binary>yuzu</binary>
|
||||
<binary>yuzu-cmd</binary>
|
||||
</provides>
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>gamepad</control>
|
||||
</supports>
|
||||
<requires>
|
||||
<memory>8192</memory>
|
||||
</requires>
|
||||
|
||||
BIN
dist/qt_themes/default/icons/256x256/yuzu.png
vendored
BIN
dist/qt_themes/default/icons/256x256/yuzu.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(
|
||||
|
||||
@@ -232,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
|
||||
fs::path GetExeDirectory() {
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
|
||||
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
|
||||
|
||||
if (!exe_path) {
|
||||
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to get the path to the executable of the current process");
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ enum class PollingError {
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
Test,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
|
||||
@@ -70,6 +70,7 @@ void LogSettings() {
|
||||
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
|
||||
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
|
||||
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
|
||||
log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
|
||||
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
|
||||
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
|
||||
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
|
||||
|
||||
@@ -601,7 +601,7 @@ struct Values {
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
|
||||
BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
|
||||
BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
|
||||
@@ -178,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
|
||||
|
||||
#endif
|
||||
|
||||
std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(input), length);
|
||||
}
|
||||
|
||||
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
|
||||
|
||||
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||
|
||||
/**
|
||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||
* `other` for equality.
|
||||
|
||||
@@ -36,6 +36,13 @@ add_library(core STATIC
|
||||
crypto/ctr_encryption_layer.h
|
||||
crypto/xts_encryption_layer.cpp
|
||||
crypto/xts_encryption_layer.h
|
||||
debugger/debugger_interface.h
|
||||
debugger/debugger.cpp
|
||||
debugger/debugger.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
device_memory.cpp
|
||||
device_memory.h
|
||||
file_sys/bis_factory.cpp
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
@@ -88,4 +89,8 @@ void ARM_Interface::LogBacktrace() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool ARM_Interface::ShouldStep() const {
|
||||
return system.DebuggerEnabled() && system.GetDebugger().IsStepping();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -66,9 +66,6 @@ public:
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
|
||||
@@ -194,6 +191,8 @@ public:
|
||||
|
||||
void LogBacktrace() const;
|
||||
|
||||
bool ShouldStep() const;
|
||||
|
||||
protected:
|
||||
/// System context that this ARM interface is running under.
|
||||
System& system;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -26,6 +28,7 @@ using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
@@ -78,11 +81,16 @@ public:
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
if (parent.system.DebuggerEnabled()) {
|
||||
parent.breakpoint_pc = pc;
|
||||
parent.jit.load()->HaltExecution(breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
@@ -234,20 +242,35 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
while (true) {
|
||||
const auto hr = jit.load()->Run();
|
||||
const auto hr = ShouldStep() ? jit.load()->Step() : jit.load()->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
|
||||
// Check to see if breakpoint is triggered.
|
||||
// Recheck step condition in case stop is no longer desired.
|
||||
Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread();
|
||||
if (Has(hr, breakpoint)) {
|
||||
jit.load()->Regs()[15] = breakpoint_pc;
|
||||
|
||||
if (system.GetDebugger().NotifyThreadStopped(current_thread)) {
|
||||
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ShouldStep()) {
|
||||
// When stepping, this should be the only thread running.
|
||||
ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread));
|
||||
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Step() {
|
||||
jit.load()->Step();
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
|
||||
std::size_t core_index_)
|
||||
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -95,6 +94,9 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Debug restart address
|
||||
u32 breakpoint_pc{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -27,6 +28,7 @@ using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
@@ -119,8 +121,13 @@ public:
|
||||
case Dynarmic::A64::Exception::SendEventLocal:
|
||||
case Dynarmic::A64::Exception::Yield:
|
||||
return;
|
||||
case Dynarmic::A64::Exception::Breakpoint:
|
||||
default:
|
||||
if (parent.system.DebuggerEnabled()) {
|
||||
parent.breakpoint_pc = pc;
|
||||
parent.jit.load()->HaltExecution(breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
@@ -299,16 +306,31 @@ void ARM_Dynarmic_64::Run() {
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
|
||||
// Check to see if breakpoint is triggered.
|
||||
// Recheck step condition in case stop is no longer desired.
|
||||
Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread();
|
||||
if (Has(hr, breakpoint)) {
|
||||
jit.load()->SetPC(breakpoint_pc);
|
||||
|
||||
if (system.GetDebugger().NotifyThreadStopped(current_thread)) {
|
||||
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ShouldStep()) {
|
||||
// When stepping, this should be the only thread running.
|
||||
ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread));
|
||||
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
jit.load()->Step();
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
|
||||
std::size_t core_index_)
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -88,6 +87,9 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Debug restart address
|
||||
u64 breakpoint_pc{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
@@ -171,6 +172,10 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
|
||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
@@ -329,6 +334,7 @@ struct System::Impl {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
debugger.reset();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
@@ -436,6 +442,9 @@ struct System::Impl {
|
||||
/// Network instance
|
||||
Network::NetworkInstance network_instance;
|
||||
|
||||
/// Debugger
|
||||
std::unique_ptr<Core::Debugger> debugger;
|
||||
|
||||
SystemResultStatus status = SystemResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
@@ -472,10 +481,6 @@ SystemResultStatus System::Pause() {
|
||||
return impl->Pause();
|
||||
}
|
||||
|
||||
SystemResultStatus System::SingleStep() {
|
||||
return SystemResultStatus::Success;
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
impl->kernel.InvalidateAllInstructionCaches();
|
||||
}
|
||||
@@ -496,6 +501,10 @@ void System::UnstallCPU() {
|
||||
impl->UnstallCPU();
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
|
||||
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
u64 program_id, std::size_t program_index) {
|
||||
return impl->Load(*this, emu_window, filepath, program_id, program_index);
|
||||
@@ -809,6 +818,18 @@ bool System::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
bool System::DebuggerEnabled() const {
|
||||
return Settings::values.use_gdbstub.GetValue();
|
||||
}
|
||||
|
||||
Core::Debugger& System::GetDebugger() {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
const Core::Debugger& System::GetDebugger() const {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
|
||||
impl->execute_program_callback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
class CpuManager;
|
||||
class Debugger;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class SpeedLimiter;
|
||||
@@ -147,12 +148,6 @@ public:
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus SingleStep();
|
||||
|
||||
/**
|
||||
* Invalidate the CPU instruction caches
|
||||
* This function should only be used by GDB Stub to support breakpoints, memory updates and
|
||||
@@ -168,6 +163,11 @@ public:
|
||||
std::unique_lock<std::mutex> StallCPU();
|
||||
void UnstallCPU();
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
*/
|
||||
void InitializeDebugger();
|
||||
|
||||
/**
|
||||
* Load an executable application.
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
@@ -354,6 +354,9 @@ public:
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
[[nodiscard]] Core::Debugger& GetDebugger();
|
||||
[[nodiscard]] const Core::Debugger& GetDebugger() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
[[nodiscard]] bool GetExitLock() const;
|
||||
|
||||
@@ -375,6 +378,9 @@ public:
|
||||
/// Tells if system is running on multicore.
|
||||
[[nodiscard]] bool IsMulticore() const;
|
||||
|
||||
/// Tells if the system debugger is enabled.
|
||||
[[nodiscard]] bool DebuggerEnabled() const;
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to re-launch the application
|
||||
/// using a specified program index.
|
||||
using ExecuteProgramCallback = std::function<void(std::size_t)>;
|
||||
|
||||
259
src/core/debugger/debugger.cpp
Normal file
259
src/core/debugger/debugger.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/debugger/debugger_interface.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
|
||||
template <typename Readable, typename Buffer, typename Callback>
|
||||
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
|
||||
r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) {
|
||||
if (!error.failed()) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
|
||||
size_t bytes_read = r.read_some(boost_buffer);
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
return received_data;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
~DebuggerImpl() {
|
||||
ShutdownServer();
|
||||
}
|
||||
|
||||
bool NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
stopped = true;
|
||||
|
||||
signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread)));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::span<const u8> ReadFromClient() override {
|
||||
return ReceiveInto(client_socket, client_data);
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
active_thread = thread;
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
return active_thread;
|
||||
}
|
||||
|
||||
bool IsStepping() const {
|
||||
return stepping;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeServer(u16 port) {
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
|
||||
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
client_socket = acceptor.accept();
|
||||
|
||||
// Run the connection thread.
|
||||
connection_thread = std::jthread([&](std::stop_token stop_token) {
|
||||
try {
|
||||
ThreadLoop(stop_token);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||
}
|
||||
|
||||
client_socket.shutdown(client_socket.shutdown_both);
|
||||
client_socket.close();
|
||||
});
|
||||
}
|
||||
|
||||
void ShutdownServer() {
|
||||
connection_thread.request_stop();
|
||||
io_context.stop();
|
||||
connection_thread.join();
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Stop the emulated CPU.
|
||||
AllCoreStop();
|
||||
|
||||
// Set the active thread.
|
||||
active_thread = ThreadList()[0];
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
|
||||
// Main event loop.
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
AllCoreStop();
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
frontend->Stopped(active_thread);
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
const auto actions{frontend->ClientData(data)};
|
||||
for (const auto action : actions) {
|
||||
switch (action) {
|
||||
case DebuggerAction::Interrupt: {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = true;
|
||||
}
|
||||
AllCoreStop();
|
||||
active_thread = ThreadList()[0];
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
frontend->Stopped(active_thread);
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::Continue:
|
||||
stepping = false;
|
||||
ResumeInactiveThreads();
|
||||
AllCoreResume();
|
||||
break;
|
||||
case DebuggerAction::StepThread:
|
||||
stepping = true;
|
||||
SuspendInactiveThreads();
|
||||
AllCoreResume();
|
||||
break;
|
||||
case DebuggerAction::ShutdownEmulation: {
|
||||
// Suspend all threads and release any locks held
|
||||
active_thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
SuspendInactiveThreads();
|
||||
AllCoreResume();
|
||||
|
||||
// Spawn another thread that will exit after shutdown,
|
||||
// to avoid a deadlock
|
||||
Core::System* system_ref{&system};
|
||||
std::thread t([system_ref] { system_ref->Exit(); });
|
||||
t.detach();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AllCoreStop() {
|
||||
if (!suspend) {
|
||||
suspend = system.StallCPU();
|
||||
}
|
||||
}
|
||||
|
||||
void AllCoreResume() {
|
||||
stopped = false;
|
||||
system.UnstallCPU();
|
||||
suspend.reset();
|
||||
}
|
||||
|
||||
void SuspendInactiveThreads() {
|
||||
for (auto* thread : ThreadList()) {
|
||||
if (thread != active_thread) {
|
||||
thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResumeInactiveThreads() {
|
||||
for (auto* thread : ThreadList()) {
|
||||
if (thread != active_thread) {
|
||||
thread->Resume(Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Kernel::KThread*>& ThreadList() {
|
||||
return system.GlobalSchedulerContext().GetThreadList();
|
||||
}
|
||||
|
||||
private:
|
||||
System& system;
|
||||
std::unique_ptr<DebuggerFrontend> frontend;
|
||||
|
||||
std::jthread connection_thread;
|
||||
std::mutex connection_lock;
|
||||
boost::asio::io_context io_context;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
std::optional<std::unique_lock<std::mutex>> suspend;
|
||||
|
||||
Kernel::KThread* active_thread;
|
||||
bool stopped;
|
||||
bool stepping;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
};
|
||||
|
||||
Debugger::Debugger(Core::System& system, u16 port) {
|
||||
try {
|
||||
impl = std::make_unique<DebuggerImpl>(system, port);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
Debugger::~Debugger() = default;
|
||||
|
||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
return impl && impl->NotifyThreadStopped(thread);
|
||||
}
|
||||
|
||||
bool Debugger::IsStepping() const {
|
||||
return impl && impl->IsStepping();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
46
src/core/debugger/debugger.h
Normal file
46
src/core/debugger/debugger.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KThread;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
||||
class DebuggerImpl;
|
||||
|
||||
class Debugger {
|
||||
public:
|
||||
/**
|
||||
* Blocks and waits for a connection on localhost, port `server_port`.
|
||||
* Does not create the debugger if the port is already in use.
|
||||
*/
|
||||
explicit Debugger(Core::System& system, u16 server_port);
|
||||
~Debugger();
|
||||
|
||||
/**
|
||||
* Notify the debugger that the given thread is stopped
|
||||
* (due to a breakpoint, or due to stopping after a successful step).
|
||||
*
|
||||
* The debugger will asynchronously halt emulation after the notification has
|
||||
* occurred. If another thread attempts to notify before emulation has stopped,
|
||||
* it is ignored and this method will return false. Otherwise it will return true.
|
||||
*/
|
||||
bool NotifyThreadStopped(Kernel::KThread* thread);
|
||||
|
||||
/**
|
||||
* Returns whether a step is in progress.
|
||||
*/
|
||||
bool IsStepping() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<DebuggerImpl> impl;
|
||||
};
|
||||
} // namespace Core
|
||||
74
src/core/debugger/debugger_interface.h
Normal file
74
src/core/debugger/debugger_interface.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KThread;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum class DebuggerAction {
|
||||
Interrupt, // Stop emulation as soon as possible.
|
||||
Continue, // Resume emulation.
|
||||
StepThread, // Step the currently-active thread.
|
||||
ShutdownEmulation, // Shut down the emulator.
|
||||
};
|
||||
|
||||
class DebuggerBackend {
|
||||
public:
|
||||
/**
|
||||
* Can be invoked from a callback to synchronously wait for more data.
|
||||
* Will return as soon as least one byte is received. Reads up to 4096 bytes.
|
||||
*/
|
||||
virtual std::span<const u8> ReadFromClient() = 0;
|
||||
|
||||
/**
|
||||
* Can be invoked from a callback to write data to the client.
|
||||
* Returns immediately after the data is sent.
|
||||
*/
|
||||
virtual void WriteToClient(std::span<const u8> data) = 0;
|
||||
|
||||
/**
|
||||
* Gets the currently active thread when the debugger is stopped.
|
||||
*/
|
||||
virtual Kernel::KThread* GetActiveThread() = 0;
|
||||
|
||||
/**
|
||||
* Sets the currently active thread when the debugger is stopped.
|
||||
*/
|
||||
virtual void SetActiveThread(Kernel::KThread* thread) = 0;
|
||||
};
|
||||
|
||||
class DebuggerFrontend {
|
||||
public:
|
||||
explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
|
||||
|
||||
/**
|
||||
* Called after the client has successfully connected to the port.
|
||||
*/
|
||||
virtual void Connected() = 0;
|
||||
|
||||
/**
|
||||
* Called when emulation has stopped.
|
||||
*/
|
||||
virtual void Stopped(Kernel::KThread* thread) = 0;
|
||||
|
||||
/**
|
||||
* Called when new data is asynchronously received on the client socket.
|
||||
* A list of actions to perform is returned.
|
||||
*/
|
||||
[[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
|
||||
|
||||
protected:
|
||||
DebuggerBackend& backend;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
382
src/core/debugger/gdbstub.cpp
Normal file
382
src/core/debugger/gdbstub.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <atomic>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
#include "core/debugger/gdbstub_arch.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
constexpr char GDB_STUB_START = '$';
|
||||
constexpr char GDB_STUB_END = '#';
|
||||
constexpr char GDB_STUB_ACK = '+';
|
||||
constexpr char GDB_STUB_NACK = '-';
|
||||
constexpr char GDB_STUB_INT3 = 0x03;
|
||||
constexpr int GDB_STUB_SIGTRAP = 5;
|
||||
|
||||
constexpr char GDB_STUB_REPLY_ERR[] = "E01";
|
||||
constexpr char GDB_STUB_REPLY_OK[] = "OK";
|
||||
constexpr char GDB_STUB_REPLY_EMPTY[] = "";
|
||||
|
||||
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
|
||||
: DebuggerFrontend(backend_), system{system_} {
|
||||
if (system.CurrentProcess()->Is64BitProcess()) {
|
||||
arch = std::make_unique<GDBStubA64>();
|
||||
} else {
|
||||
arch = std::make_unique<GDBStubA32>();
|
||||
}
|
||||
}
|
||||
|
||||
GDBStub::~GDBStub() = default;
|
||||
|
||||
void GDBStub::Connected() {}
|
||||
|
||||
void GDBStub::Stopped(Kernel::KThread* thread) {
|
||||
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
|
||||
}
|
||||
|
||||
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
|
||||
std::vector<DebuggerAction> actions;
|
||||
current_command.insert(current_command.end(), data.begin(), data.end());
|
||||
|
||||
while (current_command.size() != 0) {
|
||||
ProcessData(actions);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
|
||||
const char c{current_command[0]};
|
||||
|
||||
// Acknowledgement
|
||||
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
|
||||
current_command.erase(current_command.begin());
|
||||
return;
|
||||
}
|
||||
|
||||
// Interrupt
|
||||
if (c == GDB_STUB_INT3) {
|
||||
LOG_INFO(Debug_GDBStub, "Received interrupt");
|
||||
current_command.erase(current_command.begin());
|
||||
actions.push_back(DebuggerAction::Interrupt);
|
||||
SendStatus(GDB_STUB_ACK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, require the data to be the start of a command
|
||||
if (c != GDB_STUB_START) {
|
||||
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
|
||||
current_command.clear();
|
||||
SendStatus(GDB_STUB_NACK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue reading until command is complete
|
||||
while (CommandEnd() == current_command.end()) {
|
||||
const auto new_data{backend.ReadFromClient()};
|
||||
current_command.insert(current_command.end(), new_data.begin(), new_data.end());
|
||||
}
|
||||
|
||||
// Execute and respond to GDB
|
||||
const auto command{DetachCommand()};
|
||||
|
||||
if (command) {
|
||||
SendStatus(GDB_STUB_ACK);
|
||||
ExecuteCommand(*command, actions);
|
||||
} else {
|
||||
SendStatus(GDB_STUB_NACK);
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
|
||||
LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
|
||||
|
||||
if (packet.length() == 0) {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string_view command{packet.substr(1, packet.size())};
|
||||
|
||||
switch (packet[0]) {
|
||||
case 'H': {
|
||||
Kernel::KThread* thread{nullptr};
|
||||
s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
|
||||
if (thread_id >= 1) {
|
||||
thread = GetThreadByID(thread_id);
|
||||
}
|
||||
|
||||
if (thread) {
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
backend.SetActiveThread(thread);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'T': {
|
||||
s64 thread_id{strtoll(command.data(), nullptr, 16)};
|
||||
if (GetThreadByID(thread_id)) {
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
HandleQuery(command);
|
||||
break;
|
||||
case '?':
|
||||
SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
|
||||
break;
|
||||
case 'k':
|
||||
LOG_INFO(Debug_GDBStub, "Shutting down emulation");
|
||||
actions.push_back(DebuggerAction::ShutdownEmulation);
|
||||
break;
|
||||
case 'g':
|
||||
SendReply(arch->ReadRegisters(backend.GetActiveThread()));
|
||||
break;
|
||||
case 'G':
|
||||
arch->WriteRegisters(backend.GetActiveThread(), command);
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
break;
|
||||
case 'p': {
|
||||
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
|
||||
SendReply(arch->RegRead(backend.GetActiveThread(), reg));
|
||||
break;
|
||||
}
|
||||
case 'P': {
|
||||
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
|
||||
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
|
||||
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
|
||||
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
|
||||
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
|
||||
|
||||
if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
|
||||
std::vector<u8> mem(size);
|
||||
system.Memory().ReadBlock(addr, mem.data(), size);
|
||||
|
||||
SendReply(Common::HexToString(mem));
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'M': {
|
||||
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
|
||||
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
|
||||
|
||||
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
|
||||
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
|
||||
|
||||
const auto mem_substr{std::string_view(command).substr(mem_sep)};
|
||||
const auto mem{Common::HexStringToVector(mem_substr, false)};
|
||||
|
||||
if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
|
||||
system.Memory().WriteBlock(addr, mem.data(), size);
|
||||
system.InvalidateCpuInstructionCacheRange(addr, size);
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
actions.push_back(DebuggerAction::StepThread);
|
||||
break;
|
||||
case 'C':
|
||||
case 'c':
|
||||
actions.push_back(DebuggerAction::Continue);
|
||||
break;
|
||||
case 'Z': {
|
||||
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
|
||||
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
||||
|
||||
if (system.Memory().IsValidVirtualAddress(addr)) {
|
||||
replaced_instructions[addr] = system.Memory().Read32(addr);
|
||||
system.Memory().Write32(addr, arch->BreakpointInstruction());
|
||||
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
|
||||
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'z': {
|
||||
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
|
||||
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
||||
|
||||
const auto orig_insn{replaced_instructions.find(addr)};
|
||||
if (system.Memory().IsValidVirtualAddress(addr) &&
|
||||
orig_insn != replaced_instructions.end()) {
|
||||
system.Memory().Write32(addr, orig_insn->second);
|
||||
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
|
||||
replaced_instructions.erase(addr);
|
||||
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStub::HandleQuery(std::string_view command) {
|
||||
if (command.starts_with("TStatus")) {
|
||||
// no tracepoint support
|
||||
SendReply("T0");
|
||||
} else if (command.starts_with("Supported")) {
|
||||
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+");
|
||||
} else if (command.starts_with("Xfer:features:read:target.xml:")) {
|
||||
const auto offset{command.substr(30)};
|
||||
const auto amount{command.substr(command.find(',') + 1)};
|
||||
|
||||
const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
|
||||
const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
|
||||
const auto target_xml{arch->GetTargetXML()};
|
||||
|
||||
if (offset_val + amount_val > target_xml.size()) {
|
||||
SendReply("l" + target_xml.substr(offset_val));
|
||||
} else {
|
||||
SendReply("m" + target_xml.substr(offset_val, amount_val));
|
||||
}
|
||||
} else if (command.starts_with("Offsets")) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
const auto main = std::find_if(modules.begin(), modules.end(),
|
||||
[](const auto& key) { return key.second == "main"; });
|
||||
if (main != modules.end()) {
|
||||
SendReply(fmt::format("TextSeg={:x}", main->first));
|
||||
} else {
|
||||
SendReply(fmt::format("TextSeg={:x}",
|
||||
system.CurrentProcess()->PageTable().GetCodeRegionStart()));
|
||||
}
|
||||
} else if (command.starts_with("fThreadInfo")) {
|
||||
// beginning of list
|
||||
const auto& threads = system.GlobalSchedulerContext().GetThreadList();
|
||||
std::vector<std::string> thread_ids;
|
||||
for (const auto& thread : threads) {
|
||||
thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
|
||||
}
|
||||
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
|
||||
} else if (command.starts_with("sThreadInfo")) {
|
||||
// end of list
|
||||
SendReply("l");
|
||||
} else if (command.starts_with("Xfer:threads:read")) {
|
||||
std::string buffer;
|
||||
buffer += R"(l<?xml version="1.0"?>)";
|
||||
buffer += "<threads>";
|
||||
|
||||
const auto& threads = system.GlobalSchedulerContext().GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
buffer +=
|
||||
fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}"/>)",
|
||||
thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID());
|
||||
}
|
||||
|
||||
buffer += "</threads>";
|
||||
SendReply(buffer);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
||||
for (auto* thread : threads) {
|
||||
if (thread->GetThreadID() == thread_id) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<char>::const_iterator GDBStub::CommandEnd() const {
|
||||
// Find the end marker
|
||||
const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
|
||||
|
||||
// Require the checksum to be present
|
||||
return std::min(end + 2, current_command.end());
|
||||
}
|
||||
|
||||
std::optional<std::string> GDBStub::DetachCommand() {
|
||||
// Slice the string part from the beginning to the end marker
|
||||
const auto end{CommandEnd()};
|
||||
|
||||
// Extract possible command data
|
||||
std::string data(current_command.data(), end - current_command.begin() + 1);
|
||||
|
||||
// Shift over the remaining contents
|
||||
current_command.erase(current_command.begin(), end + 1);
|
||||
|
||||
// Validate received command
|
||||
if (data[0] != GDB_STUB_START) {
|
||||
LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
|
||||
u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
|
||||
|
||||
// Verify checksum
|
||||
if (calculated != received) {
|
||||
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
|
||||
calculated, received);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return data.substr(1, data.size() - 4);
|
||||
}
|
||||
|
||||
u8 GDBStub::CalculateChecksum(std::string_view data) {
|
||||
return static_cast<u8>(
|
||||
std::accumulate(data.begin(), data.end(), u8{0}, [](u8 lhs, u8 rhs) { return lhs + rhs; }));
|
||||
}
|
||||
|
||||
void GDBStub::SendReply(std::string_view data) {
|
||||
const auto output{
|
||||
fmt::format("{}{}{}{:02x}", GDB_STUB_START, data, GDB_STUB_END, CalculateChecksum(data))};
|
||||
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
|
||||
|
||||
// C++ string support is complete rubbish
|
||||
const u8* output_begin = reinterpret_cast<const u8*>(output.data());
|
||||
const u8* output_end = output_begin + output.size();
|
||||
backend.WriteToClient(std::span<const u8>(output_begin, output_end));
|
||||
}
|
||||
|
||||
void GDBStub::SendStatus(char status) {
|
||||
std::array<u8, 1> buf = {static_cast<u8>(status)};
|
||||
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
|
||||
backend.WriteToClient(buf);
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
47
src/core/debugger/gdbstub.h
Normal file
47
src/core/debugger/gdbstub.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "core/debugger/debugger_interface.h"
|
||||
#include "core/debugger/gdbstub_arch.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class GDBStub : public DebuggerFrontend {
|
||||
public:
|
||||
explicit GDBStub(DebuggerBackend& backend, Core::System& system);
|
||||
~GDBStub();
|
||||
|
||||
void Connected() override;
|
||||
void Stopped(Kernel::KThread* thread) override;
|
||||
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
|
||||
|
||||
private:
|
||||
void ProcessData(std::vector<DebuggerAction>& actions);
|
||||
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
||||
void HandleQuery(std::string_view command);
|
||||
std::vector<char>::const_iterator CommandEnd() const;
|
||||
std::optional<std::string> DetachCommand();
|
||||
Kernel::KThread* GetThreadByID(u64 thread_id);
|
||||
|
||||
static u8 CalculateChecksum(std::string_view data);
|
||||
void SendReply(std::string_view data);
|
||||
void SendStatus(char status);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::unique_ptr<GDBStubArch> arch;
|
||||
std::vector<char> current_command;
|
||||
std::map<VAddr, u32> replaced_instructions;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
406
src/core/debugger/gdbstub_arch.cpp
Normal file
406
src/core/debugger/gdbstub_arch.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "core/debugger/gdbstub_arch.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
template <typename T>
|
||||
static T HexToValue(std::string_view hex) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
T value{};
|
||||
const auto mem{Common::HexStringToVector(hex, false)};
|
||||
std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string ValueToHex(const T value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
std::array<u8, sizeof(T)> mem{};
|
||||
std::memcpy(mem.data(), &value, sizeof(T));
|
||||
return Common::HexToString(mem);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
T value{};
|
||||
std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
|
||||
sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
|
||||
}
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
std::string GDBStubA64::GetTargetXML() const {
|
||||
constexpr const char* target_xml =
|
||||
R"(<?xml version="1.0"?>
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target version="1.0">
|
||||
<feature name="org.gnu.gdb.aarch64.core">
|
||||
<reg name="x0" bitsize="64"/>
|
||||
<reg name="x1" bitsize="64"/>
|
||||
<reg name="x2" bitsize="64"/>
|
||||
<reg name="x3" bitsize="64"/>
|
||||
<reg name="x4" bitsize="64"/>
|
||||
<reg name="x5" bitsize="64"/>
|
||||
<reg name="x6" bitsize="64"/>
|
||||
<reg name="x7" bitsize="64"/>
|
||||
<reg name="x8" bitsize="64"/>
|
||||
<reg name="x9" bitsize="64"/>
|
||||
<reg name="x10" bitsize="64"/>
|
||||
<reg name="x11" bitsize="64"/>
|
||||
<reg name="x12" bitsize="64"/>
|
||||
<reg name="x13" bitsize="64"/>
|
||||
<reg name="x14" bitsize="64"/>
|
||||
<reg name="x15" bitsize="64"/>
|
||||
<reg name="x16" bitsize="64"/>
|
||||
<reg name="x17" bitsize="64"/>
|
||||
<reg name="x18" bitsize="64"/>
|
||||
<reg name="x19" bitsize="64"/>
|
||||
<reg name="x20" bitsize="64"/>
|
||||
<reg name="x21" bitsize="64"/>
|
||||
<reg name="x22" bitsize="64"/>
|
||||
<reg name="x23" bitsize="64"/>
|
||||
<reg name="x24" bitsize="64"/>
|
||||
<reg name="x25" bitsize="64"/>
|
||||
<reg name="x26" bitsize="64"/>
|
||||
<reg name="x27" bitsize="64"/>
|
||||
<reg name="x28" bitsize="64"/>
|
||||
<reg name="x29" bitsize="64"/>
|
||||
<reg name="x30" bitsize="64"/>
|
||||
<reg name="sp" bitsize="64" type="data_ptr"/>
|
||||
<reg name="pc" bitsize="64" type="code_ptr"/>
|
||||
<flags id="pstate_flags" size="4">
|
||||
<field name="SP" start="0" end="0"/>
|
||||
<field name="" start="1" end="1"/>
|
||||
<field name="EL" start="2" end="3"/>
|
||||
<field name="nRW" start="4" end="4"/>
|
||||
<field name="" start="5" end="5"/>
|
||||
<field name="F" start="6" end="6"/>
|
||||
<field name="I" start="7" end="7"/>
|
||||
<field name="A" start="8" end="8"/>
|
||||
<field name="D" start="9" end="9"/>
|
||||
<field name="IL" start="20" end="20"/>
|
||||
<field name="SS" start="21" end="21"/>
|
||||
<field name="V" start="28" end="28"/>
|
||||
<field name="C" start="29" end="29"/>
|
||||
<field name="Z" start="30" end="30"/>
|
||||
<field name="N" start="31" end="31"/>
|
||||
</flags>
|
||||
<reg name="pstate" bitsize="32" type="pstate_flags"/>
|
||||
</feature>
|
||||
<feature name="org.gnu.gdb.aarch64.fpu">
|
||||
</feature>
|
||||
</target>)";
|
||||
|
||||
return target_xml;
|
||||
}
|
||||
|
||||
std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
|
||||
if (!thread) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto& context{thread->GetContext64()};
|
||||
const auto& gprs{context.cpu_registers};
|
||||
const auto& fprs{context.vector_registers};
|
||||
|
||||
if (id <= SP_REGISTER) {
|
||||
return ValueToHex(gprs[id]);
|
||||
} else if (id == PC_REGISTER) {
|
||||
return ValueToHex(context.pc);
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
return ValueToHex(context.pstate);
|
||||
} else if (id >= Q0_REGISTER && id < FPCR_REGISTER) {
|
||||
return ValueToHex(fprs[id - Q0_REGISTER]);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
return ValueToHex(context.fpcr);
|
||||
} else if (id == FPSR_REGISTER) {
|
||||
return ValueToHex(context.fpsr);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& context{thread->GetContext64()};
|
||||
|
||||
if (id <= SP_REGISTER) {
|
||||
context.cpu_registers[id] = HexToValue<u64>(value);
|
||||
} else if (id == PC_REGISTER) {
|
||||
context.pc = HexToValue<u64>(value);
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
context.pstate = HexToValue<u32>(value);
|
||||
} else if (id >= Q0_REGISTER && id < FPCR_REGISTER) {
|
||||
context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
context.fpcr = HexToValue<u32>(value);
|
||||
} else if (id == FPSR_REGISTER) {
|
||||
context.fpsr = HexToValue<u32>(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
|
||||
std::string output;
|
||||
|
||||
for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
|
||||
output += RegRead(thread, reg);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
|
||||
for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
|
||||
if (reg <= SP_REGISTER || reg == PC_REGISTER) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 16));
|
||||
i += 16;
|
||||
} else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 8));
|
||||
i += 8;
|
||||
} else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 32));
|
||||
i += 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
|
||||
return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
|
||||
RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
|
||||
LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
|
||||
}
|
||||
|
||||
u32 GDBStubA64::BreakpointInstruction() const {
|
||||
// A64: brk #0
|
||||
return 0xd4200000;
|
||||
}
|
||||
|
||||
std::string GDBStubA32::GetTargetXML() const {
|
||||
constexpr const char* target_xml =
|
||||
R"(<?xml version="1.0"?>
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target version="1.0">
|
||||
<feature name="org.gnu.gdb.arm.core">
|
||||
<reg name="r0" bitsize="32" type="uint32"/>
|
||||
<reg name="r1" bitsize="32" type="uint32"/>
|
||||
<reg name="r2" bitsize="32" type="uint32"/>
|
||||
<reg name="r3" bitsize="32" type="uint32"/>
|
||||
<reg name="r4" bitsize="32" type="uint32"/>
|
||||
<reg name="r5" bitsize="32" type="uint32"/>
|
||||
<reg name="r6" bitsize="32" type="uint32"/>
|
||||
<reg name="r7" bitsize="32" type="uint32"/>
|
||||
<reg name="r8" bitsize="32" type="uint32"/>
|
||||
<reg name="r9" bitsize="32" type="uint32"/>
|
||||
<reg name="r10" bitsize="32" type="uint32"/>
|
||||
<reg name="r11" bitsize="32" type="uint32"/>
|
||||
<reg name="r12" bitsize="32" type="uint32"/>
|
||||
<reg name="sp" bitsize="32" type="data_ptr"/>
|
||||
<reg name="lr" bitsize="32" type="code_ptr"/>
|
||||
<reg name="pc" bitsize="32" type="code_ptr"/>
|
||||
<!-- The CPSR is register 25, rather than register 16, because
|
||||
the FPA registers historically were placed between the PC
|
||||
and the CPSR in the "g" packet. -->
|
||||
<reg name="cpsr" bitsize="32" regnum="25"/>
|
||||
</feature>
|
||||
<feature name="org.gnu.gdb.arm.vfp">
|
||||
<vector id="neon_uint8x8" type="uint8" count="8"/>
|
||||
<vector id="neon_uint16x4" type="uint16" count="4"/>
|
||||
<vector id="neon_uint32x2" type="uint32" count="2"/>
|
||||
<vector id="neon_float32x2" type="ieee_single" count="2"/>
|
||||
<union id="neon_d">
|
||||
<field name="u8" type="neon_uint8x8"/>
|
||||
<field name="u16" type="neon_uint16x4"/>
|
||||
<field name="u32" type="neon_uint32x2"/>
|
||||
<field name="u64" type="uint64"/>
|
||||
<field name="f32" type="neon_float32x2"/>
|
||||
<field name="f64" type="ieee_double"/>
|
||||
</union>
|
||||
<vector id="neon_uint8x16" type="uint8" count="16"/>
|
||||
<vector id="neon_uint16x8" type="uint16" count="8"/>
|
||||
<vector id="neon_uint32x4" type="uint32" count="4"/>
|
||||
<vector id="neon_uint64x2" type="uint64" count="2"/>
|
||||
<vector id="neon_float32x4" type="ieee_single" count="4"/>
|
||||
<vector id="neon_float64x2" type="ieee_double" count="2"/>
|
||||
<union id="neon_q">
|
||||
<field name="u8" type="neon_uint8x16"/>
|
||||
<field name="u16" type="neon_uint16x8"/>
|
||||
<field name="u32" type="neon_uint32x4"/>
|
||||
<field name="u64" type="neon_uint64x2"/>
|
||||
<field name="f32" type="neon_float32x4"/>
|
||||
<field name="f64" type="neon_float64x2"/>
|
||||
</union>
|
||||
<reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
|
||||
<reg name="d1" bitsize="64" type="neon_d"/>
|
||||
<reg name="d2" bitsize="64" type="neon_d"/>
|
||||
<reg name="d3" bitsize="64" type="neon_d"/>
|
||||
<reg name="d4" bitsize="64" type="neon_d"/>
|
||||
<reg name="d5" bitsize="64" type="neon_d"/>
|
||||
<reg name="d6" bitsize="64" type="neon_d"/>
|
||||
<reg name="d7" bitsize="64" type="neon_d"/>
|
||||
<reg name="d8" bitsize="64" type="neon_d"/>
|
||||
<reg name="d9" bitsize="64" type="neon_d"/>
|
||||
<reg name="d10" bitsize="64" type="neon_d"/>
|
||||
<reg name="d11" bitsize="64" type="neon_d"/>
|
||||
<reg name="d12" bitsize="64" type="neon_d"/>
|
||||
<reg name="d13" bitsize="64" type="neon_d"/>
|
||||
<reg name="d14" bitsize="64" type="neon_d"/>
|
||||
<reg name="d15" bitsize="64" type="neon_d"/>
|
||||
<reg name="d16" bitsize="64" type="neon_d"/>
|
||||
<reg name="d17" bitsize="64" type="neon_d"/>
|
||||
<reg name="d18" bitsize="64" type="neon_d"/>
|
||||
<reg name="d19" bitsize="64" type="neon_d"/>
|
||||
<reg name="d20" bitsize="64" type="neon_d"/>
|
||||
<reg name="d21" bitsize="64" type="neon_d"/>
|
||||
<reg name="d22" bitsize="64" type="neon_d"/>
|
||||
<reg name="d23" bitsize="64" type="neon_d"/>
|
||||
<reg name="d24" bitsize="64" type="neon_d"/>
|
||||
<reg name="d25" bitsize="64" type="neon_d"/>
|
||||
<reg name="d26" bitsize="64" type="neon_d"/>
|
||||
<reg name="d27" bitsize="64" type="neon_d"/>
|
||||
<reg name="d28" bitsize="64" type="neon_d"/>
|
||||
<reg name="d29" bitsize="64" type="neon_d"/>
|
||||
<reg name="d30" bitsize="64" type="neon_d"/>
|
||||
<reg name="d31" bitsize="64" type="neon_d"/>
|
||||
|
||||
<reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
|
||||
<reg name="q1" bitsize="128" type="neon_q"/>
|
||||
<reg name="q2" bitsize="128" type="neon_q"/>
|
||||
<reg name="q3" bitsize="128" type="neon_q"/>
|
||||
<reg name="q4" bitsize="128" type="neon_q"/>
|
||||
<reg name="q5" bitsize="128" type="neon_q"/>
|
||||
<reg name="q6" bitsize="128" type="neon_q"/>
|
||||
<reg name="q7" bitsize="128" type="neon_q"/>
|
||||
<reg name="q8" bitsize="128" type="neon_q"/>
|
||||
<reg name="q9" bitsize="128" type="neon_q"/>
|
||||
<reg name="q10" bitsize="128" type="neon_q"/>
|
||||
<reg name="q10" bitsize="128" type="neon_q"/>
|
||||
<reg name="q12" bitsize="128" type="neon_q"/>
|
||||
<reg name="q13" bitsize="128" type="neon_q"/>
|
||||
<reg name="q14" bitsize="128" type="neon_q"/>
|
||||
<reg name="q15" bitsize="128" type="neon_q"/>
|
||||
|
||||
<reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
|
||||
</feature>
|
||||
</target>)";
|
||||
|
||||
return target_xml;
|
||||
}
|
||||
|
||||
std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
|
||||
if (!thread) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto& context{thread->GetContext32()};
|
||||
const auto& gprs{context.cpu_registers};
|
||||
const auto& fprs{context.extension_registers};
|
||||
|
||||
if (id <= PC_REGISTER) {
|
||||
return ValueToHex(gprs[id]);
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
return ValueToHex(context.cpsr);
|
||||
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
|
||||
const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
|
||||
return ValueToHex(dN);
|
||||
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
|
||||
const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
|
||||
return ValueToHex(qN);
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
return ValueToHex(context.fpscr);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& context{thread->GetContext32()};
|
||||
auto& fprs{context.extension_registers};
|
||||
|
||||
if (id <= PC_REGISTER) {
|
||||
context.cpu_registers[id] = HexToValue<u32>(value);
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
context.cpsr = HexToValue<u32>(value);
|
||||
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
|
||||
PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
|
||||
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
|
||||
PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
context.fpscr = HexToValue<u32>(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
|
||||
std::string output;
|
||||
|
||||
for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
|
||||
const bool gpr{reg <= PC_REGISTER};
|
||||
const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
|
||||
const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
|
||||
|
||||
if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output += RegRead(thread, reg);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
|
||||
for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
|
||||
const bool gpr{reg <= PC_REGISTER};
|
||||
const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
|
||||
const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
|
||||
|
||||
if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 8));
|
||||
i += 8;
|
||||
} else if (dfpr) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 16));
|
||||
i += 16;
|
||||
} else if (qfpr) {
|
||||
RegWrite(thread, reg, register_data.substr(i, 32));
|
||||
i += 32;
|
||||
}
|
||||
|
||||
if (reg == PC_REGISTER) {
|
||||
reg = CPSR_REGISTER - 1;
|
||||
} else if (reg == CPSR_REGISTER) {
|
||||
reg = D0_REGISTER - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
|
||||
return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
|
||||
RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
|
||||
LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
|
||||
}
|
||||
|
||||
u32 GDBStubA32::BreakpointInstruction() const {
|
||||
// A32: trap
|
||||
// T32: trap + b #4
|
||||
return 0xe7ffdefe;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
67
src/core/debugger/gdbstub_arch.h
Normal file
67
src/core/debugger/gdbstub_arch.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KThread;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class GDBStubArch {
|
||||
public:
|
||||
virtual std::string GetTargetXML() const = 0;
|
||||
virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
|
||||
virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
|
||||
virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
|
||||
virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
|
||||
virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
|
||||
virtual u32 BreakpointInstruction() const = 0;
|
||||
};
|
||||
|
||||
class GDBStubA64 final : public GDBStubArch {
|
||||
public:
|
||||
std::string GetTargetXML() const override;
|
||||
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
|
||||
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
|
||||
std::string ReadRegisters(const Kernel::KThread* thread) const override;
|
||||
void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
|
||||
std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
|
||||
u32 BreakpointInstruction() const override;
|
||||
|
||||
private:
|
||||
static constexpr u32 LR_REGISTER = 30;
|
||||
static constexpr u32 SP_REGISTER = 31;
|
||||
static constexpr u32 PC_REGISTER = 32;
|
||||
static constexpr u32 PSTATE_REGISTER = 33;
|
||||
static constexpr u32 Q0_REGISTER = 34;
|
||||
static constexpr u32 FPCR_REGISTER = 66;
|
||||
static constexpr u32 FPSR_REGISTER = 67;
|
||||
};
|
||||
|
||||
class GDBStubA32 final : public GDBStubArch {
|
||||
public:
|
||||
std::string GetTargetXML() const override;
|
||||
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
|
||||
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
|
||||
std::string ReadRegisters(const Kernel::KThread* thread) const override;
|
||||
void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
|
||||
std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
|
||||
u32 BreakpointInstruction() const override;
|
||||
|
||||
private:
|
||||
static constexpr u32 SP_REGISTER = 13;
|
||||
static constexpr u32 LR_REGISTER = 14;
|
||||
static constexpr u32 PC_REGISTER = 15;
|
||||
static constexpr u32 CPSR_REGISTER = 25;
|
||||
static constexpr u32 D0_REGISTER = 32;
|
||||
static constexpr u32 Q0_REGISTER = 64;
|
||||
static constexpr u32 FPSCR_REGISTER = 80;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -27,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
|
||||
@@ -884,18 +884,42 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
static constexpr VibrationValue test_vibration = {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
if (!player.vibration_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Common::Input::VibrationStatus test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = 160.0f,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = 320.0f,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
|
||||
const Common::Input::VibrationStatus zero_vibration = {
|
||||
.low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
SetVibration(device_index, test_vibration);
|
||||
output_devices[device_index]->SetVibration(test_vibration);
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
return output_devices[device_index]->SetVibration(zero_vibration) ==
|
||||
Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
|
||||
@@ -498,6 +498,49 @@ struct SixAxisSensorFusionParameters {
|
||||
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
|
||||
"SixAxisSensorFusionParameters is an invalid size");
|
||||
|
||||
// This is nn::hid::server::SixAxisSensorProperties
|
||||
struct SixAxisSensorProperties {
|
||||
union {
|
||||
u8 raw{};
|
||||
BitField<0, 1, u8> is_newly_assigned;
|
||||
BitField<1, 1, u8> is_firmware_update_available;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorCalibrationParameter
|
||||
struct SixAxisSensorCalibrationParameter {
|
||||
std::array<u8, 0x744> unknown_data{};
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
|
||||
"SixAxisSensorCalibrationParameter is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorIcInformation
|
||||
struct SixAxisSensorIcInformation {
|
||||
f32 angular_rate{2000.0f}; // dps
|
||||
std::array<f32, 6> unknown_gyro_data1{
|
||||
-10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
|
||||
}; // dps
|
||||
std::array<f32, 9> unknown_gyro_data2{
|
||||
0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
|
||||
};
|
||||
std::array<f32, 9> unknown_gyro_data3{
|
||||
1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
|
||||
};
|
||||
f32 acceleration_range{8.0f}; // g force
|
||||
std::array<f32, 6> unknown_accel_data1{
|
||||
-0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
|
||||
}; // g force
|
||||
std::array<f32, 9> unknown_accel_data2{
|
||||
0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
|
||||
};
|
||||
std::array<f32, 9> unknown_accel_data3{
|
||||
1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
|
||||
"SixAxisSensorIcInformation is an invalid size");
|
||||
|
||||
// This is nn::hid::VibrationDeviceHandle
|
||||
struct VibrationDeviceHandle {
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include "common/input.h"
|
||||
@@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
|
||||
{
|
||||
KScopedSchedulerLock lock{kernel};
|
||||
thread->SetState(ThreadState::Runnable);
|
||||
|
||||
if (system.DebuggerEnabled()) {
|
||||
thread->RequestSuspend(SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -56,11 +56,22 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle
|
||||
return npad_id && npad_type && device_index;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||
if (!npad_id) {
|
||||
return InvalidNpadId;
|
||||
}
|
||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||
return npad_id && npad_type && device_index;
|
||||
if (!device_index) {
|
||||
return NpadDeviceIndexOutOfRange;
|
||||
}
|
||||
// This doesn't get validated on nnsdk
|
||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||
if (!npad_type) {
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
@@ -158,6 +169,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
shared_memory->system_properties.use_plus.Assign(1);
|
||||
shared_memory->system_properties.use_minus.Assign(1);
|
||||
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
|
||||
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
shared_memory->style_tag.handheld.Assign(1);
|
||||
@@ -170,16 +182,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
|
||||
shared_memory->applet_nfc_xcd.applet_footer.type =
|
||||
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
|
||||
shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
shared_memory->style_tag.joycon_dual.Assign(1);
|
||||
if (controller.is_dual_left_connected) {
|
||||
shared_memory->device_type.joycon_left.Assign(1);
|
||||
shared_memory->system_properties.use_minus.Assign(1);
|
||||
shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
|
||||
}
|
||||
if (controller.is_dual_right_connected) {
|
||||
shared_memory->device_type.joycon_right.Assign(1);
|
||||
shared_memory->system_properties.use_plus.Assign(1);
|
||||
shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
|
||||
}
|
||||
shared_memory->system_properties.use_directional_buttons.Assign(1);
|
||||
shared_memory->system_properties.is_vertical.Assign(1);
|
||||
@@ -198,6 +213,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
shared_memory->system_properties.is_horizontal.Assign(1);
|
||||
shared_memory->system_properties.use_minus.Assign(1);
|
||||
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
|
||||
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
shared_memory->style_tag.joycon_right.Assign(1);
|
||||
@@ -205,6 +221,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
shared_memory->system_properties.is_horizontal.Assign(1);
|
||||
shared_memory->system_properties.use_plus.Assign(1);
|
||||
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
|
||||
shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
shared_memory->style_tag.gamecube.Assign(1);
|
||||
@@ -215,6 +232,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
shared_memory->style_tag.palma.Assign(1);
|
||||
shared_memory->device_type.palma.Assign(1);
|
||||
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::NES:
|
||||
shared_memory->style_tag.lark.Assign(1);
|
||||
@@ -582,6 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
UNREACHABLE();
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
@@ -672,6 +691,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
|
||||
}
|
||||
|
||||
void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
|
||||
if (joy_hold_type != NpadJoyHoldType::Horizontal &&
|
||||
joy_hold_type != NpadJoyHoldType::Vertical) {
|
||||
LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
|
||||
joy_hold_type);
|
||||
return;
|
||||
}
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
@@ -695,11 +720,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
|
||||
return communication_mode;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode) {
|
||||
ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
@@ -708,7 +734,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
|
||||
}
|
||||
|
||||
if (!controller.device->IsConnected()) {
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
|
||||
@@ -717,34 +743,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
|
||||
controller.is_dual_left_connected = true;
|
||||
controller.is_dual_right_connected = false;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
DisconnectNpad(npad_id);
|
||||
controller.is_dual_left_connected = false;
|
||||
controller.is_dual_right_connected = true;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// This is for NpadJoyAssignmentMode::Single
|
||||
|
||||
// Only JoyconDual get affected by this function
|
||||
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
|
||||
DisconnectNpad(npad_id);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
|
||||
DisconnectNpad(npad_id);
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// We have two controllers connected to the same npad_id we need to split them
|
||||
@@ -762,6 +788,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
|
||||
controller_2.is_dual_right_connected = false;
|
||||
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
@@ -957,10 +984,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
|
||||
InitNewlyAddedController(npad_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
|
||||
@@ -977,6 +1004,12 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
shared_memory->device_type.raw = 0;
|
||||
shared_memory->system_properties.raw = 0;
|
||||
shared_memory->button_properties.raw = 0;
|
||||
shared_memory->sixaxis_fullkey_properties.raw = 0;
|
||||
shared_memory->sixaxis_handheld_properties.raw = 0;
|
||||
shared_memory->sixaxis_dual_left_properties.raw = 0;
|
||||
shared_memory->sixaxis_dual_right_properties.raw = 0;
|
||||
shared_memory->sixaxis_left_properties.raw = 0;
|
||||
shared_memory->sixaxis_right_properties.raw = 0;
|
||||
shared_memory->battery_level_dual = 0;
|
||||
shared_memory->battery_level_left = 0;
|
||||
shared_memory->battery_level_right = 0;
|
||||
@@ -997,346 +1030,268 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
controller.device->Disconnect();
|
||||
SignalStyleSetChangedEvent(npad_id);
|
||||
WriteEmptyEntry(shared_memory);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
GyroscopeZeroDriftMode drift_mode) {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
}
|
||||
controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.gyroscope_zero_drift_mode = drift_mode;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
GyroscopeZeroDriftMode& drift_mode) const {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
GyroscopeZeroDriftMode& drift_mode) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
}
|
||||
drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
drift_mode = sixaxis.gyroscope_zero_drift_mode;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
bool& is_at_rest) const {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
ResultCode Controller_NPad::IsSixAxisSensorAtRest(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
is_at_rest = controller.sixaxis_at_rest;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// We don't support joycon firmware updates
|
||||
is_firmware_available = false;
|
||||
const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
|
||||
is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
bool sixaxis_status) {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.unaltered_passtrough = is_enabled;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_enabled = sixaxis.unaltered_passtrough;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
calibration = sixaxis.calibration;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
ic_information = sixaxis.ic_information;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
|
||||
sixaxis_properties.is_newly_assigned.Assign(0);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
controller.sixaxis_sensor_enabled = sixaxis_status;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
|
||||
break;
|
||||
}
|
||||
is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_fusion_enabled = sixaxis.is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
bool is_fusion_enabled) {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
ResultCode Controller_NPad::SetSixAxisFusionEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
}
|
||||
controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.is_fusion_enabled = is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::SetSixAxisFusionParameters(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto param1 = sixaxis_fusion_parameters.parameter1;
|
||||
if (param1 < 0.0f || param1 > 1.0f) {
|
||||
return InvalidSixAxisFusionRange;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
}
|
||||
controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.fusion = sixaxis_fusion_parameters;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode Controller_NPad::GetSixAxisFusionParameters(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const {
|
||||
if (!IsDeviceHandleValid(sixaxis_handle)) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle");
|
||||
return NpadInvalidHandle;
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
parameters = controller.sixaxis_fullkey.fusion;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
parameters = controller.sixaxis_handheld.fusion;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
case Core::HID::NpadStyleIndex::GameCube:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
parameters = controller.sixaxis_dual_left.fusion;
|
||||
break;
|
||||
}
|
||||
parameters = controller.sixaxis_dual_right.fusion;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
parameters = controller.sixaxis_left.fusion;
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
parameters = controller.sixaxis_right.fusion;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
|
||||
return NpadInvalidHandle;
|
||||
}
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
parameters = sixaxis.fusion;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
|
||||
npad_id_2);
|
||||
return;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
|
||||
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
|
||||
const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
|
||||
const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
|
||||
bool merge_controllers = false;
|
||||
auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
|
||||
auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
|
||||
|
||||
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
|
||||
// Otherwise, do nothing.
|
||||
// Simplify this code by converting dualjoycon with only a side connected to single joycons
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
|
||||
controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
|
||||
}
|
||||
if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
|
||||
controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
|
||||
}
|
||||
}
|
||||
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
|
||||
controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
|
||||
}
|
||||
if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
|
||||
controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid merge errors
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
return NpadIsDualJoycon;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
|
||||
return NpadIsSameType;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
|
||||
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
|
||||
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
|
||||
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
}
|
||||
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
|
||||
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
|
||||
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
|
||||
merge_controllers = true;
|
||||
return NpadIsSameType;
|
||||
}
|
||||
|
||||
if (merge_controllers) {
|
||||
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
|
||||
DisconnectNpad(npad_id_2);
|
||||
controller_1.is_dual_left_connected = true;
|
||||
controller_1.is_dual_right_connected = true;
|
||||
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
|
||||
return;
|
||||
// These exceptions are handled as if they where dual joycon
|
||||
if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
return NpadIsDualJoycon;
|
||||
}
|
||||
LOG_WARNING(Service_HID,
|
||||
"Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
|
||||
"dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
|
||||
npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
|
||||
controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
|
||||
controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
|
||||
controller_2.is_dual_right_connected);
|
||||
if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
|
||||
controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
return NpadIsDualJoycon;
|
||||
}
|
||||
|
||||
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
|
||||
DisconnectNpad(npad_id_2);
|
||||
controller_1.is_dual_left_connected = true;
|
||||
controller_1.is_dual_right_connected = true;
|
||||
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::StartLRAssignmentMode() {
|
||||
@@ -1349,17 +1304,17 @@ void Controller_NPad::StopLRAssignmentMode() {
|
||||
is_in_lr_assignment_mode = false;
|
||||
}
|
||||
|
||||
bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
|
||||
npad_id_2);
|
||||
return false;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
|
||||
npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
|
||||
npad_id_2 == Core::HID::NpadIdType::Other) {
|
||||
return true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
|
||||
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
|
||||
@@ -1369,46 +1324,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
const auto is_connected_2 = controller_2->IsConnected();
|
||||
|
||||
if (!IsControllerSupported(type_index_1) && is_connected_1) {
|
||||
return false;
|
||||
return NpadNotConnected;
|
||||
}
|
||||
if (!IsControllerSupported(type_index_2) && is_connected_2) {
|
||||
return false;
|
||||
return NpadNotConnected;
|
||||
}
|
||||
|
||||
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
|
||||
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
|
||||
|
||||
return true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
|
||||
ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
|
||||
Core::HID::LedPattern& pattern) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return Core::HID::LedPattern{0, 0, 0, 0};
|
||||
return InvalidNpadId;
|
||||
}
|
||||
const auto& controller = GetControllerFromNpadIdType(npad_id).device;
|
||||
return controller->GetLedPattern();
|
||||
pattern = controller->GetLedPattern();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
|
||||
Core::HID::NpadIdType npad_id) const {
|
||||
ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
|
||||
Core::HID::NpadIdType npad_id, bool& is_valid) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
// Return the default value
|
||||
return false;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
const auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
return controller.unintended_home_button_input_protection;
|
||||
is_valid = controller.unintended_home_button_input_protection;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
|
||||
Core::HID::NpadIdType npad_id) {
|
||||
ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
|
||||
bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return;
|
||||
return InvalidNpadId;
|
||||
}
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
controller.unintended_home_button_input_protection = is_protection_enabled;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
|
||||
@@ -1546,4 +1504,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
|
||||
return controller_data[npad_index];
|
||||
}
|
||||
|
||||
Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.shared_memory->sixaxis_fullkey_properties;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.shared_memory->sixaxis_handheld_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.shared_memory->sixaxis_dual_left_properties;
|
||||
}
|
||||
return controller.shared_memory->sixaxis_dual_right_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.shared_memory->sixaxis_left_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.shared_memory->sixaxis_right_properties;
|
||||
default:
|
||||
return controller.shared_memory->sixaxis_fullkey_properties;
|
||||
}
|
||||
}
|
||||
|
||||
const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.shared_memory->sixaxis_fullkey_properties;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.shared_memory->sixaxis_handheld_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.shared_memory->sixaxis_dual_left_properties;
|
||||
}
|
||||
return controller.shared_memory->sixaxis_dual_right_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.shared_memory->sixaxis_left_properties;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.shared_memory->sixaxis_right_properties;
|
||||
default:
|
||||
return controller.shared_memory->sixaxis_fullkey_properties;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -107,8 +107,8 @@ public:
|
||||
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
|
||||
NpadCommunicationMode GetNpadCommunicationMode() const;
|
||||
|
||||
void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode);
|
||||
ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode);
|
||||
|
||||
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
|
||||
const Core::HID::VibrationValue& vibration_value);
|
||||
@@ -141,50 +141,65 @@ public:
|
||||
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
|
||||
bool connected);
|
||||
|
||||
void DisconnectNpad(Core::HID::NpadIdType npad_id);
|
||||
ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
|
||||
|
||||
ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
GyroscopeZeroDriftMode drift_mode);
|
||||
ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
GyroscopeZeroDriftMode& drift_mode) const;
|
||||
ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_at_rest) const;
|
||||
ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
|
||||
ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
|
||||
ResultCode EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
|
||||
ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
|
||||
ResultCode LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
|
||||
ResultCode GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const;
|
||||
ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
|
||||
ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status);
|
||||
ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_fusion_enabled) const;
|
||||
ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool is_fusion_enabled);
|
||||
ResultCode SetSixAxisFusionParameters(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
|
||||
ResultCode GetSixAxisFusionParameters(
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle,
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const;
|
||||
Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
|
||||
bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
|
||||
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
|
||||
Core::HID::NpadIdType npad_id);
|
||||
ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
|
||||
ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
|
||||
bool& is_enabled) const;
|
||||
ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
|
||||
Core::HID::NpadIdType npad_id);
|
||||
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
|
||||
void ClearAllConnectedControllers();
|
||||
void DisconnectAllConnectedControllers();
|
||||
void ConnectAllDisconnectedControllers();
|
||||
void ClearAllControllers();
|
||||
|
||||
void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
|
||||
ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2);
|
||||
void StartLRAssignmentMode();
|
||||
void StopLRAssignmentMode();
|
||||
bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
|
||||
ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
|
||||
|
||||
// Logical OR for all buttons presses on all controllers
|
||||
// Specifically for cheat engine and other features.
|
||||
Core::HID::NpadButton GetAndResetPressState();
|
||||
|
||||
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
|
||||
static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
|
||||
static ResultCode VerifyValidSixAxisSensorHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NPAD_COUNT = 10;
|
||||
@@ -451,9 +466,13 @@ private:
|
||||
NpadLuciaType lucia_type{};
|
||||
NpadLagonType lagon_type{};
|
||||
NpadLagerType lager_type{};
|
||||
// FW 13.x Investigate there is some sort of bitflag related to joycons
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES(0xc08); // Unknown
|
||||
Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
|
||||
Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
|
||||
Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
|
||||
Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
|
||||
Core::HID::SixAxisSensorProperties sixaxis_left_properties;
|
||||
Core::HID::SixAxisSensorProperties sixaxis_right_properties;
|
||||
INSERT_PADDING_BYTES(0xc06); // Unknown
|
||||
};
|
||||
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
|
||||
|
||||
@@ -465,7 +484,10 @@ private:
|
||||
|
||||
struct SixaxisParameters {
|
||||
bool is_fusion_enabled{true};
|
||||
bool unaltered_passtrough{false};
|
||||
Core::HID::SixAxisSensorFusionParameters fusion{};
|
||||
Core::HID::SixAxisSensorCalibrationParameter calibration{};
|
||||
Core::HID::SixAxisSensorIcInformation ic_information{};
|
||||
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
|
||||
};
|
||||
|
||||
@@ -491,6 +513,7 @@ private:
|
||||
SixaxisParameters sixaxis_dual_right{};
|
||||
SixaxisParameters sixaxis_left{};
|
||||
SixaxisParameters sixaxis_right{};
|
||||
SixaxisParameters sixaxis_unknown{};
|
||||
|
||||
// Current pad state
|
||||
NPadGenericState npad_pad_state{};
|
||||
@@ -522,6 +545,14 @@ private:
|
||||
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
|
||||
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
|
||||
|
||||
Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const SixaxisParameters& GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
|
||||
std::atomic<u64> press_state{};
|
||||
|
||||
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
|
||||
|
||||
@@ -44,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
|
||||
const auto& current_touch = touch_status[id];
|
||||
auto& finger = fingers[id];
|
||||
finger.position = current_touch.position;
|
||||
finger.id = current_touch.id;
|
||||
|
||||
if (finger.attribute.start_touch) {
|
||||
@@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
if (!finger.pressed && current_touch.pressed) {
|
||||
finger.attribute.start_touch.Assign(1);
|
||||
finger.pressed = true;
|
||||
finger.position = current_touch.position;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (finger.pressed && !current_touch.pressed) {
|
||||
finger.attribute.raw = 0;
|
||||
finger.attribute.end_touch.Assign(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only update position if touch is not on a special frame
|
||||
finger.position = current_touch.position;
|
||||
}
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
|
||||
constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
|
||||
constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
|
||||
constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
|
||||
constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
|
||||
constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
|
||||
constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -257,12 +257,12 @@ Hid::Hid(Core::System& system_)
|
||||
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
|
||||
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
|
||||
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
|
||||
{84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
|
||||
{85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
|
||||
{84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
|
||||
{85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
|
||||
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
|
||||
{87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
|
||||
{88, nullptr, "GetSixAxisSensorIcInformation"},
|
||||
{89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
|
||||
{87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
|
||||
{88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
|
||||
{89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
|
||||
{91, &Hid::ActivateGesture, "ActivateGesture"},
|
||||
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
|
||||
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
|
||||
@@ -694,11 +694,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(result1);
|
||||
return;
|
||||
}
|
||||
if (result2.IsError()) {
|
||||
rb.Push(result2);
|
||||
return;
|
||||
}
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result2);
|
||||
}
|
||||
|
||||
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -821,6 +817,144 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
|
||||
rb.Push(is_firmware_available);
|
||||
}
|
||||
|
||||
void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
bool enabled;
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
|
||||
parameters.sixaxis_handle, parameters.enabled);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
|
||||
"applet_resource_user_id={}",
|
||||
parameters.enabled, parameters.sixaxis_handle.npad_type,
|
||||
parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
bool is_unaltered_sisxaxis_enabled{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(result);
|
||||
rb.Push(is_unaltered_sisxaxis_enabled);
|
||||
}
|
||||
|
||||
void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::SixAxisSensorCalibrationParameter calibration{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result =
|
||||
controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
ctx.WriteBuffer(calibration);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
Core::HID::SixAxisSensorIcInformation ic_information{};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result =
|
||||
controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
ctx.WriteBuffer(ic_information);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Core::HID::SixAxisSensorHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result =
|
||||
controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
@@ -948,27 +1082,29 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.DisconnectNpad(parameters.npad_id);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.DisconnectNpad(parameters.npad_id);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
|
||||
|
||||
Core::HID::LedPattern pattern{0, 0, 0, 0};
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.GetLedPattern(npad_id, pattern);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.GetLedPattern(npad_id)
|
||||
.raw);
|
||||
rb.Push(result);
|
||||
rb.Push(pattern.raw);
|
||||
}
|
||||
|
||||
void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1028,15 +1164,16 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result =
|
||||
controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1051,16 +1188,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
|
||||
Controller_NPad::NpadJoyAssignmentMode::Single);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id,
|
||||
parameters.npad_joy_device_type);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1074,14 +1211,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.SetNpadMode(parameters.npad_id, {},
|
||||
Controller_NPad::NpadJoyAssignmentMode::Dual);
|
||||
|
||||
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1090,14 +1228,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
|
||||
npad_id_1, npad_id_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1157,19 +1295,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SwapNpadAssignment(npad_id_1, npad_id_2);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
|
||||
npad_id_1, npad_id_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (res) {
|
||||
rb.Push(ResultSuccess);
|
||||
} else {
|
||||
LOG_ERROR(Service_HID, "Npads are not connected!");
|
||||
rb.Push(NpadNotConnected);
|
||||
}
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1183,13 +1316,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
bool is_enabled = false;
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result =
|
||||
controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
|
||||
rb.Push(result);
|
||||
rb.Push(is_enabled);
|
||||
}
|
||||
|
||||
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1204,9 +1341,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetUnintendedHomeButtonInputProtectionEnabled(
|
||||
parameters.unintended_home_button_input_protection, parameters.npad_id);
|
||||
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
|
||||
const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
|
||||
parameters.unintended_home_button_input_protection, parameters.npad_id);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
|
||||
@@ -1215,7 +1352,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1377,6 +1514,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto can_vibrate{rp.Pop<bool>()};
|
||||
|
||||
// nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
|
||||
// by converting it to a bool
|
||||
Settings::values.vibration_enabled.SetValue(can_vibrate);
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
|
||||
@@ -1388,9 +1527,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
// nnSDK checks if a float is greater than zero. We return the bool we stored earlier
|
||||
const auto is_enabled = Settings::values.vibration_enabled.GetValue();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(Settings::values.vibration_enabled.GetValue());
|
||||
rb.Push(is_enabled);
|
||||
}
|
||||
|
||||
void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -113,6 +113,11 @@ private:
|
||||
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
|
||||
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
|
||||
void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
|
||||
void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
|
||||
void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
|
||||
void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
|
||||
void ActivateGesture(Kernel::HLERequestContext& ctx);
|
||||
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -110,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int GetMonthLength(bool is_leap_year, int month) {
|
||||
constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31};
|
||||
static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
|
||||
constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
|
||||
}
|
||||
|
||||
|
||||
@@ -594,6 +594,19 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
|
||||
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
|
||||
}
|
||||
|
||||
bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
|
||||
VAddr end = base + size;
|
||||
VAddr page = Common::AlignDown(base, PAGE_SIZE);
|
||||
|
||||
for (; page < end; page += PAGE_SIZE) {
|
||||
if (!IsValidVirtualAddress(page)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8* Memory::GetPointer(VAddr vaddr) {
|
||||
return impl->GetPointer(vaddr);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,17 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
|
||||
|
||||
/**
|
||||
* Checks whether or not the supplied range of addresses are all valid
|
||||
* virtual addresses for the current process.
|
||||
*
|
||||
* @param base The address to begin checking.
|
||||
* @param size The amount of bytes to check.
|
||||
*
|
||||
* @returns True if all bytes in the given range are valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
|
||||
|
||||
/**
|
||||
* Gets a pointer to the given address.
|
||||
*
|
||||
|
||||
@@ -434,6 +434,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||
using namespace std::chrono_literals;
|
||||
while (initialized) {
|
||||
SDL_PumpEvents();
|
||||
SendVibrations();
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
});
|
||||
@@ -531,13 +532,31 @@ Common::Input::VibrationError SDLDriver::SetRumble(
|
||||
.type = Common::Input::VibrationAmplificationType::Exponential,
|
||||
};
|
||||
|
||||
if (!joystick->RumblePlay(new_vibration)) {
|
||||
return Common::Input::VibrationError::Unknown;
|
||||
if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
|
||||
if (!joystick->RumblePlay(new_vibration)) {
|
||||
return Common::Input::VibrationError::Unknown;
|
||||
}
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
vibration_queue.Push(VibrationRequest{
|
||||
.identifier = identifier,
|
||||
.vibration = new_vibration,
|
||||
});
|
||||
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
void SDLDriver::SendVibrations() {
|
||||
while (!vibration_queue.Empty()) {
|
||||
VibrationRequest request;
|
||||
vibration_queue.Pop(request);
|
||||
const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
|
||||
static_cast<int>(request.identifier.port));
|
||||
joystick->RumblePlay(request.vibration);
|
||||
}
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
|
||||
s32 axis, float value) const {
|
||||
Common::ParamPackage params{};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
union SDL_Event;
|
||||
@@ -64,12 +65,20 @@ public:
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
private:
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
void CloseJoysticks();
|
||||
|
||||
/// Takes all vibrations from the queue and sends the command to the controller
|
||||
void SendVibrations();
|
||||
|
||||
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
|
||||
float value = 0.1f) const;
|
||||
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
|
||||
@@ -107,6 +116,9 @@ private:
|
||||
/// Returns true if the button is on the left joycon
|
||||
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
|
||||
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
@@ -14,38 +14,93 @@ constexpr PadIdentifier identifier = {
|
||||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
// Touch doesn't exist handle it as a new one
|
||||
TouchPressed(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
TouchPressed(x, y, finger);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
if (GetIndexFromFingerId(finger_id)) {
|
||||
// Touch already exist. Just update the data
|
||||
TouchMoved(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), true);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
|
||||
const auto index = GetNextFreeIndex();
|
||||
if (!index) {
|
||||
// No free entries. Ignore input
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = true;
|
||||
fingers[i].finger_id = finger_id;
|
||||
TouchMoved(x, y, finger_id);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchReleased(std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), false);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
const auto& finger = fingers[index];
|
||||
if (!finger.is_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (finger.finger_id == finger_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
if (!fingers[index].is_enabled) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TouchScreen::ClearActiveFlag() {
|
||||
for (auto& finger : fingers) {
|
||||
finger.is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseInactiveTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (!finger.is_active) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseAllTouch() {
|
||||
for (int index = 0; index < 16; ++index) {
|
||||
SetButton(identifier, index, false);
|
||||
SetAxis(identifier, index * 2, 0.0f);
|
||||
SetAxis(identifier, index * 2 + 1, 0.0f);
|
||||
for (const auto& finger : fingers) {
|
||||
if (finger.is_enabled) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,41 +3,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
* A touch device factory representing a touch screen. It receives touch events and forward them
|
||||
* to all touch devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param center_x the x-coordinate of the middle of the screen
|
||||
* @param center_y the y-coordinate of the middle of the screen
|
||||
* Signals that touch has moved and marks this touch point as active
|
||||
* @param x new horizontal position
|
||||
* @param y new vertical position
|
||||
* @param finger_id of the touch point to be updated
|
||||
*/
|
||||
void TouchMoved(float x, float y, std::size_t finger);
|
||||
void TouchMoved(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
* @param key_code the code of the key to press
|
||||
* Signals and creates a new touch point with this finger id
|
||||
* @param x starting horizontal position
|
||||
* @param y starting vertical position
|
||||
* @param finger_id to be assigned to the new touch point
|
||||
*/
|
||||
void TouchPressed(float x, float y, std::size_t finger);
|
||||
void TouchPressed(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to released
|
||||
* @param key_code the code of the key to release
|
||||
* Signals and resets the touch point related to the this finger id
|
||||
* @param finger_id to be released
|
||||
*/
|
||||
void TouchReleased(std::size_t finger);
|
||||
void TouchReleased(std::size_t finger_id);
|
||||
|
||||
/// Resets the active flag for each touch point
|
||||
void ClearActiveFlag();
|
||||
|
||||
/// Releases all touch that haven't been marked as active
|
||||
void ReleaseInactiveTouch();
|
||||
|
||||
/// Resets all inputs to their initial value
|
||||
void ReleaseAllTouch();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGER_COUNT = 16;
|
||||
|
||||
struct TouchStatus {
|
||||
std::size_t finger_id{};
|
||||
bool is_enabled{};
|
||||
bool is_active{};
|
||||
};
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -153,7 +153,7 @@ constexpr Vp9EntropyProbs default_probs{
|
||||
.high_precision{128, 128},
|
||||
};
|
||||
|
||||
constexpr std::array<s32, 256> norm_lut{
|
||||
constexpr std::array<u8, 256> norm_lut{
|
||||
0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
@@ -164,7 +164,7 @@ constexpr std::array<s32, 256> norm_lut{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
constexpr std::array<s32, 254> map_lut{
|
||||
constexpr std::array<u8, 254> map_lut{
|
||||
20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
|
||||
1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
@@ -232,7 +232,7 @@ constexpr std::array<s32, 254> map_lut{
|
||||
std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
|
||||
}
|
||||
|
||||
return map_lut[index];
|
||||
return static_cast<s32>(map_lut[index]);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -819,7 +819,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
|
||||
local_range = range - split;
|
||||
}
|
||||
|
||||
s32 shift = norm_lut[local_range];
|
||||
s32 shift = static_cast<s32>(norm_lut[local_range]);
|
||||
local_range <<= shift;
|
||||
count += shift;
|
||||
|
||||
|
||||
@@ -784,8 +784,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
});
|
||||
} else {
|
||||
// Front face defines both faces
|
||||
scheduler.Record([ref = regs.stencil_back_func_ref, write_mask = regs.stencil_back_mask,
|
||||
test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
|
||||
test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
|
||||
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask);
|
||||
|
||||
@@ -147,7 +147,7 @@ enum class SurfaceTarget {
|
||||
TextureCubeArray,
|
||||
};
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
|
||||
constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
|
||||
1, // A8B8G8R8_UNORM
|
||||
1, // A8B8G8R8_SNORM
|
||||
1, // A8B8G8R8_SINT
|
||||
@@ -249,7 +249,7 @@ constexpr u32 DefaultBlockWidth(PixelFormat format) {
|
||||
return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
|
||||
constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
|
||||
1, // A8B8G8R8_UNORM
|
||||
1, // A8B8G8R8_SNORM
|
||||
1, // A8B8G8R8_SINT
|
||||
@@ -351,7 +351,7 @@ constexpr u32 DefaultBlockHeight(PixelFormat format) {
|
||||
return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
|
||||
constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
|
||||
32, // A8B8G8R8_UNORM
|
||||
32, // A8B8G8R8_SNORM
|
||||
32, // A8B8G8R8_SINT
|
||||
|
||||
@@ -669,6 +669,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
const bool is_amd =
|
||||
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
|
||||
if (is_amd) {
|
||||
// TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor
|
||||
const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 &&
|
||||
VK_VERSION_MINOR(properties.driverVersion) == 0 &&
|
||||
VK_VERSION_PATCH(properties.driverVersion) >= 226;
|
||||
if (khr_push_descriptor && has_broken_push_descriptor) {
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor");
|
||||
khr_push_descriptor = false;
|
||||
}
|
||||
|
||||
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
|
||||
sets_per_pool = 96;
|
||||
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
|
||||
|
||||
@@ -240,7 +240,7 @@ elseif(WIN32)
|
||||
if(MSVC)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
elseif(MINGW)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@ AboutDialog::AboutDialog(QWidget* parent)
|
||||
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200));
|
||||
// Try and request the icon from Qt theme (Linux?)
|
||||
const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
|
||||
if (!yuzu_logo.isNull()) {
|
||||
ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
|
||||
}
|
||||
ui->labelBuildInfo->setText(
|
||||
ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
|
||||
QString::fromUtf8(Common::g_build_date).left(10)));
|
||||
|
||||
@@ -26,8 +26,20 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/icons/yuzu.png"/></p></body></html></string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -152,7 +164,7 @@ p, li { white-space: pre-wrap; }
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../dist/icons/icons.qrc"/>
|
||||
<include location="../../dist/qt_themes_default/default/default.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -411,11 +411,11 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
|
||||
break;
|
||||
}
|
||||
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true);
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -562,7 +562,7 @@ void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
InlineTextInsertString(entered_text.toStdU16String());
|
||||
InlineTextInsertString(Common::U16StringFromBuffer(entered_text.utf16(), entered_text.size()));
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
|
||||
@@ -1119,11 +1119,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
|
||||
}
|
||||
|
||||
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1189,7 +1189,8 @@ void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button)
|
||||
return;
|
||||
}
|
||||
|
||||
InlineTextInsertString(button->text().toStdU16String());
|
||||
const auto button_text = button->text();
|
||||
InlineTextInsertString(Common::U16StringFromBuffer(button_text.utf16(), button_text.size()));
|
||||
|
||||
// Revert the keyboard to lowercase if the shift key is active.
|
||||
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
|
||||
@@ -1282,11 +1283,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
|
||||
if (is_inline) {
|
||||
emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
|
||||
} else {
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
|
||||
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text_str));
|
||||
}
|
||||
break;
|
||||
case Core::HID::NpadButton::Y:
|
||||
|
||||
@@ -50,6 +50,7 @@ void EmuThread::run() {
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
auto stop_token = stop_source.get_token();
|
||||
bool debugger_should_start = system.DebuggerEnabled();
|
||||
|
||||
system.RegisterHostThread();
|
||||
|
||||
@@ -89,6 +90,12 @@ void EmuThread::run() {
|
||||
this->SetRunning(false);
|
||||
emit ErrorThrown(result, system.GetStatusDetails());
|
||||
}
|
||||
|
||||
if (debugger_should_start) {
|
||||
system.InitializeDebugger();
|
||||
debugger_should_start = false;
|
||||
}
|
||||
|
||||
running_wait.Wait();
|
||||
result = system.Pause();
|
||||
if (result != Core::SystemResultStatus::Success) {
|
||||
@@ -102,11 +109,9 @@ void EmuThread::run() {
|
||||
was_active = true;
|
||||
emit DebugModeEntered();
|
||||
}
|
||||
} else if (exec_step) {
|
||||
UNIMPLEMENTED();
|
||||
} else {
|
||||
std::unique_lock lock{running_mutex};
|
||||
running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; });
|
||||
running_cv.wait(lock, stop_token, [this] { return IsRunning(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,65 +777,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
|
||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
input_subsystem->GetTouchScreen()->ClearActiveFlag();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
}
|
||||
// Release all inactive points
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (!TouchExist(touch_ids[id], touch_points)) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchEndEvent() {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] != 0) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == 0) {
|
||||
touch_ids[id] = touch_point.id() + 1;
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchExist(std::size_t id,
|
||||
const QList<QTouchEvent::TouchPoint>& touch_points) const {
|
||||
return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
|
||||
return id == static_cast<std::size_t>(point.id() + 1);
|
||||
});
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
bool GRenderWindow::event(QEvent* event) {
|
||||
|
||||
@@ -54,15 +54,6 @@ public:
|
||||
*/
|
||||
void run() override;
|
||||
|
||||
/**
|
||||
* Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
|
||||
* @note This function is thread-safe
|
||||
*/
|
||||
void ExecStep() {
|
||||
exec_step = true;
|
||||
running_cv.notify_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the emulation thread is running or not
|
||||
* @param running Boolean value, set the emulation thread to running if true
|
||||
@@ -99,7 +90,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool exec_step = false;
|
||||
bool running = false;
|
||||
std::stop_source stop_source;
|
||||
std::mutex running_mutex;
|
||||
@@ -217,10 +207,6 @@ private:
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void TouchStart(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
@@ -246,8 +232,6 @@ private:
|
||||
bool first_frame = false;
|
||||
InputCommon::TasInput::TasState last_tas_state;
|
||||
|
||||
std::array<std::size_t, 16> touch_ids{};
|
||||
|
||||
Core::System& system;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -525,6 +525,9 @@ void Config::ReadDebuggingValues() {
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
Settings::values.record_frame_times =
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
|
||||
ReadBasicSetting(Settings::values.use_gdbstub);
|
||||
ReadBasicSetting(Settings::values.gdbstub_port);
|
||||
ReadBasicSetting(Settings::values.program_args);
|
||||
ReadBasicSetting(Settings::values.dump_exefs);
|
||||
ReadBasicSetting(Settings::values.dump_nso);
|
||||
@@ -1095,6 +1098,8 @@ void Config::SaveDebuggingValues() {
|
||||
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteBasicSetting(Settings::values.use_gdbstub);
|
||||
WriteBasicSetting(Settings::values.gdbstub_port);
|
||||
WriteBasicSetting(Settings::values.program_args);
|
||||
WriteBasicSetting(Settings::values.dump_exefs);
|
||||
WriteBasicSetting(Settings::values.dump_nso);
|
||||
|
||||
@@ -24,13 +24,18 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->toggle_gdbstub, &QCheckBox::toggled,
|
||||
[&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
|
||||
void ConfigureDebug::SetConfiguration() {
|
||||
const bool runtime_lock = !system.IsPoweredOn();
|
||||
|
||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
|
||||
ui->toggle_console->setEnabled(runtime_lock);
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
@@ -71,6 +76,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
|
||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
|
||||
@@ -3,6 +3,60 @@
|
||||
<class>ConfigureDebug</class>
|
||||
<widget class="QWidget" name="ConfigureDebug">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Debugger</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_gdbstub">
|
||||
<property name="text">
|
||||
<string>Enable GDB Stub</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="gdbport_spinbox">
|
||||
<property name="minimum">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
|
||||
@@ -1401,7 +1401,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
|
||||
type == StartGameType::Normal) {
|
||||
// Load per game settings
|
||||
const auto file_path = std::filesystem::path{filename.toStdU16String()};
|
||||
const auto file_path =
|
||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
||||
const auto config_file_name = title_id == 0
|
||||
? Common::FS::PathToUTF8String(file_path.filename())
|
||||
: fmt::format("{:016X}", title_id);
|
||||
@@ -1482,7 +1483,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
}
|
||||
if (res != Loader::ResultStatus::Success || title_name.empty()) {
|
||||
title_name = Common::FS::PathToUTF8String(
|
||||
std::filesystem::path{filename.toStdU16String()}.filename());
|
||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
|
||||
.filename());
|
||||
}
|
||||
const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
|
||||
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
|
||||
@@ -3150,7 +3152,7 @@ void GMainWindow::OnTasStateChanged() {
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateStatusBar() {
|
||||
if (emu_thread == nullptr) {
|
||||
if (emu_thread == nullptr || !system->IsPoweredOn()) {
|
||||
status_bar_update_timer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(0);
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
|
||||
Reference in New Issue
Block a user