mirror of https://github.com/PCSX2/pcsx2.git
Merge cdc89aa574
into 32a67c48e0
This commit is contained in:
commit
f70384a591
|
@ -22,6 +22,7 @@ LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
|
|||
SDL=SDL2-2.30.11
|
||||
QT=6.8.1
|
||||
ZSTD=1.5.6
|
||||
KDDOCKWIDGETS=2.2.1
|
||||
|
||||
SHADERC=2024.1
|
||||
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
|
@ -49,6 +50,7 @@ eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADE
|
|||
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
8693e06abee0c88517d8480b22647702a51a0708f3c876ed5385d9a4e356e1a5 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
EOF
|
||||
|
||||
curl -L \
|
||||
|
@ -68,7 +70,8 @@ curl -L \
|
|||
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
|
||||
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
|
||||
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
|
||||
-o "KDDockWidgets-$KDDOCKWIDGETS.tar.gz" "https://github.com/KDAB/KDDockWidgets/archive/v$KDDOCKWIDGETS.tar.gz"
|
||||
|
||||
shasum -a 256 --check SHASUMS
|
||||
|
||||
|
@ -233,6 +236,15 @@ cmake --build . --parallel
|
|||
ninja install
|
||||
cd ../../
|
||||
|
||||
echo "Building KDDockWidgets..."
|
||||
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
|
||||
cd "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DKDDockWidgets_QT6=true -DKDDockWidgets_STATIC=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build -G Ninja
|
||||
cmake --build build --parallel
|
||||
ninja -C build install
|
||||
cd ..
|
||||
|
||||
echo "Building shaderc..."
|
||||
rm -fr "shaderc-$SHADERC"
|
||||
tar xf "shaderc-$SHADERC.tar.gz"
|
||||
|
|
|
@ -49,6 +49,7 @@ LIBWEBP=1.5.0
|
|||
FFMPEG=6.0
|
||||
MOLTENVK=1.2.9
|
||||
QT=6.7.2
|
||||
KDDOCKWIDGETS=2.2.1
|
||||
|
||||
SHADERC=2024.1
|
||||
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
|
@ -93,6 +94,7 @@ eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADE
|
|||
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
8693e06abee0c88517d8480b22647702a51a0708f3c876ed5385d9a4e356e1a5 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
EOF
|
||||
|
||||
curl -C - -L \
|
||||
|
@ -114,7 +116,8 @@ curl -C - -L \
|
|||
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
|
||||
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
|
||||
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
|
||||
-o "KDDockWidgets-$KDDOCKWIDGETS.tar.gz" "https://github.com/KDAB/KDDockWidgets/archive/v$KDDOCKWIDGETS.tar.gz"
|
||||
|
||||
shasum -a 256 --check SHASUMS
|
||||
|
||||
|
@ -355,6 +358,15 @@ make -C build "-j$NPROCS"
|
|||
make -C build install
|
||||
cd ..
|
||||
|
||||
echo "Building KDDockWidgets..."
|
||||
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
|
||||
cd "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DKDDockWidgets_QT6=true -DKDDockWidgets_STATIC=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
|
||||
cmake --build build --parallel
|
||||
cmake --install build
|
||||
cd ..
|
||||
|
||||
echo "Installing Qt Translations..."
|
||||
rm -fr "qttranslations-everywhere-src-$QT"
|
||||
tar xf "qttranslations-everywhere-src-$QT.tar.xz"
|
||||
|
|
|
@ -31,6 +31,7 @@ LIBWEBP=1.5.0
|
|||
FFMPEG=6.0
|
||||
MOLTENVK=1.2.9
|
||||
QT=6.7.2
|
||||
KDDOCKWIDGETS=2.2.1
|
||||
|
||||
SHADERC=2024.1
|
||||
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
|
@ -73,6 +74,7 @@ eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADE
|
|||
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
03ee1a2c06f3b61008478f4abe9423454e53e580b9488b47c8071547c6a9db47 shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
8693e06abee0c88517d8480b22647702a51a0708f3c876ed5385d9a4e356e1a5 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
EOF
|
||||
|
||||
curl -L \
|
||||
|
@ -94,7 +96,8 @@ curl -L \
|
|||
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
|
||||
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
|
||||
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz"
|
||||
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
|
||||
-o "KDDockWidgets-$KDDOCKWIDGETS.tar.gz" "https://github.com/KDAB/KDDockWidgets/archive/v$KDDOCKWIDGETS.tar.gz"
|
||||
|
||||
shasum -a 256 --check SHASUMS
|
||||
|
||||
|
@ -313,6 +316,15 @@ make -C build "-j$NPROCS"
|
|||
make -C build install
|
||||
cd ..
|
||||
|
||||
echo "Building KDDockWidgets..."
|
||||
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
|
||||
cd "KDDockWidgets-$KDDOCKWIDGETS"
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DKDDockWidgets_QT6=true -DKDDockWidgets_STATIC=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
|
||||
cmake --build build --parallel
|
||||
cmake --install build
|
||||
cd ..
|
||||
|
||||
echo "Installing Qt Translations..."
|
||||
rm -fr "qttranslations-everywhere-src-$QT"
|
||||
tar xf "qttranslations-everywhere-src-$QT.tar.xz"
|
||||
|
|
|
@ -54,6 +54,7 @@ set WEBP=1.5.0
|
|||
set ZLIB=1.3.1
|
||||
set ZLIBSHORT=131
|
||||
set ZSTD=1.5.6
|
||||
set KDDOCKWIDGETS=2.2.1
|
||||
|
||||
set SHADERC=2024.1
|
||||
set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
|
@ -75,6 +76,7 @@ call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt
|
|||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
|
||||
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
|
||||
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v2.2.1.zip" 78b5e242bf47476e150175b7de934ab84069459e151beb2d5ce84fd067138aa5 || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
|
||||
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
|
||||
|
@ -243,6 +245,15 @@ cmake --build . --parallel || goto error
|
|||
ninja install || goto error
|
||||
cd ..\.. || goto error
|
||||
|
||||
echo "Building KDDockWidgets..."
|
||||
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
|
||||
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
|
||||
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_STATIC=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build -G Ninja || goto error
|
||||
cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Building shaderc...
|
||||
rmdir /S /Q "shaderc-%SHADERC%"
|
||||
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
|
||||
|
|
|
@ -52,6 +52,7 @@ set WEBP=1.5.0
|
|||
set ZLIB=1.3.1
|
||||
set ZLIBSHORT=131
|
||||
set ZSTD=1.5.6
|
||||
set KDDOCKWIDGETS=2.2.1
|
||||
|
||||
set SHADERC=2024.1
|
||||
set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
|
@ -73,6 +74,7 @@ call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt
|
|||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
|
||||
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
|
||||
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v2.2.1.zip" 78b5e242bf47476e150175b7de934ab84069459e151beb2d5ce84fd067138aa5 || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
|
||||
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
|
||||
|
@ -247,6 +249,15 @@ cmake --build . --parallel || goto error
|
|||
ninja install || goto error
|
||||
cd ..\.. || goto error
|
||||
|
||||
echo "Building KDDockWidgets..."
|
||||
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
|
||||
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
|
||||
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_STATIC=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build -G Ninja || goto error
|
||||
cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Building shaderc...
|
||||
rmdir /S /Q "shaderc-%SHADERC%"
|
||||
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
|
||||
|
|
|
@ -145,6 +145,7 @@ if(MSVC AND NOT USE_CLANG_CL)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(FALSE)
|
||||
if(MSVC)
|
||||
# Disable RTTI
|
||||
string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
|
@ -158,6 +159,7 @@ else()
|
|||
"$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CONFIG_REL_NO_DEB $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>)
|
||||
set(CONFIG_ANY_REL $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>)
|
||||
|
|
|
@ -117,6 +117,9 @@ add_subdirectory(3rdparty/demangler EXCLUDE_FROM_ALL)
|
|||
# Symbol table parser.
|
||||
add_subdirectory(3rdparty/ccc EXCLUDE_FROM_ALL)
|
||||
|
||||
# The docking system for the debugger.
|
||||
find_package(KDDockWidgets-qt6 REQUIRED)
|
||||
|
||||
# Architecture-specific.
|
||||
if(_M_X86)
|
||||
add_subdirectory(3rdparty/zydis EXCLUDE_FROM_ALL)
|
||||
|
|
|
@ -158,37 +158,52 @@ target_sources(pcsx2-qt PRIVATE
|
|||
Debugger/AnalysisOptionsDialog.cpp
|
||||
Debugger/AnalysisOptionsDialog.h
|
||||
Debugger/AnalysisOptionsDialog.ui
|
||||
Debugger/CpuWidget.cpp
|
||||
Debugger/CpuWidget.h
|
||||
Debugger/CpuWidget.ui
|
||||
Debugger/DebuggerSettingsManager.cpp
|
||||
Debugger/DebuggerSettingsManager.h
|
||||
Debugger/DebuggerWidget.cpp
|
||||
Debugger/DebuggerWidget.h
|
||||
Debugger/DebuggerWindow.cpp
|
||||
Debugger/DebuggerWindow.h
|
||||
Debugger/DebuggerWindow.ui
|
||||
Debugger/DisassemblyWidget.cpp
|
||||
Debugger/DisassemblyWidget.h
|
||||
Debugger/DisassemblyWidget.ui
|
||||
Debugger/MemorySearchWidget.cpp
|
||||
Debugger/MemorySearchWidget.h
|
||||
Debugger/MemorySearchWidget.ui
|
||||
Debugger/MemoryViewWidget.cpp
|
||||
Debugger/MemoryViewWidget.h
|
||||
Debugger/MemoryViewWidget.ui
|
||||
Debugger/JsonValueWrapper.h
|
||||
Debugger/RegisterWidget.cpp
|
||||
Debugger/RegisterWidget.h
|
||||
Debugger/RegisterWidget.ui
|
||||
Debugger/BreakpointDialog.cpp
|
||||
Debugger/BreakpointDialog.h
|
||||
Debugger/BreakpointDialog.ui
|
||||
Debugger/Models/BreakpointModel.cpp
|
||||
Debugger/Models/BreakpointModel.h
|
||||
Debugger/Models/ThreadModel.cpp
|
||||
Debugger/Models/ThreadModel.h
|
||||
Debugger/Models/StackModel.cpp
|
||||
Debugger/Models/StackModel.h
|
||||
Debugger/Models/SavedAddressesModel.cpp
|
||||
Debugger/Models/SavedAddressesModel.h
|
||||
Debugger/StackModel.cpp
|
||||
Debugger/StackModel.h
|
||||
Debugger/StackWidget.cpp
|
||||
Debugger/StackWidget.h
|
||||
Debugger/ThreadModel.cpp
|
||||
Debugger/ThreadModel.h
|
||||
Debugger/ThreadWidget.cpp
|
||||
Debugger/ThreadWidget.h
|
||||
Debugger/Breakpoints/BreakpointDialog.cpp
|
||||
Debugger/Breakpoints/BreakpointDialog.h
|
||||
Debugger/Breakpoints/BreakpointDialog.ui
|
||||
Debugger/Breakpoints/BreakpointModel.cpp
|
||||
Debugger/Breakpoints/BreakpointModel.h
|
||||
Debugger/Breakpoints/BreakpointWidget.cpp
|
||||
Debugger/Breakpoints/BreakpointWidget.h
|
||||
Debugger/Breakpoints/BreakpointWidget.ui
|
||||
Debugger/Docking/DockManager.cpp
|
||||
Debugger/Docking/DockManager.h
|
||||
Debugger/Docking/LayoutEditorDialog.cpp
|
||||
Debugger/Docking/LayoutEditorDialog.h
|
||||
Debugger/Docking/LayoutEditorDialog.ui
|
||||
Debugger/Memory/MemorySearchWidget.cpp
|
||||
Debugger/Memory/MemorySearchWidget.h
|
||||
Debugger/Memory/MemorySearchWidget.ui
|
||||
Debugger/Memory/MemoryViewWidget.cpp
|
||||
Debugger/Memory/MemoryViewWidget.h
|
||||
Debugger/Memory/MemoryViewWidget.ui
|
||||
Debugger/Memory/SavedAddressesModel.cpp
|
||||
Debugger/Memory/SavedAddressesModel.h
|
||||
Debugger/Memory/SavedAddressesWidget.cpp
|
||||
Debugger/Memory/SavedAddressesWidget.h
|
||||
Debugger/Memory/SavedAddressesWidget.ui
|
||||
Debugger/SymbolTree/NewSymbolDialogs.cpp
|
||||
Debugger/SymbolTree/NewSymbolDialogs.h
|
||||
Debugger/SymbolTree/NewSymbolDialog.ui
|
||||
|
@ -232,6 +247,7 @@ target_link_libraries(pcsx2-qt PRIVATE
|
|||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
KDAB::kddockwidgets
|
||||
)
|
||||
|
||||
# Our Qt builds may have exceptions on, so force them off.
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
#include "ui_BreakpointDialog.h"
|
||||
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
#include "BreakpointModel.h"
|
||||
|
||||
#include "Models/BreakpointModel.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "BreakpointWidget.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
#include "Debugger/DebuggerSettingsManager.h"
|
||||
#include "BreakpointDialog.h"
|
||||
#include "BreakpointModel.h"
|
||||
|
||||
#include <QClipboard>
|
||||
|
||||
BreakpointWidget::BreakpointWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
, m_model(cpu)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &BreakpointWidget::onContextMenu);
|
||||
connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &BreakpointWidget::onDoubleClicked);
|
||||
|
||||
m_ui.breakpointList->setModel(&m_model);
|
||||
for (std::size_t i = 0; auto mode : BreakpointModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
|
||||
connect(&m_model, &BreakpointModel::dataChanged, &m_model, &BreakpointModel::refreshData);
|
||||
}
|
||||
|
||||
void BreakpointWidget::onDoubleClicked(const QModelIndex& index)
|
||||
{
|
||||
if (index.isValid() && index.column() == BreakpointModel::OFFSET)
|
||||
{
|
||||
not_yet_implemented();
|
||||
//m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_model.data(index, BreakpointModel::DataRole).toUInt());
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointWidget::onContextMenu(QPoint pos)
|
||||
{
|
||||
QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList);
|
||||
if (cpu().isAlive())
|
||||
{
|
||||
|
||||
QAction* newAction = new QAction(tr("New"), m_ui.breakpointList);
|
||||
connect(newAction, &QAction::triggered, this, &BreakpointWidget::contextNew);
|
||||
contextMenu->addAction(newAction);
|
||||
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (selModel->hasSelection())
|
||||
{
|
||||
QAction* editAction = new QAction(tr("Edit"), m_ui.breakpointList);
|
||||
connect(editAction, &QAction::triggered, this, &BreakpointWidget::contextEdit);
|
||||
contextMenu->addAction(editAction);
|
||||
|
||||
if (selModel->selectedIndexes().count() == 1)
|
||||
{
|
||||
QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList);
|
||||
connect(copyAction, &QAction::triggered, this, &BreakpointWidget::contextCopy);
|
||||
contextMenu->addAction(copyAction);
|
||||
}
|
||||
|
||||
QAction* deleteAction = new QAction(tr("Delete"), m_ui.breakpointList);
|
||||
connect(deleteAction, &QAction::triggered, this, &BreakpointWidget::contextDelete);
|
||||
contextMenu->addAction(deleteAction);
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu->addSeparator();
|
||||
if (m_model.rowCount() > 0)
|
||||
{
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
// It's important to use the Export Role here to allow pasting to be translation agnostic
|
||||
QGuiApplication::clipboard()->setText(
|
||||
QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(),
|
||||
BreakpointModel::ExportRole, true));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
}
|
||||
|
||||
if (cpu().isAlive())
|
||||
{
|
||||
QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList);
|
||||
connect(actionImport, &QAction::triggered, this, &BreakpointWidget::contextPasteCSV);
|
||||
contextMenu->addAction(actionImport);
|
||||
|
||||
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.breakpointList);
|
||||
connect(actionLoad, &QAction::triggered, [this]() {
|
||||
m_model.clear();
|
||||
DebuggerSettingsManager::loadGameSettings(&m_model);
|
||||
});
|
||||
contextMenu->addAction(actionLoad);
|
||||
|
||||
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.breakpointList);
|
||||
connect(actionSave, &QAction::triggered, this, &BreakpointWidget::saveBreakpointsToDebuggerSettings);
|
||||
contextMenu->addAction(actionSave);
|
||||
}
|
||||
|
||||
contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void BreakpointWidget::contextCopy()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_model.data(selModel->currentIndex()).toString());
|
||||
}
|
||||
|
||||
void BreakpointWidget::contextDelete()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QModelIndexList rows = selModel->selectedIndexes();
|
||||
|
||||
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) {
|
||||
return a.row() > b.row();
|
||||
});
|
||||
|
||||
for (const QModelIndex& index : rows)
|
||||
{
|
||||
m_model.removeRows(index.row(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointWidget::contextNew()
|
||||
{
|
||||
BreakpointDialog* bpDialog = new BreakpointDialog(this, &cpu(), m_model);
|
||||
bpDialog->show();
|
||||
}
|
||||
|
||||
void BreakpointWidget::contextEdit()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
const int selectedRow = selModel->selectedIndexes().first().row();
|
||||
|
||||
auto bpObject = m_model.at(selectedRow);
|
||||
|
||||
BreakpointDialog* bpDialog = new BreakpointDialog(this, &cpu(), m_model, bpObject, selectedRow);
|
||||
bpDialog->show();
|
||||
}
|
||||
|
||||
void BreakpointWidget::contextPasteCSV()
|
||||
{
|
||||
QString csv = QGuiApplication::clipboard()->text();
|
||||
// Skip header
|
||||
csv = csv.mid(csv.indexOf('\n') + 1);
|
||||
|
||||
for (const QString& line : csv.split('\n'))
|
||||
{
|
||||
QStringList fields;
|
||||
// In order to handle text with commas in them we must wrap values in quotes to mark
|
||||
// where a value starts and end so that text commas aren't identified as delimiters.
|
||||
// So matches each quote pair, parse it out, and removes the quotes to get the value.
|
||||
QRegularExpression eachQuotePair(R"("([^"]|\\.)*")");
|
||||
QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString matchedValue = match.captured(0);
|
||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||
}
|
||||
m_model.loadBreakpointFromFieldList(fields);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointWidget::saveBreakpointsToDebuggerSettings()
|
||||
{
|
||||
DebuggerSettingsManager::saveGameSettings(&m_model);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_BreakpointWidget.h"
|
||||
|
||||
#include "BreakpointModel.h"
|
||||
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/DisassemblyManager.h"
|
||||
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QTabBar>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
class BreakpointWidget : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BreakpointWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
|
||||
void onDoubleClicked(const QModelIndex& index);
|
||||
void onContextMenu(QPoint pos);
|
||||
|
||||
void contextCopy();
|
||||
void contextDelete();
|
||||
void contextNew();
|
||||
void contextEdit();
|
||||
void contextPasteCSV();
|
||||
|
||||
void saveBreakpointsToDebuggerSettings();
|
||||
|
||||
private:
|
||||
Ui::BreakpointWidget m_ui;
|
||||
|
||||
BreakpointModel m_model;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BreakpointWidget</class>
|
||||
<widget class="QWidget" name="BreakpointWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="breakpointList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,732 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "CpuWidget.h"
|
||||
|
||||
#include "DisassemblyWidget.h"
|
||||
#include "BreakpointDialog.h"
|
||||
#include "Models/BreakpointModel.h"
|
||||
#include "Models/ThreadModel.h"
|
||||
#include "Models/SavedAddressesModel.h"
|
||||
#include "Debugger/DebuggerSettingsManager.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
#include "DebugTools/MipsStackWalk.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QtCore/QFutureWatcher>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QRegularExpressionMatchIterator>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
|
||||
using namespace QtUtils;
|
||||
using namespace MipsStackWalk;
|
||||
|
||||
CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
|
||||
: m_cpu(cpu)
|
||||
, m_bpModel(cpu)
|
||||
, m_threadModel(cpu)
|
||||
, m_stackModel(cpu)
|
||||
, m_savedAddressesModel(cpu)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(g_emu_thread, &EmuThread::onVMPaused, this, &CpuWidget::onVMPaused);
|
||||
connect(g_emu_thread, &EmuThread::onGameChanged, this, [this](const QString& title) {
|
||||
if (title.isEmpty())
|
||||
return;
|
||||
// Don't overwrite users BPs/Saved Addresses unless they have a clean state.
|
||||
if (m_bpModel.rowCount() == 0)
|
||||
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||
if (m_savedAddressesModel.rowCount() == 0)
|
||||
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||
});
|
||||
|
||||
connect(m_ui.registerWidget, &RegisterWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
||||
connect(m_ui.memoryviewWidget, &MemoryViewWidget::gotoInDisasm, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddress);
|
||||
connect(m_ui.memoryviewWidget, &MemoryViewWidget::addToSavedAddresses, this, &CpuWidget::addAddressToSavedAddressesList);
|
||||
|
||||
connect(m_ui.registerWidget, &RegisterWidget::gotoInMemory, this, &CpuWidget::onGotoInMemory);
|
||||
connect(m_ui.disassemblyWidget, &DisassemblyWidget::gotoInMemory, this, &CpuWidget::onGotoInMemory);
|
||||
|
||||
connect(m_ui.memoryviewWidget, &MemoryViewWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets);
|
||||
connect(m_ui.registerWidget, &RegisterWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets);
|
||||
connect(m_ui.disassemblyWidget, &DisassemblyWidget::VMUpdate, this, &CpuWidget::reloadCPUWidgets);
|
||||
|
||||
connect(m_ui.disassemblyWidget, &DisassemblyWidget::breakpointsChanged, this, &CpuWidget::updateBreakpoints);
|
||||
|
||||
connect(m_ui.breakpointList, &QTableView::customContextMenuRequested, this, &CpuWidget::onBPListContextMenu);
|
||||
connect(m_ui.breakpointList, &QTableView::doubleClicked, this, &CpuWidget::onBPListDoubleClicked);
|
||||
|
||||
m_ui.breakpointList->setModel(&m_bpModel);
|
||||
for (std::size_t i = 0; auto mode : BreakpointModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.breakpointList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
|
||||
connect(&m_bpModel, &BreakpointModel::dataChanged, this, &CpuWidget::updateBreakpoints);
|
||||
|
||||
connect(m_ui.threadList, &QTableView::customContextMenuRequested, this, &CpuWidget::onThreadListContextMenu);
|
||||
connect(m_ui.threadList, &QTableView::doubleClicked, this, &CpuWidget::onThreadListDoubleClick);
|
||||
|
||||
m_threadProxyModel.setSourceModel(&m_threadModel);
|
||||
m_threadProxyModel.setSortRole(Qt::UserRole);
|
||||
m_ui.threadList->setModel(&m_threadProxyModel);
|
||||
m_ui.threadList->setSortingEnabled(true);
|
||||
m_ui.threadList->sortByColumn(ThreadModel::ThreadColumns::ID, Qt::SortOrder::AscendingOrder);
|
||||
for (std::size_t i = 0; auto mode : ThreadModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.threadList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
|
||||
connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu);
|
||||
connect(m_ui.stackList, &QTableView::doubleClicked, this, &CpuWidget::onStackListDoubleClick);
|
||||
|
||||
m_ui.stackList->setModel(&m_stackModel);
|
||||
for (std::size_t i = 0; auto mode : StackModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.stackList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
|
||||
m_ui.disassemblyWidget->SetCpu(&cpu);
|
||||
m_ui.registerWidget->SetCpu(&cpu);
|
||||
m_ui.memoryviewWidget->SetCpu(&cpu);
|
||||
|
||||
this->repaint();
|
||||
|
||||
m_ui.savedAddressesList->setModel(&m_savedAddressesModel);
|
||||
m_ui.savedAddressesList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_ui.savedAddressesList, &QTableView::customContextMenuRequested, this, &CpuWidget::onSavedAddressesListContextMenu);
|
||||
for (std::size_t i = 0; auto mode : SavedAddressesModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.savedAddressesList->horizontalHeader()->setSectionResizeMode(i++, mode);
|
||||
}
|
||||
QTableView* savedAddressesTableView = m_ui.savedAddressesList;
|
||||
connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) {
|
||||
savedAddressesTableView->resizeColumnToContents(topLeft.column());
|
||||
});
|
||||
|
||||
setupSymbolTrees();
|
||||
|
||||
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::addAddressToSavedAddressesList, this, &CpuWidget::addAddressToSavedAddressesList);
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView,
|
||||
[this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address, true); });
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInMemoryView, m_ui.memoryviewWidget, &MemoryViewWidget::gotoAddress);
|
||||
connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab,
|
||||
[this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); });
|
||||
m_ui.memorySearchWidget->setCpu(&m_cpu);
|
||||
|
||||
m_refreshDebuggerTimer.setInterval(1000);
|
||||
connect(&m_refreshDebuggerTimer, &QTimer::timeout, this, &CpuWidget::refreshDebugger);
|
||||
m_refreshDebuggerTimer.start();
|
||||
}
|
||||
|
||||
CpuWidget::~CpuWidget() = default;
|
||||
|
||||
void CpuWidget::setupSymbolTrees()
|
||||
{
|
||||
m_ui.tabFunctions->setLayout(new QVBoxLayout());
|
||||
m_ui.tabGlobalVariables->setLayout(new QVBoxLayout());
|
||||
m_ui.tabLocalVariables->setLayout(new QVBoxLayout());
|
||||
m_ui.tabParameterVariables->setLayout(new QVBoxLayout());
|
||||
|
||||
m_ui.tabFunctions->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_ui.tabGlobalVariables->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_ui.tabLocalVariables->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_ui.tabParameterVariables->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_function_tree = new FunctionTreeWidget(m_cpu);
|
||||
m_global_variable_tree = new GlobalVariableTreeWidget(m_cpu);
|
||||
m_local_variable_tree = new LocalVariableTreeWidget(m_cpu);
|
||||
m_parameter_variable_tree = new ParameterVariableTreeWidget(m_cpu);
|
||||
|
||||
m_function_tree->updateModel();
|
||||
m_global_variable_tree->updateModel();
|
||||
m_local_variable_tree->updateModel();
|
||||
m_parameter_variable_tree->updateModel();
|
||||
|
||||
m_ui.tabFunctions->layout()->addWidget(m_function_tree);
|
||||
m_ui.tabGlobalVariables->layout()->addWidget(m_global_variable_tree);
|
||||
m_ui.tabLocalVariables->layout()->addWidget(m_local_variable_tree);
|
||||
m_ui.tabParameterVariables->layout()->addWidget(m_parameter_variable_tree);
|
||||
|
||||
connect(m_function_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
connect(m_global_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
connect(m_local_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
connect(m_parameter_variable_tree, &SymbolTreeWidget::goToInDisassembly, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
|
||||
connect(m_function_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory);
|
||||
connect(m_global_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory);
|
||||
connect(m_local_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory);
|
||||
connect(m_parameter_variable_tree, &SymbolTreeWidget::goToInMemoryView, this, &CpuWidget::onGotoInMemory);
|
||||
|
||||
connect(m_function_tree, &SymbolTreeWidget::nameColumnClicked, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
connect(m_function_tree, &SymbolTreeWidget::locationColumnClicked, m_ui.disassemblyWidget, &DisassemblyWidget::gotoAddressAndSetFocus);
|
||||
}
|
||||
|
||||
void CpuWidget::refreshDebugger()
|
||||
{
|
||||
if (!m_cpu.isAlive())
|
||||
return;
|
||||
|
||||
m_ui.registerWidget->update();
|
||||
m_ui.disassemblyWidget->update();
|
||||
m_ui.memoryviewWidget->update();
|
||||
m_ui.memorySearchWidget->update();
|
||||
|
||||
m_function_tree->updateModel();
|
||||
m_global_variable_tree->updateModel();
|
||||
m_local_variable_tree->updateModel();
|
||||
m_parameter_variable_tree->updateModel();
|
||||
}
|
||||
|
||||
void CpuWidget::reloadCPUWidgets()
|
||||
{
|
||||
updateThreads();
|
||||
updateStackFrames();
|
||||
|
||||
m_ui.registerWidget->update();
|
||||
m_ui.disassemblyWidget->update();
|
||||
m_ui.memoryviewWidget->update();
|
||||
|
||||
m_function_tree->updateModel();
|
||||
m_global_variable_tree->updateModel();
|
||||
m_local_variable_tree->updateModel();
|
||||
m_parameter_variable_tree->updateModel();
|
||||
}
|
||||
|
||||
void CpuWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
m_ui.registerWidget->update();
|
||||
m_ui.disassemblyWidget->update();
|
||||
m_ui.memoryviewWidget->update();
|
||||
m_ui.memorySearchWidget->update();
|
||||
}
|
||||
|
||||
// The cpu shouldn't be alive when these are called
|
||||
// But make sure it isn't just in case
|
||||
void CpuWidget::onStepInto()
|
||||
{
|
||||
if (!m_cpu.isAlive() || !m_cpu.isCpuPaused())
|
||||
return;
|
||||
|
||||
// Allow the cpu to skip this pc if it is a breakpoint
|
||||
CBreakPoints::SetSkipFirst(m_cpu.getCpuType(), m_cpu.getPC());
|
||||
|
||||
const u32 pc = m_cpu.getPC();
|
||||
const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(&m_cpu, pc);
|
||||
|
||||
u32 bpAddr = pc + 0x4; // Default to the next instruction
|
||||
|
||||
if (info.isBranch)
|
||||
{
|
||||
if (!info.isConditional)
|
||||
{
|
||||
bpAddr = info.branchTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info.conditionMet)
|
||||
{
|
||||
bpAddr = info.branchTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
bpAddr = pc + (2 * 4); // Skip branch delay slot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.isSyscall)
|
||||
bpAddr = info.branchTarget; // Syscalls are always taken
|
||||
|
||||
Host::RunOnCPUThread([cpu = &m_cpu, bpAddr] {
|
||||
CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true);
|
||||
cpu->resumeCpu();
|
||||
});
|
||||
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void CpuWidget::onStepOut()
|
||||
{
|
||||
if (!m_cpu.isAlive() || !m_cpu.isCpuPaused())
|
||||
return;
|
||||
|
||||
// Allow the cpu to skip this pc if it is a breakpoint
|
||||
CBreakPoints::SetSkipFirst(m_cpu.getCpuType(), m_cpu.getPC());
|
||||
|
||||
if (m_stackModel.rowCount() < 2)
|
||||
return;
|
||||
|
||||
Host::RunOnCPUThread([cpu = &m_cpu, stackModel = &m_stackModel] {
|
||||
CBreakPoints::AddBreakPoint(cpu->getCpuType(), stackModel->data(stackModel->index(1, StackModel::PC), Qt::UserRole).toUInt(), true);
|
||||
cpu->resumeCpu();
|
||||
});
|
||||
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void CpuWidget::onStepOver()
|
||||
{
|
||||
if (!m_cpu.isAlive() || !m_cpu.isCpuPaused())
|
||||
return;
|
||||
|
||||
const u32 pc = m_cpu.getPC();
|
||||
const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(&m_cpu, pc);
|
||||
|
||||
u32 bpAddr = pc + 0x4; // Default to the next instruction
|
||||
|
||||
if (info.isBranch)
|
||||
{
|
||||
if (!info.isConditional)
|
||||
{
|
||||
if (info.isLinkedBranch) // jal, jalr
|
||||
{
|
||||
// it's a function call with a delay slot - skip that too
|
||||
bpAddr += 4;
|
||||
}
|
||||
else // j, ...
|
||||
{
|
||||
// in case of absolute branches, set the breakpoint at the branch target
|
||||
bpAddr = info.branchTarget;
|
||||
}
|
||||
}
|
||||
else // beq, ...
|
||||
{
|
||||
if (info.conditionMet)
|
||||
{
|
||||
bpAddr = info.branchTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
bpAddr = pc + (2 * 4); // Skip branch delay slot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Host::RunOnCPUThread([cpu = &m_cpu, bpAddr] {
|
||||
CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true);
|
||||
cpu->resumeCpu();
|
||||
});
|
||||
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void CpuWidget::onVMPaused()
|
||||
{
|
||||
// Stops us from telling the disassembly dialog to jump somwhere because breakpoint code paused the core.
|
||||
if (CBreakPoints::GetCorePaused())
|
||||
{
|
||||
CBreakPoints::SetCorePaused(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.disassemblyWidget->gotoProgramCounterOnPause();
|
||||
}
|
||||
|
||||
reloadCPUWidgets();
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void CpuWidget::updateBreakpoints()
|
||||
{
|
||||
m_bpModel.refreshData();
|
||||
}
|
||||
|
||||
void CpuWidget::onBPListDoubleClicked(const QModelIndex& index)
|
||||
{
|
||||
if (index.isValid())
|
||||
{
|
||||
if (index.column() == BreakpointModel::OFFSET)
|
||||
{
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_bpModel.data(index, BreakpointModel::DataRole).toUInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::onBPListContextMenu(QPoint pos)
|
||||
{
|
||||
QMenu* contextMenu = new QMenu(tr("Breakpoint List Context Menu"), m_ui.breakpointList);
|
||||
if (m_cpu.isAlive())
|
||||
{
|
||||
|
||||
QAction* newAction = new QAction(tr("New"), m_ui.breakpointList);
|
||||
connect(newAction, &QAction::triggered, this, &CpuWidget::contextBPListNew);
|
||||
contextMenu->addAction(newAction);
|
||||
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (selModel->hasSelection())
|
||||
{
|
||||
QAction* editAction = new QAction(tr("Edit"), m_ui.breakpointList);
|
||||
connect(editAction, &QAction::triggered, this, &CpuWidget::contextBPListEdit);
|
||||
contextMenu->addAction(editAction);
|
||||
|
||||
if (selModel->selectedIndexes().count() == 1)
|
||||
{
|
||||
QAction* copyAction = new QAction(tr("Copy"), m_ui.breakpointList);
|
||||
connect(copyAction, &QAction::triggered, this, &CpuWidget::contextBPListCopy);
|
||||
contextMenu->addAction(copyAction);
|
||||
}
|
||||
|
||||
QAction* deleteAction = new QAction(tr("Delete"), m_ui.breakpointList);
|
||||
connect(deleteAction, &QAction::triggered, this, &CpuWidget::contextBPListDelete);
|
||||
contextMenu->addAction(deleteAction);
|
||||
}
|
||||
}
|
||||
|
||||
contextMenu->addSeparator();
|
||||
if (m_bpModel.rowCount() > 0)
|
||||
{
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
// It's important to use the Export Role here to allow pasting to be translation agnostic
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), BreakpointModel::ExportRole, true));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
}
|
||||
|
||||
if (m_cpu.isAlive())
|
||||
{
|
||||
QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList);
|
||||
connect(actionImport, &QAction::triggered, this, &CpuWidget::contextBPListPasteCSV);
|
||||
contextMenu->addAction(actionImport);
|
||||
|
||||
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.breakpointList);
|
||||
connect(actionLoad, &QAction::triggered, [this]() {
|
||||
m_bpModel.clear();
|
||||
DebuggerSettingsManager::loadGameSettings(&m_bpModel);
|
||||
});
|
||||
contextMenu->addAction(actionLoad);
|
||||
|
||||
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.breakpointList);
|
||||
connect(actionSave, &QAction::triggered, this, &CpuWidget::saveBreakpointsToDebuggerSettings);
|
||||
contextMenu->addAction(actionSave);
|
||||
}
|
||||
|
||||
contextMenu->popup(m_ui.breakpointList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::onGotoInMemory(u32 address)
|
||||
{
|
||||
m_ui.memoryviewWidget->gotoAddress(address);
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListCopy()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_bpModel.data(selModel->currentIndex()).toString());
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListDelete()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QModelIndexList rows = selModel->selectedIndexes();
|
||||
|
||||
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) {
|
||||
return a.row() > b.row();
|
||||
});
|
||||
|
||||
for (const QModelIndex& index : rows)
|
||||
{
|
||||
m_bpModel.removeRows(index.row(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListNew()
|
||||
{
|
||||
BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel);
|
||||
bpDialog->show();
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListEdit()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.breakpointList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
const int selectedRow = selModel->selectedIndexes().first().row();
|
||||
|
||||
auto bpObject = m_bpModel.at(selectedRow);
|
||||
|
||||
BreakpointDialog* bpDialog = new BreakpointDialog(this, &m_cpu, m_bpModel, bpObject, selectedRow);
|
||||
bpDialog->show();
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListPasteCSV()
|
||||
{
|
||||
QString csv = QGuiApplication::clipboard()->text();
|
||||
// Skip header
|
||||
csv = csv.mid(csv.indexOf('\n') + 1);
|
||||
|
||||
for (const QString& line : csv.split('\n'))
|
||||
{
|
||||
QStringList fields;
|
||||
// In order to handle text with commas in them we must wrap values in quotes to mark
|
||||
// where a value starts and end so that text commas aren't identified as delimiters.
|
||||
// So matches each quote pair, parse it out, and removes the quotes to get the value.
|
||||
QRegularExpression eachQuotePair(R"("([^"]|\\.)*")");
|
||||
QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString matchedValue = match.captured(0);
|
||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||
}
|
||||
m_bpModel.loadBreakpointFromFieldList(fields);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::onSavedAddressesListContextMenu(QPoint pos)
|
||||
{
|
||||
QMenu* contextMenu = new QMenu("Saved Addresses List Context Menu", m_ui.savedAddressesList);
|
||||
|
||||
QAction* newAction = new QAction(tr("New"), m_ui.savedAddressesList);
|
||||
connect(newAction, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListNew);
|
||||
contextMenu->addAction(newAction);
|
||||
|
||||
const QModelIndex indexAtPos = m_ui.savedAddressesList->indexAt(pos);
|
||||
const bool isIndexValid = indexAtPos.isValid();
|
||||
|
||||
if (isIndexValid)
|
||||
{
|
||||
if (m_cpu.isAlive())
|
||||
{
|
||||
QAction* goToAddressMemViewAction = new QAction(tr("Go to in Memory View"), m_ui.savedAddressesList);
|
||||
connect(goToAddressMemViewAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
});
|
||||
contextMenu->addAction(goToAddressMemViewAction);
|
||||
|
||||
QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList);
|
||||
connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
});
|
||||
contextMenu->addAction(goToAddressDisassemblyAction);
|
||||
}
|
||||
|
||||
QAction* copyAction = new QAction(indexAtPos.column() == 0 ? tr("Copy Address") : tr("Copy Text"), m_ui.savedAddressesList);
|
||||
connect(copyAction, &QAction::triggered, [this, indexAtPos]() {
|
||||
QGuiApplication::clipboard()->setText(m_ui.savedAddressesList->model()->data(indexAtPos, Qt::DisplayRole).toString());
|
||||
});
|
||||
contextMenu->addAction(copyAction);
|
||||
}
|
||||
|
||||
if (m_ui.savedAddressesList->model()->rowCount() > 0)
|
||||
{
|
||||
QAction* actionExportCSV = new QAction(tr("Copy all as CSV"), m_ui.savedAddressesList);
|
||||
connect(actionExportCSV, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.savedAddressesList->model(), Qt::DisplayRole, true));
|
||||
});
|
||||
contextMenu->addAction(actionExportCSV);
|
||||
}
|
||||
|
||||
QAction* actionImportCSV = new QAction(tr("Paste from CSV"), m_ui.savedAddressesList);
|
||||
connect(actionImportCSV, &QAction::triggered, this, &CpuWidget::contextSavedAddressesListPasteCSV);
|
||||
contextMenu->addAction(actionImportCSV);
|
||||
|
||||
if (m_cpu.isAlive())
|
||||
{
|
||||
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.savedAddressesList);
|
||||
connect(actionLoad, &QAction::triggered, [this]() {
|
||||
m_savedAddressesModel.clear();
|
||||
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
|
||||
});
|
||||
contextMenu->addAction(actionLoad);
|
||||
|
||||
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.savedAddressesList);
|
||||
connect(actionSave, &QAction::triggered, this, &CpuWidget::saveSavedAddressesToDebuggerSettings);
|
||||
contextMenu->addAction(actionSave);
|
||||
}
|
||||
|
||||
if (isIndexValid)
|
||||
{
|
||||
QAction* deleteAction = new QAction(tr("Delete"), m_ui.savedAddressesList);
|
||||
connect(deleteAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
m_ui.savedAddressesList->model()->removeRows(indexAtPos.row(), 1);
|
||||
});
|
||||
contextMenu->addAction(deleteAction);
|
||||
}
|
||||
|
||||
contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::contextSavedAddressesListPasteCSV()
|
||||
{
|
||||
QString csv = QGuiApplication::clipboard()->text();
|
||||
// Skip header
|
||||
csv = csv.mid(csv.indexOf('\n') + 1);
|
||||
|
||||
for (const QString& line : csv.split('\n'))
|
||||
{
|
||||
QStringList fields;
|
||||
// In order to handle text with commas in them we must wrap values in quotes to mark
|
||||
// where a value starts and end so that text commas aren't identified as delimiters.
|
||||
// So matches each quote pair, parse it out, and removes the quotes to get the value.
|
||||
QRegularExpression eachQuotePair(R"("([^"]|\\.)*")");
|
||||
QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString matchedValue = match.captured(0);
|
||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||
}
|
||||
|
||||
m_savedAddressesModel.loadSavedAddressFromFieldList(fields);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::contextSavedAddressesListNew()
|
||||
{
|
||||
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
|
||||
const u32 rowCount = m_ui.savedAddressesList->model()->rowCount();
|
||||
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 0));
|
||||
}
|
||||
|
||||
void CpuWidget::addAddressToSavedAddressesList(u32 address)
|
||||
{
|
||||
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
|
||||
const u32 rowCount = m_ui.savedAddressesList->model()->rowCount();
|
||||
const QModelIndex addressIndex = m_ui.savedAddressesList->model()->index(rowCount - 1, 0);
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_savedaddresses);
|
||||
m_ui.savedAddressesList->model()->setData(addressIndex, address, Qt::UserRole);
|
||||
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1));
|
||||
}
|
||||
|
||||
void CpuWidget::updateThreads()
|
||||
{
|
||||
m_threadModel.refreshData();
|
||||
}
|
||||
|
||||
void CpuWidget::onThreadListContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_ui.threadList->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QMenu* contextMenu = new QMenu(tr("Thread List Context Menu"), m_ui.threadList);
|
||||
|
||||
QAction* actionCopy = new QAction(tr("Copy"), m_ui.threadList);
|
||||
connect(actionCopy, &QAction::triggered, [this]() {
|
||||
const auto* selModel = m_ui.threadList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_ui.threadList->model()->data(selModel->currentIndex()).toString());
|
||||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.threadList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.threadList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.threadList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::onThreadListDoubleClick(const QModelIndex& index)
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case ThreadModel::ThreadColumns::ENTRY:
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.threadList->model()->data(index, Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
break;
|
||||
default: // Default to PC
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::updateStackFrames()
|
||||
{
|
||||
m_stackModel.refreshData();
|
||||
}
|
||||
|
||||
void CpuWidget::onStackListContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_ui.stackList->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList);
|
||||
|
||||
QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList);
|
||||
connect(actionCopy, &QAction::triggered, [this]() {
|
||||
const auto* selModel = m_ui.stackList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString());
|
||||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.stackList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.stackList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.stackList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case StackModel::StackModel::ENTRY:
|
||||
case StackModel::StackModel::ENTRY_LABEL:
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
|
||||
break;
|
||||
case StackModel::StackModel::SP:
|
||||
m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt());
|
||||
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
break;
|
||||
default: // Default to PC
|
||||
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::saveBreakpointsToDebuggerSettings()
|
||||
{
|
||||
DebuggerSettingsManager::saveGameSettings(&m_bpModel);
|
||||
}
|
||||
|
||||
void CpuWidget::saveSavedAddressesToDebuggerSettings()
|
||||
{
|
||||
DebuggerSettingsManager::saveGameSettings(&m_savedAddressesModel);
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_CpuWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include "Models/BreakpointModel.h"
|
||||
#include "Models/ThreadModel.h"
|
||||
#include "Models/StackModel.h"
|
||||
#include "Models/SavedAddressesModel.h"
|
||||
#include "Debugger/SymbolTree/SymbolTreeWidgets.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtWidgets/QTableWidget>
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace MipsStackWalk;
|
||||
|
||||
class CpuWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CpuWidget(QWidget* parent, DebugInterface& cpu);
|
||||
~CpuWidget();
|
||||
|
||||
public slots:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
|
||||
void onStepInto();
|
||||
void onStepOver();
|
||||
void onStepOut();
|
||||
|
||||
void onVMPaused();
|
||||
|
||||
void updateBreakpoints();
|
||||
void onBPListDoubleClicked(const QModelIndex& index);
|
||||
void onBPListContextMenu(QPoint pos);
|
||||
void onGotoInMemory(u32 address);
|
||||
|
||||
void contextBPListCopy();
|
||||
void contextBPListDelete();
|
||||
void contextBPListNew();
|
||||
void contextBPListEdit();
|
||||
void contextBPListPasteCSV();
|
||||
|
||||
void onSavedAddressesListContextMenu(QPoint pos);
|
||||
void contextSavedAddressesListPasteCSV();
|
||||
void contextSavedAddressesListNew();
|
||||
void addAddressToSavedAddressesList(u32 address);
|
||||
|
||||
void updateThreads();
|
||||
void onThreadListDoubleClick(const QModelIndex& index);
|
||||
void onThreadListContextMenu(QPoint pos);
|
||||
|
||||
void updateStackFrames();
|
||||
void onStackListContextMenu(QPoint pos);
|
||||
void onStackListDoubleClick(const QModelIndex& index);
|
||||
|
||||
void refreshDebugger();
|
||||
void reloadCPUWidgets();
|
||||
|
||||
void saveBreakpointsToDebuggerSettings();
|
||||
void saveSavedAddressesToDebuggerSettings();
|
||||
|
||||
private:
|
||||
void setupSymbolTrees();
|
||||
|
||||
std::vector<QTableWidget*> m_registerTableViews;
|
||||
|
||||
QMenu* m_stacklistContextMenu = 0;
|
||||
QMenu* m_funclistContextMenu = 0;
|
||||
QMenu* m_moduleTreeContextMenu = 0;
|
||||
QTimer m_refreshDebuggerTimer;
|
||||
|
||||
Ui::CpuWidget m_ui;
|
||||
|
||||
DebugInterface& m_cpu;
|
||||
|
||||
BreakpointModel m_bpModel;
|
||||
ThreadModel m_threadModel;
|
||||
QSortFilterProxyModel m_threadProxyModel;
|
||||
StackModel m_stackModel;
|
||||
SavedAddressesModel m_savedAddressesModel;
|
||||
|
||||
FunctionTreeWidget* m_function_tree = nullptr;
|
||||
GlobalVariableTreeWidget* m_global_variable_tree = nullptr;
|
||||
LocalVariableTreeWidget* m_local_variable_tree = nullptr;
|
||||
ParameterVariableTreeWidget* m_parameter_variable_tree = nullptr;
|
||||
};
|
|
@ -1,445 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CpuWidget</class>
|
||||
<widget class="QWidget" name="CpuWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>668</width>
|
||||
<height>563</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QSplitter" name="verticalSplitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QSplitter" name="horizontalSplitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QTabWidget" name="tabWidgetRegFunc">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabRegisters">
|
||||
<attribute name="title">
|
||||
<string>Registers</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="registerLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="RegisterWidget" name="registerWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabFunctions">
|
||||
<attribute name="title">
|
||||
<string>Functions</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabMemorySearch">
|
||||
<attribute name="title">
|
||||
<string>Memory Search</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="memorySearchLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="MemorySearchWidget" name="memorySearchWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="DisassemblyWidget" name="disassemblyWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::North</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_memory">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Memory</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="memoryLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="MemoryViewWidget" name="memoryviewWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_breakpoints">
|
||||
<attribute name="title">
|
||||
<string>Breakpoints</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="breakpointsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="breakpointList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_threads">
|
||||
<attribute name="title">
|
||||
<string>Threads</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="threadsLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="threadList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_callstack">
|
||||
<attribute name="title">
|
||||
<string>Active Call Stack</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="callStackLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="stackList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_savedaddresses">
|
||||
<attribute name="title">
|
||||
<string>Saved Addresses</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="savedAddressesLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="savedAddressesList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabGlobalVariables">
|
||||
<attribute name="title">
|
||||
<string>Globals</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabLocalVariables">
|
||||
<attribute name="title">
|
||||
<string>Locals</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabParameterVariables">
|
||||
<attribute name="title">
|
||||
<string>Parameters</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DisassemblyWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pcsx2-qt/Debugger/DisassemblyWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RegisterWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pcsx2-qt/Debugger/RegisterWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MemoryViewWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pcsx2-qt/Debugger/MemoryViewWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MemorySearchWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pcsx2-qt/Debugger/MemorySearchWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
#include "common/Console.h"
|
||||
#include "VMManager.h"
|
||||
#include "Models/BreakpointModel.h"
|
||||
|
||||
std::mutex DebuggerSettingsManager::writeLock;
|
||||
const QString DebuggerSettingsManager::settingsFileVersion = "0.00";
|
||||
|
||||
QJsonObject DebuggerSettingsManager::loadGameSettingsJSON() {
|
||||
QJsonObject DebuggerSettingsManager::loadGameSettingsJSON()
|
||||
{
|
||||
std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||
QFile file(QString::fromStdString(path));
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
|
@ -134,7 +134,7 @@ void DebuggerSettingsManager::saveGameSettings(QAbstractTableModel* abstractTabl
|
|||
{
|
||||
const std::string path = VMManager::GetDebuggerSettingsFilePathForCurrentGame();
|
||||
if (path.empty())
|
||||
return;
|
||||
return;
|
||||
|
||||
const std::lock_guard<std::mutex> lock(writeLock);
|
||||
QJsonObject loadedSettings = loadGameSettingsJSON();
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#include <mutex>
|
||||
|
||||
#include "Models/BreakpointModel.h"
|
||||
#include "Models/SavedAddressesModel.h"
|
||||
#include "Breakpoints/BreakpointModel.h"
|
||||
#include "Memory/SavedAddressesModel.h"
|
||||
|
||||
class DebuggerSettingsManager final
|
||||
{
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "DebuggerWidget.h"
|
||||
|
||||
#include "JsonValueWrapper.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
DebugInterface& DebuggerWidget::cpu() const
|
||||
{
|
||||
pxAssertRel(m_cpu, "DebuggerWidget::cpu() called on object that doesn't have a CPU type set.");
|
||||
return *m_cpu;
|
||||
}
|
||||
|
||||
void DebuggerWidget::setCpu(DebugInterface* cpu)
|
||||
{
|
||||
m_cpu = cpu;
|
||||
}
|
||||
|
||||
void DebuggerWidget::toJson(JsonValueWrapper& json)
|
||||
{
|
||||
rapidjson::Value cpu_name;
|
||||
switch (cpu().getCpuType())
|
||||
{
|
||||
case BREAKPOINT_EE:
|
||||
cpu_name.SetString("EE");
|
||||
break;
|
||||
case BREAKPOINT_IOP:
|
||||
cpu_name.SetString("IOP");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
json.value().AddMember("cpu", cpu_name, json.allocator());
|
||||
}
|
||||
|
||||
void DebuggerWidget::fromJson(JsonValueWrapper& json)
|
||||
{
|
||||
}
|
||||
|
||||
void DebuggerWidget::applyMonospaceFont()
|
||||
{
|
||||
// Easiest way to handle cross platform monospace fonts
|
||||
// There are issues related to TabWidget -> Children font inheritance otherwise
|
||||
#if defined(WIN32)
|
||||
setStyleSheet(QStringLiteral("font: 10pt 'Lucida Console'"));
|
||||
#elif defined(__APPLE__)
|
||||
setStyleSheet(QStringLiteral("font: 10pt 'Monaco'"));
|
||||
#else
|
||||
setStyleSheet(QStringLiteral("font: 10pt 'Monospace'"));
|
||||
#endif
|
||||
}
|
||||
|
||||
DebuggerWidget::DebuggerWidget(DebugInterface* cpu, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_cpu(cpu)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
inline void not_yet_implemented()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
class JsonValueWrapper;
|
||||
|
||||
class DebuggerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DebugInterface& cpu() const;
|
||||
void setCpu(DebugInterface* cpu);
|
||||
|
||||
virtual void toJson(JsonValueWrapper& json);
|
||||
virtual void fromJson(JsonValueWrapper& json);
|
||||
|
||||
void applyMonospaceFont();
|
||||
|
||||
size_t widget_description_index = SIZE_MAX;
|
||||
QString unique_name;
|
||||
|
||||
protected:
|
||||
DebuggerWidget(DebugInterface* cpu, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
DebugInterface* m_cpu;
|
||||
};
|
|
@ -12,20 +12,11 @@
|
|||
#include "AnalysisOptionsDialog.h"
|
||||
|
||||
DebuggerWindow::DebuggerWindow(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
: KDDockWidgets::QtWidgets::MainWindow(QStringLiteral("DebuggerWindow"), {}, parent)
|
||||
, m_dock_manager(this)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
// Easiest way to handle cross platform monospace fonts
|
||||
// There are issues related to TabWidget -> Children font inheritance otherwise
|
||||
#if defined(WIN32)
|
||||
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 8pt 'Lucida Console'"));
|
||||
#elif defined(__APPLE__)
|
||||
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 10pt 'Monaco'"));
|
||||
#else
|
||||
m_ui.cpuTabs->setStyleSheet(QStringLiteral("font: 8pt 'Monospace'"));
|
||||
#endif
|
||||
|
||||
connect(m_ui.actionRun, &QAction::triggered, this, &DebuggerWindow::onRunPause);
|
||||
connect(m_ui.actionStepInto, &QAction::triggered, this, &DebuggerWindow::onStepInto);
|
||||
connect(m_ui.actionStepOver, &QAction::triggered, this, &DebuggerWindow::onStepOver);
|
||||
|
@ -39,15 +30,26 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
|
|||
onVMStateChanged(); // If we missed a state change while we weren't loaded
|
||||
|
||||
// We can't do this in the designer, but we want to right align the actionOnTop action in the toolbar
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_ui.toolBar->insertWidget(m_ui.actionAnalyse, spacer);
|
||||
//QWidget* spacer = new QWidget(this);
|
||||
//spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
//m_ui.toolBar->insertWidget(m_ui.actionAnalyse, spacer);
|
||||
|
||||
m_cpuWidget_r5900 = new CpuWidget(this, r5900Debug);
|
||||
m_cpuWidget_r3000 = new CpuWidget(this, r3000Debug);
|
||||
//m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900");
|
||||
//m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000");
|
||||
|
||||
m_ui.cpuTabs->addTab(m_cpuWidget_r5900, "R5900");
|
||||
m_ui.cpuTabs->addTab(m_cpuWidget_r3000, "R3000");
|
||||
m_dock_manager.switchToLayout(0);
|
||||
|
||||
//QTabBar* tabs = new QTabBar();
|
||||
//tabs->addTab("Test");
|
||||
//m_ui.menuBar->layout()->addWidget(tabs);
|
||||
|
||||
QMenuBar* menu_bar = menuBar();
|
||||
|
||||
setMenuWidget(m_dock_manager.createLayoutSwitcher(menu_bar));
|
||||
|
||||
connect(m_ui.menuWindows, &QMenu::aboutToShow, this, [this]() {
|
||||
m_dock_manager.createWindowsMenu(m_ui.menuWindows);
|
||||
});
|
||||
}
|
||||
|
||||
DebuggerWindow::~DebuggerWindow() = default;
|
||||
|
@ -56,8 +58,8 @@ DebuggerWindow::~DebuggerWindow() = default;
|
|||
// Sorry colour blind people, but this is the best we can do for now
|
||||
void DebuggerWindow::setTabActiveStyle(BreakPointCpu enabledCpu)
|
||||
{
|
||||
m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r5900), (enabledCpu == BREAKPOINT_EE) ? Qt::red : this->palette().text().color());
|
||||
m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r3000), (enabledCpu == BREAKPOINT_IOP) ? Qt::red : this->palette().text().color());
|
||||
//m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r5900), (enabledCpu == BREAKPOINT_EE) ? Qt::red : this->palette().text().color());
|
||||
//m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r3000), (enabledCpu == BREAKPOINT_IOP) ? Qt::red : this->palette().text().color());
|
||||
}
|
||||
|
||||
void DebuggerWindow::onVMStateChanged()
|
||||
|
@ -87,10 +89,10 @@ void DebuggerWindow::onVMStateChanged()
|
|||
switch (triggeredCpu)
|
||||
{
|
||||
case BREAKPOINT_EE:
|
||||
m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900);
|
||||
//m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900);
|
||||
break;
|
||||
case BREAKPOINT_IOP:
|
||||
m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000);
|
||||
//m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -115,20 +117,20 @@ void DebuggerWindow::onRunPause()
|
|||
|
||||
void DebuggerWindow::onStepInto()
|
||||
{
|
||||
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
currentCpu->onStepInto();
|
||||
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
//currentCpu->onStepInto();
|
||||
}
|
||||
|
||||
void DebuggerWindow::onStepOver()
|
||||
{
|
||||
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
currentCpu->onStepOver();
|
||||
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
//currentCpu->onStepOver();
|
||||
}
|
||||
|
||||
void DebuggerWindow::onStepOut()
|
||||
{
|
||||
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
currentCpu->onStepOut();
|
||||
//CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
//currentCpu->onStepOut();
|
||||
}
|
||||
|
||||
void DebuggerWindow::onAnalyse()
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "CpuWidget.h"
|
||||
|
||||
#include "ui_DebuggerWindow.h"
|
||||
|
||||
class DebuggerWindow : public QMainWindow
|
||||
#include "Docking/DockManager.h"
|
||||
|
||||
#include <kddockwidgets/MainWindow.h>
|
||||
|
||||
class DebuggerWindow : public KDDockWidgets::QtWidgets::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -25,7 +27,7 @@ public slots:
|
|||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event);
|
||||
void hideEvent(QHideEvent *event);
|
||||
void hideEvent(QHideEvent* event);
|
||||
|
||||
private:
|
||||
Ui::DebuggerWindow m_ui;
|
||||
|
@ -34,8 +36,7 @@ private:
|
|||
QAction* m_actionStepOver;
|
||||
QAction* m_actionStepOut;
|
||||
|
||||
CpuWidget* m_cpuWidget_r5900;
|
||||
CpuWidget* m_cpuWidget_r3000;
|
||||
DockManager m_dock_manager;
|
||||
|
||||
void setTabActiveStyle(BreakPointCpu toggledCPU);
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
<width>1000</width>
|
||||
<height>750</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -18,19 +18,12 @@
|
|||
<normalon>:/icons/AppIcon64.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="cpuTabs"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<widget class="QToolBar" name="debugToolBar">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::PreventContextMenu</enum>
|
||||
<enum>Qt::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
|
@ -39,10 +32,10 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
|
@ -54,7 +47,57 @@
|
|||
<addaction name="actionStepInto"/>
|
||||
<addaction name="actionStepOver"/>
|
||||
<addaction name="actionStepOut"/>
|
||||
<addaction name="actionAnalyse"/>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1000</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionAnalyse"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDebug">
|
||||
<property name="title">
|
||||
<string>Debug</string>
|
||||
</property>
|
||||
<addaction name="actionRun"/>
|
||||
<addaction name="actionStepInto"/>
|
||||
<addaction name="actionStepOver"/>
|
||||
<addaction name="actionStepOut"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindows">
|
||||
<property name="title">
|
||||
<string>Windows</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionOnTop"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuDebug"/>
|
||||
<addaction name="menuWindows"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="viewToolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionOnTop"/>
|
||||
</widget>
|
||||
<action name="actionRun">
|
||||
|
|
|
@ -19,12 +19,16 @@
|
|||
|
||||
using namespace QtUtils;
|
||||
|
||||
DisassemblyWidget::DisassemblyWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
DisassemblyWidget::DisassemblyWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_disassemblyManager.setCpu(&cpu);
|
||||
|
||||
connect(this, &DisassemblyWidget::customContextMenuRequested, this, &DisassemblyWidget::customMenuRequested);
|
||||
|
||||
applyMonospaceFont();
|
||||
}
|
||||
|
||||
DisassemblyWidget::~DisassemblyWidget() = default;
|
||||
|
@ -46,7 +50,7 @@ void DisassemblyWidget::contextCopyInstructionText()
|
|||
|
||||
void DisassemblyWidget::contextAssembleInstruction()
|
||||
{
|
||||
if (!m_cpu->isCpuPaused())
|
||||
if (!cpu().isCpuPaused())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running"));
|
||||
return;
|
||||
|
@ -63,7 +67,7 @@ void DisassemblyWidget::contextAssembleInstruction()
|
|||
|
||||
u32 encodedInstruction;
|
||||
std::string errorText;
|
||||
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), m_cpu, m_selectedAddressStart, encodedInstruction, errorText);
|
||||
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), &cpu(), m_selectedAddressStart, encodedInstruction, errorText);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
|
@ -72,7 +76,7 @@ void DisassemblyWidget::contextAssembleInstruction()
|
|||
}
|
||||
else
|
||||
{
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu, val = encodedInstruction] {
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu(), val = encodedInstruction] {
|
||||
for (u32 i = start; i <= end; i += 4)
|
||||
{
|
||||
this->m_nopedInstructions.insert({i, cpu->read32(i)});
|
||||
|
@ -85,7 +89,7 @@ void DisassemblyWidget::contextAssembleInstruction()
|
|||
|
||||
void DisassemblyWidget::contextNoopInstruction()
|
||||
{
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] {
|
||||
for (u32 i = start; i <= end; i += 4)
|
||||
{
|
||||
this->m_nopedInstructions.insert({i, cpu->read32(i)});
|
||||
|
@ -97,7 +101,7 @@ void DisassemblyWidget::contextNoopInstruction()
|
|||
|
||||
void DisassemblyWidget::contextRestoreInstruction()
|
||||
{
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = m_cpu] {
|
||||
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] {
|
||||
for (u32 i = start; i <= end; i += 4)
|
||||
{
|
||||
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
|
||||
|
@ -113,7 +117,7 @@ void DisassemblyWidget::contextRestoreInstruction()
|
|||
void DisassemblyWidget::contextRunToCursor()
|
||||
{
|
||||
const u32 selectedAddressStart = m_selectedAddressStart;
|
||||
Host::RunOnCPUThread([cpu = m_cpu, selectedAddressStart] {
|
||||
Host::RunOnCPUThread([cpu = &cpu(), selectedAddressStart] {
|
||||
CBreakPoints::AddBreakPoint(cpu->getCpuType(), selectedAddressStart, true);
|
||||
cpu->resumeCpu();
|
||||
});
|
||||
|
@ -121,17 +125,17 @@ void DisassemblyWidget::contextRunToCursor()
|
|||
|
||||
void DisassemblyWidget::contextJumpToCursor()
|
||||
{
|
||||
m_cpu->setPc(m_selectedAddressStart);
|
||||
cpu().setPc(m_selectedAddressStart);
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextToggleBreakpoint()
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
const u32 selectedAddressStart = m_selectedAddressStart;
|
||||
const BreakPointCpu cpuType = m_cpu->getCpuType();
|
||||
const BreakPointCpu cpuType = cpu().getCpuType();
|
||||
if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddressStart))
|
||||
{
|
||||
Host::RunOnCPUThread([cpuType, selectedAddressStart] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddressStart); });
|
||||
|
@ -171,7 +175,7 @@ void DisassemblyWidget::contextGoToAddress()
|
|||
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
||||
|
@ -182,7 +186,7 @@ void DisassemblyWidget::contextGoToAddress()
|
|||
|
||||
void DisassemblyWidget::contextAddFunction()
|
||||
{
|
||||
NewFunctionDialog* dialog = new NewFunctionDialog(*m_cpu, this);
|
||||
NewFunctionDialog* dialog = new NewFunctionDialog(cpu(), this);
|
||||
dialog->setName(QString("func_%1").arg(m_selectedAddressStart, 8, 16, QChar('0')));
|
||||
dialog->setAddress(m_selectedAddressStart);
|
||||
if (m_selectedAddressEnd != m_selectedAddressStart)
|
||||
|
@ -193,13 +197,13 @@ void DisassemblyWidget::contextAddFunction()
|
|||
|
||||
void DisassemblyWidget::contextCopyFunctionName()
|
||||
{
|
||||
std::string name = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name;
|
||||
std::string name = cpu().GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name;
|
||||
QGuiApplication::clipboard()->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextRemoveFunction()
|
||||
{
|
||||
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ccc::Function* curFunc = database.functions.symbol_overlapping_address(m_selectedAddressStart);
|
||||
if (!curFunc)
|
||||
return;
|
||||
|
@ -215,7 +219,7 @@ void DisassemblyWidget::contextRemoveFunction()
|
|||
|
||||
void DisassemblyWidget::contextRenameFunction()
|
||||
{
|
||||
const FunctionInfo curFunc = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
const FunctionInfo curFunc = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
|
||||
if (!curFunc.address.valid())
|
||||
{
|
||||
|
@ -236,17 +240,17 @@ void DisassemblyWidget::contextRenameFunction()
|
|||
return;
|
||||
}
|
||||
|
||||
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.functions.rename_symbol(curFunc.handle, newName.toStdString());
|
||||
});
|
||||
}
|
||||
|
||||
void DisassemblyWidget::contextStubFunction()
|
||||
{
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
FunctionInfo function = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
u32 address = function.address.valid() ? function.address.value : m_selectedAddressStart;
|
||||
|
||||
Host::RunOnCPUThread([this, address, cpu = m_cpu] {
|
||||
Host::RunOnCPUThread([this, address, cpu = &cpu()] {
|
||||
this->m_stubbedFunctions.insert({address, {cpu->read32(address), cpu->read32(address + 4)}});
|
||||
cpu->write32(address, 0x03E00008); // jr ra
|
||||
cpu->write32(address + 4, 0x00000000); // nop
|
||||
|
@ -257,7 +261,7 @@ void DisassemblyWidget::contextStubFunction()
|
|||
void DisassemblyWidget::contextRestoreFunction()
|
||||
{
|
||||
u32 address = m_selectedAddressStart;
|
||||
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
|
||||
cpu().GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
|
||||
const ccc::Function* function = database.functions.symbol_overlapping_address(m_selectedAddressStart);
|
||||
if (function)
|
||||
address = function->address().value;
|
||||
|
@ -266,7 +270,7 @@ void DisassemblyWidget::contextRestoreFunction()
|
|||
auto stub = m_stubbedFunctions.find(address);
|
||||
if (stub != m_stubbedFunctions.end())
|
||||
{
|
||||
Host::RunOnCPUThread([this, address, cpu = m_cpu, stub] {
|
||||
Host::RunOnCPUThread([this, address, cpu = &cpu(), stub] {
|
||||
auto [first_instruction, second_instruction] = stub->second;
|
||||
cpu->write32(address, first_instruction);
|
||||
cpu->write32(address + 4, second_instruction);
|
||||
|
@ -286,12 +290,6 @@ void DisassemblyWidget::contextShowOpcode()
|
|||
this->repaint();
|
||||
}
|
||||
|
||||
void DisassemblyWidget::SetCpu(DebugInterface* cpu)
|
||||
{
|
||||
m_cpu = cpu;
|
||||
m_disassemblyManager.setCpu(cpu);
|
||||
}
|
||||
|
||||
QString DisassemblyWidget::GetLineDisasm(u32 address)
|
||||
{
|
||||
DisassemblyLineInfo lineInfo;
|
||||
|
@ -322,7 +320,7 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event)
|
|||
bool inSelectionBlock = false;
|
||||
bool alternate = m_visibleStart % 8;
|
||||
|
||||
const u32 curPC = m_cpu->getPC(); // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs
|
||||
const u32 curPC = cpu().getPC(); // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs
|
||||
|
||||
for (u32 i = 0; i <= m_visibleRows; i++)
|
||||
{
|
||||
|
@ -347,7 +345,7 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event)
|
|||
|
||||
// Breakpoint marker
|
||||
bool enabled;
|
||||
if (CBreakPoints::IsAddressBreakPoint(m_cpu->getCpuType(), rowAddress, &enabled) && !CBreakPoints::IsTempBreakPoint(m_cpu->getCpuType(), rowAddress))
|
||||
if (CBreakPoints::IsAddressBreakPoint(cpu().getCpuType(), rowAddress, &enabled) && !CBreakPoints::IsTempBreakPoint(cpu().getCpuType(), rowAddress))
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
|
@ -506,11 +504,11 @@ void DisassemblyWidget::mousePressEvent(QMouseEvent* event)
|
|||
|
||||
void DisassemblyWidget::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
const u32 selectedAddress = (static_cast<int>(event->position().y()) / m_rowHeight * 4) + m_visibleStart;
|
||||
const BreakPointCpu cpuType = m_cpu->getCpuType();
|
||||
const BreakPointCpu cpuType = cpu().getCpuType();
|
||||
if (CBreakPoints::IsAddressBreakPoint(cpuType, selectedAddress))
|
||||
{
|
||||
Host::RunOnCPUThread([cpuType, selectedAddress] { CBreakPoints::RemoveBreakPoint(cpuType, selectedAddress); });
|
||||
|
@ -598,7 +596,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event)
|
|||
contextFollowBranch();
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
gotoAddressAndSetFocus(m_cpu->getPC());
|
||||
gotoAddressAndSetFocus(cpu().getPC());
|
||||
break;
|
||||
case Qt::Key_O:
|
||||
m_showInstructionOpcode = !m_showInstructionOpcode;
|
||||
|
@ -610,7 +608,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event)
|
|||
|
||||
void DisassemblyWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
QMenu* contextMenu = new QMenu(this);
|
||||
|
@ -623,7 +621,7 @@ void DisassemblyWidget::customMenuRequested(QPoint pos)
|
|||
contextMenu->addAction(action = new QAction(tr("&Copy Instruction Text"), this));
|
||||
action->setShortcut(QKeySequence(Qt::Key_C));
|
||||
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
|
||||
if (m_cpu->GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart))
|
||||
if (cpu().GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart))
|
||||
{
|
||||
contextMenu->addAction(action = new QAction(tr("Copy Function Name"), this));
|
||||
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyFunctionName);
|
||||
|
@ -695,18 +693,18 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
|
|||
{
|
||||
DisassemblyLineInfo line;
|
||||
|
||||
if (!m_cpu->isValidAddress(address))
|
||||
if (!cpu().isValidAddress(address))
|
||||
return tr("%1 NOT VALID ADDRESS").arg(address, 8, 16, QChar('0')).toUpper();
|
||||
// Todo? support non symbol view?
|
||||
m_disassemblyManager.getLine(address, true, line);
|
||||
|
||||
const bool isConditional = line.info.isConditional && m_cpu->getPC() == address;
|
||||
const bool isConditional = line.info.isConditional && cpu().getPC() == address;
|
||||
const bool isConditionalMet = line.info.conditionMet;
|
||||
const bool isCurrentPC = m_cpu->getPC() == address;
|
||||
const bool isCurrentPC = cpu().getPC() == address;
|
||||
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(address);
|
||||
SymbolInfo symbol = m_cpu->GetSymbolGuardian().SymbolStartingAtAddress(address);
|
||||
const bool showOpcode = m_showInstructionOpcode && m_cpu->isAlive();
|
||||
FunctionInfo function = cpu().GetSymbolGuardian().FunctionStartingAtAddress(address);
|
||||
SymbolInfo symbol = cpu().GetSymbolGuardian().SymbolStartingAtAddress(address);
|
||||
const bool showOpcode = m_showInstructionOpcode && cpu().isAlive();
|
||||
|
||||
QString lineString;
|
||||
if (showOpcode)
|
||||
|
@ -739,7 +737,7 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
|
|||
|
||||
if (showOpcode)
|
||||
{
|
||||
const u32 opcode = m_cpu->read32(address);
|
||||
const u32 opcode = cpu().read32(address);
|
||||
lineString = lineString.arg(QtUtils::FilledQStringFromValue(opcode, 16));
|
||||
}
|
||||
|
||||
|
@ -789,7 +787,7 @@ QColor DisassemblyWidget::GetAddressFunctionColor(u32 address)
|
|||
// Use the address to pick the colour since the value of the handle may
|
||||
// change from run to run.
|
||||
ccc::Address function_address =
|
||||
m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address).address;
|
||||
cpu().GetSymbolGuardian().FunctionOverlappingAddress(address).address;
|
||||
if (!function_address.valid())
|
||||
return palette().text().color();
|
||||
|
||||
|
@ -817,7 +815,7 @@ QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
|
|||
}
|
||||
else // INSTRUCTIONHEX
|
||||
{
|
||||
infoBlock += FilledQStringFromValue(m_cpu->read32(i), 16);
|
||||
infoBlock += FilledQStringFromValue(cpu().read32(i), 16);
|
||||
}
|
||||
}
|
||||
return infoBlock;
|
||||
|
@ -831,7 +829,7 @@ void DisassemblyWidget::gotoAddressAndSetFocus(u32 address)
|
|||
void DisassemblyWidget::gotoProgramCounterOnPause()
|
||||
{
|
||||
if (m_goToProgramCounterOnPause)
|
||||
gotoAddress(m_cpu->getPC(), false);
|
||||
gotoAddress(cpu().getPC(), false);
|
||||
}
|
||||
|
||||
void DisassemblyWidget::gotoAddress(u32 address, bool should_set_focus)
|
||||
|
@ -861,7 +859,7 @@ bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
|
|||
|
||||
bool DisassemblyWidget::FunctionCanRestore(u32 address)
|
||||
{
|
||||
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address);
|
||||
FunctionInfo function = cpu().GetSymbolGuardian().FunctionOverlappingAddress(address);
|
||||
if (function.address.valid())
|
||||
address = function.address.value;
|
||||
|
||||
|
|
|
@ -5,24 +5,22 @@
|
|||
|
||||
#include "ui_DisassemblyWidget.h"
|
||||
|
||||
#include "DebuggerWidget.h"
|
||||
|
||||
#include "pcsx2/DebugTools/DebugInterface.h"
|
||||
#include "pcsx2/DebugTools/DisassemblyManager.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
class DisassemblyWidget final : public QWidget
|
||||
class DisassemblyWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DisassemblyWidget(QWidget* parent);
|
||||
DisassemblyWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
~DisassemblyWidget();
|
||||
|
||||
// Required because our constructor needs to take no extra arguments.
|
||||
void SetCpu(DebugInterface* cpu);
|
||||
|
||||
// Required for the breakpoint list (ugh wtf)
|
||||
QString GetLineDisasm(u32 address);
|
||||
|
||||
|
@ -69,9 +67,8 @@ signals:
|
|||
void VMUpdate();
|
||||
|
||||
private:
|
||||
Ui::DisassemblyWidget ui;
|
||||
Ui::DisassemblyWidget m_ui;
|
||||
|
||||
DebugInterface* m_cpu;
|
||||
u32 m_visibleStart = 0x00336318; // The address of the first opcode shown(row 0)
|
||||
u32 m_visibleRows;
|
||||
u32 m_selectedAddressStart = 0;
|
||||
|
|
|
@ -0,0 +1,593 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "DockManager.h"
|
||||
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
#include "Debugger/DisassemblyWidget.h"
|
||||
#include "Debugger/JsonValueWrapper.h"
|
||||
#include "Debugger/RegisterWidget.h"
|
||||
#include "Debugger/StackWidget.h"
|
||||
#include "Debugger/ThreadWidget.h"
|
||||
#include "Debugger/Breakpoints/BreakpointWidget.h"
|
||||
#include "Debugger/Docking/LayoutEditorDialog.h"
|
||||
#include "Debugger/Memory/MemorySearchWidget.h"
|
||||
#include "Debugger/Memory/MemoryViewWidget.h"
|
||||
#include "Debugger/Memory/SavedAddressesWidget.h"
|
||||
#include "Debugger/SymbolTree/SymbolTreeWidgets.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
|
||||
#include <kddockwidgets/Config.h>
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
#include <kddockwidgets/LayoutSaver.h>
|
||||
#include <kddockwidgets/core/DockRegistry.h>
|
||||
#include <kddockwidgets/core/DockWidget.h>
|
||||
#include <kddockwidgets/core/Group.h>
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QtTranslation>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
// Independent of the KDDockWidgets file format version number.
|
||||
const u32 DEBUGGER_LAYOUT_FILE_VERSION = 1;
|
||||
|
||||
struct DebuggerWidgetDescription
|
||||
{
|
||||
DebuggerWidget* (*create_widget)(DebugInterface& cpu);
|
||||
QString title;
|
||||
};
|
||||
|
||||
#define DEBUGGER_WIDGET(type, title) \
|
||||
{ \
|
||||
#type, \
|
||||
{ \
|
||||
[](DebugInterface& cpu) -> DebuggerWidget* { return new type(cpu); }, \
|
||||
title \
|
||||
} \
|
||||
}
|
||||
|
||||
static const std::map<std::string, DebuggerWidgetDescription> DEBUGGER_WIDGETS = {
|
||||
DEBUGGER_WIDGET(BreakpointWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Breakpoints")),
|
||||
DEBUGGER_WIDGET(DisassemblyWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Disassembly")),
|
||||
DEBUGGER_WIDGET(FunctionTreeWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Functions")),
|
||||
DEBUGGER_WIDGET(GlobalVariableTreeWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Globals")),
|
||||
DEBUGGER_WIDGET(LocalVariableTreeWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Locals")),
|
||||
DEBUGGER_WIDGET(MemorySearchWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Memory Search")),
|
||||
DEBUGGER_WIDGET(MemoryViewWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Memory")),
|
||||
DEBUGGER_WIDGET(ParameterVariableTreeWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Parameters")),
|
||||
DEBUGGER_WIDGET(RegisterWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Registers")),
|
||||
DEBUGGER_WIDGET(SavedAddressesWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Saved Addresses")),
|
||||
DEBUGGER_WIDGET(StackWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Stack")),
|
||||
DEBUGGER_WIDGET(ThreadWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Threads")),
|
||||
};
|
||||
|
||||
#undef DEBUGGER_WIDGET
|
||||
|
||||
enum DefaultDockGroup
|
||||
{
|
||||
ROOT = -1,
|
||||
TOP_RIGHT = 0,
|
||||
BOTTOM = 1,
|
||||
TOP_LEFT = 2,
|
||||
COUNT = 3
|
||||
};
|
||||
|
||||
struct DefaultDockGroupDescription
|
||||
{
|
||||
KDDockWidgets::Location location;
|
||||
DefaultDockGroup parent;
|
||||
};
|
||||
|
||||
static const std::vector<DefaultDockGroupDescription> DEFAULT_DOCK_GROUPS = {
|
||||
/* [DefaultDockGroup::TOP_RIGHT] = */ {KDDockWidgets::Location_OnRight, DefaultDockGroup::ROOT},
|
||||
/* [DefaultDockGroup::TOP_LEFT] = */ {KDDockWidgets::Location_OnBottom, DefaultDockGroup::TOP_RIGHT},
|
||||
/* [DefaultDockGroup::BOTTOM] = */ {KDDockWidgets::Location_OnLeft, DefaultDockGroup::TOP_RIGHT},
|
||||
};
|
||||
|
||||
struct DefaultDockWidgetDescription
|
||||
{
|
||||
std::string type;
|
||||
DefaultDockGroup group;
|
||||
};
|
||||
|
||||
static const std::vector<DefaultDockWidgetDescription> DEFAULT_DOCK_WIDGETS = {
|
||||
/* DefaultDockGroup::TOP_RIGHT */
|
||||
{"DisassemblyWidget", DefaultDockGroup::TOP_RIGHT},
|
||||
/* DefaultDockGroup::BOTTOM */
|
||||
{"MemoryViewWidget", DefaultDockGroup::BOTTOM},
|
||||
{"BreakpointWidget", DefaultDockGroup::BOTTOM},
|
||||
{"ThreadWidget", DefaultDockGroup::BOTTOM},
|
||||
{"StackWidget", DefaultDockGroup::BOTTOM},
|
||||
{"SavedAddressesWidget", DefaultDockGroup::BOTTOM},
|
||||
{"GlobalVariableTreeWidget", DefaultDockGroup::BOTTOM},
|
||||
{"LocalVariableTreeWidget", DefaultDockGroup::BOTTOM},
|
||||
{"ParameterVariableTreeWidget", DefaultDockGroup::BOTTOM},
|
||||
/* DefaultDockGroup::TOP_LEFT */
|
||||
{"RegisterWidget", DefaultDockGroup::TOP_LEFT},
|
||||
{"FunctionTreeWidget", DefaultDockGroup::TOP_LEFT},
|
||||
{"MemorySearchWidget", DefaultDockGroup::TOP_LEFT},
|
||||
};
|
||||
|
||||
DockManager::DockManager(DebuggerWindow* window)
|
||||
: QObject(window)
|
||||
, m_window(window)
|
||||
{
|
||||
loadLayouts();
|
||||
}
|
||||
|
||||
DockManager::~DockManager()
|
||||
{
|
||||
saveLayout(m_current_layout);
|
||||
|
||||
for (Layout& layout : m_layouts)
|
||||
for (QPointer<DebuggerWidget> widget : layout.widgets)
|
||||
delete widget;
|
||||
}
|
||||
|
||||
void DockManager::configureDockingSystem()
|
||||
{
|
||||
KDDockWidgets::Config::self().setFlags(
|
||||
KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible |
|
||||
KDDockWidgets::Config::Flag_AlwaysShowTabs |
|
||||
KDDockWidgets::Config::Flag_AllowReorderTabs |
|
||||
KDDockWidgets::Config::Flag_TabsHaveCloseButton |
|
||||
KDDockWidgets::Config::Flag_TitleBarIsFocusable);
|
||||
}
|
||||
|
||||
s32 DockManager::createLayout(std::string name, BreakPointCpu cpu, LayoutCreationMode mode)
|
||||
{
|
||||
s32 layout_index = static_cast<s32>(m_layouts.size());
|
||||
|
||||
Layout& layout = m_layouts.emplace_back();
|
||||
layout.name = std::move(name);
|
||||
layout.cpu = cpu;
|
||||
|
||||
DebugInterface& debug_interface = r5900Debug;
|
||||
if (cpu == BREAKPOINT_IOP)
|
||||
debug_interface = r3000Debug;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case DEFAULT_LAYOUT:
|
||||
{
|
||||
for (size_t i = 0; i < DEFAULT_DOCK_WIDGETS.size(); i++)
|
||||
{
|
||||
auto iterator = DEBUGGER_WIDGETS.find(DEFAULT_DOCK_WIDGETS[i].type);
|
||||
pxAssertRel(iterator != DEBUGGER_WIDGETS.end(), "Invalid default layout.");
|
||||
const DebuggerWidgetDescription& dock_description = iterator->second;
|
||||
|
||||
DebuggerWidget* widget = dock_description.create_widget(debug_interface);
|
||||
widget->widget_description_index = i;
|
||||
widget->unique_name = dock_description.title;
|
||||
layout.widgets.emplace_back(widget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLONE_LAYOUT:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
case BLANK_LAYOUT:
|
||||
{
|
||||
// Nothing to do.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return layout_index;
|
||||
}
|
||||
|
||||
bool DockManager::deleteLayout(s32 layout_index)
|
||||
{
|
||||
if (layout_index < 0 || layout_index >= static_cast<s32>(m_layouts.size()))
|
||||
return false;
|
||||
|
||||
if (layout_index == m_current_layout)
|
||||
{
|
||||
int other_layout = -1;
|
||||
if (layout_index + 1 <= static_cast<s32>(m_layouts.size()))
|
||||
other_layout = layout_index + 1;
|
||||
else if (layout_index > 0)
|
||||
other_layout = layout_index - 1;
|
||||
|
||||
switchToLayout(other_layout);
|
||||
}
|
||||
|
||||
Layout& layout = m_layouts[layout_index];
|
||||
for (QPointer<DebuggerWidget> widget : layout.widgets)
|
||||
{
|
||||
delete widget;
|
||||
}
|
||||
|
||||
m_layouts.erase(m_layouts.begin() + layout_index);
|
||||
|
||||
if (m_current_layout > layout_index)
|
||||
m_current_layout--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DockManager::switchToLayout(s32 layout_index)
|
||||
{
|
||||
if (layout_index == m_current_layout || layout_index >= static_cast<s32>(m_layouts.size()))
|
||||
return;
|
||||
|
||||
if (m_current_layout > -1)
|
||||
{
|
||||
freezeLayout(m_layouts[m_current_layout]);
|
||||
saveLayout(m_current_layout);
|
||||
}
|
||||
|
||||
m_current_layout = layout_index;
|
||||
|
||||
if (m_current_layout > -1)
|
||||
{
|
||||
thawLayout(m_layouts[m_current_layout]);
|
||||
}
|
||||
}
|
||||
|
||||
void DockManager::loadLayouts()
|
||||
{
|
||||
m_layouts.clear();
|
||||
|
||||
// Load the layouts.
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(
|
||||
EmuFolders::DebuggerLayouts.c_str(),
|
||||
"*.json",
|
||||
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES,
|
||||
&files);
|
||||
|
||||
for (const FILESYSTEM_FIND_DATA& ffd : files)
|
||||
loadLayout(ffd.FileName);
|
||||
|
||||
if (m_layouts.empty())
|
||||
setupDefaultLayouts();
|
||||
}
|
||||
|
||||
s32 DockManager::loadLayout(const std::string& path)
|
||||
{
|
||||
s32 layout_index = 0; // = createLayout(name);
|
||||
|
||||
return layout_index;
|
||||
}
|
||||
|
||||
bool DockManager::saveLayouts()
|
||||
{
|
||||
for (s32 i = 0; i < static_cast<s32>(m_layouts.size()); i++)
|
||||
if (!saveLayout(i))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DockManager::saveLayout(s32 layout_index)
|
||||
{
|
||||
if (layout_index < 0)
|
||||
return false;
|
||||
|
||||
Layout& layout = m_layouts[layout_index];
|
||||
|
||||
// Serialize the layout as JSON.
|
||||
rapidjson::Document json(rapidjson::kObjectType);
|
||||
rapidjson::Document geometry;
|
||||
|
||||
json.AddMember("format", "PCSX2 Debugger User Interface Layout", json.GetAllocator());
|
||||
json.AddMember("version", DEBUGGER_LAYOUT_FILE_VERSION, json.GetAllocator());
|
||||
|
||||
rapidjson::Value name;
|
||||
name.SetString(layout.name.c_str(), strlen(layout.name.c_str()));
|
||||
json.AddMember("name", name, json.GetAllocator());
|
||||
|
||||
rapidjson::Value widgets(rapidjson::kArrayType);
|
||||
for (QPointer<DebuggerWidget> widget : layout.widgets)
|
||||
{
|
||||
rapidjson::Value object(rapidjson::kObjectType);
|
||||
|
||||
JsonValueWrapper wrapper(object, json.GetAllocator());
|
||||
widget->toJson(wrapper);
|
||||
|
||||
widgets.PushBack(object, json.GetAllocator());
|
||||
}
|
||||
json.AddMember("widgets", widgets, json.GetAllocator());
|
||||
|
||||
if (!layout.geometry.isEmpty() && !geometry.Parse(layout.geometry).HasParseError())
|
||||
json.AddMember("geometry", geometry, json.GetAllocator());
|
||||
|
||||
rapidjson::StringBuffer string_buffer;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(string_buffer);
|
||||
json.Accept(writer);
|
||||
|
||||
// Write out the JSON to a file.
|
||||
std::string temp_file_path = Path::Combine(EmuFolders::DebuggerLayouts, layout.name + ".tmp");
|
||||
|
||||
if (!FileSystem::WriteStringToFile(temp_file_path.c_str(), string_buffer.GetString()))
|
||||
return false;
|
||||
|
||||
// Generate a name if a file doesn't already exist.
|
||||
if (layout.layout_file_path.empty())
|
||||
layout.layout_file_path = Path::Combine(EmuFolders::DebuggerLayouts, layout.name + ".json");
|
||||
|
||||
return FileSystem::RenamePath(temp_file_path.c_str(), layout.layout_file_path.c_str());
|
||||
}
|
||||
|
||||
void DockManager::renameLayout(s32 layout_index, std::string new_name)
|
||||
{
|
||||
}
|
||||
|
||||
void DockManager::setupDefaultLayouts()
|
||||
{
|
||||
switchToLayout(-1);
|
||||
|
||||
m_layouts.clear();
|
||||
|
||||
createLayout("R5900 (EE)", BREAKPOINT_EE, DEFAULT_LAYOUT);
|
||||
createLayout("R3000 (IOP)", BREAKPOINT_IOP, DEFAULT_LAYOUT);
|
||||
|
||||
switchToLayout(0);
|
||||
}
|
||||
|
||||
void DockManager::createWindowsMenu(QMenu* menu)
|
||||
{
|
||||
menu->clear();
|
||||
|
||||
QAction* reset_all_layouts_action = new QAction(tr("Reset All Layouts"), menu);
|
||||
connect(reset_all_layouts_action, &QAction::triggered, [this]() {
|
||||
QMessageBox::StandardButton result = QMessageBox::question(
|
||||
m_window, tr("Confirmation"), tr("Are you sure you want to reset all layouts?"));
|
||||
|
||||
if (result == QMessageBox::Yes)
|
||||
setupDefaultLayouts();
|
||||
});
|
||||
menu->addAction(reset_all_layouts_action);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
for (const auto& [type, desc] : DEBUGGER_WIDGETS)
|
||||
{
|
||||
QAction* action = new QAction(menu);
|
||||
action->setText(QCoreApplication::translate("DockWidget", desc.title.toStdString().c_str()));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(true);
|
||||
menu->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* DockManager::createLayoutSwitcher(QWidget* menu_bar)
|
||||
{
|
||||
QWidget* container = new QWidget;
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
container->setLayout(layout);
|
||||
|
||||
layout->setContentsMargins(0, 2, 0, 0);
|
||||
|
||||
QWidget* menu_wrapper = new QWidget;
|
||||
menu_wrapper->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
layout->addWidget(menu_wrapper);
|
||||
|
||||
QHBoxLayout* menu_layout = new QHBoxLayout;
|
||||
menu_layout->setContentsMargins(0, 4, 0, 4);
|
||||
menu_wrapper->setLayout(menu_layout);
|
||||
|
||||
menu_layout->addWidget(menu_bar);
|
||||
|
||||
m_switcher = new QTabBar;
|
||||
m_switcher->setContentsMargins(0, 0, 0, 0);
|
||||
m_switcher->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
m_switcher->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_switcher->setMovable(true);
|
||||
layout->addWidget(m_switcher);
|
||||
|
||||
updateLayoutSwitcher();
|
||||
|
||||
connect(m_switcher, &QTabBar::tabMoved, this, &DockManager::layoutSwitcherTabMoved);
|
||||
connect(m_switcher, &QTabBar::customContextMenuRequested, this, &DockManager::layoutSwitcherContextMenu);
|
||||
|
||||
QWidget* spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
void DockManager::updateLayoutSwitcher()
|
||||
{
|
||||
if (!m_switcher)
|
||||
return;
|
||||
|
||||
disconnect(m_tab_connection);
|
||||
|
||||
for (int i = m_switcher->count(); i > 0; i--)
|
||||
m_switcher->removeTab(i - 1);
|
||||
|
||||
for (Layout& layout : m_layouts)
|
||||
layout.switcher_tab_index = m_switcher->addTab(QString::fromStdString(layout.name));
|
||||
|
||||
m_plus_tab_index = m_switcher->addTab("+");
|
||||
m_current_tab_index = m_current_layout;
|
||||
|
||||
m_switcher->setCurrentIndex(m_current_layout);
|
||||
|
||||
m_tab_connection = connect(m_switcher, &QTabBar::currentChanged, this, &DockManager::layoutSwitcherTabChanged);
|
||||
}
|
||||
|
||||
void DockManager::layoutSwitcherTabChanged(s32 index)
|
||||
{
|
||||
if (index == m_plus_tab_index)
|
||||
{
|
||||
if (m_current_tab_index >= 0 && m_current_tab_index < m_plus_tab_index)
|
||||
m_switcher->setCurrentIndex(m_current_tab_index);
|
||||
|
||||
LayoutEditorDialog* dialog = new LayoutEditorDialog(m_window);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
s32 layout_index = createLayout(dialog->name(), dialog->cpu(), dialog->initial_state());
|
||||
switchToLayout(layout_index);
|
||||
updateLayoutSwitcher();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switchToLayout(index);
|
||||
m_current_tab_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
void DockManager::layoutSwitcherTabMoved(s32 from, s32 to)
|
||||
{
|
||||
updateLayoutSwitcher();
|
||||
}
|
||||
|
||||
void DockManager::layoutSwitcherContextMenu(QPoint pos)
|
||||
{
|
||||
s32 layout_index = m_switcher->tabAt(pos);
|
||||
if (layout_index < 0)
|
||||
return;
|
||||
|
||||
QMenu* menu = new QMenu(tr("Layout Switcher Context Menu"), m_switcher);
|
||||
|
||||
QAction* edit_action = new QAction(tr("Edit Layout"), menu);
|
||||
connect(edit_action, &QAction::triggered, [this, layout_index]() {
|
||||
if (layout_index < 0 || layout_index >= static_cast<s32>(m_layouts.size()))
|
||||
return;
|
||||
|
||||
Layout& layout = m_layouts[layout_index];
|
||||
|
||||
LayoutEditorDialog* dialog = new LayoutEditorDialog(layout.name, layout.cpu, m_window);
|
||||
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
layout.name = dialog->name();
|
||||
layout.cpu = dialog->cpu();
|
||||
updateLayoutSwitcher();
|
||||
}
|
||||
});
|
||||
menu->addAction(edit_action);
|
||||
|
||||
QAction* delete_action = new QAction(tr("Delete Layout"), menu);
|
||||
connect(delete_action, &QAction::triggered, [this, layout_index]() {
|
||||
deleteLayout(layout_index);
|
||||
updateLayoutSwitcher();
|
||||
});
|
||||
menu->addAction(delete_action);
|
||||
|
||||
menu->popup(m_switcher->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void DockManager::freezeLayout(Layout& layout)
|
||||
{
|
||||
pxAssertRel(!layout.is_frozen, "DockManager::freezeLayout called on already frozen layout.");
|
||||
layout.is_frozen = true;
|
||||
|
||||
KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow);
|
||||
|
||||
// Store the geometry of all the dock widgets as JSON.
|
||||
layout.geometry = saver.serializeLayout();
|
||||
|
||||
// Delete the dock widgets.
|
||||
for (KDDockWidgets::Core::DockWidget* dock : KDDockWidgets::DockRegistry::self()->dockwidgets())
|
||||
{
|
||||
// Make sure the dock widget releases ownership of its content.
|
||||
KDDockWidgets::QtWidgets::DockWidget* view =
|
||||
static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock->view());
|
||||
view->setWidget(new QWidget());
|
||||
|
||||
delete dock;
|
||||
}
|
||||
}
|
||||
|
||||
void DockManager::thawLayout(Layout& layout)
|
||||
{
|
||||
pxAssertRel(layout.is_frozen, "DockManager::thawLayout called on already thawed layout.");
|
||||
layout.is_frozen = false;
|
||||
|
||||
KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow);
|
||||
|
||||
if (layout.geometry.isEmpty())
|
||||
{
|
||||
// This is a newly created layout with no geometry information.
|
||||
populateDefaultLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create any dock widgets that were previously frozen during this session.
|
||||
for (QPointer<DebuggerWidget> widget : layout.widgets)
|
||||
{
|
||||
KDDockWidgets::QtWidgets::DockWidget* view = new KDDockWidgets::QtWidgets::DockWidget(widget->unique_name);
|
||||
view->setWidget(widget);
|
||||
m_window->addDockWidget(view, KDDockWidgets::Location_OnBottom);
|
||||
}
|
||||
|
||||
// Restore the geometry of the dock widgets we just recreated.
|
||||
if (!saver.restoreLayout(layout.geometry))
|
||||
{
|
||||
for (KDDockWidgets::Core::DockWidget* dock : KDDockWidgets::DockRegistry::self()->dockwidgets())
|
||||
{
|
||||
// Make sure the dock widget releases ownership of its content.
|
||||
KDDockWidgets::QtWidgets::DockWidget* view =
|
||||
static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock->view());
|
||||
view->setWidget(new QWidget());
|
||||
|
||||
delete dock;
|
||||
}
|
||||
|
||||
// We failed to restore the geometry, so just setup the default layout.
|
||||
populateDefaultLayout();
|
||||
}
|
||||
}
|
||||
|
||||
KDDockWidgets::Core::DockWidget* DockManager::createDockWidget(const QString& name)
|
||||
{
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DockManager::populateDefaultLayout()
|
||||
{
|
||||
if (m_current_layout < 0)
|
||||
return;
|
||||
|
||||
Layout& layout = m_layouts[m_current_layout];
|
||||
|
||||
KDDockWidgets::QtWidgets::DockWidget* groups[DefaultDockGroup::COUNT] = {};
|
||||
|
||||
for (QPointer<DebuggerWidget> widget : layout.widgets)
|
||||
{
|
||||
if (widget->widget_description_index >= DEFAULT_DOCK_WIDGETS.size())
|
||||
continue;
|
||||
|
||||
const DefaultDockWidgetDescription& dock_description = DEFAULT_DOCK_WIDGETS[widget->widget_description_index];
|
||||
const DefaultDockGroupDescription& group = DEFAULT_DOCK_GROUPS[dock_description.group];
|
||||
|
||||
auto debug_description_iterator = DEBUGGER_WIDGETS.find(dock_description.type);
|
||||
pxAssertRel(debug_description_iterator != DEBUGGER_WIDGETS.end(), "Invalid default dock layout.");
|
||||
const DebuggerWidgetDescription& debug_description = debug_description_iterator->second;
|
||||
|
||||
KDDockWidgets::QtWidgets::DockWidget* dock = new KDDockWidgets::QtWidgets::DockWidget(debug_description.title);
|
||||
dock->setWidget(widget);
|
||||
|
||||
if (!groups[dock_description.group])
|
||||
{
|
||||
KDDockWidgets::QtWidgets::DockWidget* parent = nullptr;
|
||||
if (group.parent != DefaultDockGroup::ROOT)
|
||||
parent = groups[group.parent];
|
||||
|
||||
m_window->addDockWidget(dock, group.location, parent);
|
||||
|
||||
groups[dock_description.group] = dock;
|
||||
}
|
||||
else
|
||||
{
|
||||
groups[dock_description.group]->addDockWidgetAsTab(dock);
|
||||
}
|
||||
}
|
||||
|
||||
for (KDDockWidgets::Core::Group* group : KDDockWidgets::DockRegistry::self()->groups())
|
||||
group->setCurrentTabIndex(0);
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include <kddockwidgets/MainWindow.h>
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
|
||||
#include <QtWidgets/QTabBar>
|
||||
|
||||
class DebuggerWindow;
|
||||
|
||||
extern const u32 DEBUGGER_LAYOUT_FILE_VERSION;
|
||||
|
||||
class DockManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DockManager(DebuggerWindow* window);
|
||||
~DockManager();
|
||||
|
||||
static void configureDockingSystem();
|
||||
|
||||
enum LayoutCreationMode
|
||||
{
|
||||
DEFAULT_LAYOUT,
|
||||
CLONE_LAYOUT,
|
||||
BLANK_LAYOUT,
|
||||
};
|
||||
|
||||
s32 createLayout(std::string name, BreakPointCpu cpu, LayoutCreationMode mode);
|
||||
bool deleteLayout(s32 layout_index);
|
||||
|
||||
void switchToLayout(s32 layout_index);
|
||||
|
||||
void loadLayouts();
|
||||
s32 loadLayout(const std::string& path);
|
||||
|
||||
bool saveLayouts();
|
||||
bool saveLayout(s32 layout_index);
|
||||
|
||||
void renameLayout(s32 layout_index, std::string new_name);
|
||||
|
||||
void setupDefaultLayouts();
|
||||
|
||||
void createWindowsMenu(QMenu* menu);
|
||||
|
||||
QWidget* createLayoutSwitcher(QWidget* menu_bar);
|
||||
void updateLayoutSwitcher();
|
||||
void layoutSwitcherTabChanged(s32 index);
|
||||
void layoutSwitcherTabMoved(s32 from, s32 to);
|
||||
void layoutSwitcherContextMenu(QPoint pos);
|
||||
|
||||
private:
|
||||
struct Layout
|
||||
{
|
||||
// The name displayed in the user interface. Also used to determine the
|
||||
// file name for the layout file.
|
||||
std::string name;
|
||||
|
||||
// The default target for dock widgets in this layout. This can be
|
||||
// overriden on a per-widget basis.
|
||||
BreakPointCpu cpu;
|
||||
|
||||
// All the dock widgets currently open in this layout. If this is the
|
||||
// active layout then these will be owned by the docking system,
|
||||
// otherwise they won't be and will need to be cleaned up separately.
|
||||
std::vector<QPointer<DebuggerWidget>> widgets;
|
||||
|
||||
int switcher_tab_index = -1;
|
||||
|
||||
// The geometry of all the dock widgets, converted to JSON by the
|
||||
// LayoutSaver class from KDDockWidgets.
|
||||
QByteArray geometry;
|
||||
bool geometry_modified = false;
|
||||
|
||||
// The absolute file path of the corresponding layout file as it
|
||||
// currently exists exists on disk, or empty if no such file exists.
|
||||
std::string layout_file_path;
|
||||
|
||||
bool is_frozen = true;
|
||||
};
|
||||
|
||||
// Save the current state of all the dock widgets to a layout.
|
||||
void freezeLayout(Layout& layout);
|
||||
|
||||
// Restore the state of all the dock widgets from a layout.
|
||||
void thawLayout(Layout& layout);
|
||||
|
||||
KDDockWidgets::Core::DockWidget* createDockWidget(const QString& name);
|
||||
|
||||
void populateDefaultLayout();
|
||||
|
||||
KDDockWidgets::QtWidgets::MainWindow* m_window;
|
||||
|
||||
std::vector<Layout> m_layouts;
|
||||
s32 m_current_layout = -1;
|
||||
|
||||
QTabBar* m_switcher = nullptr;
|
||||
s32 m_plus_tab_index = -1;
|
||||
s32 m_current_tab_index = -1;
|
||||
|
||||
QMetaObject::Connection m_tab_connection;
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "LayoutEditorDialog.h"
|
||||
|
||||
LayoutEditorDialog::LayoutEditorDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
setWindowTitle(tr("New Layout"));
|
||||
|
||||
setupComboBoxes(BREAKPOINT_EE, DockManager::DEFAULT_LAYOUT);
|
||||
}
|
||||
|
||||
LayoutEditorDialog::LayoutEditorDialog(
|
||||
std::string& name, BreakPointCpu cpu, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
setWindowTitle(tr("Edit Layout"));
|
||||
|
||||
m_ui.nameEditor->setText(QString::fromStdString(name));
|
||||
|
||||
setupComboBoxes(cpu, DockManager::DEFAULT_LAYOUT);
|
||||
|
||||
m_ui.initialStateLabel->hide();
|
||||
m_ui.initialStateEditor->hide();
|
||||
}
|
||||
|
||||
std::string LayoutEditorDialog::name()
|
||||
{
|
||||
return m_ui.nameEditor->text().toStdString();
|
||||
}
|
||||
|
||||
BreakPointCpu LayoutEditorDialog::cpu()
|
||||
{
|
||||
return static_cast<BreakPointCpu>(m_ui.cpuEditor->currentData().toInt());
|
||||
}
|
||||
|
||||
DockManager::LayoutCreationMode LayoutEditorDialog::initial_state()
|
||||
{
|
||||
return static_cast<DockManager::LayoutCreationMode>(m_ui.initialStateEditor->currentData().toInt());
|
||||
}
|
||||
|
||||
void LayoutEditorDialog::setupComboBoxes(BreakPointCpu cpu, DockManager::LayoutCreationMode initial_state)
|
||||
{
|
||||
m_ui.cpuEditor->addItem(tr("EE"), BREAKPOINT_EE);
|
||||
m_ui.cpuEditor->addItem(tr("IOP"), BREAKPOINT_IOP);
|
||||
|
||||
for (int i = 0; i < m_ui.cpuEditor->count(); i++)
|
||||
if (m_ui.cpuEditor->itemData(i).toInt() == cpu)
|
||||
m_ui.cpuEditor->setCurrentIndex(i);
|
||||
|
||||
m_ui.initialStateEditor->addItem(tr("Create Default Layout"), DockManager::DEFAULT_LAYOUT);
|
||||
m_ui.initialStateEditor->addItem(tr("Create Blank Layout"), DockManager::BLANK_LAYOUT);
|
||||
m_ui.initialStateEditor->addItem(tr("Clone Current Layout"), DockManager::CLONE_LAYOUT);
|
||||
|
||||
for (int i = 0; i < m_ui.initialStateEditor->count(); i++)
|
||||
if (m_ui.initialStateEditor->itemData(i).toInt() == initial_state)
|
||||
m_ui.initialStateEditor->setCurrentIndex(i);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_LayoutEditorDialog.h"
|
||||
|
||||
#include "Debugger/Docking/DockManager.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class LayoutEditorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Create a "New Layout" dialog.
|
||||
LayoutEditorDialog(QWidget* parent = nullptr);
|
||||
|
||||
// Create a "Edit Layout" dialog.
|
||||
LayoutEditorDialog(std::string& name, BreakPointCpu cpu, QWidget* parent = nullptr);
|
||||
|
||||
std::string name();
|
||||
BreakPointCpu cpu();
|
||||
DockManager::LayoutCreationMode initial_state();
|
||||
|
||||
private:
|
||||
void setupComboBoxes(BreakPointCpu cpu, DockManager::LayoutCreationMode initial_state);
|
||||
|
||||
Ui::LayoutEditorDialog m_ui;
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LayoutEditorDialog</class>
|
||||
<widget class="QDialog" name="LayoutEditorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>150</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="cpuLabel">
|
||||
<property name="text">
|
||||
<string>CPU</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="initialStateLabel">
|
||||
<property name="text">
|
||||
<string>Initial State</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cpuEditor"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="nameEditor"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="initialStateEditor"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LayoutEditorDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>LayoutEditorDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
|
||||
// Container for a JSON value. This exists solely so that we can forward declare
|
||||
// it to avoid pulling in rapidjson for the entire debugger.
|
||||
class JsonValueWrapper
|
||||
{
|
||||
public:
|
||||
JsonValueWrapper(
|
||||
rapidjson::Value& value,
|
||||
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& allocator)
|
||||
: m_value(value)
|
||||
, m_allocator(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
rapidjson::Value& value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& allocator()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
rapidjson::Value& m_value;
|
||||
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& m_allocator;
|
||||
};
|
|
@ -23,8 +23,8 @@ using SearchResult = MemorySearchWidget::SearchResult;
|
|||
|
||||
using namespace QtUtils;
|
||||
|
||||
MemorySearchWidget::MemorySearchWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
MemorySearchWidget::MemorySearchWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
this->repaint();
|
||||
|
@ -32,8 +32,7 @@ MemorySearchWidget::MemorySearchWidget(QWidget* parent)
|
|||
m_ui.listSearchResults->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_ui.btnSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked);
|
||||
connect(m_ui.btnFilterSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked);
|
||||
connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
|
||||
{
|
||||
connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) {
|
||||
emit switchToMemoryViewTab();
|
||||
emit goToAddressInMemoryView(item->text().toUInt(nullptr, 16));
|
||||
});
|
||||
|
@ -47,11 +46,6 @@ MemorySearchWidget::MemorySearchWidget(QWidget* parent)
|
|||
connect(&m_resultsLoadTimer, &QTimer::timeout, this, &MemorySearchWidget::loadSearchResults);
|
||||
}
|
||||
|
||||
void MemorySearchWidget::setCpu(DebugInterface* cpu)
|
||||
{
|
||||
m_cpu = cpu;
|
||||
}
|
||||
|
||||
void MemorySearchWidget::contextSearchResultGoToDisassembly()
|
||||
{
|
||||
const QItemSelectionModel* selModel = m_ui.listSearchResults->selectionModel();
|
||||
|
@ -120,15 +114,15 @@ void MemorySearchWidget::onListSearchResultsContextMenu(QPoint pos)
|
|||
contextMenu->popup(m_ui.listSearchResults->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
T readValueAtAddress(DebugInterface* cpu, u32 addr);
|
||||
template<>
|
||||
template <>
|
||||
float readValueAtAddress<float>(DebugInterface* cpu, u32 addr)
|
||||
{
|
||||
return std::bit_cast<float>(cpu->read32(addr));
|
||||
}
|
||||
|
||||
template<>
|
||||
template <>
|
||||
double readValueAtAddress<double>(DebugInterface* cpu, u32 addr)
|
||||
{
|
||||
return std::bit_cast<double>(cpu->read64(addr));
|
||||
|
@ -229,7 +223,7 @@ template <typename T>
|
|||
bool handleSearchComparison(SearchComparison searchComparison, u32 searchAddress, const SearchResult* priorResult, T searchValue, T readValue)
|
||||
{
|
||||
const bool isNotOperator = searchComparison == SearchComparison::NotEquals || searchComparison == SearchComparison::NotChanged;
|
||||
switch (searchComparison)
|
||||
switch (searchComparison)
|
||||
{
|
||||
case SearchComparison::Equals:
|
||||
case SearchComparison::NotEquals:
|
||||
|
@ -298,7 +292,7 @@ void searchWorker(DebugInterface* cpu, std::vector<SearchResult>& searchResults,
|
|||
{
|
||||
if (!cpu->isValidAddress(addr))
|
||||
continue;
|
||||
|
||||
|
||||
T readValue = readValueAtAddress<T>(cpu, addr);
|
||||
if (handleSearchComparison(searchComparison, addr, nullptr, searchValue, readValue))
|
||||
{
|
||||
|
@ -311,7 +305,7 @@ void searchWorker(DebugInterface* cpu, std::vector<SearchResult>& searchResults,
|
|||
auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [cpu, searchType, searchComparison, searchValue](SearchResult& searchResult) -> bool {
|
||||
const u32 addr = searchResult.getAddress();
|
||||
if (!cpu->isValidAddress(addr))
|
||||
return true;
|
||||
return true;
|
||||
|
||||
const auto readValue = readValueAtAddress<T>(cpu, addr);
|
||||
|
||||
|
@ -411,7 +405,7 @@ static void searchWorkerByteArray(DebugInterface* cpu, SearchType searchType, Se
|
|||
}
|
||||
else
|
||||
{
|
||||
auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [ searchComparison, searchType, searchValue, cpu ](SearchResult& searchResult) -> bool {
|
||||
auto removeIt = std::remove_if(searchResults.begin(), searchResults.end(), [searchComparison, searchType, searchValue, cpu](SearchResult& searchResult) -> bool {
|
||||
const u32 addr = searchResult.getAddress();
|
||||
if (!cpu->isValidAddress(addr))
|
||||
return true;
|
||||
|
@ -472,7 +466,7 @@ std::vector<SearchResult> startWorker(DebugInterface* cpu, const SearchType type
|
|||
|
||||
void MemorySearchWidget::onSearchButtonClicked()
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
const SearchType searchType = getCurrentSearchType();
|
||||
|
@ -556,10 +550,7 @@ void MemorySearchWidget::onSearchButtonClicked()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!isFilterSearch && (searchComparison == SearchComparison::Changed || searchComparison == SearchComparison::ChangedBy
|
||||
|| searchComparison == SearchComparison::Decreased || searchComparison == SearchComparison::DecreasedBy
|
||||
|| searchComparison == SearchComparison::Increased || searchComparison == SearchComparison::IncreasedBy
|
||||
|| searchComparison == SearchComparison::NotChanged))
|
||||
if (!isFilterSearch && (searchComparison == SearchComparison::Changed || searchComparison == SearchComparison::ChangedBy || searchComparison == SearchComparison::Decreased || searchComparison == SearchComparison::DecreasedBy || searchComparison == SearchComparison::Increased || searchComparison == SearchComparison::IncreasedBy || searchComparison == SearchComparison::NotChanged))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches."));
|
||||
return;
|
||||
|
@ -587,7 +578,7 @@ void MemorySearchWidget::onSearchButtonClicked()
|
|||
m_searchResults.clear();
|
||||
}
|
||||
|
||||
QFuture<std::vector<SearchResult>> workerFuture = QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, std::move(m_searchResults), searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
|
||||
QFuture<std::vector<SearchResult>> workerFuture = QtConcurrent::run(startWorker, &cpu(), searchType, searchComparison, std::move(m_searchResults), searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
|
||||
workerWatcher->setFuture(workerFuture);
|
||||
connect(workerWatcher, &QFutureWatcher<std::vector<SearchResult>>::finished, onSearchFinished);
|
||||
m_searchResults.clear();
|
||||
|
@ -676,7 +667,7 @@ void MemorySearchWidget::updateSearchComparisonSelections()
|
|||
std::vector<SearchComparison> MemorySearchWidget::getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult>& existingResults)
|
||||
{
|
||||
const bool hasResults = existingResults.size() > 0;
|
||||
std::vector<SearchComparison> comparisons = { SearchComparison::Equals };
|
||||
std::vector<SearchComparison> comparisons = {SearchComparison::Equals};
|
||||
|
||||
if (type == SearchType::ArrayType || type == SearchType::StringType)
|
||||
{
|
|
@ -5,22 +5,23 @@
|
|||
|
||||
#include "ui_MemorySearchWidget.h"
|
||||
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
class MemorySearchWidget final : public QWidget
|
||||
class MemorySearchWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MemorySearchWidget(QWidget* parent);
|
||||
~MemorySearchWidget() = default;
|
||||
void setCpu(DebugInterface* cpu);
|
||||
MemorySearchWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
~MemorySearchWidget() = default;
|
||||
|
||||
enum class SearchType
|
||||
enum class SearchType
|
||||
{
|
||||
ByteType,
|
||||
Int16Type,
|
||||
|
@ -75,9 +76,11 @@ public:
|
|||
{
|
||||
return labelToEnumMap.value(comparisonLabel, SearchComparison::Invalid);
|
||||
}
|
||||
QString enumToLabel(SearchComparison comparison) {
|
||||
QString enumToLabel(SearchComparison comparison)
|
||||
{
|
||||
return enumToLabelMap.value(comparison, "");
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<SearchComparison, QString> enumToLabelMap;
|
||||
QMap<QString, SearchComparison> labelToEnumMap;
|
||||
|
@ -98,7 +101,9 @@ public:
|
|||
public:
|
||||
SearchResult() {}
|
||||
SearchResult(u32 address, const QVariant& value, SearchType type)
|
||||
: address(address), value(value), type(type)
|
||||
: address(address)
|
||||
, value(value)
|
||||
, type(type)
|
||||
{
|
||||
}
|
||||
bool isIntegerValue() const { return type == SearchType::ByteType || type == SearchType::Int16Type || type == SearchType::Int32Type || type == SearchType::Int64Type; }
|
||||
|
@ -109,7 +114,7 @@ public:
|
|||
SearchType getType() const { return type; }
|
||||
QByteArray getArrayValue() const { return isArrayValue() ? value.toByteArray() : QByteArray(); }
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
T getValue() const
|
||||
{
|
||||
return value.value<T>();
|
||||
|
@ -136,14 +141,13 @@ private:
|
|||
std::vector<SearchResult> m_searchResults;
|
||||
SearchComparisonLabelMap m_searchComparisonLabelMap;
|
||||
Ui::MemorySearchWidget m_ui;
|
||||
DebugInterface* m_cpu;
|
||||
QTimer m_resultsLoadTimer;
|
||||
|
||||
u32 m_initialResultsLoadLimit = 20000;
|
||||
u32 m_numResultsAddedPerLoad = 10000;
|
||||
|
||||
void updateSearchComparisonSelections();
|
||||
std::vector<SearchComparison> getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult> &existingResults);
|
||||
std::vector<SearchComparison> getValidSearchComparisonsForState(SearchType type, std::vector<SearchResult>& existingResults);
|
||||
SearchType getCurrentSearchType();
|
||||
SearchComparison getCurrentSearchComparison();
|
||||
};
|
|
@ -173,6 +173,10 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
|
|||
|
||||
void MemoryViewTable::SelectAt(QPoint pos)
|
||||
{
|
||||
// Check if SelectAt was called before DrawTable.
|
||||
if (rowHeight == 0)
|
||||
return;
|
||||
|
||||
const u32 selectedRow = (pos.y() - 2) / (rowHeight);
|
||||
const s32 x = pos.x();
|
||||
const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0];
|
||||
|
@ -447,37 +451,37 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
|
|||
/*
|
||||
MemoryViewWidget
|
||||
*/
|
||||
MemoryViewWidget::MemoryViewWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
MemoryViewWidget::MemoryViewWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu)
|
||||
, m_table(this)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
this->setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
connect(this, &MemoryViewWidget::customContextMenuRequested, this, &MemoryViewWidget::customMenuRequested);
|
||||
|
||||
m_table.SetCpu(&cpu);
|
||||
m_table.UpdateStartAddress(0x480000);
|
||||
|
||||
applyMonospaceFont();
|
||||
}
|
||||
|
||||
MemoryViewWidget::~MemoryViewWidget() = default;
|
||||
|
||||
void MemoryViewWidget::SetCpu(DebugInterface* cpu)
|
||||
{
|
||||
m_cpu = cpu;
|
||||
m_table.SetCpu(cpu);
|
||||
m_table.UpdateStartAddress(0x480000);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
return;
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
painter.fillRect(rect(), palette().window());
|
||||
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
m_table.DrawTable(painter, this->palette(), this->height());
|
||||
}
|
||||
|
||||
void MemoryViewWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
m_table.SelectAt(event->pos());
|
||||
|
@ -486,7 +490,7 @@ void MemoryViewWidget::mousePressEvent(QMouseEvent* event)
|
|||
|
||||
void MemoryViewWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
if (!m_contextMenu)
|
||||
|
@ -571,7 +575,7 @@ void MemoryViewWidget::customMenuRequested(QPoint pos)
|
|||
|
||||
void MemoryViewWidget::contextCopyByte()
|
||||
{
|
||||
QApplication::clipboard()->setText(QString::number(m_cpu->read8(m_table.selectedAddress), 16).toUpper());
|
||||
QApplication::clipboard()->setText(QString::number(cpu().read8(m_table.selectedAddress), 16).toUpper());
|
||||
}
|
||||
|
||||
void MemoryViewWidget::contextCopySegment()
|
||||
|
@ -581,7 +585,7 @@ void MemoryViewWidget::contextCopySegment()
|
|||
|
||||
void MemoryViewWidget::contextCopyCharacter()
|
||||
{
|
||||
QApplication::clipboard()->setText(QChar::fromLatin1(m_cpu->read8(m_table.selectedAddress)).toUpper());
|
||||
QApplication::clipboard()->setText(QChar::fromLatin1(cpu().read8(m_table.selectedAddress)).toUpper());
|
||||
}
|
||||
|
||||
void MemoryViewWidget::contextPaste()
|
||||
|
@ -600,7 +604,7 @@ void MemoryViewWidget::contextGoToAddress()
|
|||
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ui_RegisterWidget.h"
|
||||
#include "ui_MemoryViewWidget.h"
|
||||
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/DisassemblyManager.h"
|
||||
|
@ -105,16 +107,14 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class MemoryViewWidget final : public QWidget
|
||||
class MemoryViewWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MemoryViewWidget(QWidget* parent);
|
||||
MemoryViewWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
~MemoryViewWidget();
|
||||
|
||||
void SetCpu(DebugInterface* cpu);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
@ -138,7 +138,7 @@ signals:
|
|||
void VMUpdate();
|
||||
|
||||
private:
|
||||
Ui::RegisterWidget ui;
|
||||
Ui::MemoryViewWidget ui;
|
||||
|
||||
QMenu* m_contextMenu = 0x0;
|
||||
QAction* m_actionLittleEndian;
|
||||
|
@ -147,6 +147,5 @@ private:
|
|||
QAction* m_actionWORD;
|
||||
QAction* m_actionDWORD;
|
||||
|
||||
DebugInterface* m_cpu;
|
||||
MemoryViewTable m_table;
|
||||
};
|
|
@ -10,6 +10,9 @@
|
|||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Memory</string>
|
||||
</property>
|
|
@ -0,0 +1,158 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "SavedAddressesWidget.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
#include "Debugger/DebuggerSettingsManager.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QMenu>
|
||||
|
||||
SavedAddressesWidget::SavedAddressesWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
, m_model(cpu)
|
||||
{
|
||||
//m_ui.savedAddressesList->setModel(&m_model);
|
||||
//m_ui.savedAddressesList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
//connect(m_ui.savedAddressesList, &QTableView::customContextMenuRequested, this, &CpuWidget::onSavedAddressesListContextMenu);
|
||||
//for (std::size_t i = 0; auto mode : SavedAddressesModel::HeaderResizeModes)
|
||||
//{
|
||||
// m_ui.savedAddressesList->horizontalHeader()->setSectionResizeMode(i++, mode);
|
||||
//}
|
||||
//QTableView* savedAddressesTableView = m_ui.savedAddressesList;
|
||||
//connect(m_ui.savedAddressesList->model(), &QAbstractItemModel::dataChanged, [savedAddressesTableView](const QModelIndex& topLeft) {
|
||||
// savedAddressesTableView->resizeColumnToContents(topLeft.column());
|
||||
//});
|
||||
}
|
||||
|
||||
void SavedAddressesWidget::onContextMenu(QPoint pos)
|
||||
{
|
||||
QMenu* contextMenu = new QMenu("Saved Addresses List Context Menu", m_ui.savedAddressesList);
|
||||
|
||||
QAction* newAction = new QAction(tr("New"), m_ui.savedAddressesList);
|
||||
connect(newAction, &QAction::triggered, this, &SavedAddressesWidget::contextNew);
|
||||
contextMenu->addAction(newAction);
|
||||
|
||||
const QModelIndex indexAtPos = m_ui.savedAddressesList->indexAt(pos);
|
||||
const bool isIndexValid = indexAtPos.isValid();
|
||||
|
||||
if (isIndexValid)
|
||||
{
|
||||
if (cpu().isAlive())
|
||||
{
|
||||
QAction* goToAddressMemViewAction = new QAction(tr("Go to in Memory View"), m_ui.savedAddressesList);
|
||||
connect(goToAddressMemViewAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
|
||||
//m_ui.memoryviewWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
//m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
not_yet_implemented();
|
||||
});
|
||||
contextMenu->addAction(goToAddressMemViewAction);
|
||||
|
||||
QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList);
|
||||
connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
const QModelIndex rowAddressIndex =
|
||||
m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
|
||||
//m_ui.disassemblyWidget->gotoAddressAndSetFocus(
|
||||
// m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
|
||||
not_yet_implemented();
|
||||
});
|
||||
contextMenu->addAction(goToAddressDisassemblyAction);
|
||||
}
|
||||
|
||||
QAction* copyAction = new QAction(indexAtPos.column() == 0 ? tr("Copy Address") : tr("Copy Text"), m_ui.savedAddressesList);
|
||||
connect(copyAction, &QAction::triggered, [this, indexAtPos]() {
|
||||
QGuiApplication::clipboard()->setText(
|
||||
m_ui.savedAddressesList->model()->data(indexAtPos, Qt::DisplayRole).toString());
|
||||
});
|
||||
contextMenu->addAction(copyAction);
|
||||
}
|
||||
|
||||
if (m_ui.savedAddressesList->model()->rowCount() > 0)
|
||||
{
|
||||
QAction* actionExportCSV = new QAction(tr("Copy all as CSV"), m_ui.savedAddressesList);
|
||||
connect(actionExportCSV, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(
|
||||
QtUtils::AbstractItemModelToCSV(m_ui.savedAddressesList->model(), Qt::DisplayRole, true));
|
||||
});
|
||||
contextMenu->addAction(actionExportCSV);
|
||||
}
|
||||
|
||||
QAction* actionImportCSV = new QAction(tr("Paste from CSV"), m_ui.savedAddressesList);
|
||||
connect(actionImportCSV, &QAction::triggered, this, &SavedAddressesWidget::contextPasteCSV);
|
||||
contextMenu->addAction(actionImportCSV);
|
||||
|
||||
if (cpu().isAlive())
|
||||
{
|
||||
QAction* actionLoad = new QAction(tr("Load from Settings"), m_ui.savedAddressesList);
|
||||
connect(actionLoad, &QAction::triggered, [this]() {
|
||||
m_model.clear();
|
||||
DebuggerSettingsManager::loadGameSettings(&m_model);
|
||||
});
|
||||
contextMenu->addAction(actionLoad);
|
||||
|
||||
QAction* actionSave = new QAction(tr("Save to Settings"), m_ui.savedAddressesList);
|
||||
connect(actionSave, &QAction::triggered, this, &SavedAddressesWidget::saveToDebuggerSettings);
|
||||
contextMenu->addAction(actionSave);
|
||||
}
|
||||
|
||||
if (isIndexValid)
|
||||
{
|
||||
QAction* deleteAction = new QAction(tr("Delete"), m_ui.savedAddressesList);
|
||||
connect(deleteAction, &QAction::triggered, this, [this, indexAtPos]() {
|
||||
m_ui.savedAddressesList->model()->removeRows(indexAtPos.row(), 1);
|
||||
});
|
||||
contextMenu->addAction(deleteAction);
|
||||
}
|
||||
|
||||
contextMenu->popup(m_ui.savedAddressesList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SavedAddressesWidget::contextPasteCSV()
|
||||
{
|
||||
QString csv = QGuiApplication::clipboard()->text();
|
||||
// Skip header
|
||||
csv = csv.mid(csv.indexOf('\n') + 1);
|
||||
|
||||
for (const QString& line : csv.split('\n'))
|
||||
{
|
||||
QStringList fields;
|
||||
// In order to handle text with commas in them we must wrap values in quotes to mark
|
||||
// where a value starts and end so that text commas aren't identified as delimiters.
|
||||
// So matches each quote pair, parse it out, and removes the quotes to get the value.
|
||||
QRegularExpression eachQuotePair(R"("([^"]|\\.)*")");
|
||||
QRegularExpressionMatchIterator it = eachQuotePair.globalMatch(line);
|
||||
while (it.hasNext())
|
||||
{
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString matchedValue = match.captured(0);
|
||||
fields << matchedValue.mid(1, matchedValue.length() - 2);
|
||||
}
|
||||
|
||||
m_model.loadSavedAddressFromFieldList(fields);
|
||||
}
|
||||
}
|
||||
|
||||
void SavedAddressesWidget::contextNew()
|
||||
{
|
||||
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
|
||||
const u32 rowCount = m_ui.savedAddressesList->model()->rowCount();
|
||||
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 0));
|
||||
}
|
||||
|
||||
void SavedAddressesWidget::addAddress(u32 address)
|
||||
{
|
||||
qobject_cast<SavedAddressesModel*>(m_ui.savedAddressesList->model())->addRow();
|
||||
const u32 rowCount = m_ui.savedAddressesList->model()->rowCount();
|
||||
const QModelIndex addressIndex = m_ui.savedAddressesList->model()->index(rowCount - 1, 0);
|
||||
//m_ui.tabWidget->setCurrentWidget(m_ui.tab_savedaddresses);
|
||||
not_yet_implemented();
|
||||
m_ui.savedAddressesList->model()->setData(addressIndex, address, Qt::UserRole);
|
||||
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1));
|
||||
}
|
||||
|
||||
void SavedAddressesWidget::saveToDebuggerSettings()
|
||||
{
|
||||
DebuggerSettingsManager::saveGameSettings(&m_model);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_SavedAddressesWidget.h"
|
||||
|
||||
#include "SavedAddressesModel.h"
|
||||
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
|
||||
class SavedAddressesWidget : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SavedAddressesWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
|
||||
void onContextMenu(QPoint pos);
|
||||
void contextPasteCSV();
|
||||
void contextNew();
|
||||
void addAddress(u32 address);
|
||||
void saveToDebuggerSettings();
|
||||
|
||||
private:
|
||||
Ui::SavedAddressesWidget m_ui;
|
||||
|
||||
SavedAddressesModel m_model;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SavedAddressesWidget</class>
|
||||
<widget class="QWidget" name="SavedAddressesWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Saved Addresses</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="savedAddressesList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
using namespace QtUtils;
|
||||
|
||||
RegisterWidget::RegisterWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
RegisterWidget::RegisterWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu)
|
||||
{
|
||||
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
|
||||
|
@ -30,21 +30,19 @@ RegisterWidget::RegisterWidget(QWidget* parent)
|
|||
|
||||
connect(this, &RegisterWidget::customContextMenuRequested, this, &RegisterWidget::customMenuRequested);
|
||||
connect(ui.registerTabs, &QTabBar::currentChanged, this, &RegisterWidget::tabCurrentChanged);
|
||||
};
|
||||
|
||||
RegisterWidget::~RegisterWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void RegisterWidget::SetCpu(DebugInterface* cpu)
|
||||
{
|
||||
m_cpu = cpu;
|
||||
for (int i = 0; i < m_cpu->getRegisterCategoryCount(); i++)
|
||||
for (int i = 0; i < cpu.getRegisterCategoryCount(); i++)
|
||||
{
|
||||
ui.registerTabs->addTab(m_cpu->getRegisterCategoryName(i));
|
||||
ui.registerTabs->addTab(cpu.getRegisterCategoryName(i));
|
||||
}
|
||||
|
||||
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); });
|
||||
|
||||
applyMonospaceFont();
|
||||
}
|
||||
|
||||
RegisterWidget::~RegisterWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void RegisterWidget::tabCurrentChanged(int cur)
|
||||
|
@ -54,9 +52,6 @@ void RegisterWidget::tabCurrentChanged(int cur)
|
|||
|
||||
void RegisterWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
if (!m_cpu)
|
||||
return;
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setPen(this->palette().text().color());
|
||||
m_renderStart = QPoint(0, ui.registerTabs->pos().y() + ui.registerTabs->size().height());
|
||||
|
@ -94,9 +89,9 @@ void RegisterWidget::paintEvent(QPaintEvent* event)
|
|||
// off of that.
|
||||
// Can probably constexpr the loop out as register names are known during runtime
|
||||
int safeValueStartX = 0;
|
||||
for (int i = 0; i < m_cpu->getRegisterCount(categoryIndex); i++)
|
||||
for (int i = 0; i < cpu().getRegisterCount(categoryIndex); i++)
|
||||
{
|
||||
const int registerNameWidth = strlen(m_cpu->getRegisterName(categoryIndex, i));
|
||||
const int registerNameWidth = strlen(cpu().getRegisterName(categoryIndex, i));
|
||||
if (safeValueStartX < registerNameWidth)
|
||||
{
|
||||
safeValueStartX = registerNameWidth;
|
||||
|
@ -110,7 +105,7 @@ void RegisterWidget::paintEvent(QPaintEvent* event)
|
|||
// Make it relative to where we start rendering
|
||||
safeValueStartX += m_renderStart.x();
|
||||
|
||||
for (s32 i = 0; i < m_cpu->getRegisterCount(categoryIndex) - m_rowStart; i++)
|
||||
for (s32 i = 0; i < cpu().getRegisterCount(categoryIndex) - m_rowStart; i++)
|
||||
{
|
||||
const s32 registerIndex = i + m_rowStart;
|
||||
const int yStart = (i * m_rowHeight) + m_renderStart.y();
|
||||
|
@ -120,11 +115,11 @@ void RegisterWidget::paintEvent(QPaintEvent* event)
|
|||
|
||||
// Draw register name
|
||||
painter.setPen(this->palette().text().color());
|
||||
painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, m_cpu->getRegisterName(categoryIndex, registerIndex));
|
||||
painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, cpu().getRegisterName(categoryIndex, registerIndex));
|
||||
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
{
|
||||
const u128 curRegister = m_cpu->getRegister(categoryIndex, registerIndex);
|
||||
const u128 curRegister = cpu().getRegister(categoryIndex, registerIndex);
|
||||
|
||||
int regIndex = 3;
|
||||
for (int j = 0; j < 4; j++)
|
||||
|
@ -136,7 +131,7 @@ void RegisterWidget::paintEvent(QPaintEvent* event)
|
|||
|
||||
if (categoryIndex == EECAT_VU0F && m_showVU0FFloat)
|
||||
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight, Qt::AlignLeft,
|
||||
painter.fontMetrics().elidedText(QString::number(std::bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth()));
|
||||
painter.fontMetrics().elidedText(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth()));
|
||||
else
|
||||
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight,
|
||||
Qt::AlignLeft, FilledQStringFromValue(curRegister._u32[regIndex], 16));
|
||||
|
@ -153,13 +148,13 @@ void RegisterWidget::paintEvent(QPaintEvent* event)
|
|||
|
||||
if (categoryIndex == EECAT_FPR && m_showFPRFloat)
|
||||
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
|
||||
QString("%1").arg(QString::number(std::bit_cast<float>(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper());
|
||||
else if (m_cpu->getRegisterSize(categoryIndex) == 64)
|
||||
QString("%1").arg(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper());
|
||||
else if (cpu().getRegisterSize(categoryIndex) == 64)
|
||||
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
|
||||
FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex).lo, 16));
|
||||
FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex).lo, 16));
|
||||
else
|
||||
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
|
||||
FilledQStringFromValue(m_cpu->getRegister(categoryIndex, registerIndex)._u32[0], 16));
|
||||
FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex)._u32[0], 16));
|
||||
}
|
||||
}
|
||||
painter.end();
|
||||
|
@ -171,7 +166,7 @@ void RegisterWidget::mousePressEvent(QMouseEvent* event)
|
|||
m_selectedRow = static_cast<int>(((event->position().y() - m_renderStart.y()) / m_rowHeight)) + m_rowStart;
|
||||
|
||||
// For 128 bit types, support selecting segments
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
{
|
||||
constexpr auto inRange = [](u32 low, u32 high, u32 val) {
|
||||
return (low <= val && val <= high);
|
||||
|
@ -190,7 +185,7 @@ void RegisterWidget::mousePressEvent(QMouseEvent* event)
|
|||
|
||||
void RegisterWidget::wheelEvent(QWheelEvent* event)
|
||||
{
|
||||
if (event->angleDelta().y() < 0 && m_rowEnd < m_cpu->getRegisterCount(ui.registerTabs->currentIndex()))
|
||||
if (event->angleDelta().y() < 0 && m_rowEnd < cpu().getRegisterCount(ui.registerTabs->currentIndex()))
|
||||
{
|
||||
m_rowStart += 1;
|
||||
}
|
||||
|
@ -204,12 +199,12 @@ void RegisterWidget::wheelEvent(QWheelEvent* event)
|
|||
|
||||
void RegisterWidget::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative)
|
||||
return;
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
contextChangeSegment();
|
||||
else
|
||||
contextChangeValue();
|
||||
|
@ -217,7 +212,7 @@ void RegisterWidget::mouseDoubleClickEvent(QMouseEvent* event)
|
|||
|
||||
void RegisterWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
if (!m_cpu->isAlive())
|
||||
if (!cpu().isAlive())
|
||||
return;
|
||||
|
||||
if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative)
|
||||
|
@ -248,7 +243,7 @@ void RegisterWidget::customMenuRequested(QPoint pos)
|
|||
m_contextMenu->addSeparator();
|
||||
}
|
||||
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
{
|
||||
m_contextMenu->addAction(action = new QAction(tr("Copy Top Half"), this));
|
||||
connect(action, &QAction::triggered, this, &RegisterWidget::contextCopyTop);
|
||||
|
@ -265,7 +260,7 @@ void RegisterWidget::customMenuRequested(QPoint pos)
|
|||
|
||||
m_contextMenu->addSeparator();
|
||||
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
{
|
||||
m_contextMenu->addAction(action = new QAction(tr("Change Top Half"), this));
|
||||
connect(action, &QAction::triggered, this, &RegisterWidget::contextChangeTop);
|
||||
|
@ -295,7 +290,7 @@ void RegisterWidget::customMenuRequested(QPoint pos)
|
|||
void RegisterWidget::contextCopyValue()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
if (CAT_SHOW_FLOAT)
|
||||
QApplication::clipboard()->setText(QString("%1").arg(QString::number(std::bit_cast<float>(val._u32[0])).toUpper(), 16));
|
||||
else
|
||||
|
@ -305,21 +300,21 @@ void RegisterWidget::contextCopyValue()
|
|||
void RegisterWidget::contextCopyTop()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
QApplication::clipboard()->setText(FilledQStringFromValue(val.hi, 16));
|
||||
}
|
||||
|
||||
void RegisterWidget::contextCopyBottom()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
QApplication::clipboard()->setText(FilledQStringFromValue(val.lo, 16));
|
||||
}
|
||||
|
||||
void RegisterWidget::contextCopySegment()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const u128 val = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
if (CAT_SHOW_FLOAT)
|
||||
QApplication::clipboard()->setText(FilledQStringFromValue(std::bit_cast<float>(val._u32[3 - m_selected128Field]), 10));
|
||||
else
|
||||
|
@ -330,7 +325,7 @@ bool RegisterWidget::contextFetchNewValue(u64& out, u64 currentValue, bool segme
|
|||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const bool floatingPoint = CAT_SHOW_FLOAT && segment;
|
||||
const int regSize = m_cpu->getRegisterSize(categoryIndex);
|
||||
const int regSize = cpu().getRegisterSize(categoryIndex);
|
||||
bool ok = false;
|
||||
|
||||
QString existingValue("%1");
|
||||
|
@ -341,7 +336,7 @@ bool RegisterWidget::contextFetchNewValue(u64& out, u64 currentValue, bool segme
|
|||
existingValue = existingValue.arg(std::bit_cast<float>((u32)currentValue));
|
||||
|
||||
//: Changing the value in a CPU register (e.g. "Change t0")
|
||||
QString input = QInputDialog::getText(this, tr("Change %1").arg(m_cpu->getRegisterName(categoryIndex, m_selectedRow)), "",
|
||||
QString input = QInputDialog::getText(this, tr("Change %1").arg(cpu().getRegisterName(categoryIndex, m_selectedRow)), "",
|
||||
QLineEdit::Normal, existingValue, &ok);
|
||||
|
||||
if (!ok)
|
||||
|
@ -373,9 +368,9 @@ void RegisterWidget::contextChangeValue()
|
|||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
u64 newVal;
|
||||
if (contextFetchNewValue(newVal, m_cpu->getRegister(categoryIndex, m_selectedRow).lo))
|
||||
if (contextFetchNewValue(newVal, cpu().getRegister(categoryIndex, m_selectedRow).lo))
|
||||
{
|
||||
m_cpu->setRegister(categoryIndex, m_selectedRow, u128::From64(newVal));
|
||||
cpu().setRegister(categoryIndex, m_selectedRow, u128::From64(newVal));
|
||||
VMUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -383,11 +378,11 @@ void RegisterWidget::contextChangeValue()
|
|||
void RegisterWidget::contextChangeTop()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal.hi))
|
||||
{
|
||||
oldVal.hi = newVal;
|
||||
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
VMUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -395,11 +390,11 @@ void RegisterWidget::contextChangeTop()
|
|||
void RegisterWidget::contextChangeBottom()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal.lo))
|
||||
{
|
||||
oldVal.lo = newVal;
|
||||
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
VMUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -407,11 +402,11 @@ void RegisterWidget::contextChangeBottom()
|
|||
void RegisterWidget::contextChangeSegment()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = m_cpu->getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal._u32[3 - m_selected128Field], true))
|
||||
{
|
||||
oldVal._u32[3 - m_selected128Field] = (u32)newVal;
|
||||
m_cpu->setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
VMUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -419,15 +414,15 @@ void RegisterWidget::contextChangeSegment()
|
|||
void RegisterWidget::contextGotoDisasm()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
u128 regVal = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
u32 addr = 0;
|
||||
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
addr = regVal._u32[3 - m_selected128Field];
|
||||
else
|
||||
addr = regVal._u32[0];
|
||||
|
||||
if (m_cpu->isValidAddress(addr))
|
||||
if (cpu().isValidAddress(addr))
|
||||
gotoInDisasm(addr);
|
||||
else
|
||||
QMessageBox::warning(this, tr("Invalid target address"), ("This register holds an invalid address."));
|
||||
|
@ -436,10 +431,10 @@ void RegisterWidget::contextGotoDisasm()
|
|||
void RegisterWidget::contextGotoMemory()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
u128 regVal = m_cpu->getRegister(categoryIndex, m_selectedRow);
|
||||
u128 regVal = cpu().getRegister(categoryIndex, m_selectedRow);
|
||||
u32 addr = 0;
|
||||
|
||||
if (m_cpu->getRegisterSize(categoryIndex) == 128)
|
||||
if (cpu().getRegisterSize(categoryIndex) == 128)
|
||||
addr = regVal._u32[3 - m_selected128Field];
|
||||
else
|
||||
addr = regVal._u32[0];
|
||||
|
|
|
@ -5,24 +5,23 @@
|
|||
|
||||
#include "ui_RegisterWidget.h"
|
||||
|
||||
#include "DebuggerWidget.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/DisassemblyManager.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QTabBar>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
class RegisterWidget final : public QWidget
|
||||
class RegisterWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RegisterWidget(QWidget* parent);
|
||||
RegisterWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
~RegisterWidget();
|
||||
|
||||
void SetCpu(DebugInterface* cpu);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
@ -58,8 +57,6 @@ private:
|
|||
// Returns true on success
|
||||
bool contextFetchNewValue(u64& out, u64 currentValue, bool segment = false);
|
||||
|
||||
DebugInterface* m_cpu;
|
||||
|
||||
// Used for the height offset the tab bar creates
|
||||
// because we share a widget
|
||||
QPoint m_renderStart;
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "StackWidget.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QMenu>
|
||||
|
||||
StackWidget::StackWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
, m_model(cpu)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &StackWidget::onContextMenu);
|
||||
connect(m_ui.stackList, &QTableView::doubleClicked, this, &StackWidget::onDoubleClick);
|
||||
|
||||
m_ui.stackList->setModel(&m_model);
|
||||
for (std::size_t i = 0; auto mode : StackModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.stackList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void StackWidget::onContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_ui.stackList->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList);
|
||||
|
||||
QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList);
|
||||
connect(actionCopy, &QAction::triggered, [this]() {
|
||||
const auto* selModel = m_ui.stackList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString());
|
||||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.stackList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.stackList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.stackList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void StackWidget::onDoubleClick(const QModelIndex& index)
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case StackModel::StackModel::ENTRY:
|
||||
case StackModel::StackModel::ENTRY_LABEL:
|
||||
//m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
|
||||
not_yet_implemented();
|
||||
break;
|
||||
case StackModel::StackModel::SP:
|
||||
//m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt());
|
||||
//m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
not_yet_implemented();
|
||||
break;
|
||||
default: // Default to PC
|
||||
//m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
|
||||
not_yet_implemented();
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_StackWidget.h"
|
||||
|
||||
#include "StackModel.h"
|
||||
|
||||
#include "DebuggerWidget.h"
|
||||
|
||||
class StackWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StackWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
|
||||
void onContextMenu(QPoint pos);
|
||||
void onDoubleClick(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
Ui::StackWidget m_ui;
|
||||
|
||||
StackModel m_model;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StackWidget</class>
|
||||
<widget class="QWidget" name="StackWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Stack</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="stackList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -6,12 +6,12 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>419</width>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string>Symbol Tree</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vertical_layout">
|
||||
<property name="spacing">
|
||||
|
|
|
@ -14,8 +14,12 @@
|
|||
|
||||
static bool testName(const QString& name, const QString& filter);
|
||||
|
||||
SymbolTreeWidget::SymbolTreeWidget(u32 flags, s32 symbol_address_alignment, DebugInterface& cpu, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
SymbolTreeWidget::SymbolTreeWidget(
|
||||
u32 flags,
|
||||
s32 symbol_address_alignment,
|
||||
DebugInterface& cpu,
|
||||
QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
, m_cpu(cpu)
|
||||
, m_flags(flags)
|
||||
, m_symbol_address_alignment(symbol_address_alignment)
|
||||
|
@ -661,7 +665,11 @@ SymbolTreeNode* SymbolTreeWidget::currentNode()
|
|||
// *****************************************************************************
|
||||
|
||||
FunctionTreeWidget::FunctionTreeWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: SymbolTreeWidget(ALLOW_GROUPING | ALLOW_MANGLED_NAME_ACTIONS, 4, cpu, parent)
|
||||
: SymbolTreeWidget(
|
||||
ALLOW_GROUPING | ALLOW_MANGLED_NAME_ACTIONS,
|
||||
4,
|
||||
cpu,
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -745,7 +753,11 @@ void FunctionTreeWidget::onNewButtonPressed()
|
|||
// *****************************************************************************
|
||||
|
||||
GlobalVariableTreeWidget::GlobalVariableTreeWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: SymbolTreeWidget(ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS | ALLOW_MANGLED_NAME_ACTIONS, 1, cpu, parent)
|
||||
: SymbolTreeWidget(
|
||||
ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS | ALLOW_MANGLED_NAME_ACTIONS,
|
||||
1,
|
||||
cpu,
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -884,7 +896,11 @@ void GlobalVariableTreeWidget::onNewButtonPressed()
|
|||
// *****************************************************************************
|
||||
|
||||
LocalVariableTreeWidget::LocalVariableTreeWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: SymbolTreeWidget(ALLOW_TYPE_ACTIONS, 1, cpu, parent)
|
||||
: SymbolTreeWidget(
|
||||
ALLOW_TYPE_ACTIONS,
|
||||
1,
|
||||
cpu,
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1009,7 +1025,11 @@ void LocalVariableTreeWidget::onNewButtonPressed()
|
|||
// *****************************************************************************
|
||||
|
||||
ParameterVariableTreeWidget::ParameterVariableTreeWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: SymbolTreeWidget(ALLOW_TYPE_ACTIONS, 1, cpu, parent)
|
||||
: SymbolTreeWidget(
|
||||
ALLOW_TYPE_ACTIONS,
|
||||
1,
|
||||
cpu,
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
#include "Debugger/DebuggerWidget.h"
|
||||
#include "SymbolTreeModel.h"
|
||||
|
||||
#include "ui_SymbolTreeWidget.h"
|
||||
|
@ -12,7 +12,7 @@ struct SymbolFilters;
|
|||
|
||||
// A symbol tree widget with its associated refresh button, filter box and
|
||||
// right-click menu. Supports grouping, sorting and various other settings.
|
||||
class SymbolTreeWidget : public QWidget
|
||||
class SymbolTreeWidget : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -41,7 +41,11 @@ protected:
|
|||
const ccc::SourceFile* source_file = nullptr;
|
||||
};
|
||||
|
||||
SymbolTreeWidget(u32 flags, s32 symbol_address_alignment, DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
SymbolTreeWidget(
|
||||
u32 flags,
|
||||
s32 symbol_address_alignment,
|
||||
DebugInterface& cpu,
|
||||
QWidget* parent = nullptr);
|
||||
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "ThreadWidget.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
ThreadWidget::ThreadWidget(DebugInterface& cpu, QWidget* parent)
|
||||
: DebuggerWidget(&cpu, parent)
|
||||
, m_model(cpu)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(m_ui.threadList, &QTableView::customContextMenuRequested, this, &ThreadWidget::onContextMenu);
|
||||
connect(m_ui.threadList, &QTableView::doubleClicked, this, &ThreadWidget::onDoubleClick);
|
||||
|
||||
m_proxy_model.setSourceModel(&m_model);
|
||||
m_proxy_model.setSortRole(Qt::UserRole);
|
||||
m_ui.threadList->setModel(&m_proxy_model);
|
||||
m_ui.threadList->setSortingEnabled(true);
|
||||
m_ui.threadList->sortByColumn(ThreadModel::ThreadColumns::ID, Qt::SortOrder::AscendingOrder);
|
||||
for (std::size_t i = 0; auto mode : ThreadModel::HeaderResizeModes)
|
||||
{
|
||||
m_ui.threadList->horizontalHeader()->setSectionResizeMode(i, mode);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadWidget::onContextMenu(QPoint pos)
|
||||
{
|
||||
if (!m_ui.threadList->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QMenu* contextMenu = new QMenu(tr("Thread List Context Menu"), m_ui.threadList);
|
||||
|
||||
QAction* actionCopy = new QAction(tr("Copy"), m_ui.threadList);
|
||||
connect(actionCopy, &QAction::triggered, [this]() {
|
||||
const auto* selModel = m_ui.threadList->selectionModel();
|
||||
|
||||
if (!selModel->hasSelection())
|
||||
return;
|
||||
|
||||
QGuiApplication::clipboard()->setText(m_ui.threadList->model()->data(selModel->currentIndex()).toString());
|
||||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.threadList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.threadList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.threadList->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void ThreadWidget::onDoubleClick(const QModelIndex& index)
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case ThreadModel::ThreadColumns::ENTRY:
|
||||
not_yet_implemented();
|
||||
//m_ui.memoryviewWidget->gotoAddress(m_ui.threadList->model()->data(index, Qt::UserRole).toUInt());
|
||||
//m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
|
||||
break;
|
||||
default: // Default to PC
|
||||
not_yet_implemented();
|
||||
//m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_ThreadWidget.h"
|
||||
|
||||
#include "DebuggerWidget.h"
|
||||
#include "ThreadModel.h"
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class ThreadWidget final : public DebuggerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThreadWidget(DebugInterface& cpu, QWidget* parent = nullptr);
|
||||
|
||||
void onContextMenu(QPoint pos);
|
||||
void onDoubleClick(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
Ui::ThreadWidget m_ui;
|
||||
|
||||
ThreadModel m_model;
|
||||
QSortFilterProxyModel m_proxy_model;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ThreadWidget</class>
|
||||
<widget class="QWidget" name="ThreadWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Threads</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="threadList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -2615,8 +2615,13 @@ void MainWindow::doSettings(const char* category /* = nullptr */)
|
|||
DebuggerWindow* MainWindow::getDebuggerWindow()
|
||||
{
|
||||
if (!m_debugger_window)
|
||||
{
|
||||
// Setup KDDockWidgets.
|
||||
DockManager::configureDockingSystem();
|
||||
|
||||
// Don't pass us (this) as the parent, otherwise the window is always on top of the mainwindow (on windows at least)
|
||||
m_debugger_window = new DebuggerWindow(nullptr);
|
||||
}
|
||||
|
||||
return m_debugger_window;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ Q_DECLARE_METATYPE(std::optional<bool>);
|
|||
Q_DECLARE_METATYPE(GSRendererType);
|
||||
Q_DECLARE_METATYPE(InputBindingKey);
|
||||
Q_DECLARE_METATYPE(CDVD_SourceType);
|
||||
Q_DECLARE_METATYPE(Achievements::LoginRequestReason);
|
||||
|
||||
class EmuThread : public QThread
|
||||
{
|
||||
|
@ -114,7 +113,7 @@ public Q_SLOTS:
|
|||
void endCapture();
|
||||
void setAudioOutputVolume(int volume, int fast_forward_volume);
|
||||
void setAudioOutputMuted(bool muted);
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
bool messageConfirmed(const QString& title, const QString& message);
|
||||
void statusMessage(const QString& message);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "Config.h"
|
||||
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class SettingsWindow;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>527</width>
|
||||
<width>647</width>
|
||||
<height>501</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -31,6 +31,11 @@
|
|||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="userInterfaceWidget">
|
||||
<attribute name="title">
|
||||
<string>User Interface</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="analysisTabWidget">
|
||||
<attribute name="title">
|
||||
<string>Analysis</string>
|
||||
|
@ -58,8 +63,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>464</height>
|
||||
<width>645</width>
|
||||
<height>469</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
|
|
|
@ -1363,7 +1363,6 @@ namespace EmuFolders
|
|||
extern std::string AppRoot;
|
||||
extern std::string DataRoot;
|
||||
extern std::string Settings;
|
||||
extern std::string DebuggerSettings;
|
||||
extern std::string Bios;
|
||||
extern std::string Snapshots;
|
||||
extern std::string Savestates;
|
||||
|
@ -1379,6 +1378,8 @@ namespace EmuFolders
|
|||
extern std::string Textures;
|
||||
extern std::string InputProfiles;
|
||||
extern std::string Videos;
|
||||
extern std::string DebuggerLayouts;
|
||||
extern std::string DebuggerSettings;
|
||||
|
||||
/// Initializes critical folders (AppRoot, DataRoot, Settings). Call once on startup.
|
||||
void SetAppRoot();
|
||||
|
|
|
@ -152,6 +152,7 @@ namespace EmuFolders
|
|||
std::string AppRoot;
|
||||
std::string DataRoot;
|
||||
std::string Settings;
|
||||
std::string DebuggerLayouts;
|
||||
std::string DebuggerSettings;
|
||||
std::string Bios;
|
||||
std::string Snapshots;
|
||||
|
@ -2203,6 +2204,8 @@ void EmuFolders::SetDefaults(SettingsInterface& si)
|
|||
si.SetStringValue("Folders", "Textures", "textures");
|
||||
si.SetStringValue("Folders", "InputProfiles", "inputprofiles");
|
||||
si.SetStringValue("Folders", "Videos", "videos");
|
||||
si.SetStringValue("Folders", "DebuggerLayouts", "debuggerlayouts");
|
||||
si.SetStringValue("Folders", "DebuggerSettings", "debuggersettings");
|
||||
}
|
||||
|
||||
static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* name, const char* def)
|
||||
|
@ -2229,6 +2232,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
|||
Textures = LoadPathFromSettings(si, DataRoot, "Textures", "textures");
|
||||
InputProfiles = LoadPathFromSettings(si, DataRoot, "InputProfiles", "inputprofiles");
|
||||
Videos = LoadPathFromSettings(si, DataRoot, "Videos", "videos");
|
||||
DebuggerLayouts = LoadPathFromSettings(si, Settings, "DebuggerLayouts", "debuggerlayouts");
|
||||
DebuggerSettings = LoadPathFromSettings(si, Settings, "DebuggerSettings", "debuggersettings");
|
||||
|
||||
Console.WriteLn("BIOS Directory: %s", Bios.c_str());
|
||||
|
@ -2246,6 +2250,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si)
|
|||
Console.WriteLn("Textures Directory: %s", Textures.c_str());
|
||||
Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str());
|
||||
Console.WriteLn("Video Dumping Directory: %s", Videos.c_str());
|
||||
Console.WriteLn("Debugger Layouts Directory: %s", DebuggerLayouts.c_str());
|
||||
Console.WriteLn("Debugger Settings Directory: %s", DebuggerSettings.c_str());
|
||||
}
|
||||
|
||||
|
@ -2262,11 +2267,12 @@ bool EmuFolders::EnsureFoldersExist()
|
|||
result = FileSystem::CreateDirectoryPath(Covers.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(UserResources.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(DebuggerSettings.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(Textures.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(Videos.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(DebuggerLayouts.c_str(), false) && result;
|
||||
result = FileSystem::CreateDirectoryPath(DebuggerSettings.c_str(), false) && result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue