Compare commits

...

2 commits

Author SHA1 Message Date
Megamouse
75b728be7e Logitech G27 cleanup
Some checks are pending
Generate Translation Template / Generate Translation Template (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04 gcc (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04-arm clang (push) Waiting to run
Build RPCS3 / RPCS3 Linux ubuntu-24.04 clang (push) Waiting to run
Build RPCS3 / RPCS3 Windows (push) Waiting to run
2025-05-03 13:21:34 +02:00
trigger
6428088e46
Qt: "Show in Memory Viewer" context action (#17131) 2025-05-03 08:35:45 +03:00
14 changed files with 1051 additions and 958 deletions

File diff suppressed because it is too large Load diff

View file

@ -16,72 +16,72 @@
#include <vector> #include <vector>
#include <thread> #include <thread>
enum logitech_g27_ffb_state enum class logitech_g27_ffb_state
{ {
G27_FFB_INACTIVE, inactive,
G27_FFB_DOWNLOADED, downloaded,
G27_FFB_PLAYING playing
}; };
struct logitech_g27_ffb_slot struct logitech_g27_ffb_slot
{ {
logitech_g27_ffb_state state; logitech_g27_ffb_state state = logitech_g27_ffb_state::inactive;
uint64_t last_update; u64 last_update = 0;
SDL_HapticEffect last_effect; SDL_HapticEffect last_effect {};
int effect_id; s32 effect_id = -1;
}; };
struct sdl_mapping struct sdl_mapping
{ {
uint32_t device_type_id; // (vendor_id << 16) | product_id u32 device_type_id = 0; // (vendor_id << 16) | product_id
sdl_mapping_type type; sdl_mapping_type type = sdl_mapping_type::button;
uint64_t id; u64 id = 0;
hat_component hat; hat_component hat = hat_component::none;
bool reverse; bool reverse = false;
bool positive_axis; bool positive_axis = false;
}; };
struct logitech_g27_sdl_mapping struct logitech_g27_sdl_mapping
{ {
sdl_mapping steering; sdl_mapping steering {};
sdl_mapping throttle; sdl_mapping throttle {};
sdl_mapping brake; sdl_mapping brake {};
sdl_mapping clutch; sdl_mapping clutch {};
sdl_mapping shift_up; sdl_mapping shift_up {};
sdl_mapping shift_down; sdl_mapping shift_down {};
sdl_mapping up; sdl_mapping up {};
sdl_mapping down; sdl_mapping down {};
sdl_mapping left; sdl_mapping left {};
sdl_mapping right; sdl_mapping right {};
sdl_mapping triangle; sdl_mapping triangle {};
sdl_mapping cross; sdl_mapping cross {};
sdl_mapping square; sdl_mapping square {};
sdl_mapping circle; sdl_mapping circle {};
// mappings based on g27 compat mode on g29 // mappings based on g27 compat mode on g29
sdl_mapping l2; sdl_mapping l2 {};
sdl_mapping l3; sdl_mapping l3 {};
sdl_mapping r2; sdl_mapping r2 {};
sdl_mapping r3; sdl_mapping r3 {};
sdl_mapping plus; sdl_mapping plus {};
sdl_mapping minus; sdl_mapping minus {};
sdl_mapping dial_clockwise; sdl_mapping dial_clockwise {};
sdl_mapping dial_anticlockwise; sdl_mapping dial_anticlockwise {};
sdl_mapping select; sdl_mapping select {};
sdl_mapping pause; sdl_mapping pause {};
sdl_mapping shifter_1; sdl_mapping shifter_1 {};
sdl_mapping shifter_2; sdl_mapping shifter_2 {};
sdl_mapping shifter_3; sdl_mapping shifter_3 {};
sdl_mapping shifter_4; sdl_mapping shifter_4 {};
sdl_mapping shifter_5; sdl_mapping shifter_5 {};
sdl_mapping shifter_6; sdl_mapping shifter_6 {};
sdl_mapping shifter_r; sdl_mapping shifter_r {};
}; };
class usb_device_logitech_g27 : public usb_device_emulated class usb_device_logitech_g27 : public usb_device_emulated
@ -98,26 +98,25 @@ public:
bool open_device() override; bool open_device() override;
private: private:
u32 m_controller_index; void sdl_refresh();
logitech_g27_sdl_mapping m_mapping; u32 m_controller_index = 0;
bool m_reverse_effects;
logitech_g27_sdl_mapping m_mapping {};
bool m_reverse_effects = false;
std::mutex m_sdl_handles_mutex; std::mutex m_sdl_handles_mutex;
SDL_Joystick* m_led_joystick_handle = nullptr; SDL_Joystick* m_led_joystick_handle = nullptr;
SDL_Haptic* m_haptic_handle = nullptr; SDL_Haptic* m_haptic_handle = nullptr;
std::map<uint32_t, std::vector<SDL_Joystick*>> m_joysticks; std::map<u32, std::vector<SDL_Joystick*>> m_joysticks;
bool m_fixed_loop = false; bool m_fixed_loop = false;
uint16_t m_wheel_range = 200; u16 m_wheel_range = 200;
logitech_g27_ffb_slot m_effect_slots[4]; std::array<logitech_g27_ffb_slot, 4> m_effect_slots {};
SDL_HapticEffect m_default_spring_effect = {0}; SDL_HapticEffect m_default_spring_effect {};
int m_default_spring_effect_id = -1; s32 m_default_spring_effect_id = -1;
bool m_enabled = false; bool m_enabled = false;
std::thread m_house_keeping_thread; std::thread m_house_keeping_thread;
std::mutex m_thread_control_mutex; atomic_t<bool> m_stop_thread { false };
bool m_stop_thread;
void sdl_refresh();
}; };

View file

@ -5,18 +5,52 @@
#include "Utilities/File.h" #include "Utilities/File.h"
#include "LogitechG27Config.h" #include "LogitechG27Config.h"
template <>
void fmt_class_string<sdl_mapping_type>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](sdl_mapping_type value)
{
switch (value)
{
case sdl_mapping_type::button: return "button";
case sdl_mapping_type::hat: return "hat";
case sdl_mapping_type::axis: return "axis";
}
return unknown;
});
}
template <>
void fmt_class_string<hat_component>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](hat_component value)
{
switch (value)
{
case hat_component::up: return "up";
case hat_component::down: return "down";
case hat_component::left: return "left";
case hat_component::right: return "right";
case hat_component::none: return "";
}
return unknown;
});
}
emulated_logitech_g27_config g_cfg_logitech_g27; emulated_logitech_g27_config g_cfg_logitech_g27;
LOG_CHANNEL(cfg_log, "CFG"); LOG_CHANNEL(cfg_log, "CFG");
emulated_logitech_g27_config::emulated_logitech_g27_config() emulated_logitech_g27_config::emulated_logitech_g27_config()
: m_path(fmt::format("%s%s.yml", fs::get_config_dir(true), "LogitechG27")) : m_path(fs::get_config_dir(true) + "LogitechG27.yml")
{ {
} }
void emulated_logitech_g27_config::reset() void emulated_logitech_g27_config::reset()
{ {
const std::lock_guard<std::mutex> lock(m_mutex); const std::lock_guard lock(m_mutex);
cfg::node::from_default(); cfg::node::from_default();
} }

View file

@ -4,31 +4,31 @@
#include <mutex> #include <mutex>
enum sdl_mapping_type enum class sdl_mapping_type
{ {
MAPPING_BUTTON = 0, button = 0,
MAPPING_HAT, hat,
MAPPING_AXIS, axis,
}; };
enum hat_component enum class hat_component
{ {
HAT_NONE = 0, none = 0,
HAT_UP, up,
HAT_DOWN, down,
HAT_LEFT, left,
HAT_RIGHT right
}; };
struct emulated_logitech_g27_mapping : cfg::node struct emulated_logitech_g27_mapping : cfg::node
{ {
cfg::uint<0, 0xFFFFFFFF> device_type_id; cfg::uint<0, 0xFFFFFFFF> device_type_id;
cfg::uint<0, 0xFFFFFFFF> type; cfg::_enum<sdl_mapping_type> type;
cfg::uint<0, 0xFFFFFFFFFFFFFFFF> id; cfg::uint<0, 0xFFFFFFFFFFFFFFFF> id;
cfg::uint<0, 0xFFFFFFFF> hat; cfg::_enum<hat_component> hat;
cfg::_bool reverse; cfg::_bool reverse;
emulated_logitech_g27_mapping(cfg::node* owner, std::string name, uint32_t device_type_id_def, sdl_mapping_type type_def, uint64_t id_def, hat_component hat_def, bool reverse_def) emulated_logitech_g27_mapping(cfg::node* owner, std::string name, u32 device_type_id_def, sdl_mapping_type type_def, uint64_t id_def, hat_component hat_def, bool reverse_def)
: cfg::node(owner, std::move(name)), : cfg::node(owner, std::move(name)),
device_type_id(this, "device_type_id", device_type_id_def), device_type_id(this, "device_type_id", device_type_id_def),
type(this, "type", type_def), type(this, "type", type_def),
@ -46,44 +46,44 @@ public:
// TODO these defaults are for a shifter-less G29 + a xbox controller for shifter testing, perhaps find a new default // TODO these defaults are for a shifter-less G29 + a xbox controller for shifter testing, perhaps find a new default
emulated_logitech_g27_mapping steering{this, "steering", 0x046dc24f, MAPPING_AXIS, 0, HAT_NONE, false}; emulated_logitech_g27_mapping steering{this, "steering", 0x046dc24f, sdl_mapping_type::axis, 0, hat_component::none, false};
emulated_logitech_g27_mapping throttle{this, "throttle", 0x046dc24f, MAPPING_AXIS, 2, HAT_NONE, false}; emulated_logitech_g27_mapping throttle{this, "throttle", 0x046dc24f, sdl_mapping_type::axis, 2, hat_component::none, false};
emulated_logitech_g27_mapping brake{this, "brake", 0x046dc24f, MAPPING_AXIS, 3, HAT_NONE, false}; emulated_logitech_g27_mapping brake{this, "brake", 0x046dc24f, sdl_mapping_type::axis, 3, hat_component::none, false};
emulated_logitech_g27_mapping clutch{this, "clutch", 0x046dc24f, MAPPING_AXIS, 1, HAT_NONE, false}; emulated_logitech_g27_mapping clutch{this, "clutch", 0x046dc24f, sdl_mapping_type::axis, 1, hat_component::none, false};
emulated_logitech_g27_mapping shift_up{this, "shift_up", 0x046dc24f, MAPPING_BUTTON, 4, HAT_NONE, false}; emulated_logitech_g27_mapping shift_up{this, "shift_up", 0x046dc24f, sdl_mapping_type::button, 4, hat_component::none, false};
emulated_logitech_g27_mapping shift_down{this, "shift_down", 0x046dc24f, MAPPING_BUTTON, 5, HAT_NONE, false}; emulated_logitech_g27_mapping shift_down{this, "shift_down", 0x046dc24f, sdl_mapping_type::button, 5, hat_component::none, false};
emulated_logitech_g27_mapping up{this, "up", 0x046dc24f, MAPPING_HAT, 0, HAT_UP, false}; emulated_logitech_g27_mapping up{this, "up", 0x046dc24f, sdl_mapping_type::hat, 0, hat_component::up, false};
emulated_logitech_g27_mapping down{this, "down", 0x046dc24f, MAPPING_HAT, 0, HAT_DOWN, false}; emulated_logitech_g27_mapping down{this, "down", 0x046dc24f, sdl_mapping_type::hat, 0, hat_component::down, false};
emulated_logitech_g27_mapping left{this, "left", 0x046dc24f, MAPPING_HAT, 0, HAT_LEFT, false}; emulated_logitech_g27_mapping left{this, "left", 0x046dc24f, sdl_mapping_type::hat, 0, hat_component::left, false};
emulated_logitech_g27_mapping right{this, "right", 0x046dc24f, MAPPING_HAT, 0, HAT_RIGHT, false}; emulated_logitech_g27_mapping right{this, "right", 0x046dc24f, sdl_mapping_type::hat, 0, hat_component::right, false};
emulated_logitech_g27_mapping triangle{this, "triangle", 0x046dc24f, MAPPING_BUTTON, 3, HAT_NONE, false}; emulated_logitech_g27_mapping triangle{this, "triangle", 0x046dc24f, sdl_mapping_type::button, 3, hat_component::none, false};
emulated_logitech_g27_mapping cross{this, "cross", 0x046dc24f, MAPPING_BUTTON, 0, HAT_NONE, false}; emulated_logitech_g27_mapping cross{this, "cross", 0x046dc24f, sdl_mapping_type::button, 0, hat_component::none, false};
emulated_logitech_g27_mapping square{this, "square", 0x046dc24f, MAPPING_BUTTON, 1, HAT_NONE, false}; emulated_logitech_g27_mapping square{this, "square", 0x046dc24f, sdl_mapping_type::button, 1, hat_component::none, false};
emulated_logitech_g27_mapping circle{this, "circle", 0x046dc24f, MAPPING_BUTTON, 2, HAT_NONE, false}; emulated_logitech_g27_mapping circle{this, "circle", 0x046dc24f, sdl_mapping_type::button, 2, hat_component::none, false};
emulated_logitech_g27_mapping l2{this, "l2", 0x046dc24f, MAPPING_BUTTON, 7, HAT_NONE, false}; emulated_logitech_g27_mapping l2{this, "l2", 0x046dc24f, sdl_mapping_type::button, 7, hat_component::none, false};
emulated_logitech_g27_mapping l3{this, "l3", 0x046dc24f, MAPPING_BUTTON, 11, HAT_NONE, false}; emulated_logitech_g27_mapping l3{this, "l3", 0x046dc24f, sdl_mapping_type::button, 11, hat_component::none, false};
emulated_logitech_g27_mapping r2{this, "r2", 0x046dc24f, MAPPING_BUTTON, 6, HAT_NONE, false}; emulated_logitech_g27_mapping r2{this, "r2", 0x046dc24f, sdl_mapping_type::button, 6, hat_component::none, false};
emulated_logitech_g27_mapping r3{this, "r3", 0x046dc24f, MAPPING_BUTTON, 10, HAT_NONE, false}; emulated_logitech_g27_mapping r3{this, "r3", 0x046dc24f, sdl_mapping_type::button, 10, hat_component::none, false};
emulated_logitech_g27_mapping plus{this, "plus", 0x046dc24f, MAPPING_BUTTON, 19, HAT_NONE, false}; emulated_logitech_g27_mapping plus{this, "plus", 0x046dc24f, sdl_mapping_type::button, 19, hat_component::none, false};
emulated_logitech_g27_mapping minus{this, "minus", 0x046dc24f, MAPPING_BUTTON, 20, HAT_NONE, false}; emulated_logitech_g27_mapping minus{this, "minus", 0x046dc24f, sdl_mapping_type::button, 20, hat_component::none, false};
emulated_logitech_g27_mapping dial_clockwise{this, "dial_clockwise", 0x046dc24f, MAPPING_BUTTON, 21, HAT_NONE, false}; emulated_logitech_g27_mapping dial_clockwise{this, "dial_clockwise", 0x046dc24f, sdl_mapping_type::button, 21, hat_component::none, false};
emulated_logitech_g27_mapping dial_anticlockwise{this, "dial_anticlockwise", 0x046dc24f, MAPPING_BUTTON, 22, HAT_NONE, false}; emulated_logitech_g27_mapping dial_anticlockwise{this, "dial_anticlockwise", 0x046dc24f, sdl_mapping_type::button, 22, hat_component::none, false};
emulated_logitech_g27_mapping select{this, "select", 0x046dc24f, MAPPING_BUTTON, 8, HAT_NONE, false}; emulated_logitech_g27_mapping select{this, "select", 0x046dc24f, sdl_mapping_type::button, 8, hat_component::none, false};
emulated_logitech_g27_mapping pause{this, "pause", 0x046dc24f, MAPPING_BUTTON, 9, HAT_NONE, false}; emulated_logitech_g27_mapping pause{this, "pause", 0x046dc24f, sdl_mapping_type::button, 9, hat_component::none, false};
emulated_logitech_g27_mapping shifter_1{this, "shifter_1", 0x045e028e, MAPPING_BUTTON, 3, HAT_NONE, false}; emulated_logitech_g27_mapping shifter_1{this, "shifter_1", 0x045e028e, sdl_mapping_type::button, 3, hat_component::none, false};
emulated_logitech_g27_mapping shifter_2{this, "shifter_2", 0x045e028e, MAPPING_BUTTON, 0, HAT_NONE, false}; emulated_logitech_g27_mapping shifter_2{this, "shifter_2", 0x045e028e, sdl_mapping_type::button, 0, hat_component::none, false};
emulated_logitech_g27_mapping shifter_3{this, "shifter_3", 0x045e028e, MAPPING_BUTTON, 2, HAT_NONE, false}; emulated_logitech_g27_mapping shifter_3{this, "shifter_3", 0x045e028e, sdl_mapping_type::button, 2, hat_component::none, false};
emulated_logitech_g27_mapping shifter_4{this, "shifter_4", 0x045e028e, MAPPING_BUTTON, 1, HAT_NONE, false}; emulated_logitech_g27_mapping shifter_4{this, "shifter_4", 0x045e028e, sdl_mapping_type::button, 1, hat_component::none, false};
emulated_logitech_g27_mapping shifter_5{this, "shifter_5", 0x045e028e, MAPPING_HAT, 0, HAT_UP, false}; emulated_logitech_g27_mapping shifter_5{this, "shifter_5", 0x045e028e, sdl_mapping_type::hat, 0, hat_component::up, false};
emulated_logitech_g27_mapping shifter_6{this, "shifter_6", 0x045e028e, MAPPING_HAT, 0, HAT_DOWN, false}; emulated_logitech_g27_mapping shifter_6{this, "shifter_6", 0x045e028e, sdl_mapping_type::hat, 0, hat_component::down, false};
emulated_logitech_g27_mapping shifter_r{this, "shifter_r", 0x045e028e, MAPPING_HAT, 0, HAT_LEFT, false}; emulated_logitech_g27_mapping shifter_r{this, "shifter_r", 0x045e028e, sdl_mapping_type::hat, 0, hat_component::left, false};
cfg::_bool reverse_effects{this, "reverse_effects", true}; cfg::_bool reverse_effects{this, "reverse_effects", true};
cfg::uint<0, 0xFFFFFFFF> ffb_device_type_id{this, "ffb_device_type_id", 0x046dc24f}; cfg::uint<0, 0xFFFFFFFF> ffb_device_type_id{this, "ffb_device_type_id", 0x046dc24f};

View file

@ -7,6 +7,7 @@
#include <QGuiApplication> #include <QGuiApplication>
#include "cheat_manager.h" #include "cheat_manager.h"
#include "memory_viewer_panel.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/Memory/vm.h" #include "Emu/Memory/vm.h"
@ -829,6 +830,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent)
QMenu* menu = new QMenu(); QMenu* menu = new QMenu();
QAction* add_to_cheat_list = new QAction(tr("Add to cheat list"), menu); QAction* add_to_cheat_list = new QAction(tr("Add to cheat list"), menu);
QAction* show_in_mem_viewer = new QAction(tr("Show in Memory Viewer"), menu);
const u32 offset = offsets_found[current_row]; const u32 offset = offsets_found[current_row];
const cheat_type type = static_cast<cheat_type>(cbx_cheat_search_type->currentIndex()); const cheat_type type = static_cast<cheat_type>(cbx_cheat_search_type->currentIndex());
@ -849,7 +851,13 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent)
update_cheat_list(); update_cheat_list();
}); });
connect(show_in_mem_viewer, &QAction::triggered, this, [offset]()
{
memory_viewer_panel::ShowAtPC(offset);
});
menu->addAction(add_to_cheat_list); menu->addAction(add_to_cheat_list);
menu->addAction(show_in_mem_viewer);
menu->exec(globalPos); menu->exec(globalPos);
}); });

View file

@ -32,6 +32,8 @@
#include <QTimer> #include <QTimer>
#include <QCheckBox> #include <QCheckBox>
#include <QMessageBox> #include <QMessageBox>
#include <QMenu>
#include <QTextDocumentFragment>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
@ -125,7 +127,8 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
m_regs = new QPlainTextEdit(this); m_regs = new QPlainTextEdit(this);
m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); m_regs->setLineWrapMode(QPlainTextEdit::NoWrap);
m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
m_regs->setContextMenuPolicy(Qt::CustomContextMenu);
m_debugger_list->setFont(m_mono); m_debugger_list->setFont(m_mono);
m_misc_state->setFont(m_mono); m_misc_state->setFont(m_mono);
m_regs->setFont(m_mono); m_regs->setFont(m_mono);
@ -158,6 +161,8 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
body->setLayout(vbox_p_main); body->setLayout(vbox_p_main);
setWidget(body); setWidget(body);
connect(m_regs, &QPlainTextEdit::customContextMenuRequested, this, &debugger_frame::OnRegsContextMenu);
connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog); connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog);
connect(m_go_to_pc, &QAbstractButton::clicked, this, [this]() { ShowPC(true); }); connect(m_go_to_pc, &QAbstractButton::clicked, this, [this]() { ShowPC(true); });
@ -1702,3 +1707,36 @@ void debugger_frame::EnableButtons(bool enable)
m_btn_step_over->setEnabled(step); m_btn_step_over->setEnabled(step);
m_btn_run->setEnabled(enable); m_btn_run->setEnabled(enable);
} }
void debugger_frame::OnRegsContextMenu(const QPoint& pos)
{
QMenu* menu = m_regs->createStandardContextMenu();
QAction* memory_viewer_action = new QAction(tr("Show in Memory Viewer"), menu);
connect(memory_viewer_action, &QAction::triggered, this, &debugger_frame::RegsShowMemoryViewerAction);
menu->addSeparator();
menu->addAction(memory_viewer_action);
menu->exec(m_regs->mapToGlobal(pos));
}
void debugger_frame::RegsShowMemoryViewerAction()
{
const QTextCursor cursor = m_regs->textCursor();
if (!cursor.hasSelection())
{
QMessageBox::warning(this, tr("No Selection"), tr("Please select a hex value first."));
return;
}
const QTextDocumentFragment frag(cursor);
const QString selected = frag.toPlainText().trimmed();
u64 pc = 0;
if (!parse_hex_qstring(selected, &pc))
{
QMessageBox::critical(this, tr("Invalid Hex"), tr("“%0” is not a valid 32-bit hex value.").arg(selected));
return;
}
memory_viewer_panel::ShowAtPC(static_cast<u32>(pc), make_check_cpu(get_cpu()));
}

View file

@ -137,9 +137,11 @@ public Q_SLOTS:
private Q_SLOTS: private Q_SLOTS:
void OnSelectUnit(); void OnSelectUnit();
void OnSelectSPUDisassembler(); void OnSelectSPUDisassembler();
void OnRegsContextMenu(const QPoint& pos);
void ShowPC(bool user_requested = false); void ShowPC(bool user_requested = false);
void EnableUpdateTimer(bool enable) const; void EnableUpdateTimer(bool enable) const;
void RunBtnPress(); void RunBtnPress();
void RegsShowMemoryViewerAction();
}; };
Q_DECLARE_METATYPE(u32) Q_DECLARE_METATYPE(u32)

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,12 @@
#pragma once #pragma once
#ifdef HAVE_SDL3
#include <QDialog> #include <QDialog>
#include <QLabel> #include <QLabel>
#include <QCheckBox> #include <QCheckBox>
#include <QScrollArea> #include <QScrollArea>
#ifdef HAVE_SDL3
#ifndef _MSC_VER #ifndef _MSC_VER
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
@ -14,7 +15,6 @@
#ifndef _MSC_VER #ifndef _MSC_VER
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif
#include <map> #include <map>
#include <vector> #include <vector>
@ -22,7 +22,7 @@
struct joystick_state struct joystick_state
{ {
std::vector<int16_t> axes; std::vector<s16> axes;
std::vector<bool> buttons; std::vector<bool> buttons;
std::vector<hat_component> hats; std::vector<hat_component> hats;
}; };
@ -36,15 +36,17 @@ class emulated_logitech_g27_settings_dialog : public QDialog
public: public:
emulated_logitech_g27_settings_dialog(QWidget* parent = nullptr); emulated_logitech_g27_settings_dialog(QWidget* parent = nullptr);
~emulated_logitech_g27_settings_dialog(); virtual ~emulated_logitech_g27_settings_dialog();
void set_state_text(const char*); void set_state_text(const QString& text);
const std::map<uint32_t, joystick_state>& get_joystick_states(); const std::map<u32, joystick_state>& get_joystick_states();
void set_enable(bool enable); void set_enable(bool enable);
private: private:
std::map<uint32_t, joystick_state> m_last_joystick_states; void load_ui_state_from_config();
// hack: need a completed dummy class when linking automoc generated with sdl-less build void save_ui_state_to_config();
std::vector<void*> m_joystick_handles;
std::map<u32, joystick_state> m_last_joystick_states;
std::vector<SDL_Joystick*> m_joystick_handles;
uint64_t m_last_joystick_states_update = 0; uint64_t m_last_joystick_states_update = 0;
bool m_sdl_initialized = false; bool m_sdl_initialized = false;
@ -97,7 +99,6 @@ private:
DeviceChoice* m_led_device = nullptr; DeviceChoice* m_led_device = nullptr;
QScrollArea* m_mapping_scroll_area = nullptr; QScrollArea* m_mapping_scroll_area = nullptr;
void load_ui_state_from_config();
void save_ui_state_to_config();
}; };
#endif

View file

@ -69,3 +69,23 @@ inline QString normalize_hex_qstring(const QString& input)
s.chop(1); s.chop(1);
return s; return s;
} }
inline bool parse_hex_qstring(const QString& input, u64* result, int max_bits = 32)
{
QString s = input;
int pos = 0;
const HexValidator validator(nullptr, max_bits);
const QValidator::State st = validator.validate(s, pos);
if (st != QValidator::Acceptable)
return false;
const QString norm = normalize_hex_qstring(input);
bool ok = false;
const quint64 value = norm.toULongLong(&ok, 16);
if (ok && result)
*result = static_cast<u64>(value);
return ok;
}

View file

@ -1,6 +1,8 @@
#include "log_frame.h" #include "log_frame.h"
#include "qt_utils.h" #include "qt_utils.h"
#include "gui_settings.h" #include "gui_settings.h"
#include "hex_validator.h"
#include "memory_viewer_panel.h"
#include "Utilities/lockless.h" #include "Utilities/lockless.h"
#include "util/asm.hpp" #include "util/asm.hpp"
@ -250,6 +252,21 @@ void log_frame::CreateAndConnectActions()
Q_EMIT PerformGoToOnDebugger(pte->textCursor().selectedText(), true); Q_EMIT PerformGoToOnDebugger(pte->textCursor().selectedText(), true);
}); });
m_perform_show_in_mem_viewer = new QAction(tr("Show in Memory Viewer"), this);
connect(m_perform_show_in_mem_viewer, &QAction::triggered, this, [this]()
{
const QPlainTextEdit* pte = (m_tabWidget->currentIndex() == 1 ? m_tty : m_log);
const QString selected = pte->textCursor().selectedText();
u64 pc = 0;
if (!parse_hex_qstring(selected, &pc))
{
QMessageBox::critical(this, tr("Invalid Hex"), tr("“%0” is not a valid 32-bit hex value.").arg(selected));
return;
}
memory_viewer_panel::ShowAtPC(static_cast<u32>(pc));
});
m_perform_goto_thread_on_debugger = new QAction(tr("Show Thread On The Debugger"), this); m_perform_goto_thread_on_debugger = new QAction(tr("Show Thread On The Debugger"), this);
connect(m_perform_goto_thread_on_debugger, &QAction::triggered, [this]() connect(m_perform_goto_thread_on_debugger, &QAction::triggered, [this]()
{ {
@ -354,13 +371,16 @@ void log_frame::CreateAndConnectActions()
menu->addAction(m_clear_act); menu->addAction(m_clear_act);
menu->addAction(m_perform_goto_on_debugger); menu->addAction(m_perform_goto_on_debugger);
menu->addAction(m_perform_goto_thread_on_debugger); menu->addAction(m_perform_goto_thread_on_debugger);
menu->addAction(m_perform_show_in_mem_viewer);
std::shared_ptr<bool> goto_signal_accepted = std::make_shared<bool>(false); std::shared_ptr<bool> goto_signal_accepted = std::make_shared<bool>(false);
Q_EMIT PerformGoToOnDebugger("", true, true, goto_signal_accepted); Q_EMIT PerformGoToOnDebugger("", true, true, goto_signal_accepted);
m_perform_goto_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted);
m_perform_goto_thread_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_thread_on_debugger->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted);
m_perform_show_in_mem_viewer->setEnabled(m_log->textCursor().hasSelection() && *goto_signal_accepted);
m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the log text on the debugger.")); m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the log text on the debugger."));
m_perform_goto_thread_on_debugger->setToolTip(tr("Show the thread that corresponds to the thread ID from the log text on the debugger.")); m_perform_goto_thread_on_debugger->setToolTip(tr("Show the thread that corresponds to the thread ID from the log text on the debugger."));
m_perform_show_in_mem_viewer->setToolTip(tr("Jump to the selected hexadecimal address from the log text on the memory viewer."));
menu->addSeparator(); menu->addSeparator();
menu->addActions(m_log_level_acts->actions()); menu->addActions(m_log_level_acts->actions());
@ -376,11 +396,14 @@ void log_frame::CreateAndConnectActions()
QMenu* menu = m_tty->createStandardContextMenu(); QMenu* menu = m_tty->createStandardContextMenu();
menu->addAction(m_clear_tty_act); menu->addAction(m_clear_tty_act);
menu->addAction(m_perform_goto_on_debugger); menu->addAction(m_perform_goto_on_debugger);
menu->addAction(m_perform_show_in_mem_viewer);
std::shared_ptr<bool> goto_signal_accepted = std::make_shared<bool>(false); std::shared_ptr<bool> goto_signal_accepted = std::make_shared<bool>(false);
Q_EMIT PerformGoToOnDebugger("", false, true, goto_signal_accepted); Q_EMIT PerformGoToOnDebugger("", false, true, goto_signal_accepted);
m_perform_goto_on_debugger->setEnabled(m_tty->textCursor().hasSelection() && *goto_signal_accepted); m_perform_goto_on_debugger->setEnabled(m_tty->textCursor().hasSelection() && *goto_signal_accepted);
m_perform_show_in_mem_viewer->setEnabled(m_tty->textCursor().hasSelection() && *goto_signal_accepted);
m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the TTY text on the debugger.")); m_perform_goto_on_debugger->setToolTip(tr("Jump to the selected hexadecimal address from the TTY text on the debugger."));
m_perform_show_in_mem_viewer->setToolTip(tr("Jump to the selected hexadecimal address from the TTY text on the memory viewer."));
menu->addSeparator(); menu->addSeparator();
menu->addAction(m_tty_act); menu->addAction(m_tty_act);

View file

@ -74,6 +74,7 @@ private:
QAction* m_clear_tty_act = nullptr; QAction* m_clear_tty_act = nullptr;
QAction* m_perform_goto_on_debugger = nullptr; QAction* m_perform_goto_on_debugger = nullptr;
QAction* m_perform_goto_thread_on_debugger = nullptr; QAction* m_perform_goto_thread_on_debugger = nullptr;
QAction* m_perform_show_in_mem_viewer = nullptr;
QActionGroup* m_log_level_acts = nullptr; QActionGroup* m_log_level_acts = nullptr;
QAction* m_nothing_act = nullptr; QAction* m_nothing_act = nullptr;

View file

@ -9,6 +9,7 @@
#include "Emu/RSX/RSXThread.h" #include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/rsx_utils.h" #include "Emu/RSX/rsx_utils.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "Emu/System.h"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QPushButton> #include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
@ -25,6 +26,7 @@
#include "util/logs.hpp" #include "util/logs.hpp"
#include "util/asm.hpp" #include "util/asm.hpp"
#include "debugger_frame.h"
LOG_CHANNEL(gui_log, "GUI"); LOG_CHANNEL(gui_log, "GUI");
@ -589,6 +591,24 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDis
idm::remove_verify<memory_viewer_handle>(id, handle_ptr); idm::remove_verify<memory_viewer_handle>(id, handle_ptr);
}); });
if (!g_fxo->try_get<memory_viewer_fxo>())
{
g_fxo->init<memory_viewer_fxo>();
}
auto& fxo = g_fxo->get<memory_viewer_fxo>();
fxo.last_opened[m_type] = this;
connect(this, &memory_viewer_panel::destroyed, this, [this]()
{
if (auto fxo = g_fxo->try_get<memory_viewer_fxo>())
{
auto it = fxo->last_opened.find(m_type);
if (it != fxo->last_opened.end() && it->second == this)
fxo->last_opened.erase(it);
}
});
} }
memory_viewer_panel::~memory_viewer_panel() memory_viewer_panel::~memory_viewer_panel()
@ -1244,3 +1264,43 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form
f_image_viewer->setFixedSize(f_image_viewer->sizeHint()); f_image_viewer->setFixedSize(f_image_viewer->sizeHint());
}); });
} }
void memory_viewer_panel::ShowAtPC(u32 pc, std::function<cpu_thread*()> func)
{
if (Emu.IsStopped())
return;
cpu_thread* cpu = func ? func() : nullptr;
thread_class type = cpu ? cpu->get_class() : thread_class::ppu;
if (type == thread_class::spu)
{
idm::make<memory_viewer_handle>(nullptr, nullptr, pc, std::move(func));
return;
}
if (const auto* fxo = g_fxo->try_get<memory_viewer_fxo>())
{
auto it = fxo->last_opened.find(type);
if (it != fxo->last_opened.end())
{
memory_viewer_panel* panel = it->second;
if (panel)
{
panel->SetPC(pc);
panel->scroll(0);
if (!panel->isVisible())
panel->show();
panel->raise();
return;
}
}
}
idm::make<memory_viewer_handle>(nullptr, nullptr, pc, std::move(func));
}

View file

@ -10,6 +10,7 @@
#include <QFontDatabase> #include <QFontDatabase>
#include <string> #include <string>
#include <map>
class QLineEdit; class QLineEdit;
class QCheckBox; class QCheckBox;
@ -55,6 +56,8 @@ public:
memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, u32 addr = 0, std::function<cpu_thread*()> func = []() -> cpu_thread* { return {}; }); memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDisAsm> disasm, u32 addr = 0, std::function<cpu_thread*()> func = []() -> cpu_thread* { return {}; });
~memory_viewer_panel(); ~memory_viewer_panel();
static void ShowAtPC(u32 pc, std::function<cpu_thread*()> func = nullptr);
enum class color_format : int enum class color_format : int
{ {
RGB, RGB,
@ -137,3 +140,12 @@ struct memory_viewer_handle
private: private:
const std::add_pointer_t<memory_viewer_panel> m_mvp; const std::add_pointer_t<memory_viewer_panel> m_mvp;
}; };
struct memory_viewer_fxo
{
std::map<thread_class, memory_viewer_panel*> last_opened;
memory_viewer_fxo() = default;
memory_viewer_fxo(const memory_viewer_fxo&) = delete;
memory_viewer_fxo& operator=(const memory_viewer_fxo&) = delete;
};