Compare commits
19 Commits
dfe1f30a64
...
4950de5d32
Author | SHA1 | Date |
---|---|---|
emoose | 4950de5d32 | |
Gliniak | 11f14e8488 | |
Adrian | 3d79874828 | |
emoose | 5917554324 | |
emoose | 2e9c9f403d | |
emoose | 220324b463 | |
emoose | c17261abf7 | |
emoose | e260d0a110 | |
emoose | a4187736ad | |
emoose | ea9cf0c8f9 | |
emoose | 0acc46e52e | |
emoose | 2ee35a2054 | |
emoose | c9778a4032 | |
emoose | 26cfa69497 | |
emoose | 0469ae2aee | |
emoose | b7ab7e966f | |
emoose | bfa6eee936 | |
emoose | 2204259781 | |
emoose | 733fe04426 |
|
@ -295,6 +295,7 @@ workspace("xenia")
|
|||
include("src/xenia/base")
|
||||
include("src/xenia/cpu")
|
||||
include("src/xenia/cpu/backend/x64")
|
||||
include("src/xenia/debug/gdb")
|
||||
include("src/xenia/debug/ui")
|
||||
include("src/xenia/gpu")
|
||||
include("src/xenia/gpu/null")
|
||||
|
|
|
@ -92,6 +92,7 @@ project("xenia-app")
|
|||
"xenia-app-discord",
|
||||
"xenia-apu-sdl",
|
||||
-- TODO(Triang3l): CPU debugger on Android.
|
||||
"xenia-debug-gdb",
|
||||
"xenia-debug-ui",
|
||||
"xenia-helper-sdl",
|
||||
"xenia-hid-sdl",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/config.h"
|
||||
#include "xenia/debug/gdb/gdbstub.h"
|
||||
#include "xenia/debug/ui/debug_window.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/kernel/xam/xam_module.h"
|
||||
|
@ -100,6 +101,10 @@ DEFINE_transient_bool(portable, true,
|
|||
"General");
|
||||
|
||||
DECLARE_bool(debug);
|
||||
DEFINE_int32(
|
||||
gdbport, 0,
|
||||
"Port for GDBStub debugger to listen on, requires --debug (0 = disable)",
|
||||
"General");
|
||||
|
||||
DEFINE_bool(discord, true, "Enable Discord rich presence", "General");
|
||||
|
||||
|
@ -228,6 +233,7 @@ class EmulatorApp final : public xe::ui::WindowedApp {
|
|||
|
||||
// Created on demand, used by the emulator.
|
||||
std::unique_ptr<xe::debug::ui::DebugWindow> debug_window_;
|
||||
std::unique_ptr<xe::debug::gdb::GDBStub> debug_gdbstub_;
|
||||
|
||||
// Refreshing the emulator - placed after its dependencies.
|
||||
std::atomic<bool> emulator_thread_quit_requested_;
|
||||
|
@ -566,14 +572,26 @@ void EmulatorApp::EmulatorThread() {
|
|||
// Set a debug handler.
|
||||
// This will respond to debugging requests so we can open the debug UI.
|
||||
if (cvars::debug) {
|
||||
if (cvars::gdbport > 0) {
|
||||
emulator_->processor()->set_debug_listener_request_handler(
|
||||
[this](xe::cpu::Processor* processor) {
|
||||
if (debug_gdbstub_) {
|
||||
return debug_gdbstub_.get();
|
||||
}
|
||||
debug_gdbstub_ = xe::debug::gdb::GDBStub::Create(emulator_.get(),
|
||||
cvars::gdbport);
|
||||
return debug_gdbstub_.get();
|
||||
});
|
||||
emulator_->processor()->ShowDebugger();
|
||||
} else {
|
||||
emulator_->processor()->set_debug_listener_request_handler(
|
||||
[this](xe::cpu::Processor* processor) {
|
||||
if (debug_window_) {
|
||||
return debug_window_.get();
|
||||
}
|
||||
app_context().CallInUIThreadSynchronous([this]() {
|
||||
debug_window_ = xe::debug::ui::DebugWindow::Create(emulator_.get(),
|
||||
app_context());
|
||||
debug_window_ = xe::debug::ui::DebugWindow::Create(
|
||||
emulator_.get(), app_context());
|
||||
debug_window_->window()->AddListener(
|
||||
&debug_window_closed_listener_);
|
||||
});
|
||||
|
@ -581,6 +599,7 @@ void EmulatorApp::EmulatorThread() {
|
|||
return debug_window_.get();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
emulator_->on_launch.AddListener([&](auto title_id, const auto& game_title) {
|
||||
if (cvars::discord) {
|
||||
|
|
|
@ -46,6 +46,9 @@ class Socket {
|
|||
// Returns true if the client is connected and can send/receive data.
|
||||
virtual bool is_connected() = 0;
|
||||
|
||||
// Sets socket non-blocking mode
|
||||
virtual void set_nonblocking(bool nonblocking) = 0;
|
||||
|
||||
// Closes the socket.
|
||||
// This will signal the wait handle.
|
||||
virtual void Close() = 0;
|
||||
|
|
|
@ -113,6 +113,15 @@ class Win32Socket : public Socket {
|
|||
return socket_ != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void set_nonblocking(bool nonblocking) override {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
return;
|
||||
}
|
||||
u_long val = nonblocking ? 1 : 0;
|
||||
ioctlsocket(socket_, FIONBIO, &val);
|
||||
}
|
||||
|
||||
void Close() override {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
|
|
|
@ -30,6 +30,9 @@ class DebugListener {
|
|||
// end.
|
||||
virtual void OnDetached() = 0;
|
||||
|
||||
// Handles exception info (will be followed by OnExecutionPaused)
|
||||
virtual void OnUnhandledException(Exception* ex) = 0;
|
||||
|
||||
// Handles execution being interrupted and transitioning to
|
||||
// ExceutionState::kPaused.
|
||||
virtual void OnExecutionPaused() = 0;
|
||||
|
@ -49,6 +52,9 @@ class DebugListener {
|
|||
// Breakpoints may be hit during stepping.
|
||||
virtual void OnBreakpointHit(Breakpoint* breakpoint,
|
||||
ThreadDebugInfo* thread_info) = 0;
|
||||
|
||||
// Handles any debug messages from the guest
|
||||
virtual void OnDebugPrint(const std::string_view message) = 0;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
|
|
|
@ -33,6 +33,8 @@ DEFINE_bool(disable_context_promotion, false,
|
|||
"some sports games, but will reduce performance.",
|
||||
"CPU");
|
||||
|
||||
DECLARE_bool(debug);
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
@ -59,7 +61,10 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
|
|||
|
||||
// Passes are executed in the order they are added. Multiple of the same
|
||||
// pass type may be used.
|
||||
if (!cvars::disable_context_promotion) {
|
||||
|
||||
// Disable context promotion for debug, otherwise register changes won't apply
|
||||
// correctly
|
||||
if (!cvars::disable_context_promotion && !cvars::debug) {
|
||||
if (validate) {
|
||||
compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
}
|
||||
|
|
|
@ -644,7 +644,8 @@ bool Processor::OnThreadBreakpointHit(Exception* ex) {
|
|||
if ((scan_breakpoint->address_type() == Breakpoint::AddressType::kGuest &&
|
||||
scan_breakpoint->guest_address() == frame.guest_pc) ||
|
||||
(scan_breakpoint->address_type() == Breakpoint::AddressType::kHost &&
|
||||
scan_breakpoint->host_address() == frame.host_pc)) {
|
||||
scan_breakpoint->host_address() == frame.host_pc) ||
|
||||
scan_breakpoint->ContainsHostAddress(frame.host_pc)) {
|
||||
breakpoint = scan_breakpoint;
|
||||
break;
|
||||
}
|
||||
|
@ -670,18 +671,19 @@ bool Processor::OnThreadBreakpointHit(Exception* ex) {
|
|||
}
|
||||
|
||||
ResumeAllThreads();
|
||||
thread_info->thread->thread()->Suspend();
|
||||
|
||||
// Apply thread context changes.
|
||||
// TODO(benvanik): apply to all threads?
|
||||
#if XE_ARCH_AMD64
|
||||
ex->set_resume_pc(thread_info->host_context.rip + 2);
|
||||
ex->set_resume_pc(thread_info->host_context.rip);
|
||||
#elif XE_ARCH_ARM64
|
||||
ex->set_resume_pc(thread_info->host_context.pc + 2);
|
||||
ex->set_resume_pc(thread_info->host_context.pc);
|
||||
#else
|
||||
#error Instruction pointer not specified for the target CPU architecture.
|
||||
#endif // XE_ARCH
|
||||
|
||||
thread_info->thread->thread()->Suspend();
|
||||
|
||||
// Resume execution.
|
||||
return true;
|
||||
}
|
||||
|
@ -722,7 +724,7 @@ bool Processor::OnUnhandledException(Exception* ex) {
|
|||
execution_state_ = ExecutionState::kPaused;
|
||||
|
||||
// Notify debugger that exceution stopped.
|
||||
// debug_listener_->OnException(info);
|
||||
debug_listener_->OnUnhandledException(ex);
|
||||
debug_listener_->OnExecutionPaused();
|
||||
|
||||
// Suspend self.
|
||||
|
@ -936,7 +938,10 @@ void Processor::StepHostInstruction(uint32_t thread_id) {
|
|||
thread_info->step_breakpoint.reset();
|
||||
OnStepCompleted(thread_info);
|
||||
}));
|
||||
AddBreakpoint(thread_info->step_breakpoint.get());
|
||||
|
||||
// Add to front of breakpoints map, so this should get evaluated first
|
||||
breakpoints_.insert(breakpoints_.begin(), thread_info->step_breakpoint.get());
|
||||
|
||||
thread_info->step_breakpoint->Resume();
|
||||
|
||||
// ResumeAllBreakpoints();
|
||||
|
@ -969,7 +974,10 @@ void Processor::StepGuestInstruction(uint32_t thread_id) {
|
|||
thread_info->step_breakpoint.reset();
|
||||
OnStepCompleted(thread_info);
|
||||
}));
|
||||
AddBreakpoint(thread_info->step_breakpoint.get());
|
||||
|
||||
// Add to front of breakpoints map, so this should get evaluated first
|
||||
breakpoints_.insert(breakpoints_.begin(), thread_info->step_breakpoint.get());
|
||||
|
||||
thread_info->step_breakpoint->Resume();
|
||||
|
||||
// ResumeAllBreakpoints();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2024 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_GDB_GDBSTUB_H_
|
||||
#define XENIA_DEBUG_GDB_GDBSTUB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
#include "xenia/base/socket.h"
|
||||
#include "xenia/cpu/breakpoint.h"
|
||||
#include "xenia/cpu/debug_listener.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace gdb {
|
||||
|
||||
class GDBStub : public cpu::DebugListener {
|
||||
enum class ControlCode : char {
|
||||
Ack = '+',
|
||||
Nack = '-',
|
||||
PacketStart = '$',
|
||||
PacketEnd = '#',
|
||||
Interrupt = '\03',
|
||||
};
|
||||
|
||||
enum class SignalCode : uint8_t { SIGILL = 4, SIGTRAP = 5, SIGSEGV = 11 };
|
||||
|
||||
enum class RegisterIndex : int {
|
||||
GPR0 = 0,
|
||||
FPR0 = 32,
|
||||
PC = 64,
|
||||
MSR = 65,
|
||||
CR = 66,
|
||||
LR = 67,
|
||||
CTR = 68,
|
||||
XER = 69,
|
||||
FPSCR = 70
|
||||
};
|
||||
|
||||
public:
|
||||
virtual ~GDBStub();
|
||||
|
||||
static std::unique_ptr<GDBStub> Create(Emulator* emulator, int listen_port);
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
|
||||
void OnFocus() override;
|
||||
void OnDetached() override;
|
||||
void OnUnhandledException(Exception* ex) override;
|
||||
void OnExecutionPaused() override;
|
||||
void OnExecutionContinued() override;
|
||||
void OnExecutionEnded() override;
|
||||
void OnStepCompleted(cpu::ThreadDebugInfo* thread_info) override;
|
||||
void OnBreakpointHit(cpu::Breakpoint* breakpoint,
|
||||
cpu::ThreadDebugInfo* thread_info) override;
|
||||
void OnDebugPrint(const std::string_view message) override;
|
||||
|
||||
private:
|
||||
struct GDBCommand {
|
||||
std::string cmd{};
|
||||
std::string data{};
|
||||
uint8_t checksum{};
|
||||
};
|
||||
|
||||
struct GDBClient {
|
||||
std::unique_ptr<Socket> socket;
|
||||
bool no_ack_mode = false;
|
||||
std::string receive_buffer;
|
||||
};
|
||||
|
||||
explicit GDBStub(Emulator* emulator, int listen_port);
|
||||
bool Initialize();
|
||||
|
||||
void Listen(GDBClient& client);
|
||||
void SendPacket(GDBClient& client, const std::string& data);
|
||||
bool ProcessIncomingData(GDBClient& client);
|
||||
bool ParsePacket(const std::string& packet, GDBCommand& out_cmd);
|
||||
std::string HandleGDBCommand(GDBClient& client, const GDBCommand& command);
|
||||
|
||||
void UpdateCache();
|
||||
|
||||
std::string DebuggerDetached();
|
||||
std::string RegisterRead(xe::cpu::ThreadDebugInfo* thread, uint32_t rid);
|
||||
std::string RegisterWrite(xe::cpu::ThreadDebugInfo* thread, uint32_t rid,
|
||||
const std::string_view value);
|
||||
std::string RegisterRead(const std::string& data);
|
||||
std::string RegisterWrite(const std::string& data);
|
||||
std::string RegisterReadAll();
|
||||
std::string RegisterWriteAll(const std::string& data);
|
||||
std::string ExecutionPause();
|
||||
std::string ExecutionContinue();
|
||||
std::string ExecutionStep();
|
||||
std::string MemoryRead(const std::string& data);
|
||||
std::string MemoryWrite(const std::string& data);
|
||||
std::string BuildThreadList();
|
||||
std::string QueryPacket(GDBClient& client, const std::string& data);
|
||||
|
||||
std::string GetThreadStateReply(uint32_t thread_id, SignalCode signal);
|
||||
|
||||
bool CreateCodeBreakpoint(uint64_t address);
|
||||
void DeleteCodeBreakpoint(uint64_t address);
|
||||
void DeleteCodeBreakpoint(cpu::Breakpoint* breakpoint);
|
||||
cpu::Breakpoint* LookupBreakpointAtAddress(uint64_t address);
|
||||
|
||||
Emulator* emulator_ = nullptr;
|
||||
cpu::Processor* processor_ = nullptr;
|
||||
|
||||
int listen_port_;
|
||||
std::unique_ptr<xe::SocketServer> socket_;
|
||||
|
||||
std::mutex mtx_;
|
||||
bool stop_thread_ = false;
|
||||
|
||||
xe::global_critical_region global_critical_region_;
|
||||
|
||||
struct EmulatorStateCache {
|
||||
uint32_t cur_thread_id = -1;
|
||||
uint32_t last_bp_thread_id = -1;
|
||||
|
||||
uint64_t notify_bp_guest_address = -1;
|
||||
uint32_t notify_thread_id = -1;
|
||||
bool notify_stopped = false;
|
||||
std::optional<Exception::AccessViolationOperation>
|
||||
notify_exception_access_violation;
|
||||
std::optional<Exception::Code> notify_exception_code;
|
||||
|
||||
std::queue<std::string> notify_debug_prints;
|
||||
|
||||
bool is_stopped = false;
|
||||
std::vector<kernel::object_ref<kernel::XModule>> modules;
|
||||
std::vector<cpu::ThreadDebugInfo*> thread_debug_infos;
|
||||
|
||||
struct {
|
||||
std::vector<std::unique_ptr<cpu::Breakpoint>> all_breakpoints;
|
||||
std::unordered_map<uint32_t, cpu::Breakpoint*>
|
||||
code_breakpoints_by_guest_address;
|
||||
} breakpoints;
|
||||
|
||||
cpu::ThreadDebugInfo* thread_info(int threadId) {
|
||||
for (auto& thread : thread_debug_infos) {
|
||||
if (thread->thread_id == threadId) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
cpu::ThreadDebugInfo* cur_thread_info() {
|
||||
return thread_info(cur_thread_id);
|
||||
}
|
||||
} cache_;
|
||||
};
|
||||
|
||||
} // namespace gdb
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_DEBUG_WINDOW_H_
|
|
@ -0,0 +1,25 @@
|
|||
project_root = "../../../.."
|
||||
include(project_root.."/tools/build")
|
||||
|
||||
group("src")
|
||||
project("xenia-debug-gdb")
|
||||
uuid("9193a274-f4c2-4746-bd85-93fcfc5c3e39")
|
||||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"imgui",
|
||||
"xenia-base",
|
||||
"xenia-cpu",
|
||||
"xenia-ui",
|
||||
})
|
||||
filter({"configurations:Release", "platforms:Windows"})
|
||||
buildoptions({
|
||||
"/Os",
|
||||
"/O1"
|
||||
})
|
||||
filter{}
|
||||
defines({
|
||||
})
|
||||
includedirs({
|
||||
})
|
||||
local_platform_files()
|
|
@ -1576,6 +1576,8 @@ void DebugWindow::OnDetached() {
|
|||
}
|
||||
}
|
||||
|
||||
void DebugWindow::OnUnhandledException(Exception* ex) {}
|
||||
|
||||
void DebugWindow::OnExecutionPaused() {
|
||||
UpdateCache();
|
||||
Focus();
|
||||
|
@ -1604,6 +1606,8 @@ void DebugWindow::OnBreakpointHit(Breakpoint* breakpoint,
|
|||
Focus();
|
||||
}
|
||||
|
||||
void DebugWindow::OnDebugPrint(const std::string_view message) {}
|
||||
|
||||
void DebugWindow::Focus() const {
|
||||
app_context_.CallInUIThread([this]() { window_->Focus(); });
|
||||
}
|
||||
|
|
|
@ -44,12 +44,14 @@ class DebugWindow : public cpu::DebugListener {
|
|||
|
||||
void OnFocus() override;
|
||||
void OnDetached() override;
|
||||
void OnUnhandledException(Exception* ex) override;
|
||||
void OnExecutionPaused() override;
|
||||
void OnExecutionContinued() override;
|
||||
void OnExecutionEnded() override;
|
||||
void OnStepCompleted(cpu::ThreadDebugInfo* thread_info) override;
|
||||
void OnBreakpointHit(cpu::Breakpoint* breakpoint,
|
||||
cpu::ThreadDebugInfo* thread_info) override;
|
||||
void OnDebugPrint(const std::string_view message) override;
|
||||
|
||||
private:
|
||||
class DebugDialog final : public xe::ui::ImGuiDialog {
|
||||
|
|
|
@ -189,6 +189,10 @@ Emulator::~Emulator() {
|
|||
ExceptionHandler::Uninstall(Emulator::ExceptionCallbackThunk, this);
|
||||
}
|
||||
|
||||
uint32_t Emulator::main_thread_id() {
|
||||
return main_thread_ ? main_thread_->thread_id() : 0;
|
||||
}
|
||||
|
||||
X_STATUS Emulator::Setup(
|
||||
ui::Window* display_window, ui::ImGuiDrawer* imgui_drawer,
|
||||
bool require_cpu_backend,
|
||||
|
|
|
@ -125,6 +125,8 @@ class Emulator {
|
|||
// Are we currently running a title?
|
||||
bool is_title_open() const { return title_id_.has_value(); }
|
||||
|
||||
uint32_t main_thread_id();
|
||||
|
||||
// Window used for displaying graphical output. Can be null.
|
||||
ui::Window* display_window() const { return display_window_; }
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ void InputSystem::AddDriver(std::unique_ptr<InputDriver> driver) {
|
|||
void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot,
|
||||
bool connected) {
|
||||
if (slot == XUserIndexAny) {
|
||||
slot = 0;
|
||||
XELOGW("{} received requrest for slot any! Unsupported", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (connected_slots.test(slot) == connected) {
|
||||
|
@ -136,18 +137,16 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
|
||||
bool any_connected = false;
|
||||
for (auto& driver : drivers_) {
|
||||
// connected_slots
|
||||
X_RESULT result = driver->GetKeystroke(user_index, flags, out_keystroke);
|
||||
if (result == X_ERROR_INVALID_PARAMETER) {
|
||||
if (result == X_ERROR_INVALID_PARAMETER ||
|
||||
result == X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result != X_ERROR_DEVICE_NOT_CONNECTED) {
|
||||
any_connected = true;
|
||||
}
|
||||
|
||||
if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) {
|
||||
UpdateUsedSlot(driver.get(), user_index, any_connected);
|
||||
|
||||
if (result == X_ERROR_SUCCESS) {
|
||||
last_used_slot = user_index;
|
||||
}
|
||||
|
@ -158,7 +157,6 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
continue;
|
||||
}
|
||||
}
|
||||
UpdateUsedSlot(nullptr, user_index, any_connected);
|
||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace xe {
|
|||
namespace hid {
|
||||
namespace winkey {
|
||||
|
||||
bool static IsPassthroughEnabled(uint32_t user_index) {
|
||||
bool static IsPassthroughEnabled() {
|
||||
return static_cast<KeyboardMode>(cvars::keyboard_mode) ==
|
||||
KeyboardMode::Passthrough;
|
||||
}
|
||||
|
@ -272,6 +272,14 @@ X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
|
|||
|
||||
X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) {
|
||||
if (!is_active()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// Pop from the queue.
|
||||
KeyEvent evt;
|
||||
{
|
||||
|
@ -284,15 +292,6 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
key_events_.pop();
|
||||
}
|
||||
|
||||
if (!IsKeyboardForUserEnabled(user_index) &&
|
||||
!IsPassthroughEnabled(user_index)) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (!is_active()) {
|
||||
return X_ERROR_EMPTY;
|
||||
}
|
||||
|
||||
X_RESULT result = X_ERROR_EMPTY;
|
||||
|
||||
ui::VirtualKey xinput_virtual_key = ui::VirtualKey::kNone;
|
||||
|
@ -302,7 +301,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
|
||||
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
|
||||
|
||||
if (!IsPassthroughEnabled(user_index)) {
|
||||
if (!IsPassthroughEnabled()) {
|
||||
if (IsKeyboardForUserEnabled(user_index)) {
|
||||
for (const KeyBinding& b : key_bindings_) {
|
||||
if (b.input_key == evt.virtual_key &&
|
||||
|
@ -338,7 +337,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
|||
keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP
|
||||
}
|
||||
|
||||
if (IsPassthroughEnabled(user_index)) {
|
||||
if (IsPassthroughEnabled()) {
|
||||
if (GetKeyboardState(key_map_)) {
|
||||
WCHAR buf;
|
||||
if (ToUnicode(uint8_t(xinput_virtual_key), 0, key_map_, &buf, 1, 0) ==
|
||||
|
@ -373,7 +372,8 @@ void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyUp(ui::KeyEvent& e) {
|
|||
}
|
||||
|
||||
void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
||||
if (!is_active()) {
|
||||
if (!is_active() || static_cast<KeyboardMode>(cvars::keyboard_mode) ==
|
||||
KeyboardMode::Disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,9 @@ UserProfile::UserProfile(uint64_t xuid, X_XAMACCOUNTINFO* account_info)
|
|||
// XPROFILE_GAMER_CONTROL_SENSITIVITY
|
||||
AddSetting(std::make_unique<UserSetting>(0x10040018, 0));
|
||||
// Preferred color 1
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001D, 0xFFFF0000u));
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001D, PREFERRED_COLOR_NONE));
|
||||
// Preferred color 2
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001E, 0xFF00FF00u));
|
||||
AddSetting(std::make_unique<UserSetting>(0x1004001E, PREFERRED_COLOR_NONE));
|
||||
// XPROFILE_GAMER_ACTION_AUTO_AIM
|
||||
AddSetting(std::make_unique<UserSetting>(0x10040022, 1));
|
||||
// XPROFILE_GAMER_ACTION_AUTO_CENTER
|
||||
|
|
|
@ -35,6 +35,21 @@ enum class X_USER_PROFILE_SETTING_SOURCE : uint32_t {
|
|||
UNKNOWN = 3,
|
||||
};
|
||||
|
||||
enum PREFERRED_COLOR_OPTIONS : uint32_t {
|
||||
PREFERRED_COLOR_NONE,
|
||||
PREFERRED_COLOR_BLACK,
|
||||
PREFERRED_COLOR_WHITE,
|
||||
PREFERRED_COLOR_YELLOW,
|
||||
PREFERRED_COLOR_ORANGE,
|
||||
PREFERRED_COLOR_PINK,
|
||||
PREFERRED_COLOR_RED,
|
||||
PREFERRED_COLOR_PURPLE,
|
||||
PREFERRED_COLOR_BLUE,
|
||||
PREFERRED_COLOR_GREEN,
|
||||
PREFERRED_COLOR_BROWN,
|
||||
PREFERRED_COLOR_SILVER
|
||||
};
|
||||
|
||||
// Each setting contains 0x18 bytes long header
|
||||
struct X_USER_PROFILE_SETTING_HEADER {
|
||||
xe::be<uint32_t> setting_id;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/xbdm/xbdm_private.h"
|
||||
|
@ -69,7 +70,14 @@ DECLARE_XBDM_EXPORT1(DmGetXboxName, kDebug, kImplemented)
|
|||
dword_result_t DmIsDebuggerPresent_entry() { return 0; }
|
||||
DECLARE_XBDM_EXPORT1(DmIsDebuggerPresent, kDebug, kStub);
|
||||
|
||||
void DmSendNotificationString_entry(lpdword_t unk0_ptr) {}
|
||||
void DmSendNotificationString_entry(lpstring_t message) {
|
||||
XELOGI("(DmSendNotificationString) {}", message.value());
|
||||
|
||||
if (cpu::DebugListener* listener =
|
||||
kernel_state()->processor()->debug_listener()) {
|
||||
listener->OnDebugPrint(message.value());
|
||||
}
|
||||
}
|
||||
DECLARE_XBDM_EXPORT1(DmSendNotificationString, kDebug, kStub);
|
||||
|
||||
dword_result_t DmRegisterCommandProcessor_entry(lpdword_t name_ptr,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
|
@ -850,6 +851,10 @@ SHIM_CALL DbgPrint_entry(PPCContext* ppc_context) {
|
|||
|
||||
XELOGI("(DbgPrint) {}", str);
|
||||
|
||||
if (cpu::DebugListener* listener = ppc_context->processor->debug_listener()) {
|
||||
listener->OnDebugPrint(str);
|
||||
}
|
||||
|
||||
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue