removed rpcs3-qt ui
|
|
@ -1,27 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
CONFIGURE_ARGS="
|
||||
-DWITH_LLVM=ON
|
||||
-DUSE_SDL=OFF
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF
|
||||
-DUSE_SYSTEM_FFMPEG=ON
|
||||
-DUSE_SYSTEM_CURL=ON
|
||||
-DUSE_SYSTEM_LIBPNG=ON
|
||||
-DUSE_SYSTEM_OPENCV=ON
|
||||
"
|
||||
|
||||
# base Clang workaround (missing clang-scan-deps)
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS -DCMAKE_CXX_SCAN_FOR_MODULES=OFF"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cmake -B build -G Ninja $CONFIGURE_ARGS
|
||||
cmake --build build
|
||||
|
||||
ccache --show-stats
|
||||
ccache --zero-stats
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
export CC=gcc-14
|
||||
export CXX=g++-14
|
||||
else
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export CFLAGS="$CFLAGS -fuse-ld=lld"
|
||||
fi
|
||||
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CFLAGS" \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF \
|
||||
-DUSE_SYSTEM_CURL=ON \
|
||||
-DUSE_SDL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_SYSTEM_CURL=OFF \
|
||||
-DUSE_SYSTEM_OPENAL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
-DBUILD_LLVM=on \
|
||||
-DWITH_RPCSX=off \
|
||||
-DWITH_RPCS3=on \
|
||||
-DWITH_RPCS3_QT_UI=on \
|
||||
-G Ninja
|
||||
|
||||
cmake --build build; build_status=$?;
|
||||
|
||||
shellcheck .ci/*.sh
|
||||
|
||||
# If it compiled succesfully let's deploy.
|
||||
# Azure and Cirrus publish PRs as artifacts only.
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-linux.sh "aarch64"
|
||||
fi
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=gcc-14
|
||||
export CXX=g++-14
|
||||
else
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export LD=clang
|
||||
export CFLAGS="$CFLAGS -fuse-ld=lld"
|
||||
fi
|
||||
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CFLAGS" \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF \
|
||||
-DUSE_SDL=OFF \
|
||||
-DUSE_SYSTEM_CURL=OFF \
|
||||
-DUSE_SYSTEM_OPENAL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
-DBUILD_LLVM=on \
|
||||
-DWITH_RPCSX=off \
|
||||
-DWITH_RPCS3=on \
|
||||
-DWITH_RPCS3_QT_UI=on \
|
||||
-G Ninja
|
||||
|
||||
cmake --build build; build_status=$?;
|
||||
|
||||
shellcheck .ci/*.sh
|
||||
|
||||
# If it compiled succesfully let's deploy.
|
||||
# Azure and Cirrus publish PRs as artifacts only.
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-linux.sh "x86_64"
|
||||
fi
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
brew_arm64_install_packages() {
|
||||
for pkg in "$@"; do
|
||||
echo "Fetching bottle for $pkg (arm64)..."
|
||||
bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")"
|
||||
if [ ! -f "$bottle_path" ]; then
|
||||
if ! "$BREW_ARM64_PATH/bin/brew" fetch --force --verbose --debug --bottle-tag=arm64_sonoma "$pkg"; then
|
||||
echo "Failed to fetch bottle for $pkg"
|
||||
return 1
|
||||
fi
|
||||
bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")"
|
||||
fi
|
||||
|
||||
echo "Installing $pkg (arm64)..."
|
||||
"$BREW_ARM64_PATH/bin/brew" install --force --force-bottle --ignore-dependencies "$bottle_path" || true
|
||||
done
|
||||
}
|
||||
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
|
||||
/usr/local/bin/brew update
|
||||
sudo rm -rf /usr/local/Cellar/curl /usr/local/opt/curl
|
||||
/usr/local/bin/brew install -f --overwrite curl
|
||||
/usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg
|
||||
/usr/local/bin/brew install -f --build-from-source ffmpeg@5 || true
|
||||
/usr/local/bin/brew install -f --overwrite python || true
|
||||
/usr/local/bin/brew link --overwrite python || true
|
||||
/usr/local/bin/brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg
|
||||
/usr/local/bin/brew link -f curl || true
|
||||
/usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils
|
||||
/usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 || true
|
||||
|
||||
export BREW_ARM64_PATH="/opt/homebrew1"
|
||||
sudo mkdir -p "$BREW_ARM64_PATH"
|
||||
sudo chmod 777 "$BREW_ARM64_PATH"
|
||||
curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C "$BREW_ARM64_PATH"
|
||||
|
||||
#"$BREW_ARM64_PATH/bin/brew" update
|
||||
# libvorbis requires Homebrew-installed curl, but we can't run it on x64, and we also need the aarch64 libs, so we swap the binary
|
||||
brew_arm64_install_packages curl
|
||||
mv /opt/homebrew1/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl.bak
|
||||
ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl
|
||||
|
||||
brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl3 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd
|
||||
"$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5
|
||||
ln -s "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib"
|
||||
|
||||
# moltenvk based on commit for 1.2.11 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb
|
||||
/usr/local/bin/brew install -f --overwrite ./molten-vk.rb
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
export BREW_PATH;
|
||||
BREW_PATH="$(brew --prefix)"
|
||||
export BREW_X64_PATH;
|
||||
BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)"
|
||||
export BREW_BIN="/usr/local/bin"
|
||||
export BREW_SBIN="/usr/local/sbin"
|
||||
export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=arm64'
|
||||
|
||||
export WORKDIR;
|
||||
WORKDIR="$(pwd)"
|
||||
|
||||
# Get Qt
|
||||
if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
||||
mkdir -p "/tmp/Qt"
|
||||
git clone https://github.com/engnr/qt-downloader.git
|
||||
cd qt-downloader
|
||||
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
|
||||
# nested Qt 6.8.3 URL workaround
|
||||
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround
|
||||
"$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
|
||||
|
||||
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
|
||||
export SDL3_DIR="$BREW_ARM64_PATH/opt/sdl3/lib/cmake/SDL3"
|
||||
|
||||
export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
export LDFLAGS="-L$BREW_ARM64_PATH/lib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_ARM64_PATH/lib/libSDL3.dylib $BREW_ARM64_PATH/lib/libGLEW.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib -Wl,-rpath,$BREW_ARM64_PATH/lib"
|
||||
export CPPFLAGS="-I$BREW_ARM64_PATH/include -I$BREW_X64_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export LIBRARY_PATH="$BREW_ARM64_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_ARM64_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_ARM64_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="$BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
# exclude ffmpeg, LLVM, and sdl from submodule update
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SDL/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
rm -rf build
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
|
||||
"$BREW_X64_PATH/bin/cmake" .. \
|
||||
-DUSE_SDL=ON \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DUSE_VULKAN=ON \
|
||||
-DUSE_ALSA=OFF \
|
||||
-DUSE_PULSE=OFF \
|
||||
-DUSE_AUDIOUNIT=ON \
|
||||
-DUSE_SYSTEM_FFMPEG=ON \
|
||||
-DLLVM_CCACHE_BUILD=OFF \
|
||||
-DLLVM_BUILD_RUNTIME=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_DOCS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_UTILS=OFF \
|
||||
-DLLVM_USE_PERF=OFF \
|
||||
-DLLVM_ENABLE_Z3_SOLVER=OFF \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_SYSTEM_MVK=ON \
|
||||
-DUSE_SYSTEM_FAUDIO=OFF \
|
||||
-DUSE_SYSTEM_SDL=ON \
|
||||
-DUSE_SYSTEM_OPENCV=ON \
|
||||
$CMAKE_EXTRA_OPTS \
|
||||
-DLLVM_TARGET_ARCH=arm64 \
|
||||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \
|
||||
-DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=arm64 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
||||
cd ..
|
||||
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-mac-arm64.sh
|
||||
fi
|
||||
123
.ci/build-mac.sh
|
|
@ -1,123 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
||||
brew unlink certifi
|
||||
brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg
|
||||
|
||||
#/usr/sbin/softwareupdate --install-rosetta --agree-to-license
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew update
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite python || arch -x86_64 /usr/local/bin/brew link --overwrite python
|
||||
arch -x86_64 /usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg
|
||||
arch -x86_64 /usr/local/bin/brew install -f --build-from-source ffmpeg@5
|
||||
arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetype
|
||||
arch -x86_64 /usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils
|
||||
arch -x86_64 /usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5
|
||||
|
||||
# moltenvk based on commit for 1.2.11 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
export BREW_PATH;
|
||||
BREW_PATH="$(brew --prefix)"
|
||||
export BREW_X64_PATH;
|
||||
BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)"
|
||||
export BREW_BIN="/usr/local/bin"
|
||||
export BREW_SBIN="/usr/local/sbin"
|
||||
export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=X86'
|
||||
|
||||
export WORKDIR;
|
||||
WORKDIR="$(pwd)"
|
||||
|
||||
# Get Qt
|
||||
if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
||||
mkdir -p "/tmp/Qt"
|
||||
git clone https://github.com/engnr/qt-downloader.git
|
||||
cd qt-downloader
|
||||
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
|
||||
# nested Qt 6.8.3 URL workaround
|
||||
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround
|
||||
"$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
|
||||
|
||||
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
|
||||
export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3"
|
||||
|
||||
export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib"
|
||||
export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export LIBRARY_PATH="$BREW_X64_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_X64_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib"
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
# exclude ffmpeg, LLVM, opencv, and sdl from submodule update
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
|
||||
"$BREW_X64_PATH/bin/cmake" .. \
|
||||
-DUSE_SDL=ON \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DUSE_VULKAN=ON \
|
||||
-DUSE_ALSA=OFF \
|
||||
-DUSE_PULSE=OFF \
|
||||
-DUSE_AUDIOUNIT=ON \
|
||||
-DUSE_SYSTEM_FFMPEG=ON \
|
||||
-DLLVM_CCACHE_BUILD=OFF \
|
||||
-DLLVM_BUILD_RUNTIME=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_DOCS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_UTILS=OFF \
|
||||
-DLLVM_USE_PERF=OFF \
|
||||
-DLLVM_ENABLE_Z3_SOLVER=OFF \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_SYSTEM_MVK=ON \
|
||||
-DUSE_SYSTEM_FAUDIO=OFF \
|
||||
-DUSE_SYSTEM_SDL=ON \
|
||||
-DUSE_SYSTEM_OPENCV=ON \
|
||||
$CMAKE_EXTRA_OPTS \
|
||||
-DLLVM_TARGET_ARCH=X86_64 \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
||||
cd ..
|
||||
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-mac.sh
|
||||
fi
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
cd build || exit 1
|
||||
|
||||
CPU_ARCH="${1:-x86_64}"
|
||||
|
||||
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
DESTDIR=AppDir ninja install
|
||||
|
||||
sudo curl -fsSLo /usr/bin/linuxdeploy "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$CPU_ARCH.AppImage"
|
||||
sudo chmod a+x /usr/bin/linuxdeploy
|
||||
sudo curl -fsSLo /usr/bin/linuxdeploy-plugin-qt "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-$CPU_ARCH.AppImage"
|
||||
sudo chmod a+x /usr/bin/linuxdeploy-plugin-qt
|
||||
curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh
|
||||
chmod +x ./linuxdeploy-plugin-checkrt.sh
|
||||
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor"
|
||||
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt
|
||||
|
||||
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
|
||||
rm -f ./AppDir/usr/lib/libwayland-client.so*
|
||||
|
||||
# Remove libvulkan because it causes issues with gamescope
|
||||
rm -f ./AppDir/usr/lib/libvulkan.so*
|
||||
|
||||
# Remove git directory containing local commit history file
|
||||
rm -rf ./AppDir/usr/share/rpcs3/git
|
||||
|
||||
linuxdeploy --appimage-extract
|
||||
./squashfs-root/plugins/linuxdeploy-plugin-appimage/usr/bin/appimagetool AppDir -g
|
||||
|
||||
mv ./*.AppImage ../RPCS3-Qt-UI.AppImage
|
||||
fi
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cd build || exit 1
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/" || true
|
||||
|
||||
cp "$(realpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
|
||||
cp "$(realpath /opt/homebrew1/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
|
||||
cp "$(realpath /opt/homebrew1/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"
|
||||
|
||||
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQuick.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \
|
||||
"rpcs3.app/Contents/Plugins/platforminputcontexts" \
|
||||
"rpcs3.app/Contents/Plugins/virtualkeyboard" \
|
||||
"rpcs3.app/Contents/Resources/git"
|
||||
|
||||
../../.ci/optimize-mac.sh rpcs3.app
|
||||
|
||||
# Hack
|
||||
install_name_tool \
|
||||
-delete_rpath /opt/homebrew1/lib \
|
||||
-delete_rpath /opt/homebrew/lib \
|
||||
-delete_rpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib \
|
||||
-delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3
|
||||
#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib
|
||||
|
||||
# Need to do this rename hack due to case insensitive filesystem
|
||||
mv rpcs3.app RPCS3_.app
|
||||
mv RPCS3_.app RPCS3.app
|
||||
|
||||
# NOTE: "--deep" is deprecated
|
||||
codesign --deep -fs - RPCS3.app
|
||||
|
||||
echo "[InternetShortcut]" > Quickstart.url
|
||||
echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url
|
||||
echo "IconIndex=0" >> Quickstart.url
|
||||
|
||||
#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.dmg"
|
||||
#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \
|
||||
#--window-size 800 400 \
|
||||
#--icon-size 100 \
|
||||
#--icon rpcs3.app 200 190 \
|
||||
#--add-file Quickstart.url Quickstart.url 400 20 \
|
||||
#--hide-extension rpcs3.app \
|
||||
#--hide-extension Quickstart.url \
|
||||
#--app-drop-link 600 185 \
|
||||
#--skip-jenkins \
|
||||
#--format ULMO \
|
||||
#"$DMG_FILEPATH" \
|
||||
#RPCS3.app
|
||||
#FILESIZE=$(stat -f %z "$DMG_FILEPATH")
|
||||
#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.7z"
|
||||
"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url
|
||||
FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH")
|
||||
SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
cd ..
|
||||
echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE"
|
||||
cd bin
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cd build || exit 1
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/"
|
||||
|
||||
cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
|
||||
cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
|
||||
cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"
|
||||
|
||||
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQuick.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \
|
||||
"rpcs3.app/Contents/Plugins/platforminputcontexts" \
|
||||
"rpcs3.app/Contents/Plugins/virtualkeyboard" \
|
||||
"rpcs3.app/Contents/Resources/git"
|
||||
|
||||
../../.ci/optimize-mac.sh rpcs3.app
|
||||
|
||||
# Need to do this rename hack due to case insensitive filesystem
|
||||
mv rpcs3.app RPCS3_.app
|
||||
mv RPCS3_.app RPCS3.app
|
||||
|
||||
# Hack
|
||||
install_name_tool \
|
||||
-delete_rpath /usr/local/lib \
|
||||
-delete_rpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3
|
||||
#-delete_rpath /usr/local/Cellar/sdl3/3.2.8/lib
|
||||
|
||||
# NOTE: "--deep" is deprecated
|
||||
codesign --deep -fs - RPCS3.app
|
||||
|
||||
echo "[InternetShortcut]" > Quickstart.url
|
||||
echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url
|
||||
echo "IconIndex=0" >> Quickstart.url
|
||||
|
||||
#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg"
|
||||
#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \
|
||||
#--window-size 800 400 \
|
||||
#--icon-size 100 \
|
||||
#--icon rpcs3.app 200 190 \
|
||||
#--add-file Quickstart.url Quickstart.url 400 20 \
|
||||
#--hide-extension rpcs3.app \
|
||||
#--hide-extension Quickstart.url \
|
||||
#--app-drop-link 600 185 \
|
||||
#--skip-jenkins \
|
||||
#--format ULMO \
|
||||
#"$DMG_FILEPATH" \
|
||||
#RPCS3.app
|
||||
#FILESIZE=$(stat -f %z "$DMG_FILEPATH")
|
||||
#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.7z"
|
||||
"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url
|
||||
FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH")
|
||||
SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
cd ..
|
||||
echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE"
|
||||
cd bin
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# First let's see print some info about our caches
|
||||
"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v
|
||||
|
||||
# Remove unecessary files
|
||||
rm -f ./rpcs3/bin/rpcs3.exp ./rpcs3/bin/rpcs3.lib ./rpcs3/bin/rpcs3.pdb ./rpcs3/bin/vc_redist.x64.exe
|
||||
rm -rf ./rpcs3/bin/git
|
||||
|
||||
# Prepare compatibility and SDL database for packaging
|
||||
mkdir -p ./rpcs3/bin/config/input_configs
|
||||
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./rpcs3/bin/config/input_configs/gamecontrollerdb.txt
|
||||
cp -rf ./build-msvc/bin/ ./rpcs3/bin/
|
||||
|
||||
# Package artifacts
|
||||
7z a -m0=LZMA2 -mx9 "$BUILD" ./rpcs3/bin/*
|
||||
|
||||
# Move files to publishing directory
|
||||
cp -- "$BUILD" ./RPCS3-Qt-UI.7z
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# Variables set by Azure Pipelines
|
||||
CI_HAS_ARTIFACTS
|
||||
BUILD_REASON
|
||||
BUILD_SOURCEVERSION
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY
|
||||
BUILD_REPOSITORY_NAME
|
||||
BUILD_SOURCEBRANCHNAME
|
||||
APPDIR
|
||||
ARTDIR
|
||||
RELEASE_MESSAGE
|
||||
# Variables for build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private variables
|
||||
GITHUB_TOKEN
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Export variables for later stages of the Azure pipeline
|
||||
# Values done in this manner will appear as environment variables
|
||||
# in later stages.
|
||||
|
||||
# From pure-sh-bible
|
||||
# Setting 'IFS' tells 'read' where to split the string.
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip over lines containing comments.
|
||||
[ "${key##\#*}" ] || continue
|
||||
echo "##vso[task.setvariable variable=$key]$val"
|
||||
done < ".ci/ci-vars.env"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Export variables for later stages of the Cirrus pipeline
|
||||
# Values done in this manner will appear as environment variables
|
||||
# in later stages.
|
||||
|
||||
# From pure-sh-bible
|
||||
# Setting 'IFS' tells 'read' where to split the string.
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip over lines containing comments.
|
||||
[ "${key##\#*}" ] || continue
|
||||
export "$key"="$val"
|
||||
done < ".ci/ci-vars.env"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
mkdir -p ../translations
|
||||
|
||||
LUPDATE_PATH=$(find /usr -name lupdate -type f 2>/dev/null | head -n 1)
|
||||
if [ -z "$LUPDATE_PATH" ]; then
|
||||
echo "Error: lupdate not found!"
|
||||
exit 1
|
||||
else
|
||||
echo "lupdate found at: $LUPDATE_PATH"
|
||||
$LUPDATE_PATH -recursive . -ts ../translations/rpcs3_template.ts
|
||||
sed -i 's|filename="\.\./|filename="./|g' ../translations/rpcs3_template.ts
|
||||
fi
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z.sha256"
|
||||
curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256"
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY"
|
||||
generate_post_data()
|
||||
{
|
||||
body=$(cat GitHubReleaseMessage.txt)
|
||||
cat <<EOF
|
||||
{
|
||||
"tag_name": "build-${BUILD_SOURCEVERSION}",
|
||||
"target_commitish": "${UPLOAD_COMMIT_HASH}",
|
||||
"name": "${AVVER}",
|
||||
"body": "$body",
|
||||
"draft": false,
|
||||
"prerelease": false
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
curl -fsS \
|
||||
-H "Authorization: token ${RPCS3_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
--data "$(generate_post_data)" "https://api.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases" >> release.json
|
||||
|
||||
cat release.json
|
||||
id=$(grep '"id"' release.json | cut -d ':' -f2 | head -n1 | awk '{$1=$1;print}')
|
||||
id=${id%?}
|
||||
echo "${id:?}"
|
||||
|
||||
upload_file()
|
||||
{
|
||||
curl -fsS \
|
||||
-H "Authorization: token ${RPCS3_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$2"/"$3" \
|
||||
"https://uploads.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases/$1/assets?name=$3"
|
||||
}
|
||||
|
||||
for file in "$ARTIFACT_DIR"/*; do
|
||||
name=$(basename "$file")
|
||||
upload_file "$id" "$ARTIFACT_DIR" "$name"
|
||||
done
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env -S su -m root -ex
|
||||
# NOTE: this script is run under root permissions
|
||||
# shellcheck shell=sh disable=SC2096
|
||||
|
||||
# RPCS3 often needs recent Qt and Vulkan-Headers
|
||||
sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
|
||||
export ASSUME_ALWAYS_YES=true
|
||||
pkg info # debug
|
||||
|
||||
# WITH_LLVM
|
||||
pkg install "llvm$LLVM_COMPILER_VER"
|
||||
|
||||
# Mandatory dependencies (qtX-base is pulled via qtX-multimedia)
|
||||
pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg
|
||||
|
||||
# Optional dependencies (libevdev is pulled by qtX-base)
|
||||
pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
file_path=$(find "$1/Contents/MacOS" -type f -print0 | head -n 1)
|
||||
|
||||
if [ -z "$file_path" ]; then
|
||||
echo "No executable file found in $1/Contents/MacOS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
target_architecture="$(lipo "$file_path" -archs)"
|
||||
|
||||
if [ -z "$target_architecture" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC3045
|
||||
find "$1" -type f -print0 | while IFS= read -r -d '' file; do
|
||||
echo Thinning "$file" -> "$target_architecture"
|
||||
lipo "$file" -thin "$target_architecture" -output "$file" || true
|
||||
done
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# These are Azure specific, so we wrap them for portability
|
||||
REPO_NAME="$BUILD_REPOSITORY_NAME"
|
||||
REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH"
|
||||
PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
|
||||
|
||||
# Resource/dependency URLs
|
||||
# Qt mirrors can be volatile and slow, so we list 2
|
||||
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
QT_HOST="http://qt.mirror.constant.com/"
|
||||
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
|
||||
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
|
||||
QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
|
||||
QT_SUFFIX="-Windows-Windows_11_23H2-${QT_VER_MSVC_UP}-Windows-Windows_11_23H2-X86_64.7z"
|
||||
QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}"
|
||||
QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}"
|
||||
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}"
|
||||
QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}"
|
||||
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
|
||||
# LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
|
||||
# GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
|
||||
|
||||
DEP_URLS=" \
|
||||
$QT_BASE_URL \
|
||||
$QT_DECL_URL \
|
||||
$QT_TOOL_URL \
|
||||
$QT_MM_URL \
|
||||
$QT_SVG_URL \
|
||||
$LLVMLIBS_URL \
|
||||
$GLSLANG_URL \
|
||||
$VULKAN_SDK_URL\
|
||||
$CCACHE_URL"
|
||||
|
||||
# Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually
|
||||
[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR"
|
||||
|
||||
# Pull all the submodules except llvm, since it is built separately and we just download that build
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ { print $3 }' .gitmodules)
|
||||
|
||||
# Git bash doesn't have rev, so here it is
|
||||
rev()
|
||||
{
|
||||
echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }'
|
||||
}
|
||||
|
||||
# Usage: download_and_verify url checksum algo file
|
||||
# Check to see if a file is already cached, and the checksum matches. If not, download it.
|
||||
# Tries up to 3 times
|
||||
download_and_verify()
|
||||
{
|
||||
url="$1"
|
||||
correctChecksum="$2"
|
||||
algo="$3"
|
||||
fileName="$4"
|
||||
|
||||
for _ in 1 2 3; do
|
||||
[ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url"
|
||||
fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }')
|
||||
[ "$fileChecksum" = "$correctChecksum" ] && return 0
|
||||
done
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Some dependencies install here
|
||||
[ -d "./rpcs3/build/lib_ext/Release-x64" ] || mkdir -p "./rpcs3/build/lib_ext/Release-x64"
|
||||
|
||||
for url in $DEP_URLS; do
|
||||
# Get the filename from the URL and remove query strings (?arg=something).
|
||||
fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)"
|
||||
[ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1
|
||||
|
||||
# shellcheck disable=SC1003
|
||||
case "$url" in
|
||||
*qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;;
|
||||
*llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;;
|
||||
*glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;;
|
||||
*ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;;
|
||||
*Vulkan*)
|
||||
# Vulkan setup needs to be run in batch environment
|
||||
# Need to subshell this or else it doesn't wait
|
||||
download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName"
|
||||
cp "$DEPS_CACHE_DIR/$fileName" .
|
||||
_=$(echo "$fileName --accept-licenses --default-answer --confirm-command install" | cmd)
|
||||
continue
|
||||
;;
|
||||
*) echo "Unknown url resource: $url"; exit 1 ;;
|
||||
esac
|
||||
|
||||
download_and_verify "$url" "$checksum" "$algo" "$fileName"
|
||||
7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir"
|
||||
done
|
||||
|
||||
# Setup ccache tool
|
||||
[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")"
|
||||
CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR")
|
||||
mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR"
|
||||
cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
# Format the above into filenames
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
AVVER="${COMM_TAG}-${COMM_HASH}"
|
||||
BUILD_RAW="rpcs3-v${AVVER}_win64"
|
||||
BUILD="${BUILD_RAW}.7z"
|
||||
else
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64"
|
||||
BUILD="${BUILD_RAW}.7z"
|
||||
fi
|
||||
|
||||
# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp.
|
||||
# BUILD is the name of the release artifact
|
||||
# BUILD_RAW is just filename
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
BRANCH="${REPO_NAME}/${REPO_BRANCH}"
|
||||
|
||||
# SC2129
|
||||
{
|
||||
echo "BRANCH=$BRANCH"
|
||||
echo "BUILD=$BUILD"
|
||||
echo "BUILD_RAW=$BUILD_RAW"
|
||||
echo "AVVER=$AVVER"
|
||||
} >> .ci/ci-vars.env
|
||||
222
.github/workflows/rpcs3.yml
vendored
|
|
@ -1,222 +0,0 @@
|
|||
name: Build RPCS3 Qt UI (Legacy)
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "**/*.md"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**/*.md"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
BUILD_REPOSITORY_NAME: ${{ github.repository }}
|
||||
BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }}
|
||||
BUILD_SOURCEVERSION: ${{ github.sha }}
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/
|
||||
|
||||
jobs:
|
||||
Linux_Build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
build_sh: ".ci/build-linux.sh"
|
||||
compiler: clang
|
||||
- os: ubuntu-24.04
|
||||
build_sh: ".ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
build_sh: ".ci/build-linux-aarch64.sh"
|
||||
compiler: clang
|
||||
name: RPCS3 Qt UI (Legacy) for Linux ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CI_HAS_ARTIFACTS: true
|
||||
DEPLOY_APPIMAGE: true
|
||||
APPDIR: "./appdir"
|
||||
ARTDIR: "./artifacts"
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
RX_VERSION: "Unknown"
|
||||
RX_SHA: "Unknown"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: actions/cache@main
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-
|
||||
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
echo "Types: deb" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "URIs: ${{ matrix.os == 'ubuntu-24.04-arm' && 'http://ports.ubuntu.com/ubuntu-ports' || 'http://azure.archive.ubuntu.com/ubuntu/' }}" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Suites: plucky plucky-updates plucky-security" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Components: main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y cmake build-essential libunwind-dev \
|
||||
libvulkan-dev vulkan-validationlayers \
|
||||
libsox-dev ninja-build libasound2-dev libglfw3-dev nasm libudev-dev \
|
||||
libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev \
|
||||
libevdev-dev libjack-dev libsndio-dev libglvnd-dev \
|
||||
qt6-base-dev qt6-svg-dev qt6-base-private-dev qt6-multimedia-dev \
|
||||
clang lld gcc-14 g++-14 \
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
${{ matrix.build_sh }}
|
||||
|
||||
RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'`
|
||||
RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'`
|
||||
|
||||
echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}"
|
||||
echo "RX_SHA=$RX_SHA" >> "${{ github.env }}"
|
||||
mv RPCS3-Qt-UI.AppImage RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: RPCS3 Qt UI (Legacy) for Linux (${{ runner.arch }}, ${{ matrix.compiler }})
|
||||
path: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
compression-level: 0
|
||||
|
||||
- name: Deploy build
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: |
|
||||
github.event_name != 'pull_request' &&
|
||||
github.ref == 'refs/heads/master' &&
|
||||
github.repository == 'RPCSX/rpcsx'
|
||||
with:
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
repository: RPCSX/rpcsx-build
|
||||
token: ${{ secrets.BUILD_TOKEN }}
|
||||
tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }}
|
||||
files: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
|
||||
|
||||
|
||||
Windows_Build:
|
||||
name: RPCS3 Qt UI (Legacy) for Windows
|
||||
runs-on: windows-2025
|
||||
env:
|
||||
COMPILER: msvc
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.8.3'
|
||||
QT_VER_MSVC: 'msvc2022'
|
||||
QT_DATE: '202503201308'
|
||||
LLVM_VER: '19.1.7'
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
|
||||
CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
CCACHE_INODECACHE: 'true'
|
||||
CCACHE_SLOPPINESS: 'time_macros'
|
||||
DEPS_CACHE_DIR: ./dependency_cache
|
||||
RX_VERSION: 'Unknown'
|
||||
RX_SHA: 'Unknown'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup env
|
||||
run: |
|
||||
echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }}
|
||||
echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }}
|
||||
|
||||
# - name: Get Cache Keys
|
||||
# run: .ci/get_keys-windows.sh
|
||||
|
||||
# - name: Setup Build Ccache
|
||||
# uses: actions/cache@main
|
||||
# with:
|
||||
# path: ${{ env.CCACHE_DIR }}
|
||||
# key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}"
|
||||
# restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}-
|
||||
|
||||
# - name: Setup Dependencies Cache
|
||||
# uses: actions/cache@main
|
||||
# with:
|
||||
# path: ${{ env.DEPS_CACHE_DIR }}
|
||||
# key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}"
|
||||
# restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-
|
||||
|
||||
- name: Download and unpack dependencies
|
||||
run: .ci/setup-windows.sh
|
||||
|
||||
- name: Export Variables
|
||||
run: |
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip lines that are empty or start with '#'
|
||||
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
||||
echo "$key=$val" >> "${{ github.env }}"
|
||||
done < .ci/ci-vars.env
|
||||
|
||||
# - name: Add msbuild to PATH
|
||||
# uses: microsoft/setup-msbuild@main
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -B build-msvc -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DWITH_RPCSX=off -DWITH_RPCS3=on -DWITH_RPCS3_QT_UI=on -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DCMAKE_INSTALL_PREFIX="${sourceDir}/out/install/${presetName}" -DUSE_NATIVE_INSTRUCTIONS=on -DUSE_PRECOMPILED_HEADERS=on -DUSE_FAUDIO=off -DUSE_SYSTEM_CURL=off -DUSE_SYSTEM_ZLIB=off -DUSE_SYSTEM_OPENAL=off -DUSE_SYSTEM_OPENCV=off -DBUILD_LLVM=on -DSTATIC_LINK_LLVM=on
|
||||
|
||||
- name: Export Version
|
||||
run: |
|
||||
RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'`
|
||||
RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'`
|
||||
echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}"
|
||||
echo "RX_SHA=$RX_SHA" >> "${{ github.env }}"
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build-msvc --config Release
|
||||
|
||||
|
||||
- name: Pack up build artifacts
|
||||
run: |
|
||||
mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}"
|
||||
.ci/deploy-windows.sh
|
||||
mv RPCS3-Qt-UI.7z RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
|
||||
- name: Upload artifacts (7z)
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: RPCS3 Qt UI (Legacy) for Windows (MSVC)
|
||||
path: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
compression-level: 0
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Deploy build
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: |
|
||||
github.event_name != 'pull_request' &&
|
||||
github.ref == 'refs/heads/master' &&
|
||||
github.repository == 'RPCSX/rpcsx'
|
||||
with:
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
repository: RPCSX/rpcsx-build
|
||||
token: ${{ secrets.BUILD_TOKEN }}
|
||||
tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }}
|
||||
files: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
|
||||
|
|
@ -131,9 +131,9 @@ function(target_base_address target address)
|
|||
endfunction()
|
||||
|
||||
|
||||
option(WITH_RPCSX "Enable RPCSX" ON)
|
||||
option(WITH_RPCS3 "Enable RPCS3" OFF)
|
||||
option(WITH_RPCS3_QT_UI "Enable RPCS3 UI" OFF)
|
||||
option(WITH_PS3 "Enable PS3 emulation support" OFF)
|
||||
option(WITH_PS4 "Enable PS4 emulation support" ON)
|
||||
|
||||
option(WITHOUT_OPENGL "Disable OpenGL" OFF)
|
||||
option(WITHOUT_OPENGLEW "Disable OpenGLEW" OFF)
|
||||
option(WITHOUT_OPENAL "Disable OpenAL" OFF)
|
||||
|
|
@ -159,8 +159,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
|
|||
option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF)
|
||||
option(USE_LTO "Use LTO for building" ON)
|
||||
|
||||
|
||||
if (NOT WITH_RPCS3)
|
||||
if (NOT WITH_PS3)
|
||||
set(WITHOUT_OPENGL on)
|
||||
set(WITHOUT_OPENGLEW on)
|
||||
set(WITHOUT_OPENAL on)
|
||||
|
|
@ -252,6 +251,8 @@ add_subdirectory(3rdparty EXCLUDE_FROM_ALL)
|
|||
add_subdirectory(rx EXCLUDE_FROM_ALL)
|
||||
|
||||
include(3rdparty/llvm/CMakeLists.txt)
|
||||
include(ConfigureCompiler)
|
||||
include(CheckFunctionExists)
|
||||
|
||||
if (NOT RX_TAG)
|
||||
set(RX_TAG 0)
|
||||
|
|
@ -266,7 +267,7 @@ target_compile_definitions(rx PRIVATE
|
|||
RX_TAG_VERSION=${RX_TAG_VERSION}
|
||||
)
|
||||
|
||||
if (WITH_RPCSX)
|
||||
if (WITH_PS4)
|
||||
find_package(nlohmann_json CONFIG)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(orbis-kernel)
|
||||
|
|
@ -274,14 +275,7 @@ endif()
|
|||
|
||||
add_subdirectory(rpcsx)
|
||||
|
||||
if (WITH_RPCS3)
|
||||
include(ConfigureCompiler)
|
||||
include(CheckFunctionExists)
|
||||
|
||||
if (WITH_PS3)
|
||||
add_subdirectory(rpcs3)
|
||||
add_subdirectory(ps3fw)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND WITH_RPCS3_QT_UI AND WITH_RPCS3)
|
||||
add_subdirectory(rpcs3qt-legacy)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ target_link_libraries(3rdparty_ffmpeg INTERFACE
|
|||
|
||||
add_dependencies(3rdparty_ffmpeg ffmpeg-unpack)
|
||||
|
||||
set(WITH_RPCSX off)
|
||||
set(WITH_RPCS3 on)
|
||||
set(WITH_PS4 off)
|
||||
set(WITH_PS3 on)
|
||||
set(USE_SYSTEM_LIBUSB off)
|
||||
set(USE_SYSTEM_CURL off)
|
||||
set(USE_DISCORD_RPC off)
|
||||
|
|
@ -113,8 +113,6 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
|
|||
src/rpcsx-android.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC rpcs3/rpcs3)
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
rpcs3
|
||||
rpcsx::fw::ps3
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
Standard: c++20
|
||||
UseTab: AlignWithSpaces
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
PointerAlignment: Left
|
||||
NamespaceIndentation: All
|
||||
ColumnLimit: 0
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
ReflowComments: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignTrailingComments: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
include(qt6.cmake)
|
||||
|
||||
add_library(rpcs3_ui STATIC
|
||||
about_dialog.cpp
|
||||
auto_pause_settings_dialog.cpp
|
||||
basic_mouse_settings_dialog.cpp
|
||||
breakpoint_handler.cpp
|
||||
breakpoint_list.cpp
|
||||
call_stack_list.cpp
|
||||
camera_settings_dialog.cpp
|
||||
cg_disasm_window.cpp
|
||||
cheat_manager.cpp
|
||||
config_adapter.cpp
|
||||
config_checker.cpp
|
||||
curl_handle.cpp
|
||||
custom_dialog.cpp
|
||||
custom_table_widget_item.cpp
|
||||
debugger_add_bp_window.cpp
|
||||
debugger_frame.cpp
|
||||
debugger_list.cpp
|
||||
downloader.cpp
|
||||
dimensions_dialog.cpp
|
||||
_discord_utils.cpp
|
||||
emu_settings.cpp
|
||||
elf_memory_dumping_dialog.cpp
|
||||
emulated_pad_settings_dialog.cpp
|
||||
fatal_error_dialog.cpp
|
||||
find_dialog.cpp
|
||||
flow_layout.cpp
|
||||
flow_widget.cpp
|
||||
flow_widget_item.cpp
|
||||
game_compatibility.cpp
|
||||
game_list.cpp
|
||||
game_list_base.cpp
|
||||
game_list_delegate.cpp
|
||||
game_list_frame.cpp
|
||||
game_list_grid.cpp
|
||||
game_list_grid_item.cpp
|
||||
game_list_table.cpp
|
||||
gui_application.cpp
|
||||
gl_gs_frame.cpp
|
||||
gs_frame.cpp
|
||||
gui_game_info.cpp
|
||||
gui_settings.cpp
|
||||
infinity_dialog.cpp
|
||||
input_dialog.cpp
|
||||
instruction_editor_dialog.cpp
|
||||
ipc_settings_dialog.cpp
|
||||
kernel_explorer.cpp
|
||||
localized.cpp
|
||||
localized_emu.cpp
|
||||
log_frame.cpp
|
||||
log_viewer.cpp
|
||||
main_window.cpp
|
||||
memory_string_searcher.cpp
|
||||
memory_viewer_panel.cpp
|
||||
microphone_creator.cpp
|
||||
midi_creator.cpp
|
||||
movie_item.cpp
|
||||
movie_item_base.cpp
|
||||
msg_dialog_frame.cpp
|
||||
osk_dialog_frame.cpp
|
||||
pad_led_settings_dialog.cpp
|
||||
pad_motion_settings_dialog.cpp
|
||||
pad_settings_dialog.cpp
|
||||
patch_creator_dialog.cpp
|
||||
patch_manager_dialog.cpp
|
||||
permissions.cpp
|
||||
persistent_settings.cpp
|
||||
pkg_install_dialog.cpp
|
||||
progress_dialog.cpp
|
||||
progress_indicator.cpp
|
||||
ps_move_tracker_dialog.cpp
|
||||
qt_camera_handler.cpp
|
||||
qt_camera_video_sink.cpp
|
||||
qt_music_handler.cpp
|
||||
qt_utils.cpp
|
||||
qt_video_source.cpp
|
||||
raw_mouse_settings_dialog.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
render_creator.cpp
|
||||
rpcn_settings_dialog.cpp
|
||||
rsx_debugger.cpp
|
||||
save_data_dialog.cpp
|
||||
save_data_info_dialog.cpp
|
||||
save_data_list_dialog.cpp
|
||||
save_manager_dialog.cpp
|
||||
savestate_manager_dialog.cpp
|
||||
screenshot_item.cpp
|
||||
screenshot_manager_dialog.cpp
|
||||
screenshot_preview.cpp
|
||||
sendmessage_dialog_frame.cpp
|
||||
settings.cpp
|
||||
settings_dialog.cpp
|
||||
shortcut_utils.cpp
|
||||
shortcut_dialog.cpp
|
||||
shortcut_handler.cpp
|
||||
shortcut_settings.cpp
|
||||
skylander_dialog.cpp
|
||||
syntax_highlighter.cpp
|
||||
system_cmd_dialog.cpp
|
||||
table_item_delegate.cpp
|
||||
tooltips.cpp
|
||||
trophy_manager_dialog.cpp
|
||||
trophy_notification_frame.cpp
|
||||
trophy_notification_helper.cpp
|
||||
update_manager.cpp
|
||||
user_account.cpp
|
||||
user_manager_dialog.cpp
|
||||
uuid.cpp
|
||||
vfs_dialog.cpp
|
||||
vfs_dialog_path_widget.cpp
|
||||
vfs_dialog_tab.cpp
|
||||
vfs_dialog_usb_input.cpp
|
||||
vfs_dialog_usb_tab.cpp
|
||||
vfs_tool_dialog.cpp
|
||||
video_label.cpp
|
||||
welcome_dialog.cpp
|
||||
|
||||
about_dialog.ui
|
||||
camera_settings_dialog.ui
|
||||
main_window.ui
|
||||
pad_led_settings_dialog.ui
|
||||
pad_motion_settings_dialog.ui
|
||||
pad_settings_dialog.ui
|
||||
patch_creator_dialog.ui
|
||||
patch_manager_dialog.ui
|
||||
ps_move_tracker_dialog.ui
|
||||
settings_dialog.ui
|
||||
shortcut_dialog.ui
|
||||
welcome_dialog.ui
|
||||
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
if(HAS_MEMORY_BREAKPOINTS)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(rpcs3_ui PUBLIC windows.qrc)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE)
|
||||
endif()
|
||||
|
||||
set_target_properties(rpcs3_ui
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON)
|
||||
|
||||
# AUTOMOC brings Windows.h to the sources, which have some definitions conflicting with winsock2.h
|
||||
# define WIN32_LEAN_AND_MEAN resolve the problem
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#faster-builds-with-smaller-header-files
|
||||
target_compile_definitions(rpcs3_ui PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
|
||||
target_link_libraries(rpcs3_ui
|
||||
PUBLIC
|
||||
3rdparty::qt6
|
||||
3rdparty::yaml-cpp
|
||||
rpcs3
|
||||
rpcsx::fw::ps3::api
|
||||
)
|
||||
|
||||
if(TARGET OpenGL::EGL)
|
||||
target_link_libraries(rpcs3_ui PUBLIC OpenGL::EGL)
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT ANDROID)
|
||||
if(WIN32)
|
||||
add_executable(rpcs3qt-ui-legacy WIN32)
|
||||
target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.rc)
|
||||
target_compile_definitions(rpcs3qt-ui-legacy PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX UNICODE _UNICODE)
|
||||
elseif(APPLE)
|
||||
add_executable(rpcs3qt-ui-legacy MACOSX_BUNDLE)
|
||||
target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.icns update_helper.sh)
|
||||
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set_target_properties(rpcs3qt-ui-legacy
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
||||
else()
|
||||
add_executable(rpcs3qt-ui-legacy)
|
||||
endif()
|
||||
|
||||
set_target_properties(rpcs3qt-ui-legacy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
target_sources(rpcs3qt-ui-legacy
|
||||
PRIVATE
|
||||
display_sleep_control.cpp
|
||||
headless_application.cpp
|
||||
main.cpp
|
||||
main_application.cpp
|
||||
|
||||
Input/basic_keyboard_handler.cpp
|
||||
Input/basic_mouse_handler.cpp
|
||||
Input/gui_pad_thread.cpp
|
||||
Input/keyboard_pad_handler.cpp
|
||||
)
|
||||
|
||||
set_target_properties(rpcs3qt-ui-legacy
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON)
|
||||
|
||||
target_link_libraries(rpcs3qt-ui-legacy
|
||||
PRIVATE
|
||||
rpcs3_emu
|
||||
rpcs3_ui
|
||||
3rdparty::qt6
|
||||
rpcsx::fw::ps3
|
||||
rpcsx::fw::ps3::api)
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE bcrypt ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi pdh)
|
||||
else()
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
# Copy icons to executable directory
|
||||
if(APPLE)
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
set(QT_DEPLOY_FLAGS "-no-strip")
|
||||
else()
|
||||
set(QT_DEPLOY_FLAGS "")
|
||||
endif()
|
||||
qt_finalize_target(rpcs3qt-ui-legacy)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/rpcs3.icns
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/git
|
||||
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}")
|
||||
elseif(UNIX)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/git)
|
||||
elseif(WIN32)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:OpenAL::OpenAL> $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/git
|
||||
COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt
|
||||
--no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import
|
||||
--plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/plugins,$<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/share/qt6/plugins>"
|
||||
--verbose 0 "$<TARGET_FILE:rpcs3qt-ui-legacy>")
|
||||
endif()
|
||||
|
||||
# Unix installation
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Install the binary
|
||||
install(TARGETS rpcs3qt-ui-legacy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
# Install the application icon and menu item
|
||||
install(FILES rpcs3.svg
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps)
|
||||
install(FILES rpcs3.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps)
|
||||
install(FILES rpcs3.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||
install(FILES rpcs3.metainfo.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||
# Install other files
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/git
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/test
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<svg id="DualShock_3_SVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="250" viewBox="0, 0, 400, 250"><!-- DualShock 3 SVG by HerrHulaHoop -->
|
||||
<path id="MainOutline" d="M 116.5 41.6 C 116.9 40.5, 116 35.6, 115 29.9 C 112.7 14.7, 112.5 14.3, 108.9 12 C 108 11.4, 106.4 10.2, 105.4 9.3 C 100.4 4.6, 89 2.3, 77.4 3.5 C 70.7 4.2, 65.6 6.1, 62.2 9.2 C 61 10.2, 59.5 11.4, 58.7 11.9 C 56.8 13, 55 15.1, 54.6 16.8 C 54.2 18.1, 51.6 34, 50 42.4 M 25.4 74.2 C 22 81.1, 18.5 102.3, 10.4 159.5 C 8.1 175.6, 5.6 192.4, 4.8 196.7 C 3 207.4, 2.9 208.8, 3.4 212.7 C 7.2 241.6, 46.2 259.1, 69.4 242.3 C 77.3 236.6, 80.3 231.6, 103.5 186.3 L 110 174.1 M 292 173.5 C 292.5 174.5, 295 179.2, 297.9 184.9 C 308.1 205.1, 320.6 228.7, 323.2 232.6 C 335.6 250.8, 359.9 254.2, 380.5 240.4 C 394.5 231.1, 400.8 217.3, 398.1 202.1 C 396.5 193.4, 394.9 182.8, 390.4 151.1 C 383.3 101.3, 380 81.4, 376.8 75 M 351.9 42.3 C 351.8 40.5, 351 35.6, 350 29.9 C 347.7 14.7, 347.5 14.3, 343.9 11.5 C 343 10, 341.4 9.2, 342.6 9.8 C 335.4 4.6, 324 2.3, 312.4 3.5 C 305.7 4.2, 300.6 6.1, 297.1 9.2 C 296 10.2, 294.5 11.4, 293.7 11.9 C 291.8 13, 290.1 15.1, 289.6 16.8 C 289.2 18.1, 286.6 34, 285 42.4" stroke="black" fill="none" />
|
||||
<path id="LeftShoulder" d="M 107 10.6 C 94.4 6.7, 74.8 6.5, 60.5 10.6" stroke="black" fill="none" />
|
||||
<path id="RightShoulder" d="M 343.7 11.2 C 329.4 6.7, 309.8 6.5, 295.5 10.6" stroke="black" fill="none" />
|
||||
<path id="UpperLine1" d="M 116.5 41.6 H 186 A 80 80, 0, 0 0, 216.5 41.6 H 286" stroke="black" fill="none" />
|
||||
<line id="UpperLine2" x1="129" y1="52" x2="273" y2="52" stroke="black" fill="none" />
|
||||
<line id="LowerLine1" x1="181.5" y1="164" x2="220" y2="164" stroke="black" fill="none" />
|
||||
<line id="LowerLine2" x1="179" y1="170.5" x2="222.5" y2="170.5" stroke="black" fill="none" />
|
||||
<rect id="SelectButton" x="155" y="95" width="20" height="10" stroke="black" fill="none" />
|
||||
<text id="SelectText" x="150" y="90.5" font-family="sans-serif" font-size="8" font-weight="bold" fill="black">SELECT</text>
|
||||
<path id="StartButton" d="M 226 95 V 105 L 244 100 Z" stroke="black" fill="none" />
|
||||
<text id="StartText" x="222" y="90.5" font-family="sans-serif" font-size="8" font-weight="bold" fill="black">START</text>
|
||||
<circle id="PSButton" cx="201" cy="129" r="13" stroke="black" fill="none" />
|
||||
<circle id="RightJoystickOuter" cx="258" cy="154" r="39" stroke="black" fill="none" />
|
||||
<circle id="RightJoystickMiddle" cx="258" cy="154" r="30" stroke="black" fill="none" />
|
||||
<circle id="RightJoystickInner" cx="258" cy="154" r="27" stroke="black" fill="none" />
|
||||
<circle id="LeftJoystickOuter" cx="143.4" cy="154" r="39" stroke="black" fill="none" />
|
||||
<circle id="LeftJoystickMiddle" cx="143.4" cy="154" r="30" stroke="black" fill="none" />
|
||||
<circle id="LeftJoystickInner" cx="143.4" cy="154" r="27" stroke="black" fill="none" />
|
||||
<path id="RightFacebuttonCircle" d="M 297.3 152.5 A 61.8 61.8, 0, 1 0, 260 115" stroke="black" fill="none" />
|
||||
<path id="RightButtonBorder" d="M 301 76 H 278 A 6 6, 0, 0 0, 272 82 V 105 A 6 6, 0, 0 0, 278 111 H 301 V 134 A 6 6, 0, 0 0, 307 140 H 330 A 6 6, 0, 0 0, 336 134 V 111 H 359 A 6 6, 0, 0 0, 365 105 V 82 A 6 6, 0, 0 0, 359 76 H 336 V 53 A 6 6, 0, 0 0, 330 47 H 307 A 6 6, 0, 0 0, 301 53 V 76.5" stroke="black" fill="none" />
|
||||
<circle id="ButtonBorder1" cx="289.5" cy="93.5" r="13" stroke="black" fill="none" />
|
||||
<rect id="ButtonSquare" x="282" y="86.5" width="15" height="15" stroke="black" fill="none" />
|
||||
<circle id="ButtonBorder2" cx="347.5" cy="93.5" r="13" stroke="black" fill="none" />
|
||||
<circle id="ButtonCircle" cx="347.5" cy="93.5" r="9" stroke="black" fill="none" />
|
||||
<circle id="ButtonBorder3" cx="318.5" cy="64.5" r="13" stroke="black" fill="none" />
|
||||
<path id="ButtonTriangle" d="M 310 70 H 327 L 318.5 56 Z" stroke="black" fill="none" />
|
||||
<circle id="ButtonBorder4" cx="318.5" cy="122.5" r="13" stroke="black" fill="none" />
|
||||
<path id="ButtonCross" d="M 311.5 115.5 L 325.5 129.5 M 311.5 129.5 L 325.5 115.5" stroke="black" fill="none" />
|
||||
<path id="LeftDpadCircle" d="M 104.2 152.5 A 61.8 61.8, 0, 1 1, 142 115" stroke="black" fill="none" />
|
||||
<path id="LeftDpadBorder" d="M 66 76 H 43 A 6 6, 0, 0 0, 37 82 V 105 A 6 6, 0, 0 0, 43 111 H 66 V 134 A 6 6, 0, 0 0, 72 140 H 95 A 6 6, 0, 0 0, 101 134 V 111 H 124 A 6 6, 0, 0 0, 130 105 V 82 A 6 6, 0, 0 0, 124 76 H 101 V 53 A 6 6, 0, 0 0, 95 47 H 72 A 6 6, 0, 0 0, 66 53 V 76.5" stroke="black" fill="none" />
|
||||
<path id="DpadButtonLeft" d="M 52 85 H 69 L 75 93.5 L 69 102 H 52 Z M 48 88 V 99 L 43 93.5 Z" stroke="black" fill="none" />
|
||||
<path id="DpadButtonRight" d="M 115 85 H 98 L 92 93.5 L 98 102 H 115 Z M 119 88 V 99 L 124 93.5 Z" stroke="black" fill="none" />
|
||||
<path id="DpadButtonTop" d="M 75 62 V 79 L 83.5 85 L 92 79 V 62 Z M 78 58 H 89 L 83.5 53 Z" stroke="black" fill="none" />
|
||||
<path id="DpadButtonBottom" d="M 75 125 V 108 L 83.5 102 L 92 108 V 125 Z M 78 129 H 89 L 83.5 134 Z" stroke="black" fill="none" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
|
@ -1,347 +0,0 @@
|
|||
#include "basic_keyboard_handler.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(input_log, "Input");
|
||||
|
||||
void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_connect)
|
||||
{
|
||||
KbInfo& info = consumer.GetInfo();
|
||||
std::vector<Keyboard>& keyboards = consumer.GetKeyboards();
|
||||
|
||||
info = {};
|
||||
keyboards.clear();
|
||||
|
||||
for (u32 i = 0; i < max_connect; i++)
|
||||
{
|
||||
Keyboard kb{};
|
||||
kb.m_config.arrange = g_cfg.sys.keyboard_type;
|
||||
|
||||
if (consumer.id() == keyboard_consumer::identifier::overlays)
|
||||
{
|
||||
// Enable key repeat
|
||||
kb.m_key_repeat = true;
|
||||
}
|
||||
|
||||
LoadSettings(kb);
|
||||
|
||||
keyboards.emplace_back(kb);
|
||||
}
|
||||
|
||||
info.max_connect = max_connect;
|
||||
info.now_connect = std::min(::size32(keyboards), max_connect);
|
||||
info.info = input::g_keyboards_intercepted ? CELL_KB_INFO_INTERCEPTED : 0; // Ownership of keyboard data: 0=Application, 1=System
|
||||
info.status[0] = CELL_KB_STATUS_CONNECTED; // (TODO: Support for more keyboards)
|
||||
}
|
||||
|
||||
/* Sets the target window for the event handler, and also installs an event filter on the target. */
|
||||
void basic_keyboard_handler::SetTargetWindow(QWindow* target)
|
||||
{
|
||||
if (target != nullptr)
|
||||
{
|
||||
m_target = target;
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load.
|
||||
// We still want events so filter from application instead since target is null.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
input_log.error("Trying to set keyboard handler to a null target window.");
|
||||
}
|
||||
}
|
||||
|
||||
bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
|
||||
{
|
||||
if (!m_keys_released)
|
||||
{
|
||||
ReleaseAllKeys();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_keys_released = false;
|
||||
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
|
||||
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
|
||||
if (!m_target || !m_target->isVisible() || watched == m_target)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
{
|
||||
keyPressEvent(static_cast<QKeyEvent*>(event));
|
||||
break;
|
||||
}
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
keyReleaseEvent(static_cast<QKeyEvent*>(event));
|
||||
break;
|
||||
}
|
||||
case QEvent::FocusOut:
|
||||
{
|
||||
ReleaseAllKeys();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_consumers.empty())
|
||||
{
|
||||
keyEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = getUnmodifiedKey(keyEvent);
|
||||
|
||||
if (key < 0 || !HandleKey(static_cast<u32>(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
|
||||
{
|
||||
keyEvent->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_consumers.empty())
|
||||
{
|
||||
keyEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = getUnmodifiedKey(keyEvent);
|
||||
|
||||
if (key < 0 || !HandleKey(static_cast<u32>(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
|
||||
{
|
||||
keyEvent->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
// This should get the actual unmodified key without getting too crazy.
|
||||
// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _)
|
||||
s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int key = keyEvent->key();
|
||||
|
||||
if (key < 0)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
u32 raw_key = static_cast<u32>(key);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty())
|
||||
{
|
||||
u32 mapped_key = static_cast<u32>(MapVirtualKeyA(static_cast<UINT>(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR));
|
||||
|
||||
if (raw_key != mapped_key)
|
||||
{
|
||||
if (mapped_key > 0x80000000) // diacritics
|
||||
{
|
||||
mapped_key -= 0x80000000;
|
||||
}
|
||||
raw_key = mapped_key;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<s32>(raw_key);
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::LoadSettings(Keyboard& keyboard)
|
||||
{
|
||||
std::vector<KbButton> buttons;
|
||||
|
||||
// Meta Keys
|
||||
buttons.emplace_back(Qt::Key_Control, CELL_KB_MKEY_L_CTRL);
|
||||
buttons.emplace_back(Qt::Key_Shift, CELL_KB_MKEY_L_SHIFT);
|
||||
buttons.emplace_back(Qt::Key_Alt, CELL_KB_MKEY_L_ALT);
|
||||
buttons.emplace_back(Qt::Key_Meta, CELL_KB_MKEY_L_WIN);
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_CTRL); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_SHIFT); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_ALT); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_WIN); // There is no way to know if it's left or right in Qt at the moment
|
||||
|
||||
buttons.emplace_back(Qt::Key_Super_L, CELL_KB_MKEY_L_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both.
|
||||
buttons.emplace_back(Qt::Key_Super_R, CELL_KB_MKEY_R_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both.
|
||||
|
||||
// CELL_KB_RAWDAT
|
||||
// buttons.emplace_back(, CELL_KEYC_NO_EVENT); // Redundant, listed for completeness
|
||||
// buttons.emplace_back(, CELL_KEYC_E_ROLLOVER);
|
||||
// buttons.emplace_back(, CELL_KEYC_E_POSTFAIL);
|
||||
// buttons.emplace_back(, CELL_KEYC_E_UNDEF);
|
||||
buttons.emplace_back(Qt::Key_Escape, CELL_KEYC_ESCAPE);
|
||||
buttons.emplace_back(Qt::Key_Kanji, CELL_KEYC_106_KANJI);
|
||||
buttons.emplace_back(Qt::Key_CapsLock, CELL_KEYC_CAPS_LOCK);
|
||||
buttons.emplace_back(Qt::Key_F1, CELL_KEYC_F1);
|
||||
buttons.emplace_back(Qt::Key_F2, CELL_KEYC_F2);
|
||||
buttons.emplace_back(Qt::Key_F3, CELL_KEYC_F3);
|
||||
buttons.emplace_back(Qt::Key_F4, CELL_KEYC_F4);
|
||||
buttons.emplace_back(Qt::Key_F5, CELL_KEYC_F5);
|
||||
buttons.emplace_back(Qt::Key_F6, CELL_KEYC_F6);
|
||||
buttons.emplace_back(Qt::Key_F7, CELL_KEYC_F7);
|
||||
buttons.emplace_back(Qt::Key_F8, CELL_KEYC_F8);
|
||||
buttons.emplace_back(Qt::Key_F9, CELL_KEYC_F9);
|
||||
buttons.emplace_back(Qt::Key_F10, CELL_KEYC_F10);
|
||||
buttons.emplace_back(Qt::Key_F11, CELL_KEYC_F11);
|
||||
buttons.emplace_back(Qt::Key_F12, CELL_KEYC_F12);
|
||||
buttons.emplace_back(Qt::Key_Print, CELL_KEYC_PRINTSCREEN);
|
||||
buttons.emplace_back(Qt::Key_ScrollLock, CELL_KEYC_SCROLL_LOCK);
|
||||
buttons.emplace_back(Qt::Key_Pause, CELL_KEYC_PAUSE);
|
||||
buttons.emplace_back(Qt::Key_Insert, CELL_KEYC_INSERT);
|
||||
buttons.emplace_back(Qt::Key_Home, CELL_KEYC_HOME);
|
||||
buttons.emplace_back(Qt::Key_PageUp, CELL_KEYC_PAGE_UP);
|
||||
buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_DELETE);
|
||||
buttons.emplace_back(Qt::Key_End, CELL_KEYC_END);
|
||||
buttons.emplace_back(Qt::Key_PageDown, CELL_KEYC_PAGE_DOWN);
|
||||
buttons.emplace_back(Qt::Key_Right, CELL_KEYC_RIGHT_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Left, CELL_KEYC_LEFT_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Down, CELL_KEYC_DOWN_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Up, CELL_KEYC_UP_ARROW);
|
||||
// buttons.emplace_back(, CELL_KEYC_NUM_LOCK);
|
||||
// buttons.emplace_back(, CELL_KEYC_APPLICATION); // This is probably the PS key on the PS3 keyboard
|
||||
buttons.emplace_back(Qt::Key_Kana_Shift, CELL_KEYC_KANA); // maybe Key_Kana_Lock
|
||||
buttons.emplace_back(Qt::Key_Henkan, CELL_KEYC_HENKAN);
|
||||
buttons.emplace_back(Qt::Key_Muhenkan, CELL_KEYC_MUHENKAN);
|
||||
|
||||
// CELL_KB_KEYPAD
|
||||
buttons.emplace_back(Qt::Key_NumLock, CELL_KEYC_KPAD_NUMLOCK);
|
||||
buttons.emplace_back(Qt::Key_division, CELL_KEYC_KPAD_SLASH); // should ideally be slash but that's occupied obviously
|
||||
buttons.emplace_back(Qt::Key_multiply, CELL_KEYC_KPAD_ASTERISK); // should ideally be asterisk but that's occupied obviously
|
||||
// buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_KPAD_MINUS); // should ideally be minus but that's occupied obviously
|
||||
buttons.emplace_back(Qt::Key_Plus, CELL_KEYC_KPAD_PLUS);
|
||||
buttons.emplace_back(Qt::Key_Enter, CELL_KEYC_KPAD_ENTER);
|
||||
// buttons.emplace_back(Qt::Key_1, CELL_KEYC_KPAD_1);
|
||||
// buttons.emplace_back(Qt::Key_2, CELL_KEYC_KPAD_2);
|
||||
// buttons.emplace_back(Qt::Key_3, CELL_KEYC_KPAD_3);
|
||||
// buttons.emplace_back(Qt::Key_4, CELL_KEYC_KPAD_4);
|
||||
// buttons.emplace_back(Qt::Key_5, CELL_KEYC_KPAD_5);
|
||||
// buttons.emplace_back(Qt::Key_6, CELL_KEYC_KPAD_6);
|
||||
// buttons.emplace_back(Qt::Key_7, CELL_KEYC_KPAD_7);
|
||||
// buttons.emplace_back(Qt::Key_8, CELL_KEYC_KPAD_8);
|
||||
// buttons.emplace_back(Qt::Key_9, CELL_KEYC_KPAD_9);
|
||||
// buttons.emplace_back(Qt::Key_0, CELL_KEYC_KPAD_0);
|
||||
// buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_KPAD_PERIOD);
|
||||
|
||||
// ASCII Printable characters
|
||||
buttons.emplace_back(Qt::Key_A, CELL_KEYC_A);
|
||||
buttons.emplace_back(Qt::Key_B, CELL_KEYC_B);
|
||||
buttons.emplace_back(Qt::Key_C, CELL_KEYC_C);
|
||||
buttons.emplace_back(Qt::Key_D, CELL_KEYC_D);
|
||||
buttons.emplace_back(Qt::Key_E, CELL_KEYC_E);
|
||||
buttons.emplace_back(Qt::Key_F, CELL_KEYC_F);
|
||||
buttons.emplace_back(Qt::Key_G, CELL_KEYC_G);
|
||||
buttons.emplace_back(Qt::Key_H, CELL_KEYC_H);
|
||||
buttons.emplace_back(Qt::Key_I, CELL_KEYC_I);
|
||||
buttons.emplace_back(Qt::Key_J, CELL_KEYC_J);
|
||||
buttons.emplace_back(Qt::Key_K, CELL_KEYC_K);
|
||||
buttons.emplace_back(Qt::Key_L, CELL_KEYC_L);
|
||||
buttons.emplace_back(Qt::Key_M, CELL_KEYC_M);
|
||||
buttons.emplace_back(Qt::Key_N, CELL_KEYC_N);
|
||||
buttons.emplace_back(Qt::Key_O, CELL_KEYC_O);
|
||||
buttons.emplace_back(Qt::Key_P, CELL_KEYC_P);
|
||||
buttons.emplace_back(Qt::Key_Q, CELL_KEYC_Q);
|
||||
buttons.emplace_back(Qt::Key_R, CELL_KEYC_R);
|
||||
buttons.emplace_back(Qt::Key_S, CELL_KEYC_S);
|
||||
buttons.emplace_back(Qt::Key_T, CELL_KEYC_T);
|
||||
buttons.emplace_back(Qt::Key_U, CELL_KEYC_U);
|
||||
buttons.emplace_back(Qt::Key_V, CELL_KEYC_V);
|
||||
buttons.emplace_back(Qt::Key_W, CELL_KEYC_W);
|
||||
buttons.emplace_back(Qt::Key_X, CELL_KEYC_X);
|
||||
buttons.emplace_back(Qt::Key_Y, CELL_KEYC_Y);
|
||||
buttons.emplace_back(Qt::Key_Z, CELL_KEYC_Z);
|
||||
|
||||
buttons.emplace_back(Qt::Key_1, CELL_KEYC_1);
|
||||
buttons.emplace_back(Qt::Key_2, CELL_KEYC_2);
|
||||
buttons.emplace_back(Qt::Key_3, CELL_KEYC_3);
|
||||
buttons.emplace_back(Qt::Key_4, CELL_KEYC_4);
|
||||
buttons.emplace_back(Qt::Key_5, CELL_KEYC_5);
|
||||
buttons.emplace_back(Qt::Key_6, CELL_KEYC_6);
|
||||
buttons.emplace_back(Qt::Key_7, CELL_KEYC_7);
|
||||
buttons.emplace_back(Qt::Key_8, CELL_KEYC_8);
|
||||
buttons.emplace_back(Qt::Key_9, CELL_KEYC_9);
|
||||
buttons.emplace_back(Qt::Key_0, CELL_KEYC_0);
|
||||
|
||||
buttons.emplace_back(Qt::Key_Return, CELL_KEYC_ENTER);
|
||||
buttons.emplace_back(Qt::Key_Backspace, CELL_KEYC_BS);
|
||||
buttons.emplace_back(Qt::Key_Tab, CELL_KEYC_TAB);
|
||||
buttons.emplace_back(Qt::Key_Space, CELL_KEYC_SPACE);
|
||||
buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_MINUS);
|
||||
buttons.emplace_back(Qt::Key_Equal, CELL_KEYC_EQUAL_101);
|
||||
buttons.emplace_back(Qt::Key_AsciiCircum, CELL_KEYC_ACCENT_CIRCONFLEX_106);
|
||||
buttons.emplace_back(Qt::Key_At, CELL_KEYC_ATMARK_106);
|
||||
buttons.emplace_back(Qt::Key_Semicolon, CELL_KEYC_SEMICOLON);
|
||||
buttons.emplace_back(Qt::Key_Apostrophe, CELL_KEYC_QUOTATION_101);
|
||||
buttons.emplace_back(Qt::Key_Colon, CELL_KEYC_COLON_106);
|
||||
buttons.emplace_back(Qt::Key_Comma, CELL_KEYC_COMMA);
|
||||
buttons.emplace_back(Qt::Key_Period, CELL_KEYC_PERIOD);
|
||||
buttons.emplace_back(Qt::Key_Slash, CELL_KEYC_SLASH);
|
||||
buttons.emplace_back(Qt::Key_yen, CELL_KEYC_YEN_106);
|
||||
|
||||
// Some buttons share the same key code on different layouts
|
||||
if (keyboard.m_config.arrange == CELL_KB_MAPPING_106)
|
||||
{
|
||||
buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_106);
|
||||
buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_106);
|
||||
buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_106);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_101);
|
||||
buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_101);
|
||||
buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_101);
|
||||
}
|
||||
|
||||
// Made up helper buttons
|
||||
buttons.emplace_back(Qt::Key_Less, CELL_KEYC_LESS);
|
||||
buttons.emplace_back(Qt::Key_NumberSign, CELL_KEYC_HASHTAG);
|
||||
buttons.emplace_back(Qt::Key_ssharp, CELL_KEYC_SSHARP);
|
||||
buttons.emplace_back(Qt::Key_QuoteLeft, CELL_KEYC_BACK_QUOTE);
|
||||
buttons.emplace_back(Qt::Key_acute, CELL_KEYC_BACK_QUOTE);
|
||||
|
||||
// Fill our map
|
||||
for (const KbButton& button : buttons)
|
||||
{
|
||||
if (!keyboard.m_keys.try_emplace(button.m_keyCode, button).second)
|
||||
{
|
||||
input_log.error("basic_keyboard_handler failed to set key code %d", button.m_keyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QWindow>
|
||||
|
||||
class basic_keyboard_handler final : public KeyboardHandlerBase, public QObject
|
||||
{
|
||||
using KeyboardHandlerBase::KeyboardHandlerBase;
|
||||
|
||||
public:
|
||||
void Init(keyboard_consumer& consumer, const u32 max_connect) override;
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
static s32 getUnmodifiedKey(QKeyEvent* event);
|
||||
|
||||
private:
|
||||
void LoadSettings(Keyboard& keyboard);
|
||||
|
||||
QWindow* m_target = nullptr;
|
||||
};
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#include "basic_mouse_handler.h"
|
||||
#include "keyboard_pad_handler.h"
|
||||
#include "../gs_frame.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
#include "Emu/Io/mouse_config.h"
|
||||
|
||||
mouse_config g_cfg_mouse;
|
||||
|
||||
void basic_mouse_handler::Init(const u32 max_connect)
|
||||
{
|
||||
if (m_info.max_connect > 0)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_cfg_mouse.load())
|
||||
{
|
||||
input_log.notice("basic_mouse_handler: Could not load basic mouse config. Using defaults.");
|
||||
}
|
||||
|
||||
reload_config();
|
||||
|
||||
m_mice.clear();
|
||||
m_mice.emplace_back(Mouse());
|
||||
|
||||
m_info = {};
|
||||
m_info.max_connect = max_connect;
|
||||
m_info.now_connect = std::min(::size32(m_mice), max_connect);
|
||||
m_info.info = input::g_mice_intercepted ? CELL_MOUSE_INFO_INTERCEPTED : 0; // Ownership of mouse data: 0=Application, 1=System
|
||||
for (u32 i = 1; i < max_connect; i++)
|
||||
{
|
||||
m_info.status[i] = CELL_MOUSE_STATUS_DISCONNECTED;
|
||||
m_info.mode[i] = CELL_MOUSE_INFO_TABLET_MOUSE_MODE;
|
||||
m_info.tablet_is_supported[i] = CELL_MOUSE_INFO_TABLET_NOT_SUPPORTED;
|
||||
}
|
||||
m_info.status[0] = CELL_MOUSE_STATUS_CONNECTED; // (TODO: Support for more mice)
|
||||
m_info.vendor_id[0] = 0x1234;
|
||||
m_info.product_id[0] = 0x1234;
|
||||
|
||||
type = mouse_handler::basic;
|
||||
}
|
||||
|
||||
void basic_mouse_handler::reload_config()
|
||||
{
|
||||
input_log.notice("Basic mouse config=\n%s", g_cfg_mouse.to_string());
|
||||
|
||||
m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1);
|
||||
m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2);
|
||||
m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3);
|
||||
m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4);
|
||||
m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5);
|
||||
m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6);
|
||||
m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7);
|
||||
m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8);
|
||||
}
|
||||
|
||||
/* Sets the target window for the event handler, and also installs an event filter on the target. */
|
||||
void basic_mouse_handler::SetTargetWindow(QWindow* target)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
m_target = target;
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load.
|
||||
// We still want events so filter from application instead since target is null.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
input_log.error("Trying to set mouse handler to a null target window.");
|
||||
}
|
||||
}
|
||||
|
||||
bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
|
||||
{
|
||||
if (m_info.max_connect == 0)
|
||||
{
|
||||
// Not initialized
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ev) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load to ensure events still occur
|
||||
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
|
||||
if (!m_target || !m_target->isVisible() || target == m_target)
|
||||
{
|
||||
if (g_cfg_mouse.reload_requested.exchange(false))
|
||||
{
|
||||
reload_config();
|
||||
}
|
||||
|
||||
switch (ev->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
MouseButton(static_cast<QMouseEvent*>(ev), true);
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
MouseButton(static_cast<QMouseEvent*>(ev), false);
|
||||
break;
|
||||
case QEvent::MouseMove:
|
||||
MouseMove(static_cast<QMouseEvent*>(ev));
|
||||
break;
|
||||
case QEvent::Wheel:
|
||||
MouseScroll(static_cast<QWheelEvent*>(ev));
|
||||
break;
|
||||
case QEvent::KeyPress:
|
||||
Key(static_cast<QKeyEvent*>(ev), true);
|
||||
break;
|
||||
case QEvent::KeyRelease:
|
||||
Key(static_cast<QKeyEvent*>(ev), false);
|
||||
break;
|
||||
case QEvent::Leave:
|
||||
{
|
||||
// Issue mouse move on leave. Otherwise we may not get any mouse event at the screen borders.
|
||||
const QPoint window_pos = m_target->mapToGlobal(m_target->position()) / m_target->devicePixelRatio();
|
||||
const QPoint cursor_pos = QCursor::pos() - window_pos;
|
||||
|
||||
if (cursor_pos.x() <= 0 || cursor_pos.x() >= m_target->width() || cursor_pos.y() <= 0 || cursor_pos.y() >= m_target->height())
|
||||
{
|
||||
MouseMove(cursor_pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void basic_mouse_handler::Key(QKeyEvent* event, bool pressed)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = event->key();
|
||||
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [key](const auto& entry)
|
||||
{
|
||||
return entry.second.code == key && entry.second.is_key;
|
||||
});
|
||||
it != m_buttons.cend())
|
||||
{
|
||||
MouseHandlerBase::Button(0, it->first, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseButton(QMouseEvent* event, bool pressed)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int button = event->button();
|
||||
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry)
|
||||
{
|
||||
return entry.second.code == button && !entry.second.is_key;
|
||||
});
|
||||
it != m_buttons.cend())
|
||||
{
|
||||
MouseHandlerBase::Button(0, it->first, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseScroll(QWheelEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QPoint delta = event->angleDelta();
|
||||
const s8 x = std::clamp(delta.x() / 120, -128, 127);
|
||||
const s8 y = std::clamp(delta.y() / 120, -128, 127);
|
||||
MouseHandlerBase::Scroll(0, x, y);
|
||||
}
|
||||
|
||||
bool basic_mouse_handler::get_mouse_lock_state() const
|
||||
{
|
||||
if (auto game_frame = dynamic_cast<gs_frame*>(m_target))
|
||||
return game_frame->get_mouse_lock_state();
|
||||
return false;
|
||||
}
|
||||
|
||||
basic_mouse_handler::mouse_button basic_mouse_handler::get_mouse_button(const cfg::string& button)
|
||||
{
|
||||
const std::string name = button.to_string();
|
||||
const auto it = std::find_if(mouse_list.cbegin(), mouse_list.cend(), [&name](const auto& entry)
|
||||
{
|
||||
return entry.second == name;
|
||||
});
|
||||
|
||||
if (it != mouse_list.cend())
|
||||
{
|
||||
return mouse_button{
|
||||
.code = static_cast<int>(it->first),
|
||||
.is_key = false};
|
||||
}
|
||||
|
||||
if (const u32 key = keyboard_pad_handler::GetKeyCode(QString::fromStdString(name)))
|
||||
{
|
||||
return mouse_button{
|
||||
.code = static_cast<int>(key),
|
||||
.is_key = true};
|
||||
}
|
||||
|
||||
return mouse_button{
|
||||
.code = Qt::MouseButton::NoButton,
|
||||
.is_key = false};
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseMove(QMouseEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_time_for_update())
|
||||
{
|
||||
MouseMove(event->pos());
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseMove(const QPoint& e_pos)
|
||||
{
|
||||
if (!m_target)
|
||||
return;
|
||||
|
||||
// get the screen dimensions
|
||||
const QSize screen = m_target->size();
|
||||
|
||||
if (m_target->isActive() && get_mouse_lock_state())
|
||||
{
|
||||
// get the center of the screen in global coordinates
|
||||
QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2);
|
||||
|
||||
// reset the mouse to the center for consistent results since edge movement won't be registered
|
||||
QCursor::setPos(m_target->screen(), p_center);
|
||||
|
||||
// convert the center into screen coordinates
|
||||
p_center = m_target->mapFromGlobal(p_center);
|
||||
|
||||
// current mouse position, starting at the center
|
||||
static QPoint p_real(p_center);
|
||||
|
||||
// get the delta of the mouse position to the screen center
|
||||
const QPoint p_delta = e_pos - p_center;
|
||||
|
||||
// update the current position without leaving the screen borders
|
||||
p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width()));
|
||||
p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height()));
|
||||
|
||||
// pass the 'real' position and the current delta to the screen center
|
||||
MouseHandlerBase::Move(0, p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y());
|
||||
}
|
||||
else
|
||||
{
|
||||
// pass the absolute position
|
||||
MouseHandlerBase::Move(0, e_pos.x(), e_pos.y(), screen.width(), screen.height());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/MouseHandler.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
class string;
|
||||
}
|
||||
|
||||
class basic_mouse_handler final : public MouseHandlerBase, public QObject
|
||||
{
|
||||
using MouseHandlerBase::MouseHandlerBase;
|
||||
|
||||
public:
|
||||
void Init(const u32 max_connect) override;
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
void Key(QKeyEvent* event, bool pressed);
|
||||
void MouseButton(QMouseEvent* event, bool pressed);
|
||||
void MouseScroll(QWheelEvent* event);
|
||||
void MouseMove(QMouseEvent* event);
|
||||
void MouseMove(const QPoint& e_pos);
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private:
|
||||
void reload_config();
|
||||
bool get_mouse_lock_state() const;
|
||||
|
||||
struct mouse_button
|
||||
{
|
||||
int code = Qt::MouseButton::NoButton;
|
||||
bool is_key = false;
|
||||
};
|
||||
static mouse_button get_mouse_button(const cfg::string& button);
|
||||
|
||||
QWindow* m_target = nullptr;
|
||||
std::map<u8, mouse_button> m_buttons;
|
||||
};
|
||||
|
|
@ -1,815 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "gui_pad_thread.h"
|
||||
#include "Input/ds3_pad_handler.h"
|
||||
#include "Input/ds4_pad_handler.h"
|
||||
#include "Input/dualsense_pad_handler.h"
|
||||
#include "Input/skateboard_pad_handler.h"
|
||||
#ifdef _WIN32
|
||||
#include "Input/xinput_pad_handler.h"
|
||||
#include "Input/mm_joystick_handler.h"
|
||||
#elif HAVE_LIBEVDEV
|
||||
#include "Input/evdev_joystick_handler.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
#include "Input/sdl_pad_handler.h"
|
||||
#endif
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "../gui_settings.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/uinput.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#define CHECK_IOCTRL_RET(res) \
|
||||
if (res == -1) \
|
||||
{ \
|
||||
gui_log.error("gui_pad_thread: ioctl failed (errno=%d=%s)", res, strerror(errno)); \
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-declarations"
|
||||
#pragma GCC diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion"
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
atomic_t<bool> gui_pad_thread::m_reset = false;
|
||||
|
||||
gui_pad_thread::gui_pad_thread()
|
||||
{
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("Gui Pad Thread", [this]()
|
||||
{
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
gui_pad_thread::~gui_pad_thread()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
auto& thread = *m_thread;
|
||||
thread = thread_state::aborting;
|
||||
thread();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (m_uinput_fd != 1)
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: closing /dev/uinput");
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_DESTROY));
|
||||
int res = close(m_uinput_fd);
|
||||
if (res == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to close /dev/uinput (errno=%d=%s)", res, strerror(errno));
|
||||
}
|
||||
m_uinput_fd = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::update_settings(const std::shared_ptr<gui_settings>& settings)
|
||||
{
|
||||
ensure(!!settings);
|
||||
|
||||
m_allow_global_input = settings->GetValue(gui::nav_global).toBool();
|
||||
}
|
||||
|
||||
bool gui_pad_thread::init()
|
||||
{
|
||||
m_handler.reset();
|
||||
m_pad.reset();
|
||||
|
||||
// Initialize last button states as pressed to avoid unwanted button presses when starting the thread.
|
||||
m_last_button_state.fill(true);
|
||||
m_timestamp = steady_clock::now();
|
||||
m_initial_timestamp = steady_clock::now();
|
||||
m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
|
||||
g_cfg_input_configs.load();
|
||||
|
||||
std::string active_config = g_cfg_input_configs.active_configs.get_value("");
|
||||
|
||||
if (active_config.empty())
|
||||
{
|
||||
active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Using input configuration: '%s'", active_config);
|
||||
|
||||
// Load in order to get the pad handlers
|
||||
if (!g_cfg_input.load("", active_config))
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Loaded empty pad config");
|
||||
}
|
||||
|
||||
// Adjust to the different pad handlers
|
||||
for (usz i = 0; i < g_cfg_input.player.size(); i++)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> handler;
|
||||
gui_pad_thread::InitPadConfig(g_cfg_input.player[i]->config, g_cfg_input.player[i]->handler, handler);
|
||||
}
|
||||
|
||||
// Reload with proper defaults
|
||||
if (!g_cfg_input.load("", active_config))
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Reloaded empty pad config");
|
||||
}
|
||||
|
||||
gui_log.trace("gui_pad_thread: Using pad config:\n%s", g_cfg_input);
|
||||
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads
|
||||
{
|
||||
cfg_player* cfg = g_cfg_input.player[i];
|
||||
|
||||
const pad_handler handler_type = cfg->handler.get();
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(handler_type);
|
||||
|
||||
if (!cur_pad_handler)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cur_pad_handler->Init();
|
||||
|
||||
m_handler = cur_pad_handler;
|
||||
m_pad = std::make_shared<Pad>(handler_type, i, CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD);
|
||||
|
||||
if (!cur_pad_handler->bindPadToDevice(m_pad))
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to bind device '%s' to handler %s.", cfg->device.to_string(), handler_type);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x",
|
||||
i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile);
|
||||
|
||||
if (handler_type != pad_handler::null)
|
||||
{
|
||||
input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string());
|
||||
}
|
||||
|
||||
// We only use one pad
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_handler || !m_pad)
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: No devices configured.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
gui_log.notice("gui_pad_thread: opening /dev/uinput");
|
||||
|
||||
m_uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
||||
if (m_uinput_fd == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to open /dev/uinput (errno=%d=%s)", m_uinput_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct uinput_setup usetup{};
|
||||
usetup.id.bustype = BUS_USB;
|
||||
usetup.id.vendor = 0x1234;
|
||||
usetup.id.product = 0x1234;
|
||||
std::strcpy(usetup.name, "RPCS3 GUI Input Device");
|
||||
|
||||
// The ioctls below will enable the device that is about to be created to pass events.
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ESC));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ENTER));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_BACKSPACE));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_TAB));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_LEFT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_RIGHT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_UP));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_DOWN));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_LEFT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_RIGHT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_REL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_X));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_Y));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_WHEEL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_HWHEEL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_SETUP, &usetup));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_CREATE));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> gui_pad_thread::GetHandler(pad_handler type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case pad_handler::null:
|
||||
case pad_handler::keyboard:
|
||||
case pad_handler::move:
|
||||
// Makes no sense to use this if we are in the GUI anyway
|
||||
return nullptr;
|
||||
case pad_handler::ds3:
|
||||
return std::make_shared<ds3_pad_handler>();
|
||||
case pad_handler::ds4:
|
||||
return std::make_shared<ds4_pad_handler>();
|
||||
case pad_handler::dualsense:
|
||||
return std::make_shared<dualsense_pad_handler>();
|
||||
case pad_handler::skateboard:
|
||||
return std::make_shared<skateboard_pad_handler>();
|
||||
#ifdef _WIN32
|
||||
case pad_handler::xinput:
|
||||
return std::make_shared<xinput_pad_handler>();
|
||||
case pad_handler::mm:
|
||||
return std::make_shared<mm_joystick_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
case pad_handler::sdl:
|
||||
return std::make_shared<sdl_pad_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev:
|
||||
return std::make_shared<evdev_joystick_handler>();
|
||||
#endif
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void gui_pad_thread::InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
handler = GetHandler(type);
|
||||
|
||||
if (handler)
|
||||
{
|
||||
handler->init_config(&cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gui_pad_thread::run()
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Pad thread started");
|
||||
|
||||
m_reset = true;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (m_reset && m_reset.exchange(false))
|
||||
{
|
||||
if (!init())
|
||||
{
|
||||
gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only process input if there is an active window
|
||||
if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow()))
|
||||
{
|
||||
m_handler->process();
|
||||
|
||||
if (thread_ctrl::state() == thread_state::aborting)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
process_input();
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(10000);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Pad thread stopped");
|
||||
}
|
||||
|
||||
void gui_pad_thread::process_input()
|
||||
{
|
||||
if (!m_pad || !(m_pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr u64 ms_threshold = 500;
|
||||
|
||||
const auto on_button_pressed = [this](pad_button button_id, bool pressed, u16 value)
|
||||
{
|
||||
if (button_id == m_mouse_boost_button)
|
||||
{
|
||||
m_boost_mouse = pressed;
|
||||
return;
|
||||
}
|
||||
|
||||
u16 key = 0;
|
||||
mouse_button btn = mouse_button::none;
|
||||
mouse_wheel wheel = mouse_wheel::none;
|
||||
float wheel_delta = 0.0f;
|
||||
const float wheel_multiplier = pressed ? (m_boost_mouse ? 10.0f : 1.0f) : 0.0f;
|
||||
const float move_multiplier = pressed ? (m_boost_mouse ? 40.0f : 20.0f) : 0.0f;
|
||||
|
||||
switch (button_id)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case pad_button::dpad_up: key = VK_UP; break;
|
||||
case pad_button::dpad_down: key = VK_DOWN; break;
|
||||
case pad_button::dpad_left: key = VK_LEFT; break;
|
||||
case pad_button::dpad_right: key = VK_RIGHT; break;
|
||||
case pad_button::circle: key = VK_ESCAPE; break;
|
||||
case pad_button::cross: key = VK_RETURN; break;
|
||||
case pad_button::square: key = VK_BACK; break;
|
||||
case pad_button::triangle: key = VK_TAB; break;
|
||||
#elif defined(__linux__)
|
||||
case pad_button::dpad_up: key = KEY_UP; break;
|
||||
case pad_button::dpad_down: key = KEY_DOWN; break;
|
||||
case pad_button::dpad_left: key = KEY_LEFT; break;
|
||||
case pad_button::dpad_right: key = KEY_RIGHT; break;
|
||||
case pad_button::circle: key = KEY_ESC; break;
|
||||
case pad_button::cross: key = KEY_ENTER; break;
|
||||
case pad_button::square: key = KEY_BACKSPACE; break;
|
||||
case pad_button::triangle: key = KEY_TAB; break;
|
||||
#elif defined(__APPLE__)
|
||||
case pad_button::dpad_up: key = kVK_UpArrow; break;
|
||||
case pad_button::dpad_down: key = kVK_DownArrow; break;
|
||||
case pad_button::dpad_left: key = kVK_LeftArrow; break;
|
||||
case pad_button::dpad_right: key = kVK_RightArrow; break;
|
||||
case pad_button::circle: key = kVK_Escape; break;
|
||||
case pad_button::cross: key = kVK_Return; break;
|
||||
case pad_button::square: key = kVK_Delete; break;
|
||||
case pad_button::triangle: key = kVK_Tab; break;
|
||||
#endif
|
||||
case pad_button::L1: btn = mouse_button::left; break;
|
||||
case pad_button::R1: btn = mouse_button::right; break;
|
||||
case pad_button::rs_up:
|
||||
wheel = mouse_wheel::vertical;
|
||||
wheel_delta = 10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_down:
|
||||
wheel = mouse_wheel::vertical;
|
||||
wheel_delta = -10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_left:
|
||||
wheel = mouse_wheel::horizontal;
|
||||
wheel_delta = -10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_right:
|
||||
wheel = mouse_wheel::horizontal;
|
||||
wheel_delta = 10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::ls_up: m_mouse_delta_y -= (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_down: m_mouse_delta_y += (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_left: m_mouse_delta_x -= (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_right: m_mouse_delta_x += (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
send_key_event(key, pressed);
|
||||
}
|
||||
else if (btn != mouse_button::none)
|
||||
{
|
||||
send_mouse_button_event(btn, pressed);
|
||||
}
|
||||
else if (wheel != mouse_wheel::none && pressed)
|
||||
{
|
||||
send_mouse_wheel_event(wheel, wheel_delta);
|
||||
}
|
||||
};
|
||||
|
||||
const auto handle_button_press = [&](pad_button button_id, bool pressed, u16 value)
|
||||
{
|
||||
if (button_id >= pad_button::pad_button_max_enum)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool& last_state = m_last_button_state[static_cast<u32>(button_id)];
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
const bool is_auto_repeat_button = m_auto_repeat_buttons.contains(button_id);
|
||||
const bool is_mouse_move_button = m_mouse_move_buttons.contains(button_id);
|
||||
|
||||
if (!last_state)
|
||||
{
|
||||
if (button_id != m_mouse_boost_button && !is_mouse_move_button)
|
||||
{
|
||||
// The button was not pressed before, so this is a new button press. Reset auto-repeat.
|
||||
m_timestamp = steady_clock::now();
|
||||
m_initial_timestamp = m_timestamp;
|
||||
m_last_auto_repeat_button = is_auto_repeat_button ? button_id : pad_button::pad_button_max_enum;
|
||||
}
|
||||
|
||||
on_button_pressed(static_cast<pad_button>(button_id), true, value);
|
||||
}
|
||||
else if (is_auto_repeat_button)
|
||||
{
|
||||
if (m_last_auto_repeat_button == button_id && m_input_timer.GetMsSince(m_initial_timestamp) > ms_threshold && m_input_timer.GetMsSince(m_timestamp) > m_auto_repeat_buttons.at(button_id))
|
||||
{
|
||||
// The auto-repeat button was pressed for at least the given threshold in ms and will trigger at an interval.
|
||||
m_timestamp = steady_clock::now();
|
||||
on_button_pressed(static_cast<pad_button>(button_id), true, value);
|
||||
}
|
||||
else if (m_last_auto_repeat_button == pad_button::pad_button_max_enum)
|
||||
{
|
||||
// An auto-repeat button was already pressed before and will now start triggering again after the next threshold.
|
||||
m_last_auto_repeat_button = button_id;
|
||||
}
|
||||
}
|
||||
else if (is_mouse_move_button)
|
||||
{
|
||||
on_button_pressed(static_cast<pad_button>(button_id), pressed, value);
|
||||
}
|
||||
}
|
||||
else if (last_state)
|
||||
{
|
||||
if (m_last_auto_repeat_button == button_id)
|
||||
{
|
||||
// We stopped pressing an auto-repeat button, so re-enable auto-repeat for other buttons.
|
||||
m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
}
|
||||
|
||||
on_button_pressed(static_cast<pad_button>(button_id), false, value);
|
||||
}
|
||||
|
||||
last_state = pressed;
|
||||
};
|
||||
|
||||
for (const auto& button : m_pad->m_buttons)
|
||||
{
|
||||
pad_button button_id = pad_button::pad_button_max_enum;
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_LEFT:
|
||||
button_id = pad_button::dpad_left;
|
||||
break;
|
||||
case CELL_PAD_CTRL_RIGHT:
|
||||
button_id = pad_button::dpad_right;
|
||||
break;
|
||||
case CELL_PAD_CTRL_DOWN:
|
||||
button_id = pad_button::dpad_down;
|
||||
break;
|
||||
case CELL_PAD_CTRL_UP:
|
||||
button_id = pad_button::dpad_up;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L3:
|
||||
button_id = pad_button::L3;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R3:
|
||||
button_id = pad_button::R3;
|
||||
break;
|
||||
case CELL_PAD_CTRL_SELECT:
|
||||
button_id = pad_button::select;
|
||||
break;
|
||||
case CELL_PAD_CTRL_START:
|
||||
button_id = pad_button::start;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_TRIANGLE:
|
||||
button_id = pad_button::triangle;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CIRCLE:
|
||||
button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::cross : pad_button::circle;
|
||||
break;
|
||||
case CELL_PAD_CTRL_SQUARE:
|
||||
button_id = pad_button::square;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CROSS:
|
||||
button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::circle : pad_button::cross;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L1:
|
||||
button_id = pad_button::L1;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R1:
|
||||
button_id = pad_button::R1;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L2:
|
||||
button_id = pad_button::L2;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R2:
|
||||
button_id = pad_button::R2;
|
||||
break;
|
||||
case CELL_PAD_CTRL_PS:
|
||||
button_id = pad_button::ps;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle_button_press(button_id, button.m_pressed, button.m_value);
|
||||
}
|
||||
|
||||
for (const AnalogStick& stick : m_pad->m_sticks)
|
||||
{
|
||||
pad_button button_id = pad_button::pad_button_max_enum;
|
||||
pad_button release_id = pad_button::pad_button_max_enum;
|
||||
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::ls_left : pad_button::ls_right;
|
||||
release_id = (stick.m_value > 128) ? pad_button::ls_left : pad_button::ls_right;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::ls_up : pad_button::ls_down;
|
||||
release_id = (stick.m_value > 128) ? pad_button::ls_up : pad_button::ls_down;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::rs_left : pad_button::rs_right;
|
||||
release_id = (stick.m_value > 128) ? pad_button::rs_left : pad_button::rs_right;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::rs_up : pad_button::rs_down;
|
||||
release_id = (stick.m_value > 128) ? pad_button::rs_up : pad_button::rs_down;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool pressed;
|
||||
|
||||
if (m_mouse_move_buttons.contains(button_id))
|
||||
{
|
||||
// Mouse move sticks are always pressed if they surpass a tiny deadzone.
|
||||
constexpr int deadzone = 5;
|
||||
pressed = std::abs(stick.m_value - 128) > deadzone;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's say other sticks are only pressed if they are almost completely tilted. Otherwise navigation feels really wacky.
|
||||
pressed = stick.m_value < 30 || stick.m_value > 225;
|
||||
}
|
||||
|
||||
// Release other direction on the same axis first
|
||||
handle_button_press(release_id, false, stick.m_value);
|
||||
|
||||
// Handle currently pressed stick direction
|
||||
handle_button_press(button_id, pressed, stick.m_value);
|
||||
}
|
||||
|
||||
// Send mouse move event at the end to prevent redundant calls
|
||||
|
||||
// Round to lower integer
|
||||
const int delta_x = m_mouse_delta_x;
|
||||
const int delta_y = m_mouse_delta_y;
|
||||
|
||||
if (delta_x || delta_y)
|
||||
{
|
||||
// Remove integer part
|
||||
m_mouse_delta_x -= delta_x;
|
||||
m_mouse_delta_y -= delta_y;
|
||||
|
||||
// Send event
|
||||
send_mouse_move_event(delta_x, delta_y);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
void gui_pad_thread::emit_event(int type, int code, int val)
|
||||
{
|
||||
struct input_event ie{};
|
||||
ie.type = type;
|
||||
ie.code = code;
|
||||
ie.value = val;
|
||||
|
||||
int res = write(m_uinput_fd, &ie, sizeof(ie));
|
||||
if (res == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread::emit_event: write failed (errno=%d=%s)", res, strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void gui_pad_thread::send_key_event(u32 key, bool pressed)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_key_event: key=%d, pressed=%d", key, pressed);
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = key;
|
||||
|
||||
if (!pressed)
|
||||
{
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
emit_event(EV_KEY, key, pressed ? 1 : 0);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGEventRef ev = CGEventCreateKeyboardEvent(NULL, static_cast<CGKeyCode>(key), pressed);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateKeyboardEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_button_event(mouse_button btn, bool pressed)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_button_event: btn=%d, pressed=%d", static_cast<int>(btn), pressed);
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left: input.mi.dwFlags = pressed ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; break;
|
||||
case mouse_button::right: input.mi.dwFlags = pressed ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; break;
|
||||
case mouse_button::middle: input.mi.dwFlags = pressed ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; break;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
int key = 0;
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left: key = BTN_LEFT; break;
|
||||
case mouse_button::right: key = BTN_RIGHT; break;
|
||||
case mouse_button::middle: key = BTN_MIDDLE; break;
|
||||
}
|
||||
|
||||
emit_event(EV_KEY, key, pressed ? 1 : 0);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGEventType type{};
|
||||
CGMouseButton mouse_btn{};
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left:
|
||||
type = pressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp;
|
||||
mouse_btn = kCGMouseButtonLeft;
|
||||
break;
|
||||
case mouse_button::right:
|
||||
type = pressed ? kCGEventRightMouseDown : kCGEventRightMouseUp;
|
||||
mouse_btn = kCGMouseButtonRight;
|
||||
break;
|
||||
case mouse_button::middle:
|
||||
type = pressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp;
|
||||
mouse_btn = kCGMouseButtonCenter;
|
||||
break;
|
||||
}
|
||||
|
||||
CGEventRef ev = CGEventCreateMouseEvent(NULL, type, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), mouse_btn);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_wheel_event(mouse_wheel wheel, int delta)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_wheel_event: wheel=%d, delta=%d", static_cast<int>(wheel), delta);
|
||||
|
||||
if (!delta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.mouseData = delta;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: input.mi.dwFlags = MOUSEEVENTF_WHEEL; break;
|
||||
case mouse_wheel::horizontal: input.mi.dwFlags = MOUSEEVENTF_HWHEEL; break;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
int axis = 0;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: axis = REL_WHEEL; break;
|
||||
case mouse_wheel::horizontal: axis = REL_HWHEEL; break;
|
||||
}
|
||||
|
||||
emit_event(EV_REL, axis, delta);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
int v_delta = 0;
|
||||
int h_delta = 0;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: v_delta = delta; break;
|
||||
case mouse_wheel::horizontal: h_delta = delta; break;
|
||||
}
|
||||
|
||||
constexpr u32 wheel_count = 2;
|
||||
CGEventRef ev = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, wheel_count, v_delta, h_delta);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateScrollWheelEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_move_event(int delta_x, int delta_y)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_move_event: delta_x=%d, delta_y=%d", delta_x, delta_y);
|
||||
|
||||
if (!delta_x && !delta_y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
input.mi.dx = delta_x;
|
||||
input.mi.dy = delta_y;
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (delta_x)
|
||||
emit_event(EV_REL, REL_X, delta_x);
|
||||
if (delta_y)
|
||||
emit_event(EV_REL, REL_Y, delta_y);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGDirectDisplayID display = CGMainDisplayID();
|
||||
const usz width = CGDisplayPixelsWide(display);
|
||||
const usz height = CGDisplayPixelsHigh(display);
|
||||
const float mouse_abs_x = std::clamp(m_mouse_abs_x + delta_x, 0.0f, width - 1.0f);
|
||||
const float mouse_abs_y = std::clamp(m_mouse_abs_y + delta_y, 0.0f, height - 1.0f);
|
||||
|
||||
if (m_mouse_abs_x == mouse_abs_x && m_mouse_abs_y == mouse_abs_y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mouse_abs_x = mouse_abs_x;
|
||||
m_mouse_abs_y = mouse_abs_y;
|
||||
|
||||
CGEventRef ev = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), {});
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Emu/Io/pad_types.h"
|
||||
#include "Emu/Io/pad_config.h"
|
||||
#include "Emu/Io/pad_config_types.h"
|
||||
#include "util/Timer.h"
|
||||
#include "util/Thread.h"
|
||||
|
||||
class PadHandlerBase;
|
||||
class gui_settings;
|
||||
|
||||
class gui_pad_thread
|
||||
{
|
||||
public:
|
||||
gui_pad_thread();
|
||||
virtual ~gui_pad_thread();
|
||||
|
||||
void update_settings(const std::shared_ptr<gui_settings>& settings);
|
||||
|
||||
static std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type);
|
||||
static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler);
|
||||
|
||||
static void reset()
|
||||
{
|
||||
m_reset = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool init();
|
||||
void run();
|
||||
|
||||
void process_input();
|
||||
|
||||
enum class mouse_button
|
||||
{
|
||||
none,
|
||||
left,
|
||||
right,
|
||||
middle
|
||||
};
|
||||
|
||||
enum class mouse_wheel
|
||||
{
|
||||
none,
|
||||
vertical,
|
||||
horizontal
|
||||
};
|
||||
|
||||
void send_key_event(u32 key, bool pressed);
|
||||
void send_mouse_button_event(mouse_button btn, bool pressed);
|
||||
void send_mouse_wheel_event(mouse_wheel wheel, int delta);
|
||||
void send_mouse_move_event(int delta_x, int delta_y);
|
||||
|
||||
#ifdef __linux__
|
||||
int m_uinput_fd = -1;
|
||||
void emit_event(int type, int code, int val);
|
||||
#endif
|
||||
|
||||
std::shared_ptr<PadHandlerBase> m_handler;
|
||||
std::shared_ptr<Pad> m_pad;
|
||||
|
||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||
atomic_t<bool> m_allow_global_input = false;
|
||||
static atomic_t<bool> m_reset;
|
||||
|
||||
std::array<bool, static_cast<u32>(pad_button::pad_button_max_enum)> m_last_button_state{};
|
||||
|
||||
steady_clock::time_point m_timestamp;
|
||||
steady_clock::time_point m_initial_timestamp;
|
||||
Timer m_input_timer;
|
||||
|
||||
static constexpr u64 auto_repeat_ms_interval_default = 200;
|
||||
pad_button m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
std::map<pad_button, u64> m_auto_repeat_buttons = {
|
||||
{pad_button::dpad_up, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_down, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_left, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_right, auto_repeat_ms_interval_default},
|
||||
{pad_button::rs_up, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_down, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_left, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_right, 1} // used for wheel scrolling by default
|
||||
};
|
||||
|
||||
// Mouse movement should just work without delays
|
||||
std::map<pad_button, u64> m_mouse_move_buttons = {
|
||||
{pad_button::ls_up, 1},
|
||||
{pad_button::ls_down, 1},
|
||||
{pad_button::ls_left, 1},
|
||||
{pad_button::ls_right, 1}};
|
||||
pad_button m_mouse_boost_button = pad_button::L2;
|
||||
bool m_boost_mouse = false;
|
||||
float m_mouse_delta_x = 0.0f;
|
||||
float m_mouse_delta_y = 0.0f;
|
||||
#ifdef __APPLE__
|
||||
float m_mouse_abs_x = 0.0f;
|
||||
float m_mouse_abs_y = 0.0f;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QKeyEvent>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
enum mouse
|
||||
{
|
||||
move_left = 0x05555550,
|
||||
move_right = 0x05555551,
|
||||
move_up = 0x05555552,
|
||||
move_down = 0x05555553,
|
||||
wheel_up = 0x05555554,
|
||||
wheel_down = 0x05555555,
|
||||
wheel_left = 0x05555556,
|
||||
wheel_right = 0x05555557
|
||||
};
|
||||
|
||||
// Unique button names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> mouse_list =
|
||||
{
|
||||
{Qt::NoButton, ""},
|
||||
{Qt::LeftButton, "Mouse Left"},
|
||||
{Qt::RightButton, "Mouse Right"},
|
||||
{Qt::MiddleButton, "Mouse Middle"},
|
||||
{Qt::BackButton, "Mouse Back"},
|
||||
{Qt::ForwardButton, "Mouse Fwd"},
|
||||
{Qt::TaskButton, "Mouse Task"},
|
||||
{Qt::ExtraButton4, "Mouse 4"},
|
||||
{Qt::ExtraButton5, "Mouse 5"},
|
||||
{Qt::ExtraButton6, "Mouse 6"},
|
||||
{Qt::ExtraButton7, "Mouse 7"},
|
||||
{Qt::ExtraButton8, "Mouse 8"},
|
||||
{Qt::ExtraButton9, "Mouse 9"},
|
||||
{Qt::ExtraButton10, "Mouse 10"},
|
||||
{Qt::ExtraButton11, "Mouse 11"},
|
||||
{Qt::ExtraButton12, "Mouse 12"},
|
||||
{Qt::ExtraButton13, "Mouse 13"},
|
||||
{Qt::ExtraButton14, "Mouse 14"},
|
||||
{Qt::ExtraButton15, "Mouse 15"},
|
||||
{Qt::ExtraButton16, "Mouse 16"},
|
||||
{Qt::ExtraButton17, "Mouse 17"},
|
||||
{Qt::ExtraButton18, "Mouse 18"},
|
||||
{Qt::ExtraButton19, "Mouse 19"},
|
||||
{Qt::ExtraButton20, "Mouse 20"},
|
||||
{Qt::ExtraButton21, "Mouse 21"},
|
||||
{Qt::ExtraButton22, "Mouse 22"},
|
||||
{Qt::ExtraButton23, "Mouse 23"},
|
||||
{Qt::ExtraButton24, "Mouse 24"},
|
||||
|
||||
{mouse::move_left, "Mouse MLeft"},
|
||||
{mouse::move_right, "Mouse MRight"},
|
||||
{mouse::move_up, "Mouse MUp"},
|
||||
{mouse::move_down, "Mouse MDown"},
|
||||
|
||||
{mouse::wheel_up, "Wheel Up"},
|
||||
{mouse::wheel_down, "Wheel Down"},
|
||||
{mouse::wheel_left, "Wheel Left"},
|
||||
{mouse::wheel_right, "Wheel Right"},
|
||||
};
|
||||
|
||||
class keyboard_pad_handler final : public QObject, public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
bool Init() override;
|
||||
|
||||
keyboard_pad_handler();
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
void processKeyEvent(QKeyEvent* event, bool pressed);
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseWheelEvent(QWheelEvent* event);
|
||||
|
||||
bool eventFilter(QObject* target, QEvent* ev) override;
|
||||
|
||||
void init_config(cfg_pad* cfg) override;
|
||||
std::vector<pad_list_entry> list_devices() override;
|
||||
connection get_next_button_press(const std::string& /*padId*/, const pad_callback& /*callback*/, const pad_fail_callback& /*fail_callback*/, gui_call_type /*call_type*/, const std::vector<std::string>& /*buttons*/) override
|
||||
{
|
||||
return connection::connected;
|
||||
}
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad) override;
|
||||
void process() override;
|
||||
|
||||
static std::string GetMouseName(const QMouseEvent* event);
|
||||
static std::string GetMouseName(u32 button);
|
||||
static QStringList GetKeyNames(const QKeyEvent* keyEvent);
|
||||
static std::string GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers);
|
||||
static std::string GetKeyName(const u32& keyCode);
|
||||
static std::set<u32> GetKeyCodes(const cfg::string& cfg_string);
|
||||
static u32 GetKeyCode(const QString& keyName);
|
||||
|
||||
static int native_scan_code_from_string(const std::string& key);
|
||||
static std::string native_scan_code_to_string(int native_scan_code);
|
||||
|
||||
protected:
|
||||
void Key(const u32 code, bool pressed, u16 value = 255);
|
||||
|
||||
private:
|
||||
QWindow* m_target = nullptr;
|
||||
mouse_movement_mode m_mouse_movement_mode = mouse_movement_mode::relative;
|
||||
bool m_keys_released = false;
|
||||
bool m_mouse_move_used = false;
|
||||
bool m_mouse_wheel_used = false;
|
||||
bool get_mouse_lock_state() const;
|
||||
void release_all_keys();
|
||||
|
||||
std::vector<Pad> m_pads_internal; // Accumulates input until the next poll. Only used for user input!
|
||||
|
||||
// Button Movements
|
||||
steady_clock::time_point m_button_time;
|
||||
f32 m_analog_lerp_factor = 1.0f;
|
||||
f32 m_trigger_lerp_factor = 1.0f;
|
||||
bool m_analog_limiter_toggle_mode = false;
|
||||
bool m_pressure_intensity_toggle_mode = false;
|
||||
u32 m_pressure_intensity_deadzone = 0;
|
||||
|
||||
// Stick Movements
|
||||
steady_clock::time_point m_stick_time;
|
||||
f32 m_l_stick_lerp_factor = 1.0f;
|
||||
f32 m_r_stick_lerp_factor = 1.0f;
|
||||
u32 m_l_stick_multiplier = 100;
|
||||
u32 m_r_stick_multiplier = 100;
|
||||
|
||||
static constexpr usz max_sticks = 4;
|
||||
std::array<u8, max_sticks> m_stick_min{0, 0, 0, 0};
|
||||
std::array<u8, max_sticks> m_stick_max{128, 128, 128, 128};
|
||||
std::array<u8, max_sticks> m_stick_val{128, 128, 128, 128};
|
||||
|
||||
// Mouse Movements
|
||||
steady_clock::time_point m_last_mouse_move_left;
|
||||
steady_clock::time_point m_last_mouse_move_right;
|
||||
steady_clock::time_point m_last_mouse_move_up;
|
||||
steady_clock::time_point m_last_mouse_move_down;
|
||||
int m_deadzone_x = 60;
|
||||
int m_deadzone_y = 60;
|
||||
double m_multi_x = 2;
|
||||
double m_multi_y = 2.5;
|
||||
|
||||
// Mousewheel
|
||||
steady_clock::time_point m_last_wheel_move_up;
|
||||
steady_clock::time_point m_last_wheel_move_down;
|
||||
steady_clock::time_point m_last_wheel_move_left;
|
||||
steady_clock::time_point m_last_wheel_move_right;
|
||||
};
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#ifdef WITH_DISCORD_RPC
|
||||
#include "_discord_utils.h"
|
||||
#include "discord_rpc.h"
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace discord
|
||||
{
|
||||
void initialize(const std::string& application_id)
|
||||
{
|
||||
DiscordEventHandlers handlers = {};
|
||||
Discord_Initialize(application_id.c_str(), &handlers, 1, nullptr);
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
Discord_Shutdown();
|
||||
}
|
||||
|
||||
void update_presence(const std::string& state, const std::string& details, bool reset_timer)
|
||||
{
|
||||
DiscordRichPresence discordPresence = {};
|
||||
discordPresence.details = details.c_str();
|
||||
discordPresence.state = state.c_str();
|
||||
discordPresence.largeImageKey = "rpcs3_logo";
|
||||
discordPresence.largeImageText = "RPCS3 is the world's first PlayStation 3 emulator.";
|
||||
|
||||
if (reset_timer)
|
||||
{
|
||||
discordPresence.startTimestamp = std::time(nullptr);
|
||||
}
|
||||
|
||||
Discord_UpdatePresence(&discordPresence);
|
||||
}
|
||||
} // namespace discord
|
||||
#endif
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace discord
|
||||
{
|
||||
// Convenience function for initialization
|
||||
void initialize(const std::string& application_id = "424004941485572097");
|
||||
|
||||
// Convenience function for shutdown
|
||||
void shutdown();
|
||||
|
||||
// Convenience function for status updates. The default is set to idle.
|
||||
void update_presence(const std::string& state = "", const std::string& details = "Idle", bool reset_timer = true);
|
||||
} // namespace discord
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#include "about_dialog.h"
|
||||
#include "ui_about_dialog.h"
|
||||
|
||||
#include "rpcs3_version.h"
|
||||
#include "qt_utils.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
#include <QSvgWidget>
|
||||
|
||||
about_dialog::about_dialog(QWidget* parent) : QDialog(parent), ui(new Ui::about_dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
ui->close->setDefault(true);
|
||||
ui->icon->load(QStringLiteral(":/rpcs3.svg"));
|
||||
ui->version->setText(tr("RPCS3 Version: %1").arg(QString::fromStdString(rpcs3::get_verbose_version())));
|
||||
ui->description->setText(gui::utils::make_paragraph(tr(
|
||||
"RPCS3 is an open-source Sony PlayStation 3 emulator and debugger.\n"
|
||||
"It is written in C++ for Windows, Linux, FreeBSD and MacOS, funded with %0.\n"
|
||||
"Our developers and contributors are always working hard to ensure this project is the best that it can be.\n"
|
||||
"There are still plenty of implementations to make and optimizations to do.")
|
||||
.arg(gui::utils::make_link(tr("Patreon"), "https://rpcs3.net/patreon"))));
|
||||
|
||||
// Events
|
||||
connect(ui->gitHub, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://www.github.com/RPCS3"));
|
||||
});
|
||||
connect(ui->website, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://rpcs3.net"));
|
||||
});
|
||||
connect(ui->forum, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://forums.rpcs3.net"));
|
||||
});
|
||||
connect(ui->patreon, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon"));
|
||||
});
|
||||
connect(ui->discord, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://discord.me/RPCS3"));
|
||||
});
|
||||
connect(ui->wiki, &QPushButton::clicked, []
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://wiki.rpcs3.net/index.php?title=Main_Page"));
|
||||
});
|
||||
connect(ui->close, &QPushButton::clicked, this, &QWidget::close);
|
||||
}
|
||||
|
||||
about_dialog::~about_dialog()
|
||||
{
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class about_dialog;
|
||||
}
|
||||
|
||||
class about_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit about_dialog(QWidget* parent = nullptr);
|
||||
~about_dialog();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::about_dialog> ui;
|
||||
};
|
||||
|
|
@ -1,468 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>about_dialog</class>
|
||||
<widget class="QDialog" name="about_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>805</width>
|
||||
<height>555</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About RPCS3</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/rpcs3.ico</normaloff>:/rpcs3.ico</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="about_dialog_layout" stretch="0,1,0">
|
||||
<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="QWidget" name="header_section" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="header_section_layout" stretch="0,1">
|
||||
<item>
|
||||
<widget class="QWidget" name="icon_widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="icon_widget_layout">
|
||||
<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="QSvgWidget" name="icon" native="true">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="icon_spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="text_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>14</pointsize>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RPCS3 PlayStation 3 Emulator</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="description">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<kerning>true</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="version">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<kerning>true</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="name_section">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="names">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>793</width>
|
||||
<height>2638</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="names_layout" stretch="1,1,1,1">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="developerLayout" stretch="0,1">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="developersTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Developers:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="developers">
|
||||
<property name="text">
|
||||
<string notr="true"><p>¬DH<br>¬AlexAltea<br>¬Hykem<br>¬Oil<br>Nekotekina<br>¬Bigpet<br>¬gopalsr83<br>¬tambry<br>¬vlj<br>kd-11<br>¬jarveson<br>¬raven02<br>AniLeo<br>¬cornytrace<br>¬ssshadow<br>¬Numan<br>hcorion<br>Megamouse<br>¬flash-fire<br>DAGINATSUKO<br>GalCiv<br>eladash</p></string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="contributorsLayout" stretch="0,1">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="contributorsTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contributors:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="contributors">
|
||||
<property name="text">
|
||||
<string notr="true"><p>BlackDaemon<br>elisha464<br>Aishou<br>krofna<br>xsacha<br>danilaml<br>unknownbrackets<br>Zangetsu38<br>lioncash<br>achurch<br>darkf<br>Syphurith<br>Blaypeg<br>Survanium90<br>georgemoralis<br>ikki84<br>scribam<br>TGE<br>velocity<br>Farseer<br>Dangles<br>ruipin<br>jbeich<br>CookiePLMonster<br>Whatcookie<br>rajkosto<br>Admiral Thrawn<br>FlexBy<br>Dunastique<br>Jonathan44062<br>yurinator557<br>Satan<br>HoldTheMourning<br>illusion0001</p></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="supportersLayout" stretch="0,1">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="supportersTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Supporters:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="supporters">
|
||||
<property name="text">
|
||||
<string notr="true"><p>Howard Garrison<br>EXPotemkin<br>Marko V.<br>danhp<br>Jake (5315825)<br>Ian Reid<br>Tad Sherlock<br>Tyler Friesen<br>Folzar<br>Payton Williams<br>RedPill Australia<br>yanghong<br>Mohammed El-Serougi<br>Дима ~Ximer13~ Кулин<br>James Reed<br>BaroqueSonata<br>Bonzay0<br>Henrijs Kons<br>eoiz<br>Lena Stöffler<br>Michael Holder<br>Max Bosse<br>Tyler Whisinnand<br>Gato Harvey<br>cain4355<br>Thomas Peltier<br>Loli Co.<br>MapleLoonie<br>Travis McEwen<br>Scott Singratsomboune<br>T.E<br>Lukas Rieger<br>Dane Madsen<br>JMS<br>Jonatan R<br>Luke Johnson<br>Thomas Zaorski<br>MyOwnFan<br>Alexandros Mandravillis<br>Socker Bopper<br>Faris Leonhart<br>Fabien Net<br>Raves<br>Barrowsx<br>kilsuton<br>Max Mason<br>Ethan Condon<br>jfidone<br>iaDRM<br>Kazer2.0<br>Bryce Quintin<br>Yuri Kunde Schlesner<br>Abdulla Altayer<br>Nicolas Jallamion<br>Vorvek<br>Ian Faddis<br>Leon<br>Mohammad Taleb<br>Jokez<br>crashX<br>Raveskirza<br>Grant Deacon<br>michael<br>David Zech<br>Ben Manoochehri<br>Adnan Kovacevic<br>Mighty J<br>Sam Shan Jiang<br>TheAnig<br>Rodney Coleman<br>FiniteAce<br>Kian Soon Alex Chiam<br>yukkuri<br>Justin Chadwick<br>toxic Itzi<br>Templerror<br>Myles Wesley Carlson<br>Max Bosse<br>Ethan Clark<br>LupineDream<br>CheatCodesOfLife<br>Jan Zykmund<br>Francesco Cinquemani<br>Andylg<br>Julia H de Camargos<br>Suvodip Mitra<br>Goh<br>Dmitry<br>Steel Brain<br>VarieZ<br>William Swango<br>Matthew Messersmith<br>Duane Locsin<br>Shuddertrix<br>Loweys Litsman<br>Shuddertrix<br>Mason Ferrie<br>Richard Kaplan<br>Hugues Valois<br>richard(lath..ch@)<br>Johnathan (Virtuous John)<br>eoiz<br>Dany Huguenin<br>doobieashtray<br>dean(mag..94@)<br>Pommier Jean-Philippe<br>Douglas Alan Albino<br>Ryan Mull<br>Thor-Erling Engen<br>Nick Carpenter<br>curryking3<br>Jared Tracton<br>alex(koo..oh@)<br>Jason O'Brien<br>Skeletal Charizard<br>Ace00<br>Brandon Corujo<br>HyperBitHero<br>佳文 李<br>sorryboi
|
||||
<br>Johnson Bui
|
||||
<br>itachi1986
|
||||
<br>Mortano
|
||||
<br>Xythera
|
||||
<br>Albert Quinteros
|
||||
<br>Uzair Sheikh
|
||||
<br>Ethan Hoppins
|
||||
<br>optic
|
||||
<br>Quill Slyver
|
||||
<br>Averie
|
||||
<br>StevenCarson
|
||||
<br>YuriNator557
|
||||
<br>Deanmaxx
|
||||
<br>linkQatar G
|
||||
<br>Jack Collie
|
||||
<br>TAL BERKOVITZ
|
||||
<br>cjtk
|
||||
<br>Comexzone
|
||||
<br>mapleglass
|
||||
<br>Liquidbings
|
||||
<br>Dormant_Hero@0230
|
||||
<br>Theodore Raney
|
||||
<br>Morito
|
||||
<br>Chaining Ten
|
||||
<br>Xeropel
|
||||
<br>Marko Gatzouris
|
||||
<br>Steven (...weller@gmail.com)
|
||||
<br>Real Gamer
|
||||
<br>Spencer Robinson
|
||||
<br>naps
|
||||
<br>Matthew Stevens
|
||||
<br>Orion Clark
|
||||
<br>William K. Leung
|
||||
<br>Trent M
|
||||
<br>Viktor Ni
|
||||
<br>Marc Tönsing
|
||||
<br>Amogus
|
||||
<br>Scott Davis
|
||||
<br>秉軒 侯
|
||||
<br>Wiktor Tkaczyński
|
||||
<br>Vekkar
|
||||
<br>Jackson Abney
|
||||
</p></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="names_spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="button_section">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="gitHub">
|
||||
<property name="text">
|
||||
<string>GitHub</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="website">
|
||||
<property name="text">
|
||||
<string>Website</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="forum">
|
||||
<property name="text">
|
||||
<string>Forum</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="discord">
|
||||
<property name="text">
|
||||
<string>Discord</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="wiki">
|
||||
<property name="text">
|
||||
<string>Wiki</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="patreon">
|
||||
<property name="text">
|
||||
<string>Patreon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="button_spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QSvgWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qsvgwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
#include "auto_pause_settings_dialog.h"
|
||||
#include "table_item_delegate.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "util/logs.hpp"
|
||||
#include "util/File.h"
|
||||
|
||||
LOG_CHANNEL(autopause_log, "AutoPause");
|
||||
|
||||
auto_pause_settings_dialog::auto_pause_settings_dialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
QLabel* description = new QLabel(tr("To use auto pause: enter the ID(s) of a function or a system call.\nRestart of the game is required to apply. You can enable/disable this in the settings."), this);
|
||||
|
||||
m_pause_list = new QTableWidget(this);
|
||||
m_pause_list->setColumnCount(2);
|
||||
m_pause_list->setHorizontalHeaderLabels(QStringList() << tr("Call ID") << tr("Type"));
|
||||
// m_pause_list->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_pause_list->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_pause_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_pause_list->setItemDelegate(new table_item_delegate(this));
|
||||
m_pause_list->setShowGrid(false);
|
||||
|
||||
QPushButton* clearButton = new QPushButton(tr("Clear"), this);
|
||||
QPushButton* reloadButton = new QPushButton(tr("Reload"), this);
|
||||
QPushButton* saveButton = new QPushButton(tr("Save"), this);
|
||||
QPushButton* cancelButton = new QPushButton(tr("Cancel"), this);
|
||||
cancelButton->setDefault(true);
|
||||
|
||||
QHBoxLayout* buttonsLayout = new QHBoxLayout();
|
||||
buttonsLayout->addWidget(clearButton);
|
||||
buttonsLayout->addWidget(reloadButton);
|
||||
buttonsLayout->addStretch();
|
||||
buttonsLayout->addWidget(saveButton);
|
||||
buttonsLayout->addWidget(cancelButton);
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(description);
|
||||
mainLayout->addWidget(m_pause_list);
|
||||
mainLayout->addLayout(buttonsLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setMinimumSize(QSize(400, 360));
|
||||
setWindowTitle(tr("Auto Pause Manager"));
|
||||
setObjectName("auto_pause_manager");
|
||||
|
||||
// Events
|
||||
connect(m_pause_list, &QTableWidget::customContextMenuRequested, this, &auto_pause_settings_dialog::ShowContextMenu);
|
||||
connect(clearButton, &QAbstractButton::clicked, [this]()
|
||||
{
|
||||
m_entries.clear();
|
||||
UpdateList();
|
||||
});
|
||||
connect(reloadButton, &QAbstractButton::clicked, [this]()
|
||||
{
|
||||
LoadEntries();
|
||||
UpdateList();
|
||||
});
|
||||
connect(saveButton, &QAbstractButton::clicked, [this]()
|
||||
{
|
||||
SaveEntries();
|
||||
autopause_log.success("File pause.bin was updated.");
|
||||
});
|
||||
connect(cancelButton, &QAbstractButton::clicked, this, &QWidget::close);
|
||||
|
||||
Emu.GracefulShutdown(false);
|
||||
|
||||
LoadEntries();
|
||||
UpdateList();
|
||||
setFixedSize(sizeHint());
|
||||
}
|
||||
|
||||
// Copied some from AutoPause.
|
||||
void auto_pause_settings_dialog::LoadEntries()
|
||||
{
|
||||
m_entries.clear();
|
||||
m_entries.reserve(16);
|
||||
|
||||
const fs::file list(fs::get_config_dir() + "pause.bin");
|
||||
|
||||
if (list)
|
||||
{
|
||||
// System calls ID and Function calls ID are all u32 iirc.
|
||||
u32 num;
|
||||
const usz fmax = list.size();
|
||||
usz fcur = 0;
|
||||
list.seek(0);
|
||||
while (fcur <= fmax - sizeof(u32))
|
||||
{
|
||||
list.read(&num, sizeof(u32));
|
||||
fcur += sizeof(u32);
|
||||
if (num == 0xFFFFFFFF)
|
||||
break;
|
||||
|
||||
m_entries.emplace_back(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copied some from AutoPause.
|
||||
// Tip: This one doesn't check for the file is being read or not.
|
||||
// This would always use a 0xFFFFFFFF as end of the pause.bin
|
||||
void auto_pause_settings_dialog::SaveEntries()
|
||||
{
|
||||
fs::file list(fs::get_config_dir() + "pause.bin", fs::rewrite);
|
||||
// System calls ID and Function calls ID are all u32 iirc.
|
||||
u32 num = 0;
|
||||
list.seek(0);
|
||||
for (usz i = 0; i < m_entries.size(); ++i)
|
||||
{
|
||||
if (num == 0xFFFFFFFF)
|
||||
continue;
|
||||
num = m_entries[i];
|
||||
list.write(&num, sizeof(u32));
|
||||
}
|
||||
num = 0xFFFFFFFF;
|
||||
list.write(&num, sizeof(u32));
|
||||
}
|
||||
|
||||
void auto_pause_settings_dialog::UpdateList()
|
||||
{
|
||||
const int entries_size = static_cast<int>(m_entries.size());
|
||||
m_pause_list->clearContents();
|
||||
m_pause_list->setRowCount(entries_size);
|
||||
for (int i = 0; i < entries_size; ++i)
|
||||
{
|
||||
QTableWidgetItem* callItem = new QTableWidgetItem;
|
||||
QTableWidgetItem* typeItem = new QTableWidgetItem;
|
||||
callItem->setFlags(callItem->flags() & ~Qt::ItemIsEditable);
|
||||
typeItem->setFlags(typeItem->flags() & ~Qt::ItemIsEditable);
|
||||
if (m_entries[i] != 0xFFFFFFFF)
|
||||
{
|
||||
callItem->setData(Qt::DisplayRole, QString::fromStdString(fmt::format("%08x", m_entries[i])));
|
||||
}
|
||||
else
|
||||
{
|
||||
callItem->setData(Qt::DisplayRole, tr("Unset"));
|
||||
}
|
||||
|
||||
if (m_entries[i] < 1024)
|
||||
{
|
||||
typeItem->setData(Qt::DisplayRole, tr("System Call"));
|
||||
}
|
||||
else
|
||||
{
|
||||
typeItem->setData(Qt::DisplayRole, tr("Function Call"));
|
||||
}
|
||||
|
||||
m_pause_list->setItem(i, 0, callItem);
|
||||
m_pause_list->setItem(i, 1, typeItem);
|
||||
}
|
||||
}
|
||||
|
||||
void auto_pause_settings_dialog::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
const int row = m_pause_list->indexAt(pos).row();
|
||||
|
||||
QMenu myMenu;
|
||||
|
||||
// Make Actions
|
||||
QAction* add = myMenu.addAction(tr("&Add"));
|
||||
QAction* remove = myMenu.addAction(tr("&Remove"));
|
||||
myMenu.addSeparator();
|
||||
QAction* config = myMenu.addAction(tr("&Config"));
|
||||
|
||||
if (row == -1)
|
||||
{
|
||||
remove->setEnabled(false);
|
||||
config->setEnabled(false);
|
||||
}
|
||||
|
||||
auto OnEntryConfig = [this](int row, bool newEntry)
|
||||
{
|
||||
AutoPauseConfigDialog* config = new AutoPauseConfigDialog(this, this, newEntry, &m_entries[row]);
|
||||
config->setModal(true);
|
||||
config->exec();
|
||||
UpdateList();
|
||||
};
|
||||
|
||||
connect(add, &QAction::triggered, this, [=, this]()
|
||||
{
|
||||
m_entries.emplace_back(0xFFFFFFFF);
|
||||
UpdateList();
|
||||
const int idx = static_cast<int>(m_entries.size()) - 1;
|
||||
m_pause_list->selectRow(idx);
|
||||
OnEntryConfig(idx, true);
|
||||
});
|
||||
connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove);
|
||||
connect(config, &QAction::triggered, this, [=, this]()
|
||||
{
|
||||
OnEntryConfig(row, false);
|
||||
});
|
||||
|
||||
myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void auto_pause_settings_dialog::OnRemove()
|
||||
{
|
||||
QModelIndexList selection = m_pause_list->selectionModel()->selectedRows();
|
||||
std::sort(selection.begin(), selection.end());
|
||||
for (int i = selection.count() - 1; i >= 0; i--)
|
||||
{
|
||||
m_entries.erase(m_entries.begin() + ::at32(selection, i).row());
|
||||
}
|
||||
UpdateList();
|
||||
}
|
||||
|
||||
void auto_pause_settings_dialog::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (event->isAutoRepeat())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->key() == Qt::Key_Delete)
|
||||
{
|
||||
OnRemove();
|
||||
}
|
||||
}
|
||||
|
||||
AutoPauseConfigDialog::AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry)
|
||||
: QDialog(parent), m_presult(entry), m_newEntry(newEntry), m_apsd(apsd)
|
||||
{
|
||||
m_entry = *m_presult;
|
||||
setMinimumSize(QSize(300, -1));
|
||||
|
||||
QPushButton* button_ok = new QPushButton(tr("&Ok"), this);
|
||||
QPushButton* button_cancel = new QPushButton(tr("&Cancel"), this);
|
||||
button_ok->setFixedWidth(50);
|
||||
button_cancel->setFixedWidth(50);
|
||||
|
||||
QLabel* description = new QLabel(tr("Specify ID of System Call or Function Call below. You need to use a Hexadecimal ID."), this);
|
||||
description->setWordWrap(true);
|
||||
|
||||
m_current_converted = new QLabel(tr("Currently it gets an id of \"Unset\"."), this);
|
||||
m_current_converted->setWordWrap(true);
|
||||
|
||||
m_id = new QLineEdit(this);
|
||||
m_id->setText(QString::fromStdString(fmt::format("%08x", m_entry)));
|
||||
m_id->setPlaceholderText("ffffffff");
|
||||
m_id->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
m_id->setMaxLength(8);
|
||||
m_id->setFixedWidth(65);
|
||||
setWindowTitle("Auto Pause Setting: " + m_id->text());
|
||||
|
||||
connect(button_cancel, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnCancel);
|
||||
connect(button_ok, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnOk);
|
||||
connect(m_id, &QLineEdit::textChanged, this, &AutoPauseConfigDialog::OnUpdateValue);
|
||||
|
||||
QHBoxLayout* configHBox = new QHBoxLayout();
|
||||
configHBox->addWidget(m_id);
|
||||
configHBox->addWidget(button_ok);
|
||||
configHBox->addWidget(button_cancel);
|
||||
configHBox->setAlignment(Qt::AlignCenter);
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(description);
|
||||
mainLayout->addLayout(configHBox);
|
||||
mainLayout->addWidget(m_current_converted);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setFixedSize(QSize(300, sizeHint().height()));
|
||||
|
||||
OnUpdateValue();
|
||||
}
|
||||
|
||||
void AutoPauseConfigDialog::OnOk()
|
||||
{
|
||||
bool ok;
|
||||
const ullong value = m_id->text().toULongLong(&ok, 16);
|
||||
|
||||
m_entry = value;
|
||||
*m_presult = m_entry;
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
void AutoPauseConfigDialog::OnCancel()
|
||||
{
|
||||
if (m_newEntry)
|
||||
{
|
||||
m_apsd->OnRemove();
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
void AutoPauseConfigDialog::OnUpdateValue() const
|
||||
{
|
||||
bool ok;
|
||||
const ullong value = m_id->text().toULongLong(&ok, 16);
|
||||
const bool is_ok = ok && value <= u32{umax};
|
||||
|
||||
m_current_converted->setText(tr("Current value: %1 (%2)").arg(value, 8, 16).arg(is_ok ? tr("OK") : tr("Conversion failed")));
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QTableWidget>
|
||||
#include <QLineEdit>
|
||||
#include <vector>
|
||||
|
||||
class auto_pause_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum
|
||||
{
|
||||
id_add,
|
||||
id_remove,
|
||||
id_config,
|
||||
};
|
||||
|
||||
std::vector<u32> m_entries;
|
||||
QTableWidget* m_pause_list;
|
||||
|
||||
public:
|
||||
explicit auto_pause_settings_dialog(QWidget* parent);
|
||||
void UpdateList();
|
||||
void LoadEntries();
|
||||
void SaveEntries();
|
||||
|
||||
public Q_SLOTS:
|
||||
void OnRemove();
|
||||
private Q_SLOTS:
|
||||
void ShowContextMenu(const QPoint& pos);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
||||
|
||||
class AutoPauseConfigDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
u32 m_entry;
|
||||
u32* m_presult;
|
||||
bool m_newEntry;
|
||||
QLineEdit* m_id;
|
||||
QLabel* m_current_converted;
|
||||
auto_pause_settings_dialog* m_apsd;
|
||||
|
||||
public:
|
||||
explicit AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry);
|
||||
|
||||
private Q_SLOTS:
|
||||
void OnOk();
|
||||
void OnCancel();
|
||||
void OnUpdateValue() const;
|
||||
};
|
||||
|
|
@ -1,298 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "basic_mouse_settings_dialog.h"
|
||||
#include "localized_emu.h"
|
||||
#include "Input/basic_mouse_handler.h"
|
||||
#include "Input/keyboard_pad_handler.h"
|
||||
#include "Emu/Io/mouse_config.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QMessageBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
enum button_role
|
||||
{
|
||||
button_name = Qt::UserRole,
|
||||
button_code
|
||||
};
|
||||
|
||||
basic_mouse_settings_dialog::basic_mouse_settings_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setObjectName("basic_mouse_settings_dialog");
|
||||
setWindowTitle(tr("Configure Basic Mouse Handler"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAttribute(Qt::WA_StyledBackground);
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* v_layout = new QVBoxLayout(this);
|
||||
|
||||
m_button_box = new QDialogButtonBox(this);
|
||||
m_button_box->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults);
|
||||
|
||||
connect(m_button_box, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button)
|
||||
{
|
||||
if (button == m_button_box->button(QDialogButtonBox::Apply))
|
||||
{
|
||||
g_cfg_mouse.save();
|
||||
}
|
||||
else if (button == m_button_box->button(QDialogButtonBox::Save))
|
||||
{
|
||||
g_cfg_mouse.save();
|
||||
accept();
|
||||
}
|
||||
else if (button == m_button_box->button(QDialogButtonBox::RestoreDefaults))
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all settings?")) != QMessageBox::Yes)
|
||||
return;
|
||||
reset_config();
|
||||
}
|
||||
else if (button == m_button_box->button(QDialogButtonBox::Cancel))
|
||||
{
|
||||
// Restore config
|
||||
if (!g_cfg_mouse.load())
|
||||
{
|
||||
cfg_log.notice("Could not restore mouse config. Using defaults.");
|
||||
}
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
if (!g_cfg_mouse.load())
|
||||
{
|
||||
cfg_log.notice("Could not load basic mouse config. Using defaults.");
|
||||
}
|
||||
|
||||
m_buttons = new QButtonGroup(this);
|
||||
|
||||
connect(m_buttons, &QButtonGroup::idClicked, this, &basic_mouse_settings_dialog::on_button_click);
|
||||
|
||||
connect(&m_remap_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
auto button = m_buttons->button(m_button_id);
|
||||
|
||||
if (--m_seconds <= 0)
|
||||
{
|
||||
if (button)
|
||||
{
|
||||
if (const int button_id = m_buttons->id(button))
|
||||
{
|
||||
const std::string name = g_cfg_mouse.get_button(button_id).to_string();
|
||||
button->setText(name.empty() ? QStringLiteral("-") : QString::fromStdString(name));
|
||||
}
|
||||
}
|
||||
reactivate_buttons();
|
||||
return;
|
||||
}
|
||||
if (button)
|
||||
{
|
||||
button->setText(tr("[ Waiting %1 ]").arg(m_seconds));
|
||||
}
|
||||
});
|
||||
|
||||
const auto insert_button = [this](int id, QPushButton* button)
|
||||
{
|
||||
m_buttons->addButton(button, id);
|
||||
button->installEventFilter(this);
|
||||
};
|
||||
|
||||
constexpr u32 button_count = 8;
|
||||
constexpr u32 max_items_per_column = 4;
|
||||
int rows = button_count;
|
||||
|
||||
for (u32 cols = 1; utils::aligned_div(button_count, cols) > max_items_per_column;)
|
||||
{
|
||||
rows = utils::aligned_div(button_count, ++cols);
|
||||
}
|
||||
|
||||
QWidget* widget = new QWidget(this);
|
||||
QGridLayout* grid_layout = new QGridLayout(this);
|
||||
|
||||
for (int i = 0, row = 0, col = 0; i < static_cast<int>(button_count); i++, row++)
|
||||
{
|
||||
const int cell_code = get_mouse_button_code(i);
|
||||
const QString translated_cell_button = localized_emu::translated_mouse_button(cell_code);
|
||||
|
||||
QHBoxLayout* h_layout = new QHBoxLayout(this);
|
||||
QGroupBox* gb = new QGroupBox(translated_cell_button, this);
|
||||
QPushButton* pb = new QPushButton(this);
|
||||
|
||||
insert_button(cell_code, pb);
|
||||
|
||||
const std::string saved_btn = g_cfg_mouse.get_button(cell_code);
|
||||
|
||||
pb->setText(saved_btn.empty() ? QStringLiteral("-") : QString::fromStdString(saved_btn));
|
||||
|
||||
if (row >= rows)
|
||||
{
|
||||
row = 0;
|
||||
col++;
|
||||
}
|
||||
|
||||
m_push_buttons[cell_code] = pb;
|
||||
h_layout->addWidget(pb);
|
||||
gb->setLayout(h_layout);
|
||||
grid_layout->addWidget(gb, row, col);
|
||||
}
|
||||
|
||||
widget->setLayout(grid_layout);
|
||||
|
||||
v_layout->addWidget(widget);
|
||||
v_layout->addWidget(m_button_box);
|
||||
setLayout(v_layout);
|
||||
|
||||
m_palette = m_push_buttons[CELL_MOUSE_BUTTON_1]->palette(); // save normal palette
|
||||
}
|
||||
|
||||
void basic_mouse_settings_dialog::reset_config()
|
||||
{
|
||||
g_cfg_mouse.from_default();
|
||||
|
||||
for (auto& [cell_code, pb] : m_push_buttons)
|
||||
{
|
||||
if (!pb)
|
||||
continue;
|
||||
|
||||
const QString text = QString::fromStdString(g_cfg_mouse.get_button(cell_code).def);
|
||||
pb->setText(text.isEmpty() ? QStringLiteral("-") : text);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_settings_dialog::on_button_click(int id)
|
||||
{
|
||||
if (id < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto but : m_buttons->buttons())
|
||||
{
|
||||
but->setEnabled(false);
|
||||
but->setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
m_button_box->setEnabled(false);
|
||||
for (auto but : m_button_box->buttons())
|
||||
{
|
||||
but->setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
m_button_id = id;
|
||||
if (auto button = m_buttons->button(m_button_id))
|
||||
{
|
||||
button->setText(tr("[ Waiting %1 ]").arg(MAX_SECONDS));
|
||||
button->setPalette(QPalette(Qt::blue));
|
||||
button->grabMouse();
|
||||
}
|
||||
|
||||
m_remap_timer.start(1000);
|
||||
}
|
||||
|
||||
void basic_mouse_settings_dialog::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (m_button_id < 0)
|
||||
{
|
||||
// We are not remapping a button, so pass the event to the base class.
|
||||
QDialog::keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string name = keyboard_pad_handler::GetKeyName(event, false);
|
||||
g_cfg_mouse.get_button(m_button_id).from_string(name);
|
||||
|
||||
if (auto button = m_buttons->button(m_button_id))
|
||||
{
|
||||
button->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
reactivate_buttons();
|
||||
}
|
||||
|
||||
void basic_mouse_settings_dialog::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (m_button_id < 0)
|
||||
{
|
||||
// We are not remapping a button, so pass the event to the base class.
|
||||
QDialog::mouseReleaseEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string name = keyboard_pad_handler::GetMouseName(event);
|
||||
g_cfg_mouse.get_button(m_button_id).from_string(name);
|
||||
|
||||
if (auto button = m_buttons->button(m_button_id))
|
||||
{
|
||||
button->setText(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
reactivate_buttons();
|
||||
}
|
||||
|
||||
bool basic_mouse_settings_dialog::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
// On right click clear binding if we are not remapping pad button
|
||||
if (m_button_id < 0)
|
||||
{
|
||||
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
if (const auto button = qobject_cast<QPushButton*>(object); button && button->isEnabled() && mouse_event->button() == Qt::RightButton)
|
||||
{
|
||||
if (const int button_id = m_buttons->id(button))
|
||||
{
|
||||
button->setText(QStringLiteral("-"));
|
||||
g_cfg_mouse.get_button(button_id).from_string("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled buttons should not absorb mouseclicks
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QDialog::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void basic_mouse_settings_dialog::reactivate_buttons()
|
||||
{
|
||||
m_remap_timer.stop();
|
||||
m_seconds = MAX_SECONDS;
|
||||
|
||||
if (m_button_id < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto button = m_buttons->button(m_button_id))
|
||||
{
|
||||
button->setPalette(m_palette);
|
||||
button->releaseMouse();
|
||||
}
|
||||
|
||||
m_button_id = -1;
|
||||
|
||||
// Enable all buttons
|
||||
m_button_box->setEnabled(true);
|
||||
|
||||
for (auto but : m_button_box->buttons())
|
||||
{
|
||||
but->setFocusPolicy(Qt::StrongFocus);
|
||||
}
|
||||
|
||||
for (auto but : m_buttons->buttons())
|
||||
{
|
||||
but->setEnabled(true);
|
||||
but->setFocusPolicy(Qt::StrongFocus);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QPushButton>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QTimer>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class basic_mouse_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
basic_mouse_settings_dialog(QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
void reset_config();
|
||||
|
||||
void on_button_click(int id);
|
||||
void reactivate_buttons();
|
||||
|
||||
// Buttons
|
||||
QDialogButtonBox* m_button_box = nullptr;
|
||||
QButtonGroup* m_buttons = nullptr;
|
||||
std::unordered_map<int, QPushButton*> m_push_buttons;
|
||||
int m_button_id = -1;
|
||||
|
||||
// Backup for standard button palette
|
||||
QPalette m_palette;
|
||||
|
||||
// Remap Timer
|
||||
static constexpr int MAX_SECONDS = 5;
|
||||
int m_seconds = MAX_SECONDS;
|
||||
QTimer m_remap_timer;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
};
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#include "breakpoint_handler.h"
|
||||
|
||||
extern bool ppu_breakpoint(u32 loc, bool is_adding);
|
||||
|
||||
bool breakpoint_handler::IsBreakOnBPM() const
|
||||
{
|
||||
return m_break_on_bpm;
|
||||
}
|
||||
|
||||
void breakpoint_handler::SetBreakOnBPM(bool break_on_bpm)
|
||||
{
|
||||
m_break_on_bpm = break_on_bpm;
|
||||
}
|
||||
|
||||
bool breakpoint_handler::HasBreakpoint(u32 loc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
return m_breakpoints.contains(loc) && ((m_breakpoints.at(loc) & type) == type);
|
||||
}
|
||||
|
||||
bool breakpoint_handler::AddBreakpoint(u32 loc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
if ((type & breakpoint_types::bp_exec) && !ppu_breakpoint(loc, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_breakpoints.insert({loc, type}).second;
|
||||
}
|
||||
|
||||
bool breakpoint_handler::RemoveBreakpoint(u32 loc)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
bs_t<breakpoint_types> bp_type{};
|
||||
if (m_breakpoints.contains(loc))
|
||||
{
|
||||
bp_type = m_breakpoints.at(loc);
|
||||
}
|
||||
|
||||
if (m_breakpoints.erase(loc) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bp_type & breakpoint_types::bp_exec)
|
||||
{
|
||||
ensure(ppu_breakpoint(loc, false));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/bit_set.h"
|
||||
#include <map>
|
||||
#include "util/mutex.h"
|
||||
|
||||
enum class breakpoint_types
|
||||
{
|
||||
bp_read = 0x1,
|
||||
bp_write = 0x2,
|
||||
bp_exec = 0x4,
|
||||
__bitset_enum_max
|
||||
};
|
||||
|
||||
/*
|
||||
* This class acts as a layer between the UI and Emu for breakpoints.
|
||||
*/
|
||||
class breakpoint_handler
|
||||
{
|
||||
|
||||
public:
|
||||
breakpoint_handler() = default;
|
||||
~breakpoint_handler() = default;
|
||||
|
||||
bool IsBreakOnBPM() const;
|
||||
void SetBreakOnBPM(bool break_on_bpm);
|
||||
|
||||
/**
|
||||
* Returns true iff breakpoint exists at loc.
|
||||
* TODO: Add arg for flags, gameid, and maybe even thread if it should be thread local breakpoint.... breakpoint struct is probably what'll happen
|
||||
*/
|
||||
bool HasBreakpoint(u32 loc, bs_t<breakpoint_types> type);
|
||||
|
||||
/**
|
||||
* Returns true if added successfully. TODO: flags
|
||||
*/
|
||||
bool AddBreakpoint(u32 loc, bs_t<breakpoint_types> type);
|
||||
|
||||
/**
|
||||
* Returns true if removed breakpoint at loc successfully.
|
||||
*/
|
||||
bool RemoveBreakpoint(u32 loc);
|
||||
|
||||
private:
|
||||
// TODO : generalize to hold multiple games and handle flags.Probably do : std::map<std::string (gameid), std::set<breakpoint>>.
|
||||
// Although, externally, they'll only be accessed by loc (I think) so a map of maps may also do?
|
||||
shared_mutex mutex_breakpoints;
|
||||
std::map<u32, bs_t<breakpoint_types>> m_breakpoints; //! Holds all breakpoints.
|
||||
bool m_break_on_bpm = false;
|
||||
};
|
||||
|
||||
extern breakpoint_handler g_breakpoint_handler;
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
#include "breakpoint_list.h"
|
||||
#include "breakpoint_handler.h"
|
||||
|
||||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "debugger_add_bp_window.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QMouseEvent>
|
||||
|
||||
extern bool is_using_interpreter(thread_class t_class);
|
||||
|
||||
breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_ppu_breakpoint_handler(handler)
|
||||
{
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
connect(this, &QListWidget::itemDoubleClicked, this, &breakpoint_list::OnBreakpointListDoubleClicked);
|
||||
connect(this, &QListWidget::customContextMenuRequested, this, &breakpoint_list::OnBreakpointListRightClicked);
|
||||
|
||||
m_delete_action = new QAction(tr("&Delete"), this);
|
||||
m_delete_action->setShortcut(Qt::Key_Delete);
|
||||
m_delete_action->setShortcutContext(Qt::WidgetShortcut);
|
||||
connect(m_delete_action, &QAction::triggered, this, &breakpoint_list::OnBreakpointListDelete);
|
||||
addAction(m_delete_action);
|
||||
|
||||
// Hide until used in order to allow as much space for registers panel as possible
|
||||
hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this.
|
||||
*/
|
||||
void breakpoint_list::UpdateCPUData(std::shared_ptr<CPUDisAsm> disasm)
|
||||
{
|
||||
m_disasm = std::move(disasm);
|
||||
}
|
||||
|
||||
void breakpoint_list::ClearBreakpoints()
|
||||
{
|
||||
while (count())
|
||||
{
|
||||
auto* currentItem = takeItem(0);
|
||||
const u32 loc = currentItem->data(Qt::UserRole).value<u32>();
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(loc);
|
||||
delete currentItem;
|
||||
}
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
void breakpoint_list::RemoveBreakpoint(u32 addr)
|
||||
{
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(addr);
|
||||
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
QListWidgetItem* currentItem = item(i);
|
||||
|
||||
if (currentItem->data(Qt::UserRole).value<u32>() == addr)
|
||||
{
|
||||
delete takeItem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count())
|
||||
{
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool breakpoint_list::AddBreakpoint(u32 pc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
if (!m_ppu_breakpoint_handler->AddBreakpoint(pc, type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QString breakpoint_item_text;
|
||||
|
||||
if (type == breakpoint_types::bp_exec)
|
||||
{
|
||||
m_disasm->disasm(m_disasm->dump_pc = pc);
|
||||
breakpoint_item_text = QString::fromStdString(m_disasm->last_opcode);
|
||||
breakpoint_item_text.remove(10, 13);
|
||||
}
|
||||
else if (type == breakpoint_types::bp_read)
|
||||
{
|
||||
breakpoint_item_text = QString("BPMR: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
else if (type == breakpoint_types::bp_write)
|
||||
{
|
||||
breakpoint_item_text = QString("BPMW: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
else if (type == (breakpoint_types::bp_read + breakpoint_types::bp_write))
|
||||
{
|
||||
breakpoint_item_text = QString("BPMRW: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
|
||||
QListWidgetItem* breakpoint_item = new QListWidgetItem(breakpoint_item_text);
|
||||
breakpoint_item->setForeground(m_text_color_bp);
|
||||
breakpoint_item->setBackground(m_color_bp);
|
||||
breakpoint_item->setData(Qt::UserRole, pc);
|
||||
addItem(breakpoint_item);
|
||||
|
||||
show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow.
|
||||
*/
|
||||
void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add)
|
||||
{
|
||||
const auto cpu = m_disasm ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
if (!cpu || cpu->state & cpu_flag::exit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_using_interpreter(cpu->get_class()))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Interpreters-Only Feature!"), tr("Cannot set breakpoints on non-interpreter decoders."));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cpu->get_class())
|
||||
{
|
||||
case thread_class::spu:
|
||||
{
|
||||
if (loc >= SPU_LS_SIZE || loc % 4)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-SPU executable memory!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto spu = static_cast<spu_thread*>(cpu);
|
||||
auto& list = spu->local_breakpoints;
|
||||
const u32 pos_at = loc / 4;
|
||||
const u32 pos_bit = 1u << (pos_at % 8);
|
||||
|
||||
if (list[pos_at / 8].fetch_xor(pos_bit) & pos_bit)
|
||||
{
|
||||
if (std::none_of(list.begin(), list.end(), [](auto& val)
|
||||
{
|
||||
return val.load();
|
||||
}))
|
||||
{
|
||||
spu->has_active_local_bps = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!spu->has_active_local_bps.exchange(true))
|
||||
{
|
||||
spu->state.atomic_op([](bs_t<cpu_flag>& flags)
|
||||
{
|
||||
if (flags & cpu_flag::pending)
|
||||
{
|
||||
flags += cpu_flag::pending_recheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags += cpu_flag::pending;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case thread_class::ppu:
|
||||
break;
|
||||
default:
|
||||
QMessageBox::warning(this, tr("Unimplemented Breakpoints For Thread Type!"), tr("Cannot set breakpoints on a thread not an PPU/SPU currently, sorry."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vm::check_addr(loc, vm::page_executable))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-executable memory!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ppu_breakpoint_handler->HasBreakpoint(loc, breakpoint_types::bp_exec))
|
||||
{
|
||||
if (!only_add)
|
||||
{
|
||||
RemoveBreakpoint(loc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AddBreakpoint(loc, breakpoint_types::bp_exec))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Unknown error while setting breakpoint!"), tr("Failed to set breakpoints."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void breakpoint_list::OnBreakpointListDoubleClicked()
|
||||
{
|
||||
if (QListWidgetItem* item = currentItem())
|
||||
{
|
||||
const u32 address = item->data(Qt::UserRole).value<u32>();
|
||||
Q_EMIT RequestShowAddress(address);
|
||||
}
|
||||
}
|
||||
|
||||
void breakpoint_list::OnBreakpointListRightClicked(const QPoint& pos)
|
||||
{
|
||||
m_context_menu = new QMenu();
|
||||
|
||||
if (selectedItems().count() == 1)
|
||||
{
|
||||
QAction* rename_action = m_context_menu->addAction(tr("&Rename"));
|
||||
connect(rename_action, &QAction::triggered, this, [this]()
|
||||
{
|
||||
QListWidgetItem* current_item = selectedItems().first();
|
||||
current_item->setFlags(current_item->flags() | Qt::ItemIsEditable);
|
||||
editItem(current_item);
|
||||
});
|
||||
m_context_menu->addSeparator();
|
||||
}
|
||||
|
||||
if (selectedItems().count() >= 1)
|
||||
{
|
||||
m_context_menu->addAction(m_delete_action);
|
||||
}
|
||||
|
||||
QAction* m_addbp = new QAction(tr("Add Breakpoint"), this);
|
||||
connect(m_addbp, &QAction::triggered, this, [this]
|
||||
{
|
||||
debugger_add_bp_window dlg(this, this);
|
||||
dlg.exec();
|
||||
});
|
||||
m_context_menu->addAction(m_addbp);
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
QAction* m_tglbpmbreak = new QAction(m_ppu_breakpoint_handler->IsBreakOnBPM() ? tr("Disable BPM") : tr("Enable BPM"), this);
|
||||
connect(m_tglbpmbreak, &QAction::triggered, [this]
|
||||
{
|
||||
m_ppu_breakpoint_handler->SetBreakOnBPM(!m_ppu_breakpoint_handler->IsBreakOnBPM());
|
||||
});
|
||||
m_context_menu->addAction(m_tglbpmbreak);
|
||||
#endif
|
||||
|
||||
m_context_menu->exec(viewport()->mapToGlobal(pos));
|
||||
m_context_menu->deleteLater();
|
||||
m_context_menu = nullptr;
|
||||
}
|
||||
|
||||
void breakpoint_list::OnBreakpointListDelete()
|
||||
{
|
||||
for (int i = selectedItems().count() - 1; i >= 0; i--)
|
||||
{
|
||||
RemoveBreakpoint(::at32(selectedItems(), i)->data(Qt::UserRole).value<u32>());
|
||||
}
|
||||
|
||||
if (m_context_menu)
|
||||
{
|
||||
m_context_menu->close();
|
||||
}
|
||||
}
|
||||
|
||||
void breakpoint_list::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
// Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button.
|
||||
// So we have to ignore this event when another button is pressed.
|
||||
if (ev->button() != Qt::LeftButton)
|
||||
{
|
||||
ev->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidget::mouseDoubleClickEvent(ev);
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QListWidget>
|
||||
|
||||
#include "breakpoint_handler.h"
|
||||
|
||||
class CPUDisAsm;
|
||||
class cpu_thread;
|
||||
|
||||
class breakpoint_list : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
breakpoint_list(QWidget* parent, breakpoint_handler* handler);
|
||||
void UpdateCPUData(std::shared_ptr<CPUDisAsm> disasm);
|
||||
void ClearBreakpoints();
|
||||
void RemoveBreakpoint(u32 addr);
|
||||
bool AddBreakpoint(u32 pc, bs_t<breakpoint_types> type);
|
||||
|
||||
QColor m_text_color_bp;
|
||||
QColor m_color_bp;
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent* ev) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false);
|
||||
public Q_SLOTS:
|
||||
void HandleBreakpointRequest(u32 loc, bool add_only);
|
||||
|
||||
private Q_SLOTS:
|
||||
void OnBreakpointListDoubleClicked();
|
||||
void OnBreakpointListRightClicked(const QPoint& pos);
|
||||
void OnBreakpointListDelete();
|
||||
|
||||
private:
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
QMenu* m_context_menu = nullptr;
|
||||
QAction* m_delete_action;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm = nullptr;
|
||||
};
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#include "call_stack_list.h"
|
||||
|
||||
#include "util/StrFmt.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
|
||||
call_stack_list::call_stack_list(QWidget* parent) : QListWidget(parent)
|
||||
{
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
// connects
|
||||
connect(this, &QListWidget::itemDoubleClicked, this, &call_stack_list::ShowItemAddress);
|
||||
|
||||
// Hide until used in order to allow as much space for registers panel as possible
|
||||
hide();
|
||||
}
|
||||
|
||||
void call_stack_list::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
QListWidget::keyPressEvent(event);
|
||||
event->ignore(); // Propagate the event to debugger_frame
|
||||
|
||||
if (!event->modifiers() && event->key() == Qt::Key_Return)
|
||||
{
|
||||
ShowItemAddress();
|
||||
}
|
||||
}
|
||||
|
||||
void call_stack_list::HandleUpdate(const std::vector<std::pair<u32, u32>>& call_stack)
|
||||
{
|
||||
clear();
|
||||
|
||||
for (const auto& addr : call_stack)
|
||||
{
|
||||
const QString text = QString::fromStdString(fmt::format("0x%08llx (sp=0x%08llx)", addr.first, addr.second));
|
||||
QListWidgetItem* call_stack_item = new QListWidgetItem(text);
|
||||
call_stack_item->setData(Qt::UserRole, {addr.first});
|
||||
addItem(call_stack_item);
|
||||
}
|
||||
|
||||
setVisible(!call_stack.empty());
|
||||
}
|
||||
|
||||
void call_stack_list::ShowItemAddress()
|
||||
{
|
||||
if (QListWidgetItem* call_stack_item = currentItem())
|
||||
{
|
||||
const u32 address = call_stack_item->data(Qt::UserRole).value<u32>();
|
||||
Q_EMIT RequestShowAddress(address);
|
||||
}
|
||||
}
|
||||
|
||||
void call_stack_list::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
// Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button.
|
||||
// So we have to ignore this event when another button is pressed.
|
||||
if (ev->button() != Qt::LeftButton)
|
||||
{
|
||||
ev->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidget::mouseDoubleClickEvent(ev);
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QListWidget>
|
||||
#include <vector>
|
||||
|
||||
class cpu_thread;
|
||||
class CPUDisAsm;
|
||||
|
||||
class call_stack_list : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit call_stack_list(QWidget* parent);
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent* ev) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false);
|
||||
public Q_SLOTS:
|
||||
void HandleUpdate(const std::vector<std::pair<u32, u32>>& call_stack);
|
||||
|
||||
private Q_SLOTS:
|
||||
void ShowItemAddress();
|
||||
|
||||
private:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "camera_settings_dialog.h"
|
||||
#include "ui_camera_settings_dialog.h"
|
||||
#include "permissions.h"
|
||||
#include "Emu/Io/camera_config.h"
|
||||
|
||||
#include <QCameraDevice>
|
||||
#include <QMediaDevices>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
LOG_CHANNEL(camera_log, "Camera");
|
||||
|
||||
template <>
|
||||
void fmt_class_string<QVideoFrameFormat::PixelFormat>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](QVideoFrameFormat::PixelFormat value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case QVideoFrameFormat::Format_ARGB8888: return "ARGB8888";
|
||||
case QVideoFrameFormat::Format_ARGB8888_Premultiplied: return "ARGB8888_Premultiplied";
|
||||
case QVideoFrameFormat::Format_XRGB8888: return "XRGB8888";
|
||||
case QVideoFrameFormat::Format_BGRA8888: return "BGRA8888";
|
||||
case QVideoFrameFormat::Format_BGRA8888_Premultiplied: return "BGRA8888_Premultiplied";
|
||||
case QVideoFrameFormat::Format_BGRX8888: return "BGRX8888";
|
||||
case QVideoFrameFormat::Format_ABGR8888: return "ABGR8888";
|
||||
case QVideoFrameFormat::Format_XBGR8888: return "XBGR8888";
|
||||
case QVideoFrameFormat::Format_RGBA8888: return "RGBA8888";
|
||||
case QVideoFrameFormat::Format_RGBX8888: return "RGBX8888";
|
||||
case QVideoFrameFormat::Format_AYUV: return "AYUV";
|
||||
case QVideoFrameFormat::Format_AYUV_Premultiplied: return "AYUV_Premultiplied";
|
||||
case QVideoFrameFormat::Format_YUV420P: return "YUV420P";
|
||||
case QVideoFrameFormat::Format_YUV422P: return "YUV422P";
|
||||
case QVideoFrameFormat::Format_YV12: return "YV12";
|
||||
case QVideoFrameFormat::Format_UYVY: return "UYVY";
|
||||
case QVideoFrameFormat::Format_YUYV: return "YUYV";
|
||||
case QVideoFrameFormat::Format_NV12: return "NV12";
|
||||
case QVideoFrameFormat::Format_NV21: return "NV21";
|
||||
case QVideoFrameFormat::Format_IMC1: return "IMC1";
|
||||
case QVideoFrameFormat::Format_IMC2: return "IMC2";
|
||||
case QVideoFrameFormat::Format_IMC3: return "IMC3";
|
||||
case QVideoFrameFormat::Format_IMC4: return "IMC4";
|
||||
case QVideoFrameFormat::Format_Y8: return "Y8";
|
||||
case QVideoFrameFormat::Format_Y16: return "Y16";
|
||||
case QVideoFrameFormat::Format_P010: return "P010";
|
||||
case QVideoFrameFormat::Format_P016: return "P016";
|
||||
case QVideoFrameFormat::Format_SamplerExternalOES: return "SamplerExternalOES";
|
||||
case QVideoFrameFormat::Format_Jpeg: return "Jpeg";
|
||||
case QVideoFrameFormat::Format_SamplerRect: return "SamplerRect";
|
||||
default: return unknown;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QCameraDevice);
|
||||
|
||||
camera_settings_dialog::camera_settings_dialog(QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::camera_settings_dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
load_config();
|
||||
|
||||
for (const QCameraDevice& camera_info : QMediaDevices::videoInputs())
|
||||
{
|
||||
if (camera_info.isNull())
|
||||
continue;
|
||||
ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info));
|
||||
camera_log.notice("Found camera: '%s'", camera_info.description());
|
||||
}
|
||||
|
||||
connect(ui->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_camera_change);
|
||||
connect(ui->combo_settings, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_settings_change);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
|
||||
{
|
||||
if (button == ui->buttonBox->button(QDialogButtonBox::Save))
|
||||
{
|
||||
save_config();
|
||||
accept();
|
||||
}
|
||||
else if (button == ui->buttonBox->button(QDialogButtonBox::Apply))
|
||||
{
|
||||
save_config();
|
||||
}
|
||||
});
|
||||
|
||||
if (ui->combo_camera->count() == 0)
|
||||
{
|
||||
ui->combo_camera->setEnabled(false);
|
||||
ui->combo_settings->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: show camera ID somewhere
|
||||
ui->combo_camera->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
camera_settings_dialog::~camera_settings_dialog()
|
||||
{
|
||||
}
|
||||
|
||||
void camera_settings_dialog::handle_camera_change(int index)
|
||||
{
|
||||
if (index < 0 || !ui->combo_camera->itemData(index).canConvert<QCameraDevice>())
|
||||
{
|
||||
ui->combo_settings->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const QCameraDevice camera_info = ui->combo_camera->itemData(index).value<QCameraDevice>();
|
||||
|
||||
if (camera_info.isNull())
|
||||
{
|
||||
ui->combo_settings->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_camera.reset(new QCamera(camera_info));
|
||||
m_media_capture_session.reset(new QMediaCaptureSession(nullptr));
|
||||
m_media_capture_session->setCamera(m_camera.get());
|
||||
m_media_capture_session->setVideoSink(ui->videoWidget->videoSink());
|
||||
|
||||
if (!m_camera->isAvailable())
|
||||
{
|
||||
ui->combo_settings->clear();
|
||||
QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application."));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->combo_settings->blockSignals(true);
|
||||
ui->combo_settings->clear();
|
||||
|
||||
QList<QCameraFormat> settings = camera_info.videoFormats();
|
||||
std::sort(settings.begin(), settings.end(), [](const QCameraFormat& l, const QCameraFormat& r) -> bool
|
||||
{
|
||||
if (l.resolution().width() > r.resolution().width())
|
||||
return true;
|
||||
if (l.resolution().width() < r.resolution().width())
|
||||
return false;
|
||||
if (l.resolution().height() > r.resolution().height())
|
||||
return true;
|
||||
if (l.resolution().height() < r.resolution().height())
|
||||
return false;
|
||||
if (l.minFrameRate() > r.minFrameRate())
|
||||
return true;
|
||||
if (l.minFrameRate() < r.minFrameRate())
|
||||
return false;
|
||||
if (l.maxFrameRate() > r.maxFrameRate())
|
||||
return true;
|
||||
if (l.maxFrameRate() < r.maxFrameRate())
|
||||
return false;
|
||||
if (l.pixelFormat() > r.pixelFormat())
|
||||
return true;
|
||||
if (l.pixelFormat() < r.pixelFormat())
|
||||
return false;
|
||||
return false;
|
||||
});
|
||||
|
||||
for (const QCameraFormat& setting : settings)
|
||||
{
|
||||
if (setting.isNull())
|
||||
continue;
|
||||
const QString description = tr("%0x%1, %2-%3 FPS, Format=%4")
|
||||
.arg(setting.resolution().width())
|
||||
.arg(setting.resolution().height())
|
||||
.arg(setting.minFrameRate())
|
||||
.arg(setting.maxFrameRate())
|
||||
.arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat())));
|
||||
ui->combo_settings->addItem(description, QVariant::fromValue(setting));
|
||||
}
|
||||
ui->combo_settings->blockSignals(false);
|
||||
|
||||
if (ui->combo_settings->count() == 0)
|
||||
{
|
||||
ui->combo_settings->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load selected settings from config file
|
||||
int index = 0;
|
||||
bool success = false;
|
||||
const std::string key = camera_info.id().toStdString();
|
||||
cfg_camera::camera_setting cfg_setting = g_cfg_camera.get_camera_setting(key, success);
|
||||
|
||||
if (success)
|
||||
{
|
||||
camera_log.notice("Found config entry for camera \"%s\"", key);
|
||||
|
||||
// Select matching drowdown entry
|
||||
const double epsilon = 0.001;
|
||||
|
||||
for (int i = 0; i < ui->combo_settings->count(); i++)
|
||||
{
|
||||
const QCameraFormat tmp = ui->combo_settings->itemData(i).value<QCameraFormat>();
|
||||
|
||||
if (tmp.resolution().width() == cfg_setting.width &&
|
||||
tmp.resolution().height() == cfg_setting.height &&
|
||||
tmp.minFrameRate() >= (cfg_setting.min_fps - epsilon) &&
|
||||
tmp.minFrameRate() <= (cfg_setting.min_fps + epsilon) &&
|
||||
tmp.maxFrameRate() >= (cfg_setting.max_fps - epsilon) &&
|
||||
tmp.maxFrameRate() <= (cfg_setting.max_fps + epsilon) &&
|
||||
tmp.pixelFormat() == static_cast<QVideoFrameFormat::PixelFormat>(cfg_setting.format))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui->combo_settings->setCurrentIndex(std::max<int>(0, index));
|
||||
ui->combo_settings->setEnabled(true);
|
||||
|
||||
// Update config to match user interface outcome
|
||||
const QCameraFormat setting = ui->combo_settings->currentData().value<QCameraFormat>();
|
||||
cfg_setting.width = setting.resolution().width();
|
||||
cfg_setting.height = setting.resolution().height();
|
||||
cfg_setting.min_fps = setting.minFrameRate();
|
||||
cfg_setting.max_fps = setting.maxFrameRate();
|
||||
cfg_setting.format = static_cast<int>(setting.pixelFormat());
|
||||
g_cfg_camera.set_camera_setting(key, cfg_setting);
|
||||
}
|
||||
}
|
||||
|
||||
void camera_settings_dialog::handle_settings_change(int index)
|
||||
{
|
||||
if (!m_camera)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_camera->isAvailable())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gui::utils::check_camera_permission(this, [this, index]()
|
||||
{
|
||||
handle_settings_change(index);
|
||||
},
|
||||
[this]()
|
||||
{
|
||||
QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device."));
|
||||
}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index >= 0 && ui->combo_settings->itemData(index).canConvert<QCameraFormat>() && ui->combo_camera->currentData().canConvert<QCameraDevice>())
|
||||
{
|
||||
const QCameraFormat setting = ui->combo_settings->itemData(index).value<QCameraFormat>();
|
||||
if (!setting.isNull())
|
||||
{
|
||||
m_camera->setCameraFormat(setting);
|
||||
}
|
||||
|
||||
cfg_camera::camera_setting cfg_setting;
|
||||
cfg_setting.width = setting.resolution().width();
|
||||
cfg_setting.height = setting.resolution().height();
|
||||
cfg_setting.min_fps = setting.minFrameRate();
|
||||
cfg_setting.max_fps = setting.maxFrameRate();
|
||||
cfg_setting.format = static_cast<int>(setting.pixelFormat());
|
||||
g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value<QCameraDevice>().id().toStdString(), cfg_setting);
|
||||
}
|
||||
|
||||
m_camera->start();
|
||||
}
|
||||
|
||||
void camera_settings_dialog::load_config()
|
||||
{
|
||||
if (!g_cfg_camera.load())
|
||||
{
|
||||
camera_log.notice("Could not load camera config. Using defaults.");
|
||||
}
|
||||
}
|
||||
|
||||
void camera_settings_dialog::save_config()
|
||||
{
|
||||
g_cfg_camera.save();
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QCamera>
|
||||
#include <QDialog>
|
||||
#include <QMediaCaptureSession>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class camera_settings_dialog;
|
||||
}
|
||||
|
||||
class camera_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
camera_settings_dialog(QWidget* parent = nullptr);
|
||||
virtual ~camera_settings_dialog();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handle_camera_change(int index);
|
||||
void handle_settings_change(int index);
|
||||
|
||||
private:
|
||||
void load_config();
|
||||
void save_config();
|
||||
|
||||
std::unique_ptr<Ui::camera_settings_dialog> ui;
|
||||
std::unique_ptr<QCamera> m_camera;
|
||||
std::unique_ptr<QMediaCaptureSession> m_media_capture_session;
|
||||
};
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>camera_settings_dialog</class>
|
||||
<widget class="QDialog" name="camera_settings_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>356</width>
|
||||
<height>380</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Camera Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="mainLayout" stretch="0,1,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="settingsLayout" stretch="1,2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbCamera">
|
||||
<property name="title">
|
||||
<string>Camera</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="camera_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_camera">
|
||||
<property name="placeholderText">
|
||||
<string>No cameras found</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbSettings">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="settings_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_settings">
|
||||
<property name="placeholderText">
|
||||
<string>No settings found</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbPreview">
|
||||
<property name="title">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="preview_layout">
|
||||
<item>
|
||||
<widget class="QVideoWidget" name="videoWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QVideoWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qvideowidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>camera_settings_dialog</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>camera_settings_dialog</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>
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
enum Category
|
||||
{
|
||||
Disc_Game,
|
||||
HDD_Game,
|
||||
PS1_Game,
|
||||
PS2_Game,
|
||||
PSP_Game,
|
||||
Home,
|
||||
Media,
|
||||
Data,
|
||||
OS,
|
||||
Unknown_Cat,
|
||||
Others,
|
||||
};
|
||||
|
||||
namespace cat
|
||||
{
|
||||
const QString cat_app_music = "AM";
|
||||
const QString cat_app_photo = "AP";
|
||||
const QString cat_app_store = "AS";
|
||||
const QString cat_app_tv = "AT";
|
||||
const QString cat_app_video = "AV";
|
||||
const QString cat_bc_video = "BV";
|
||||
const QString cat_web_tv = "WT";
|
||||
const QString cat_home = "HM";
|
||||
const QString cat_network = "CB";
|
||||
const QString cat_store_fe = "SF";
|
||||
const QString cat_disc_game = "DG";
|
||||
const QString cat_hdd_game = "HG";
|
||||
const QString cat_ps2_game = "2P";
|
||||
const QString cat_ps2_inst = "2G";
|
||||
const QString cat_ps1_game = "1P";
|
||||
const QString cat_psp_game = "PP";
|
||||
const QString cat_psp_mini = "MN";
|
||||
const QString cat_psp_rema = "PE";
|
||||
const QString cat_ps3_data = "GD";
|
||||
const QString cat_ps2_data = "2D";
|
||||
const QString cat_ps3_save = "SD";
|
||||
const QString cat_psp_save = "MS";
|
||||
|
||||
const QString cat_ps3_os = "/OS";
|
||||
|
||||
const QString cat_unknown = "Unknown";
|
||||
|
||||
const QStringList ps2_games = {cat_ps2_game, cat_ps2_inst};
|
||||
const QStringList psp_games = {cat_psp_game, cat_psp_mini, cat_psp_rema};
|
||||
const QStringList media = {cat_app_photo, cat_app_video, cat_bc_video, cat_app_music, cat_app_store, cat_app_tv, cat_web_tv};
|
||||
const QStringList data = {cat_ps3_data, cat_ps2_data, cat_ps3_save, cat_psp_save};
|
||||
const QStringList os = {cat_ps3_os};
|
||||
const QStringList others = {cat_network, cat_store_fe};
|
||||
} // namespace cat
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#include "cg_disasm_window.h"
|
||||
#include "gui_settings.h"
|
||||
#include "syntax_highlighter.h"
|
||||
|
||||
#include <QSplitter>
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFontDatabase>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "Emu/RSX/Program/CgBinaryProgram.h"
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
cg_disasm_window::cg_disasm_window(std::shared_ptr<gui_settings> gui_settings)
|
||||
: m_gui_settings(std::move(gui_settings))
|
||||
{
|
||||
setWindowTitle(tr("Cg Disasm"));
|
||||
setObjectName("cg_disasm");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAttribute(Qt::WA_StyledBackground);
|
||||
setAcceptDrops(true);
|
||||
setMinimumSize(QSize(200, 150)); // seems fine on win 10
|
||||
resize(QSize(620, 395));
|
||||
|
||||
m_path_last = m_gui_settings->GetValue(gui::fd_cg_disasm).toString();
|
||||
|
||||
m_disasm_text = new QTextEdit(this);
|
||||
m_disasm_text->setReadOnly(true);
|
||||
m_disasm_text->setWordWrapMode(QTextOption::NoWrap);
|
||||
m_disasm_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
|
||||
m_glsl_text = new QTextEdit(this);
|
||||
m_glsl_text->setReadOnly(true);
|
||||
m_glsl_text->setWordWrapMode(QTextOption::NoWrap);
|
||||
m_glsl_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
|
||||
// m_disasm_text syntax highlighter
|
||||
sh_asm = new AsmHighlighter(m_disasm_text->document());
|
||||
|
||||
// m_glsl_text syntax highlighter
|
||||
sh_glsl = new GlslHighlighter(m_glsl_text->document());
|
||||
|
||||
QSplitter* splitter = new QSplitter();
|
||||
splitter->addWidget(m_disasm_text);
|
||||
splitter->addWidget(m_glsl_text);
|
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout();
|
||||
layout->addWidget(splitter);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
m_disasm_text->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_glsl_text->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_disasm_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu);
|
||||
connect(m_glsl_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu);
|
||||
|
||||
ShowDisasm();
|
||||
}
|
||||
|
||||
void cg_disasm_window::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction* clear = new QAction(tr("&Clear"));
|
||||
QAction* open = new QAction(tr("Open &Cg binary program"));
|
||||
|
||||
menu.addAction(open);
|
||||
menu.addSeparator();
|
||||
menu.addAction(clear);
|
||||
|
||||
connect(clear, &QAction::triggered, [this]()
|
||||
{
|
||||
m_disasm_text->clear();
|
||||
m_glsl_text->clear();
|
||||
});
|
||||
|
||||
connect(open, &QAction::triggered, [this]()
|
||||
{
|
||||
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Cg program object"), m_path_last, tr("Cg program objects (*.fpo;*.vpo);;"));
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
m_path_last = file_path;
|
||||
ShowDisasm();
|
||||
});
|
||||
|
||||
const auto obj = qobject_cast<QTextEdit*>(sender());
|
||||
|
||||
QPoint origin;
|
||||
|
||||
if (obj == m_disasm_text)
|
||||
{
|
||||
origin = m_disasm_text->viewport()->mapToGlobal(pos);
|
||||
}
|
||||
else if (obj == m_glsl_text)
|
||||
{
|
||||
origin = m_glsl_text->viewport()->mapToGlobal(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = mapToGlobal(pos);
|
||||
}
|
||||
|
||||
menu.exec(origin);
|
||||
}
|
||||
|
||||
void cg_disasm_window::ShowDisasm() const
|
||||
{
|
||||
if (QFileInfo(m_path_last).isFile())
|
||||
{
|
||||
CgBinaryDisasm disasm(m_path_last.toStdString());
|
||||
disasm.BuildShaderBody();
|
||||
m_disasm_text->setText(QString::fromStdString(disasm.GetArbShader()));
|
||||
m_glsl_text->setText(QString::fromStdString(disasm.GetGlslShader()));
|
||||
m_gui_settings->SetValue(gui::fd_cg_disasm, m_path_last);
|
||||
}
|
||||
else if (!m_path_last.isEmpty())
|
||||
{
|
||||
gui_log.error("CgDisasm: Failed to open %s", m_path_last);
|
||||
}
|
||||
}
|
||||
|
||||
bool cg_disasm_window::IsValidFile(const QMimeData& md, bool save)
|
||||
{
|
||||
const QList<QUrl> urls = md.urls();
|
||||
|
||||
if (urls.count() > 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString suff = QFileInfo(urls[0].fileName()).suffix().toLower();
|
||||
|
||||
if (suff == "fpo" || suff == "vpo")
|
||||
{
|
||||
if (save)
|
||||
{
|
||||
m_path_last = urls[0].toLocalFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cg_disasm_window::dropEvent(QDropEvent* ev)
|
||||
{
|
||||
if (IsValidFile(*ev->mimeData(), true))
|
||||
{
|
||||
ShowDisasm();
|
||||
}
|
||||
}
|
||||
|
||||
void cg_disasm_window::dragEnterEvent(QDragEnterEvent* ev)
|
||||
{
|
||||
if (IsValidFile(*ev->mimeData()))
|
||||
{
|
||||
ev->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void cg_disasm_window::dragMoveEvent(QDragMoveEvent* ev)
|
||||
{
|
||||
if (IsValidFile(*ev->mimeData()))
|
||||
{
|
||||
ev->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void cg_disasm_window::dragLeaveEvent(QDragLeaveEvent* ev)
|
||||
{
|
||||
ev->accept();
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QTextEdit>
|
||||
#include <QDropEvent>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class AsmHighlighter;
|
||||
class GlslHighlighter;
|
||||
class gui_settings;
|
||||
|
||||
class cg_disasm_window : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void ShowContextMenu(const QPoint& pos);
|
||||
|
||||
private:
|
||||
void ShowDisasm() const;
|
||||
bool IsValidFile(const QMimeData& md, bool save = false);
|
||||
|
||||
QString m_path_last;
|
||||
QTextEdit* m_disasm_text;
|
||||
QTextEdit* m_glsl_text;
|
||||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
AsmHighlighter* sh_asm;
|
||||
GlslHighlighter* sh_glsl;
|
||||
|
||||
public:
|
||||
explicit cg_disasm_window(std::shared_ptr<gui_settings> xSettings);
|
||||
|
||||
protected:
|
||||
void dropEvent(QDropEvent* ev) override;
|
||||
void dragEnterEvent(QDragEnterEvent* ev) override;
|
||||
void dragMoveEvent(QDragMoveEvent* ev) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent* ev) override;
|
||||
};
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QListWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "util/cheat_info.h"
|
||||
|
||||
class cheat_engine
|
||||
{
|
||||
public:
|
||||
cheat_engine();
|
||||
|
||||
bool exist(const std::string& game, const u32 offset) const;
|
||||
void add(const std::string& game, const std::string& description, const cheat_type type, const u32 offset, const std::string& red_script);
|
||||
cheat_info* get(const std::string& game, const u32 offset);
|
||||
bool erase(const std::string& game, const u32 offset);
|
||||
|
||||
void import_cheats_from_str(const std::string& str_cheats);
|
||||
std::string export_cheats_to_str() const;
|
||||
void save() const;
|
||||
|
||||
// Static functions to find/get/set values in ps3 memory
|
||||
static bool resolve_script(u32& final_offset, const u32 offset, const std::string& red_script);
|
||||
|
||||
template <typename T>
|
||||
static std::vector<u32> search(const T value, const std::vector<u32>& to_filter);
|
||||
|
||||
template <typename T>
|
||||
static T get_value(const u32 offset, bool& success);
|
||||
template <typename T>
|
||||
static bool set_value(const u32 offset, const T value);
|
||||
|
||||
static bool is_addr_safe(const u32 offset);
|
||||
static u32 reverse_lookup(const u32 addr, const u32 max_offset, const u32 max_depth, const u32 cur_depth = 0);
|
||||
|
||||
std::map<std::string, std::map<u32, cheat_info>> cheats;
|
||||
|
||||
private:
|
||||
const std::string m_cheats_filename = "cheats.yml";
|
||||
};
|
||||
|
||||
class cheat_manager_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
cheat_manager_dialog(QWidget* parent = nullptr);
|
||||
~cheat_manager_dialog();
|
||||
static cheat_manager_dialog* get_dlg(QWidget* parent = nullptr);
|
||||
|
||||
cheat_manager_dialog(cheat_manager_dialog const&) = delete;
|
||||
void operator=(cheat_manager_dialog const&) = delete;
|
||||
|
||||
protected:
|
||||
void update_cheat_list();
|
||||
void do_the_search();
|
||||
|
||||
template <typename T>
|
||||
T convert_from_QString(const QString& str, bool& success);
|
||||
|
||||
template <typename T>
|
||||
bool convert_and_search();
|
||||
template <typename T>
|
||||
std::pair<bool, bool> convert_and_set(u32 offset);
|
||||
|
||||
protected:
|
||||
QTableWidget* tbl_cheats = nullptr;
|
||||
QListWidget* lst_search = nullptr;
|
||||
|
||||
QLineEdit* edt_value_final = nullptr;
|
||||
QPushButton* btn_apply = nullptr;
|
||||
|
||||
QLineEdit* edt_cheat_search_value = nullptr;
|
||||
QComboBox* cbx_cheat_search_type = nullptr;
|
||||
|
||||
QPushButton* btn_filter_results = nullptr;
|
||||
|
||||
u32 current_offset{};
|
||||
std::vector<u32> offsets_found;
|
||||
|
||||
cheat_engine g_cheat;
|
||||
|
||||
private:
|
||||
static cheat_manager_dialog* inst;
|
||||
|
||||
QString get_localized_cheat_type(cheat_type type);
|
||||
};
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#include "config_adapter.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
// Helper methods to interact with YAML and the config settings.
|
||||
namespace cfg_adapter
|
||||
{
|
||||
static cfg::_base& get_cfg(const cfg::_base& root, const std::string& name)
|
||||
{
|
||||
if (root.get_type() == cfg::type::node)
|
||||
{
|
||||
for (const auto& node : static_cast<const cfg::node&>(root).get_nodes())
|
||||
{
|
||||
if (node->get_name() == name)
|
||||
{
|
||||
return *node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Node not found: %s", name);
|
||||
}
|
||||
|
||||
static cfg::_base& get_cfg(cfg::_base& root, const cfg_location::const_iterator begin, const cfg_location::const_iterator end)
|
||||
{
|
||||
return begin == end ? root : get_cfg(get_cfg(root, *begin), begin + 1, end);
|
||||
}
|
||||
|
||||
YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end)
|
||||
{
|
||||
if (begin == end)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!node || !node.IsMap())
|
||||
{
|
||||
cfg_log.fatal("Node error. A cfg_location does not match its cfg::node (location: %s)", get_yaml_node_location(node));
|
||||
return YAML::Node();
|
||||
}
|
||||
|
||||
return get_node(node[*begin], begin + 1, end); // TODO
|
||||
}
|
||||
|
||||
YAML::Node get_node(const YAML::Node& node, const cfg_location& location)
|
||||
{
|
||||
return get_node(node, location.cbegin(), location.cend());
|
||||
}
|
||||
|
||||
std::vector<std::string> get_options(const cfg_location& location)
|
||||
{
|
||||
return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).to_list();
|
||||
}
|
||||
|
||||
static bool get_is_dynamic(const cfg_location& location)
|
||||
{
|
||||
return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).get_is_dynamic();
|
||||
}
|
||||
|
||||
bool get_is_dynamic(emu_settings_type type)
|
||||
{
|
||||
return get_is_dynamic(::at32(settings_location, type));
|
||||
}
|
||||
|
||||
std::string get_setting_name(emu_settings_type type)
|
||||
{
|
||||
const cfg_location& loc = ::at32(settings_location, type);
|
||||
return ::at32(loc, loc.size() - 1);
|
||||
}
|
||||
} // namespace cfg_adapter
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "emu_settings_type.h"
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
// Helper methods to interact with YAML and the config settings.
|
||||
namespace cfg_adapter
|
||||
{
|
||||
YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end);
|
||||
|
||||
/** Syntactic sugar to get a setting at a given config location. */
|
||||
YAML::Node get_node(const YAML::Node& node, const cfg_location& location);
|
||||
|
||||
/** Returns possible options for values for some particular setting.*/
|
||||
std::vector<std::string> get_options(const cfg_location& location);
|
||||
|
||||
/** Returns dynamic property for some particular setting.*/
|
||||
bool get_is_dynamic(emu_settings_type type);
|
||||
|
||||
/** Returns the string for a given setting.*/
|
||||
std::string get_setting_name(emu_settings_type type);
|
||||
} // namespace cfg_adapter
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "config_checker.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
config_checker::config_checker(QWidget* parent, const QString& content, bool is_log) : QDialog(parent)
|
||||
{
|
||||
setObjectName("config_checker");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
QLabel* label = new QLabel(this);
|
||||
layout->addWidget(label);
|
||||
|
||||
QString result;
|
||||
|
||||
if (check_config(content, result, is_log))
|
||||
{
|
||||
setWindowTitle(tr("Interesting!"));
|
||||
|
||||
if (result.isEmpty())
|
||||
{
|
||||
label->setText(tr("Found config.\nIt seems to match the default config."));
|
||||
}
|
||||
else
|
||||
{
|
||||
label->setText(tr("Found config.\nSome settings seem to deviate from the default config:"));
|
||||
|
||||
QTextEdit* text_box = new QTextEdit();
|
||||
text_box->setReadOnly(true);
|
||||
text_box->setHtml(result);
|
||||
layout->addWidget(text_box);
|
||||
|
||||
resize(400, 600);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setWindowTitle(tr("Ooops!"));
|
||||
label->setText(result);
|
||||
}
|
||||
|
||||
QDialogButtonBox* box = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(box);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
bool config_checker::check_config(QString content, QString& result, bool is_log)
|
||||
{
|
||||
cfg_root config{};
|
||||
|
||||
if (is_log)
|
||||
{
|
||||
const QString start_token = "SYS: Used configuration:\n";
|
||||
const QString end_token = "\n·";
|
||||
|
||||
qsizetype start = content.indexOf(start_token);
|
||||
qsizetype end = -1;
|
||||
|
||||
if (start >= 0)
|
||||
{
|
||||
start += start_token.size();
|
||||
end = content.indexOf(end_token, start);
|
||||
}
|
||||
|
||||
if (end < 0)
|
||||
{
|
||||
result = tr("Cannot find any config!");
|
||||
return false;
|
||||
}
|
||||
|
||||
content = content.mid(start, end - start);
|
||||
}
|
||||
|
||||
if (!config.from_string(content.toStdString()))
|
||||
{
|
||||
gui_log.error("log_viewer: Failed to parse config:\n%s", content);
|
||||
result = tr("Cannot find any config!");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::function<void(const cfg::_base*, std::string&, int)> print_diff_recursive;
|
||||
print_diff_recursive = [&print_diff_recursive](const cfg::_base* base, std::string& diff, int indentation) -> void
|
||||
{
|
||||
if (!base)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto indent = [](std::string& str, int indentation)
|
||||
{
|
||||
for (int i = 0; i < indentation * 2; i++)
|
||||
{
|
||||
str += " ";
|
||||
}
|
||||
};
|
||||
|
||||
switch (base->get_type())
|
||||
{
|
||||
case cfg::type::node:
|
||||
{
|
||||
if (const auto& node = static_cast<const cfg::node*>(base))
|
||||
{
|
||||
std::string diff_tmp;
|
||||
|
||||
for (const auto& n : node->get_nodes())
|
||||
{
|
||||
print_diff_recursive(n, diff_tmp, indentation + 1);
|
||||
}
|
||||
|
||||
if (!diff_tmp.empty())
|
||||
{
|
||||
indent(diff, indentation);
|
||||
|
||||
if (!base->get_name().empty())
|
||||
{
|
||||
fmt::append(diff, "<b>%s:</b><br>", base->get_name());
|
||||
}
|
||||
|
||||
fmt::append(diff, "%s", diff_tmp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cfg::type::_bool:
|
||||
case cfg::type::_enum:
|
||||
case cfg::type::_int:
|
||||
case cfg::type::uint:
|
||||
case cfg::type::string:
|
||||
{
|
||||
const std::string val = base->to_string();
|
||||
const std::string def = base->def_to_string();
|
||||
|
||||
if (val != def)
|
||||
{
|
||||
indent(diff, indentation);
|
||||
|
||||
if (def.empty())
|
||||
{
|
||||
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span><br>", base->get_name(), val);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span> <span style=\"color:gray;\">default:</span> <span style=\"color:green;\">%s</span><br>", base->get_name(), val, def);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cfg::type::set:
|
||||
{
|
||||
if (const auto& node = static_cast<const cfg::set_entry*>(base))
|
||||
{
|
||||
const std::vector<std::string> set_entries = node->to_list();
|
||||
|
||||
if (!set_entries.empty())
|
||||
{
|
||||
indent(diff, indentation);
|
||||
fmt::append(diff, "<b>%s:</b><br>", base->get_name());
|
||||
|
||||
for (const std::string& entry : set_entries)
|
||||
{
|
||||
indent(diff, indentation + 1);
|
||||
fmt::append(diff, "- <span style=\"color:red;\">%s</span><br>", entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cfg::type::log:
|
||||
{
|
||||
if (const auto& node = static_cast<const cfg::log_entry*>(base))
|
||||
{
|
||||
const auto& log_entries = node->get_map();
|
||||
|
||||
if (!log_entries.empty())
|
||||
{
|
||||
indent(diff, indentation);
|
||||
fmt::append(diff, "<b>%s:</b><br>", base->get_name());
|
||||
|
||||
for (const auto& entry : log_entries)
|
||||
{
|
||||
indent(diff, indentation + 1);
|
||||
fmt::append(diff, "<span style=\"color:red;\">%s: %s</span><br>", entry.first, entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cfg::type::map:
|
||||
case cfg::type::node_map:
|
||||
case cfg::type::device:
|
||||
{
|
||||
// Ignored
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string diff;
|
||||
print_diff_recursive(&config, diff, 0);
|
||||
result = QString::fromStdString(diff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class config_checker : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
config_checker(QWidget* parent, const QString& path, bool is_log);
|
||||
|
||||
bool check_config(QString content, QString& result, bool is_log);
|
||||
};
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "curl_handle.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "util/StrUtil.h"
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(network_log, "NET");
|
||||
|
||||
namespace rpcs3::curl
|
||||
{
|
||||
|
||||
curl_handle::curl_handle()
|
||||
{
|
||||
reset_error_buffer();
|
||||
|
||||
m_curl = curl_easy_init();
|
||||
|
||||
CURLcode err = curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_error_buffer.data());
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_ERRORBUFFER): %s", curl_easy_strerror(err));
|
||||
|
||||
m_uses_error_buffer = err == CURLE_OK;
|
||||
|
||||
err = curl_easy_setopt(m_curl, CURLOPT_VERBOSE, g_curl_verbose);
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_VERBOSE, %d): %s", g_curl_verbose, curl_easy_strerror(err));
|
||||
|
||||
#ifdef _WIN32
|
||||
// Tell curl to use the native CA store for certificate verification
|
||||
err = curl_easy_setopt(m_curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_SSL_OPTIONS): %s", curl_easy_strerror(err));
|
||||
#endif
|
||||
}
|
||||
|
||||
curl_handle::~curl_handle()
|
||||
{
|
||||
curl_easy_cleanup(m_curl);
|
||||
}
|
||||
|
||||
CURL* curl_handle::get_curl() const
|
||||
{
|
||||
return m_curl;
|
||||
}
|
||||
|
||||
void curl_handle::reset_error_buffer()
|
||||
{
|
||||
ensure(m_error_buffer.size() == CURL_ERROR_SIZE);
|
||||
m_error_buffer[0] = 0;
|
||||
}
|
||||
|
||||
std::string curl_handle::get_verbose_error(CURLcode code) const
|
||||
{
|
||||
if (m_uses_error_buffer)
|
||||
{
|
||||
ensure(m_error_buffer.size() == CURL_ERROR_SIZE);
|
||||
if (m_error_buffer[0])
|
||||
{
|
||||
return fmt::format("Curl error (%d): %s\nDetails: %s", static_cast<int>(code), curl_easy_strerror(code), m_error_buffer.data());
|
||||
}
|
||||
}
|
||||
|
||||
return fmt::format("Curl error (%d): %s", static_cast<int>(code), curl_easy_strerror(code));
|
||||
}
|
||||
|
||||
} // namespace rpcs3::curl
|
||||
|
||||
#ifdef _WIN32
|
||||
// Functions exported from our user_settings.h in WolfSSL, implemented in RPCS3
|
||||
extern "C"
|
||||
{
|
||||
|
||||
FILE* wolfSSL_fopen_utf8(const char* name, const char* mode)
|
||||
{
|
||||
return _wfopen(utf8_to_wchar(name).c_str(), utf8_to_wchar(mode).c_str());
|
||||
}
|
||||
|
||||
int wolfSSL_stat_utf8(const char* path, struct _stat* buffer)
|
||||
{
|
||||
return _wstat(utf8_to_wchar(path).c_str(), buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#ifndef CURL_STATICLIB
|
||||
#define CURL_STATICLIB
|
||||
#endif
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace rpcs3::curl
|
||||
{
|
||||
inline bool g_curl_verbose = false;
|
||||
|
||||
class curl_handle
|
||||
{
|
||||
public:
|
||||
explicit curl_handle();
|
||||
~curl_handle();
|
||||
|
||||
CURL* get_curl() const;
|
||||
|
||||
operator CURL*() const
|
||||
{
|
||||
return get_curl();
|
||||
}
|
||||
|
||||
void reset_error_buffer();
|
||||
std::string get_verbose_error(CURLcode code) const;
|
||||
|
||||
private:
|
||||
CURL* m_curl = nullptr;
|
||||
bool m_uses_error_buffer = false;
|
||||
std::array<char, CURL_ERROR_SIZE> m_error_buffer;
|
||||
};
|
||||
|
||||
} // namespace rpcs3::curl
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#include "custom_dialog.h"
|
||||
|
||||
custom_dialog::custom_dialog(bool disableCancel, QWidget* parent)
|
||||
: QDialog(parent), m_disable_cancel(disableCancel)
|
||||
{
|
||||
if (m_disable_cancel)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint);
|
||||
}
|
||||
}
|
||||
|
||||
void custom_dialog::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
// this won't work with Alt+F4, the window still closes
|
||||
if (m_disable_cancel && event->key() == Qt::Key_Escape)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
QDialog::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void custom_dialog::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
// spontaneous: don't close on external system level events like Alt+F4
|
||||
if (m_disable_cancel && event->spontaneous())
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
QDialog::closeEvent(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QKeyEvent>
|
||||
|
||||
class custom_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit custom_dialog(bool disableCancel, QWidget* parent = nullptr);
|
||||
bool m_disable_cancel;
|
||||
|
||||
private:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
};
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QStyleOption>
|
||||
#include <QPainter>
|
||||
|
||||
class custom_dock_widget : public QDockWidget
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<QWidget> m_title_bar_widget;
|
||||
bool m_is_title_bar_visible = true;
|
||||
|
||||
public:
|
||||
explicit custom_dock_widget(const QString& title, QWidget* parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags())
|
||||
: QDockWidget(title, parent, flags)
|
||||
{
|
||||
m_title_bar_widget.reset(titleBarWidget());
|
||||
|
||||
connect(this, &QDockWidget::topLevelChanged, [this](bool /* topLevel*/)
|
||||
{
|
||||
SetTitleBarVisible(m_is_title_bar_visible);
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
});
|
||||
}
|
||||
|
||||
void SetTitleBarVisible(bool visible)
|
||||
{
|
||||
if (visible || isFloating())
|
||||
{
|
||||
if (m_title_bar_widget.get() != titleBarWidget())
|
||||
{
|
||||
setTitleBarWidget(m_title_bar_widget.get());
|
||||
QMargins margins = widget()->contentsMargins();
|
||||
margins.setTop(0);
|
||||
widget()->setContentsMargins(margins);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setTitleBarWidget(new QWidget());
|
||||
QMargins margins = widget()->contentsMargins();
|
||||
margins.setTop(1);
|
||||
widget()->setContentsMargins(margins);
|
||||
}
|
||||
|
||||
m_is_title_bar_visible = visible;
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override
|
||||
{
|
||||
// We need to repaint the dock widgets as plain widgets in floating mode.
|
||||
// Source: https://stackoverflow.com/questions/10272091/cannot-add-a-background-image-to-a-qdockwidget
|
||||
if (isFloating())
|
||||
{
|
||||
QStyleOption opt;
|
||||
opt.initFrom(this);
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use inherited method for docked mode because otherwise the dock would lose the title etc.
|
||||
QDockWidget::paintEvent(event);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#include "custom_table_widget_item.h"
|
||||
#include "util/StrFmt.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value)
|
||||
: movie_item(QString::fromStdString(text).simplified()) // simplified() forces single line text
|
||||
{
|
||||
if (sort_role != Qt::DisplayRole)
|
||||
{
|
||||
setData(sort_role, sort_value, true);
|
||||
}
|
||||
}
|
||||
|
||||
custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value)
|
||||
: movie_item(text.simplified()) // simplified() forces single line text
|
||||
{
|
||||
if (sort_role != Qt::DisplayRole)
|
||||
{
|
||||
setData(sort_role, sort_value, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool custom_table_widget_item::operator<(const QTableWidgetItem& other) const
|
||||
{
|
||||
if (m_sort_role == Qt::DisplayRole)
|
||||
{
|
||||
return QTableWidgetItem::operator<(other);
|
||||
}
|
||||
|
||||
const QVariant data_l = data(m_sort_role);
|
||||
const QVariant data_r = other.data(m_sort_role);
|
||||
const int type_l = data_l.metaType().id();
|
||||
const int type_r = data_r.metaType().id();
|
||||
|
||||
ensure(type_l == type_r);
|
||||
|
||||
switch (type_l)
|
||||
{
|
||||
case QMetaType::Type::Bool:
|
||||
case QMetaType::Type::Int:
|
||||
return data_l.toInt() < data_r.toInt();
|
||||
case QMetaType::Type::UInt:
|
||||
return data_l.toUInt() < data_r.toUInt();
|
||||
case QMetaType::Type::LongLong:
|
||||
return data_l.toLongLong() < data_r.toLongLong();
|
||||
case QMetaType::Type::ULongLong:
|
||||
return data_l.toULongLong() < data_r.toULongLong();
|
||||
case QMetaType::Type::Double:
|
||||
return data_l.toDouble() < data_r.toDouble();
|
||||
case QMetaType::Type::QDate:
|
||||
return data_l.toDate() < data_r.toDate();
|
||||
case QMetaType::Type::QTime:
|
||||
return data_l.toTime() < data_r.toTime();
|
||||
case QMetaType::Type::QDateTime:
|
||||
return data_l.toDateTime() < data_r.toDateTime();
|
||||
case QMetaType::Type::Char:
|
||||
case QMetaType::Type::QString:
|
||||
return data_l.toString() < data_r.toString();
|
||||
default:
|
||||
fmt::throw_exception("Unimplemented type %s", QMetaType(type_l).name());
|
||||
}
|
||||
}
|
||||
|
||||
void custom_table_widget_item::setData(int role, const QVariant& value, bool assign_sort_role)
|
||||
{
|
||||
if (assign_sort_role)
|
||||
{
|
||||
m_sort_role = role;
|
||||
}
|
||||
QTableWidgetItem::setData(role, value);
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "movie_item.h"
|
||||
|
||||
class custom_table_widget_item : public movie_item
|
||||
{
|
||||
private:
|
||||
int m_sort_role = Qt::DisplayRole;
|
||||
|
||||
public:
|
||||
using QTableWidgetItem::setData;
|
||||
|
||||
custom_table_widget_item() = default;
|
||||
custom_table_widget_item(const std::string& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0);
|
||||
custom_table_widget_item(const QString& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0);
|
||||
|
||||
bool operator<(const QTableWidgetItem& other) const override;
|
||||
|
||||
void setData(int role, const QVariant& value, bool assign_sort_role);
|
||||
};
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#include "debugger_add_bp_window.h"
|
||||
#include "util/StrFmt.h"
|
||||
#include "util/StrUtil.h"
|
||||
#include "breakpoint_handler.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
|
||||
debugger_add_bp_window::debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
ensure(bp_list);
|
||||
|
||||
setWindowTitle(tr("Add a breakpoint"));
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
QHBoxLayout* hbox_top = new QHBoxLayout();
|
||||
QLabel* l_address = new QLabel(tr("Address"));
|
||||
QLineEdit* t_address = new QLineEdit();
|
||||
t_address->setPlaceholderText(tr("Address here"));
|
||||
t_address->setFocus();
|
||||
|
||||
hbox_top->addWidget(l_address);
|
||||
hbox_top->addWidget(t_address);
|
||||
vbox_panel->addLayout(hbox_top);
|
||||
|
||||
QHBoxLayout* hbox_bot = new QHBoxLayout();
|
||||
QComboBox* co_bptype = new QComboBox(this);
|
||||
QStringList qstr_breakpoint_types;
|
||||
|
||||
qstr_breakpoint_types
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
<< tr("Memory Read")
|
||||
<< tr("Memory Write")
|
||||
<< tr("Memory Read&Write")
|
||||
#endif
|
||||
<< tr("Execution");
|
||||
|
||||
co_bptype->addItems(qstr_breakpoint_types);
|
||||
|
||||
hbox_bot->addWidget(co_bptype);
|
||||
vbox_panel->addLayout(hbox_bot);
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
button_box->button(QDialogButtonBox::Ok)->setText(tr("Add"));
|
||||
|
||||
hbox_buttons->addWidget(button_box);
|
||||
vbox_panel->addLayout(hbox_buttons);
|
||||
|
||||
setLayout(vbox_panel);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, this, [=, this]
|
||||
{
|
||||
const std::string str_address = t_address->text().toStdString();
|
||||
|
||||
if (str_address.empty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add BP error"), tr("Address is empty!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// We always want hex
|
||||
const std::string parsed_string = (!str_address.starts_with("0x") && !str_address.starts_with("0X")) ? fmt::format("0x%s", str_address) : str_address;
|
||||
u64 parsed_address = 0;
|
||||
|
||||
// We don't accept 0
|
||||
if (!try_to_uint64(&parsed_address, parsed_string, 1, 0xFF'FF'FF'FF))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add BP error"), tr("Address is invalid!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 address = static_cast<u32>(parsed_address);
|
||||
bs_t<breakpoint_types> bp_t{};
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
switch (co_bptype->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
bp_t = breakpoint_types::bp_read;
|
||||
break;
|
||||
case 1:
|
||||
bp_t = breakpoint_types::bp_write;
|
||||
break;
|
||||
case 2:
|
||||
bp_t = breakpoint_types::bp_read + breakpoint_types::bp_write;
|
||||
break;
|
||||
case 3:
|
||||
bp_t = breakpoint_types::bp_exec;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#else
|
||||
bp_t = breakpoint_types::bp_exec;
|
||||
#endif
|
||||
|
||||
if (bp_t)
|
||||
bp_list->AddBreakpoint(address, bp_t);
|
||||
|
||||
QDialog::accept();
|
||||
});
|
||||
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
move(QCursor::pos());
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "breakpoint_list.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class debugger_add_bp_window : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent = nullptr);
|
||||
};
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
#include "custom_dock_widget.h"
|
||||
|
||||
#include <QSplitter>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <any>
|
||||
#include <functional>
|
||||
|
||||
class CPUDisAsm;
|
||||
class cpu_thread;
|
||||
class gui_settings;
|
||||
class debugger_list;
|
||||
class breakpoint_list;
|
||||
class breakpoint_handler;
|
||||
class call_stack_list;
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
class thread;
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class shm;
|
||||
}
|
||||
|
||||
enum class system_state : u32;
|
||||
|
||||
class instruction_editor_dialog;
|
||||
class register_editor_dialog;
|
||||
|
||||
class debugger_frame : public custom_dock_widget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QString NoThreadString = tr("No Thread");
|
||||
const QString RunString = tr("Run");
|
||||
const QString PauseString = tr("Pause");
|
||||
|
||||
debugger_list* m_debugger_list = nullptr;
|
||||
QSplitter* m_right_splitter = nullptr;
|
||||
QFont m_mono;
|
||||
QPlainTextEdit* m_misc_state = nullptr;
|
||||
QPlainTextEdit* m_regs = nullptr;
|
||||
QPushButton* m_go_to_addr = nullptr;
|
||||
QPushButton* m_go_to_pc = nullptr;
|
||||
QPushButton* m_btn_step = nullptr;
|
||||
QPushButton* m_btn_step_over = nullptr;
|
||||
QPushButton* m_btn_add_bp = nullptr;
|
||||
QPushButton* m_btn_run = nullptr;
|
||||
|
||||
QComboBox* m_choice_units = nullptr;
|
||||
QTimer* m_update = nullptr;
|
||||
QSplitter* m_splitter = nullptr;
|
||||
|
||||
u64 m_threads_created = -1;
|
||||
u64 m_threads_deleted = -1;
|
||||
system_state m_emu_state{};
|
||||
u64 m_emulation_id{};
|
||||
u32 m_last_pc = -1;
|
||||
std::vector<char> m_last_query_state;
|
||||
std::string m_last_reg_state;
|
||||
std::any m_dump_reg_func_data;
|
||||
std::vector<std::function<cpu_thread*()>> m_threads_info;
|
||||
u32 m_last_step_over_breakpoint = -1;
|
||||
u64 m_ui_update_ctr = 0;
|
||||
u64 m_ui_fast_update_permission_deadline = 0;
|
||||
bool m_thread_list_pending_update = false;
|
||||
|
||||
std::shared_ptr<CPUDisAsm> m_disasm; // Only shared to allow base/derived functionality
|
||||
shared_ptr<cpu_thread> m_cpu;
|
||||
rsx::thread* m_rsx = nullptr;
|
||||
std::shared_ptr<utils::shm> m_spu_disasm_memory;
|
||||
u32 m_spu_disasm_origin_eal = 0;
|
||||
u32 m_spu_disasm_pc = 0;
|
||||
bool m_is_spu_disasm_mode = false;
|
||||
|
||||
breakpoint_list* m_breakpoint_list = nullptr;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
call_stack_list* m_call_stack_list = nullptr;
|
||||
instruction_editor_dialog* m_inst_editor = nullptr;
|
||||
register_editor_dialog* m_reg_editor = nullptr;
|
||||
QDialog* m_goto_dialog = nullptr;
|
||||
QDialog* m_spu_disasm_dialog = nullptr;
|
||||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
cpu_thread* get_cpu();
|
||||
std::function<cpu_thread*()> make_check_cpu(cpu_thread* cpu, bool unlocked = false);
|
||||
void open_breakpoints_settings();
|
||||
|
||||
public:
|
||||
explicit debugger_frame(std::shared_ptr<gui_settings> settings, QWidget* parent = nullptr);
|
||||
|
||||
void SaveSettings() const;
|
||||
void ChangeColors() const;
|
||||
|
||||
void UpdateUI();
|
||||
void UpdateUnitList();
|
||||
|
||||
void DoUpdate();
|
||||
void WritePanels();
|
||||
void EnableButtons(bool enable);
|
||||
void ShowGotoAddressDialog();
|
||||
void PerformGoToRequest(const QString& text_argument);
|
||||
void PerformGoToThreadRequest(const QString& text_argument);
|
||||
void PerformAddBreakpointRequest(u32 addr);
|
||||
u64 EvaluateExpression(const QString& expression);
|
||||
void ClearBreakpoints() const; // Fallthrough method into breakpoint_list.
|
||||
void ClearCallStack();
|
||||
|
||||
/** Needed so key press events work when other objects are selected in debugger_frame. */
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
protected:
|
||||
/** Override inherited method from Qt to allow signalling when close happened.*/
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void DebugFrameClosed();
|
||||
void CallStackUpdateRequested(const std::vector<std::pair<u32, u32>>& call_stack);
|
||||
|
||||
public Q_SLOTS:
|
||||
void DoStep(bool step_over = false);
|
||||
|
||||
private Q_SLOTS:
|
||||
void OnSelectUnit();
|
||||
void OnSelectSPUDisassembler();
|
||||
void ShowPC(bool user_requested = false);
|
||||
void EnableUpdateTimer(bool enable) const;
|
||||
void RunBtnPress();
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(u32)
|
||||
|
|
@ -1,497 +0,0 @@
|
|||
#include "debugger_list.h"
|
||||
#include "gui_settings.h"
|
||||
#include "qt_utils.h"
|
||||
#include "breakpoint_handler.h"
|
||||
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/RSX/RSXDisAsm.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
debugger_list::debugger_list(QWidget* parent, std::shared_ptr<gui_settings> gui_settings, breakpoint_handler* handler)
|
||||
: QListWidget(parent), m_gui_settings(std::move(gui_settings)), m_ppu_breakpoint_handler(handler)
|
||||
{
|
||||
setWindowTitle(tr("ASM"));
|
||||
|
||||
for (uint i = 0; i < m_item_count; ++i)
|
||||
{
|
||||
insertItem(i, new QListWidgetItem(""));
|
||||
}
|
||||
|
||||
setSizeAdjustPolicy(QListWidget::AdjustToContents);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
connect(this, &QListWidget::currentRowChanged, this, [this](int row)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
m_selected_instruction = -1;
|
||||
m_showing_selected_instruction = false;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 pc = m_start_addr;
|
||||
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
for (; cpu && cpu->get_class() == thread_class::rsx && row; row--)
|
||||
{
|
||||
// If scrolling forwards (downwards), we can skip entire commands
|
||||
pc += std::max<u32>(m_disasm->disasm(pc), 4);
|
||||
}
|
||||
|
||||
m_selected_instruction = pc + row * 4;
|
||||
});
|
||||
}
|
||||
|
||||
void debugger_list::UpdateCPUData(std::shared_ptr<CPUDisAsm> disasm)
|
||||
{
|
||||
if ((!m_disasm) != (!disasm) || (m_disasm && disasm->get_cpu() != m_disasm->get_cpu()))
|
||||
{
|
||||
m_selected_instruction = -1;
|
||||
m_showing_selected_instruction = false;
|
||||
}
|
||||
|
||||
m_disasm = std::move(disasm);
|
||||
}
|
||||
|
||||
u32 debugger_list::GetStartAddress(u32 address)
|
||||
{
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
const u32 steps = m_item_count / 3;
|
||||
const u32 inst_count_jump_on_step = std::min<u32>(steps, 4);
|
||||
|
||||
const bool is_spu = IsSpu();
|
||||
const u32 address_mask = (is_spu ? 0x3fffc : ~3);
|
||||
|
||||
u32 result = address & address_mask;
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::rsx)
|
||||
{
|
||||
if (auto [count, res] = static_cast<rsx::thread*>(cpu)->try_get_pc_of_x_cmds_backwards(steps, address); count == steps)
|
||||
{
|
||||
result = res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (address - (steps * 4)) & address_mask;
|
||||
}
|
||||
|
||||
u32 upper_bound = (m_start_addr + (steps * 4)) & address_mask;
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::rsx)
|
||||
{
|
||||
if (auto [count, res] = static_cast<rsx::thread*>(cpu)->try_get_pc_of_x_cmds_backwards(0 - steps, m_start_addr); count == steps)
|
||||
{
|
||||
upper_bound = res;
|
||||
}
|
||||
}
|
||||
|
||||
bool goto_addr = false;
|
||||
|
||||
if (upper_bound > m_start_addr)
|
||||
{
|
||||
goto_addr = address < m_start_addr || address >= upper_bound;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overflowing bounds case
|
||||
goto_addr = address < m_start_addr && address >= upper_bound;
|
||||
}
|
||||
|
||||
if (goto_addr)
|
||||
{
|
||||
m_pc = address;
|
||||
|
||||
if (address > upper_bound && address - upper_bound < inst_count_jump_on_step * 4)
|
||||
{
|
||||
m_start_addr = result + inst_count_jump_on_step * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_start_addr = result;
|
||||
}
|
||||
}
|
||||
|
||||
return m_start_addr;
|
||||
}
|
||||
|
||||
bool debugger_list::IsSpu() const
|
||||
{
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
return (cpu && cpu->get_class() == thread_class::spu) || (m_disasm && !cpu);
|
||||
}
|
||||
|
||||
void debugger_list::ShowAddress(u32 addr, bool select_addr, bool direct)
|
||||
{
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
const decltype(spu_thread::local_breakpoints)* spu_bps_list{};
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::spu)
|
||||
{
|
||||
spu_bps_list = &static_cast<spu_thread*>(cpu)->local_breakpoints;
|
||||
}
|
||||
|
||||
auto IsBreakpoint = [&](u32 pc)
|
||||
{
|
||||
switch (cpu ? cpu->get_class() : thread_class::general)
|
||||
{
|
||||
case thread_class::ppu:
|
||||
{
|
||||
return m_ppu_breakpoint_handler->HasBreakpoint(pc, breakpoint_types::bp_exec);
|
||||
}
|
||||
case thread_class::spu:
|
||||
{
|
||||
const u32 pos_at = pc / 4;
|
||||
const u32 pos_bit = 1u << (pos_at % 8);
|
||||
|
||||
return !!((*spu_bps_list)[pos_at / 8] & pos_bit);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
|
||||
if (select_addr || direct)
|
||||
{
|
||||
// The user wants to survey a specific memory location, do not interfere from this point forth
|
||||
m_follow_thread = false;
|
||||
}
|
||||
|
||||
m_dirty_flag = false;
|
||||
|
||||
u32 pc = m_start_addr;
|
||||
|
||||
if (!direct && (m_follow_thread || select_addr))
|
||||
{
|
||||
pc = GetStartAddress(addr);
|
||||
}
|
||||
|
||||
const auto& default_foreground = palette().color(foregroundRole());
|
||||
const auto& default_background = palette().color(backgroundRole());
|
||||
|
||||
m_showing_selected_instruction = false;
|
||||
|
||||
if (select_addr)
|
||||
{
|
||||
m_selected_instruction = addr;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < m_item_count; ++i)
|
||||
{
|
||||
if (auto list_item = item(i); list_item->isSelected())
|
||||
{
|
||||
list_item->setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_disasm || (cpu && cpu->state.all_of(cpu_flag::exit + cpu_flag::wait)))
|
||||
{
|
||||
for (uint i = 0; i < m_item_count; ++i)
|
||||
{
|
||||
QListWidgetItem* list_item = item(i);
|
||||
list_item->setText(qstr(fmt::format(" [%08x] ?? ?? ?? ??:", 0)));
|
||||
list_item->setForeground(default_foreground);
|
||||
list_item->setBackground(default_background);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool is_spu = IsSpu();
|
||||
const u32 address_limits = (is_spu ? 0x3fffc : ~3);
|
||||
const u32 current_pc = (cpu ? cpu->get_pc() : 0);
|
||||
m_start_addr &= address_limits;
|
||||
pc = m_start_addr;
|
||||
|
||||
for (uint i = 0, count = 4; i < m_item_count; ++i, pc = (pc + count) & address_limits)
|
||||
{
|
||||
QListWidgetItem* list_item = item(i);
|
||||
|
||||
if (pc == current_pc)
|
||||
{
|
||||
list_item->setForeground(m_text_color_pc);
|
||||
list_item->setBackground(m_color_pc);
|
||||
}
|
||||
else if (pc == m_selected_instruction)
|
||||
{
|
||||
// setSelected may invoke a resize event which causes stack overflow, terminate recursion
|
||||
if (!list_item->isSelected())
|
||||
{
|
||||
list_item->setSelected(true);
|
||||
}
|
||||
|
||||
m_showing_selected_instruction = true;
|
||||
}
|
||||
else if (IsBreakpoint(pc))
|
||||
{
|
||||
list_item->setForeground(m_text_color_bp);
|
||||
list_item->setBackground(m_color_bp);
|
||||
}
|
||||
else
|
||||
{
|
||||
list_item->setForeground(default_foreground);
|
||||
list_item->setBackground(default_background);
|
||||
}
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, 0))
|
||||
{
|
||||
list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc)));
|
||||
count = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, vm::page_executable))
|
||||
{
|
||||
const u32 data = *vm::get_super_ptr<atomic_be_t<u32>>(pc);
|
||||
list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc,
|
||||
static_cast<u8>(data >> 24),
|
||||
static_cast<u8>(data >> 16),
|
||||
static_cast<u8>(data >> 8),
|
||||
static_cast<u8>(data >> 0))));
|
||||
count = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
count = m_disasm->disasm(pc);
|
||||
|
||||
if (!count)
|
||||
{
|
||||
list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ??? ?? ??", pc)));
|
||||
count = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(m_disasm->last_opcode));
|
||||
}
|
||||
}
|
||||
|
||||
setLineWidth(-1);
|
||||
}
|
||||
|
||||
void debugger_list::RefreshView()
|
||||
{
|
||||
const bool old = std::exchange(m_follow_thread, false);
|
||||
ShowAddress(0, false);
|
||||
m_follow_thread = old;
|
||||
}
|
||||
|
||||
void debugger_list::EnableThreadFollowing(bool enable)
|
||||
{
|
||||
m_follow_thread = enable;
|
||||
}
|
||||
|
||||
void debugger_list::scroll(s32 steps)
|
||||
{
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
for (; cpu && cpu->get_class() == thread_class::rsx && steps > 0; steps--)
|
||||
{
|
||||
// If scrolling forwards (downwards), we can skip entire commands
|
||||
m_start_addr += std::max<u32>(m_disasm->disasm(m_start_addr), 4);
|
||||
}
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::rsx && steps < 0)
|
||||
{
|
||||
// If scrolling backwards (upwards), try to obtain the start of commands tail
|
||||
if (auto [count, res] = static_cast<rsx::thread*>(cpu)->try_get_pc_of_x_cmds_backwards(-steps, m_start_addr); count == 0u - steps)
|
||||
{
|
||||
steps = 0;
|
||||
m_start_addr = res;
|
||||
}
|
||||
}
|
||||
|
||||
EnableThreadFollowing(false);
|
||||
m_start_addr += steps * 4;
|
||||
ShowAddress(0, false, true);
|
||||
}
|
||||
|
||||
void debugger_list::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
// Always accept event (so it would not bubble upwards, debugger_frame already sees it)
|
||||
struct accept_event_t
|
||||
{
|
||||
QKeyEvent* event;
|
||||
|
||||
~accept_event_t() noexcept
|
||||
{
|
||||
event->accept();
|
||||
}
|
||||
} accept_event{event};
|
||||
|
||||
if (!isActiveWindow())
|
||||
{
|
||||
QListWidget::keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->modifiers())
|
||||
{
|
||||
QListWidget::keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_PageUp: scroll(0 - m_item_count); return;
|
||||
case Qt::Key_PageDown: scroll(m_item_count); return;
|
||||
case Qt::Key_Up: scroll(-1); return;
|
||||
case Qt::Key_Down: scroll(1); return;
|
||||
case Qt::Key_I:
|
||||
{
|
||||
if (event->isAutoRepeat())
|
||||
{
|
||||
QListWidget::keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu && cpu->get_class() == thread_class::rsx)
|
||||
{
|
||||
create_rsx_command_detail(m_showing_selected_instruction ? m_selected_instruction : m_pc);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
QListWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void debugger_list::showEvent(QShowEvent* event)
|
||||
{
|
||||
if (m_cmd_detail)
|
||||
m_cmd_detail->show();
|
||||
QListWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void debugger_list::hideEvent(QHideEvent* event)
|
||||
{
|
||||
if (m_cmd_detail)
|
||||
m_cmd_detail->hide();
|
||||
QListWidget::hideEvent(event);
|
||||
}
|
||||
|
||||
void debugger_list::create_rsx_command_detail(u32 pc)
|
||||
{
|
||||
RSXDisAsm rsx_dis = static_cast<RSXDisAsm&>(*m_disasm);
|
||||
rsx_dis.change_mode(cpu_disasm_mode::list);
|
||||
|
||||
// Either invalid or not a method
|
||||
if (rsx_dis.disasm(pc) <= 4)
|
||||
return;
|
||||
|
||||
if (m_cmd_detail)
|
||||
{
|
||||
// Edit the existing dialog
|
||||
m_detail_label->setText(QString::fromStdString(rsx_dis.last_opcode));
|
||||
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
|
||||
return;
|
||||
}
|
||||
|
||||
m_cmd_detail = new QDialog(this);
|
||||
m_cmd_detail->setWindowTitle(tr("RSX Command Detail"));
|
||||
|
||||
m_detail_label = new QLabel(QString::fromStdString(rsx_dis.last_opcode), this);
|
||||
m_detail_label->setFont(font());
|
||||
gui::utils::set_font_size(*m_detail_label, 10);
|
||||
m_detail_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->addWidget(m_detail_label);
|
||||
m_cmd_detail->setLayout(layout);
|
||||
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
|
||||
m_cmd_detail->show();
|
||||
|
||||
connect(m_cmd_detail, &QDialog::finished, [this](int)
|
||||
{
|
||||
// Cleanup
|
||||
std::exchange(m_cmd_detail, nullptr)->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void debugger_list::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
int i = currentRow();
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
u32 pc = m_start_addr;
|
||||
|
||||
const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr;
|
||||
|
||||
for (; cpu && cpu->get_class() == thread_class::rsx && i; i--)
|
||||
{
|
||||
// If scrolling forwards (downwards), we can skip entire commands
|
||||
pc += std::max<u32>(m_disasm->disasm(pc), 4);
|
||||
}
|
||||
|
||||
pc += i * 4;
|
||||
m_selected_instruction = pc;
|
||||
|
||||
// Let debugger_frame know about breakpoint.
|
||||
// Other option is to add to breakpoint manager directly and have a signal there instead.
|
||||
// Either the flow goes from debugger_list->breakpoint_manager->debugger_frame, or it goes debugger_list->debugger_frame, and I felt this was easier to read for now.
|
||||
Q_EMIT BreakpointRequested(pc);
|
||||
}
|
||||
}
|
||||
|
||||
void debugger_list::wheelEvent(QWheelEvent* event)
|
||||
{
|
||||
const QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta
|
||||
const int value = numSteps.y();
|
||||
const auto direction = (event->modifiers() == Qt::ControlModifier);
|
||||
|
||||
scroll(direction ? value : -value);
|
||||
}
|
||||
|
||||
void debugger_list::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QListWidget::resizeEvent(event);
|
||||
|
||||
if (count() < 1 || visualItemRect(item(0)).height() < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 old_size = m_item_count;
|
||||
|
||||
// It is fine if the QWidgetList is a tad bit larger than the frame
|
||||
m_item_count = utils::aligned_div<u32>(rect().height() - frameWidth() * 2, visualItemRect(item(0)).height());
|
||||
|
||||
if (old_size <= m_item_count)
|
||||
{
|
||||
for (u32 i = old_size; i < m_item_count; ++i)
|
||||
{
|
||||
insertItem(i, new QListWidgetItem(""));
|
||||
m_dirty_flag = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = old_size - 1; i >= m_item_count; --i)
|
||||
{
|
||||
delete takeItem(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QListWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class breakpoint_handler;
|
||||
class CPUDisAsm;
|
||||
class cpu_thread;
|
||||
class gui_settings;
|
||||
class QLabel;
|
||||
|
||||
class debugger_list : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
u32 m_pc = 0;
|
||||
u32 m_start_addr = 0;
|
||||
u32 m_item_count = 30;
|
||||
u32 m_selected_instruction = -1;
|
||||
bool m_follow_thread = true; // If true, follow the selected thread to wherever it goes in code
|
||||
bool m_showing_selected_instruction = false;
|
||||
bool m_dirty_flag = false;
|
||||
QColor m_color_bp;
|
||||
QColor m_color_pc;
|
||||
QColor m_text_color_bp;
|
||||
QColor m_text_color_pc;
|
||||
|
||||
Q_SIGNALS:
|
||||
void BreakpointRequested(u32 loc, bool only_add = false);
|
||||
|
||||
public:
|
||||
debugger_list(QWidget* parent, std::shared_ptr<gui_settings> settings, breakpoint_handler* handler);
|
||||
void UpdateCPUData(std::shared_ptr<CPUDisAsm> disasm);
|
||||
void EnableThreadFollowing(bool enable = true);
|
||||
public Q_SLOTS:
|
||||
void ShowAddress(u32 addr, bool select_addr = true, bool direct = false);
|
||||
void RefreshView();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void scroll(s32 steps);
|
||||
void create_rsx_command_detail(u32 pc);
|
||||
|
||||
private:
|
||||
/**
|
||||
* It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent.
|
||||
*/
|
||||
u32 GetStartAddress(u32 address);
|
||||
bool IsSpu() const;
|
||||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
cpu_thread* m_cpu = nullptr;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm;
|
||||
QDialog* m_cmd_detail = nullptr;
|
||||
QLabel* m_detail_label = nullptr;
|
||||
};
|
||||
|
|
@ -1,779 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "util/File.h"
|
||||
#include "dimensions_dialog.h"
|
||||
#include "Emu/Io/Dimensions.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGroupBox>
|
||||
#include <QFileDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
#include <QCompleter>
|
||||
#include <QGridLayout>
|
||||
|
||||
dimensions_dialog* dimensions_dialog::inst = nullptr;
|
||||
std::array<std::optional<u32>, 7> figure_slots = {};
|
||||
static QString s_last_figure_path;
|
||||
|
||||
LOG_CHANNEL(dimensions_log, "dimensions");
|
||||
|
||||
const std::map<const u32, const std::string> list_minifigs = {
|
||||
{0, "Blank Tag"},
|
||||
{1, "Batman"},
|
||||
{2, "Gandalf"},
|
||||
{3, "Wyldstyle"},
|
||||
{4, "Aquaman"},
|
||||
{5, "Bad Cop"},
|
||||
{6, "Bane"},
|
||||
{7, "Bart Simpson"},
|
||||
{8, "Benny"},
|
||||
{9, "Chell"},
|
||||
{10, "Cole"},
|
||||
{11, "Cragger"},
|
||||
{12, "Cyborg"},
|
||||
{13, "Cyberman"},
|
||||
{14, "Doc Brown"},
|
||||
{15, "The Doctor"},
|
||||
{16, "Emmet"},
|
||||
{17, "Eris"},
|
||||
{18, "Gimli"},
|
||||
{19, "Gollum"},
|
||||
{20, "Harley Quinn"},
|
||||
{21, "Homer Simpson"},
|
||||
{22, "Jay"},
|
||||
{23, "Joker"},
|
||||
{24, "Kai"},
|
||||
{25, "ACU Trooper"},
|
||||
{26, "Gamer Kid"},
|
||||
{27, "Krusty the Clown"},
|
||||
{28, "Laval"},
|
||||
{29, "Legolas"},
|
||||
{30, "Lloyd"},
|
||||
{31, "Marty McFly"},
|
||||
{32, "Nya"},
|
||||
{33, "Owen Grady"},
|
||||
{34, "Peter Venkman"},
|
||||
{35, "Slimer"},
|
||||
{36, "Scooby-Doo"},
|
||||
{37, "Sensei Wu"},
|
||||
{38, "Shaggy"},
|
||||
{39, "Stay Puft"},
|
||||
{40, "Superman"},
|
||||
{41, "Unikitty"},
|
||||
{42, "Wicked Witch of the West"},
|
||||
{43, "Wonder Woman"},
|
||||
{44, "Zane"},
|
||||
{45, "Green Arrow"},
|
||||
{46, "Supergirl"},
|
||||
{47, "Abby Yates"},
|
||||
{48, "Finn the Human"},
|
||||
{49, "Ethan Hunt"},
|
||||
{50, "Lumpy Space Princess"},
|
||||
{51, "Jake the Dog"},
|
||||
{52, "Harry Potter"},
|
||||
{53, "Lord Voldemort"},
|
||||
{54, "Michael Knight"},
|
||||
{55, "B.A. Baracus"},
|
||||
{56, "Newt Scamander"},
|
||||
{57, "Sonic the Hedgehog"},
|
||||
{58, "Future Update (unreleased)"},
|
||||
{59, "Gizmo"},
|
||||
{60, "Stripe"},
|
||||
{61, "E.T."},
|
||||
{62, "Tina Goldstein"},
|
||||
{63, "Marceline the Vampire Queen"},
|
||||
{64, "Batgirl"},
|
||||
{65, "Robin"},
|
||||
{66, "Sloth"},
|
||||
{67, "Hermione Granger"},
|
||||
{68, "Chase McCain"},
|
||||
{69, "Excalibur Batman"},
|
||||
{70, "Raven"},
|
||||
{71, "Beast Boy"},
|
||||
{72, "Betelgeuse"},
|
||||
{73, "Lord Vortech (unreleased)"},
|
||||
{74, "Blossom"},
|
||||
{75, "Bubbles"},
|
||||
{76, "Buttercup"},
|
||||
{77, "Starfire"},
|
||||
{78, "World 15 (unreleased)"},
|
||||
{79, "World 16 (unreleased)"},
|
||||
{80, "World 17 (unreleased)"},
|
||||
{81, "World 18 (unreleased)"},
|
||||
{82, "World 19 (unreleased)"},
|
||||
{83, "World 20 (unreleased)"},
|
||||
{768, "Unknown 768"},
|
||||
{769, "Supergirl Red Lantern"},
|
||||
{770, "Unknown 770"}};
|
||||
|
||||
const std::map<const u32, const std::string> list_tokens = {
|
||||
{1000, "Police Car"},
|
||||
{1001, "Aerial Squad Car"},
|
||||
{1002, "Missile Striker"},
|
||||
{1003, "Gravity Sprinter"},
|
||||
{1004, "Street Shredder"},
|
||||
{1005, "Sky Clobberer"},
|
||||
{1006, "Batmobile"},
|
||||
{1007, "Batblaster"},
|
||||
{1008, "Sonic Batray"},
|
||||
{1009, "Benny's Spaceship"},
|
||||
{1010, "Lasercraft"},
|
||||
{1011, "The Annihilator"},
|
||||
{1012, "DeLorean Time Machine"},
|
||||
{1013, "Electric Time Machine"},
|
||||
{1014, "Ultra Time Machine"},
|
||||
{1015, "Hoverboard"},
|
||||
{1016, "Cyclone Board"},
|
||||
{1017, "Ultimate Hoverjet"},
|
||||
{1018, "Eagle Interceptor"},
|
||||
{1019, "Eagle Sky Blazer"},
|
||||
{1020, "Eagle Swoop Diver"},
|
||||
{1021, "Swamp Skimmer"},
|
||||
{1022, "Cragger's Fireship"},
|
||||
{1023, "Croc Command Sub"},
|
||||
{1024, "Cyber-Guard"},
|
||||
{1025, "Cyber-Wrecker"},
|
||||
{1026, "Laser Robot Walker"},
|
||||
{1027, "K-9"},
|
||||
{1028, "K-9 Ruff Rover"},
|
||||
{1029, "K-9 Laser Cutter"},
|
||||
{1030, "TARDIS"},
|
||||
{1031, "Laser-Pulse TARDIS"},
|
||||
{1032, "Energy-Burst TARDIS"},
|
||||
{1033, "Emmet's Excavator"},
|
||||
{1034, "Destroy Dozer"},
|
||||
{1035, "Destruct-o-Mech"},
|
||||
{1036, "Winged Monkey"},
|
||||
{1037, "Battle Monkey"},
|
||||
{1038, "Commander Monkey"},
|
||||
{1039, "Axe Chariot"},
|
||||
{1040, "Axe Hurler"},
|
||||
{1041, "Soaring Chariot"},
|
||||
{1042, "Shelob the Great"},
|
||||
{1043, "8-Legged Stalker"},
|
||||
{1044, "Poison Slinger"},
|
||||
{1045, "Homer's Car"},
|
||||
{1047, "The SubmaHomer"},
|
||||
{1046, "The Homercraft"},
|
||||
{1048, "Taunt-o-Vision"},
|
||||
{1050, "The MechaHomer"},
|
||||
{1049, "Blast Cam"},
|
||||
{1051, "Velociraptor"},
|
||||
{1053, "Venom Raptor"},
|
||||
{1052, "Spike Attack Raptor"},
|
||||
{1054, "Gyrosphere"},
|
||||
{1055, "Sonic Beam Gyrosphere"},
|
||||
{1056, " Gyrosphere"},
|
||||
{1057, "Clown Bike"},
|
||||
{1058, "Cannon Bike"},
|
||||
{1059, "Anti-Gravity Rocket Bike"},
|
||||
{1060, "Mighty Lion Rider"},
|
||||
{1061, "Lion Blazer"},
|
||||
{1062, "Fire Lion"},
|
||||
{1063, "Arrow Launcher"},
|
||||
{1064, "Seeking Shooter"},
|
||||
{1065, "Triple Ballista"},
|
||||
{1066, "Mystery Machine"},
|
||||
{1067, "Mystery Tow & Go"},
|
||||
{1068, "Mystery Monster"},
|
||||
{1069, "Boulder Bomber"},
|
||||
{1070, "Boulder Blaster"},
|
||||
{1071, "Cyclone Jet"},
|
||||
{1072, "Storm Fighter"},
|
||||
{1073, "Lightning Jet"},
|
||||
{1074, "Electro-Shooter"},
|
||||
{1075, "Blade Bike"},
|
||||
{1076, "Flight Fire Bike"},
|
||||
{1077, "Blades of Fire"},
|
||||
{1078, "Samurai Mech"},
|
||||
{1079, "Samurai Shooter"},
|
||||
{1080, "Soaring Samurai Mech"},
|
||||
{1081, "Companion Cube"},
|
||||
{1082, "Laser Deflector"},
|
||||
{1083, "Gold Heart Emitter"},
|
||||
{1084, "Sentry Turret"},
|
||||
{1085, "Turret Striker"},
|
||||
{1086, "Flight Turret Carrier"},
|
||||
{1087, "Scooby Snack"},
|
||||
{1088, "Scooby Fire Snack"},
|
||||
{1089, "Scooby Ghost Snack"},
|
||||
{1090, "Cloud Cuckoo Car"},
|
||||
{1091, "X-Stream Soaker"},
|
||||
{1092, "Rainbow Cannon"},
|
||||
{1093, "Invisible Jet"},
|
||||
{1094, "Laser Shooter"},
|
||||
{1095, "Torpedo Bomber"},
|
||||
{1096, "NinjaCopter"},
|
||||
{1097, "Glaciator"},
|
||||
{1098, "Freeze Fighter"},
|
||||
{1099, "Travelling Time Train"},
|
||||
{1100, "Flight Time Train"},
|
||||
{1101, "Missile Blast Time Train"},
|
||||
{1102, "Aqua Watercraft"},
|
||||
{1103, "Seven Seas Speeder"},
|
||||
{1104, "Trident of Fire"},
|
||||
{1105, "Drill Driver"},
|
||||
{1106, "Bane Dig 'n' Drill"},
|
||||
{1107, "Bane Drill 'n' Blast"},
|
||||
{1108, "Quinn Mobile"},
|
||||
{1109, "Quinn Ultra Racer"},
|
||||
{1110, "Missile Launcher"},
|
||||
{1111, "The Joker's Chopper"},
|
||||
{1112, "Mischievous Missile Blaster"},
|
||||
{1113, "Lock 'n' Laser Jet"},
|
||||
{1114, "Hover Pod"},
|
||||
{1115, "Krypton Striker"},
|
||||
{1116, "Super Stealth Pod"},
|
||||
{1117, "Dalek"},
|
||||
{1118, "Fire 'n' Ride Dalek"},
|
||||
{1119, "Silver Shooter Dalek"},
|
||||
{1120, "Ecto-1"},
|
||||
{1121, "Ecto-1 Blaster"},
|
||||
{1122, "Ecto-1 Water Diver"},
|
||||
{1123, "Ghost Trap"},
|
||||
{1124, "Ghost Stun 'n' Trap"},
|
||||
{1125, "Proton Zapper"},
|
||||
{1126, "Unknown"},
|
||||
{1127, "Unknown"},
|
||||
{1128, "Unknown"},
|
||||
{1129, "Unknown"},
|
||||
{1130, "Unknown"},
|
||||
{1131, "Unknown"},
|
||||
{1132, "Lloyd's Golden Dragon"},
|
||||
{1133, "Sword Projector Dragon"},
|
||||
{1134, "Unknown"},
|
||||
{1135, "Unknown"},
|
||||
{1136, "Unknown"},
|
||||
{1137, "Unknown"},
|
||||
{1138, "Unknown"},
|
||||
{1139, "Unknown"},
|
||||
{1140, "Unknown"},
|
||||
{1141, "Unknown"},
|
||||
{1142, "Unknown"},
|
||||
{1143, "Unknown"},
|
||||
{1144, "Mega Flight Dragon"},
|
||||
{1145, "Unknown"},
|
||||
{1146, "Unknown"},
|
||||
{1147, "Unknown"},
|
||||
{1148, "Unknown"},
|
||||
{1149, "Unknown"},
|
||||
{1150, "Unknown"},
|
||||
{1151, "Unknown"},
|
||||
{1152, "Unknown"},
|
||||
{1153, "Unknown"},
|
||||
{1154, "Unknown"},
|
||||
{1155, "Flying White Dragon"},
|
||||
{1156, "Golden Fire Dragon"},
|
||||
{1157, "Ultra Destruction Dragon"},
|
||||
{1158, "Arcade Machine"},
|
||||
{1159, "8-Bit Shooter"},
|
||||
{1160, "The Pixelator"},
|
||||
{1161, "G-6155 Spy Hunter"},
|
||||
{1162, "Interdiver"},
|
||||
{1163, "Aerial Spyhunter"},
|
||||
{1164, "Slime Shooter"},
|
||||
{1165, "Slime Exploder"},
|
||||
{1166, "Slime Streamer"},
|
||||
{1167, "Terror Dog"},
|
||||
{1168, "Terror Dog Destroyer"},
|
||||
{1169, "Soaring Terror Dog"},
|
||||
{1170, "Ancient Psychic Tandem War Elephant"},
|
||||
{1171, "Cosmic Squid"},
|
||||
{1172, "Psychic Submarine"},
|
||||
{1173, "BMO"},
|
||||
{1174, "DOGMO"},
|
||||
{1175, "SNAKEMO"},
|
||||
{1176, "Jakemobile"},
|
||||
{1177, "Snail Dude Jake"},
|
||||
{1178, "Hover Jake"},
|
||||
{1179, "Lumpy Car"},
|
||||
{1181, "Lumpy Land Whale"},
|
||||
{1180, "Lumpy Truck"},
|
||||
{1182, "Lunatic Amp"},
|
||||
{1183, "Shadow Scorpion"},
|
||||
{1184, "Heavy Metal Monster"},
|
||||
{1185, "B.A.'s Van"},
|
||||
{1186, "Fool Smasher"},
|
||||
{1187, "Pain Plane"},
|
||||
{1188, "Phone Home"},
|
||||
{1189, "Mobile Uplink"},
|
||||
{1190, "Super-Charged Satellite"},
|
||||
{1191, "Niffler"},
|
||||
{1192, "Sinister Scorpion"},
|
||||
{1193, "Vicious Vulture"},
|
||||
{1194, "Swooping Evil"},
|
||||
{1195, "Brutal Bloom"},
|
||||
{1196, "Crawling Creeper"},
|
||||
{1197, "Ecto-1 (2016)"},
|
||||
{1198, "Ectozer"},
|
||||
{1199, "PerfEcto"},
|
||||
{1200, "Flash 'n' Finish"},
|
||||
{1201, "Rampage Record Player"},
|
||||
{1202, "Stripe's Throne"},
|
||||
{1203, "R.C. Racer"},
|
||||
{1204, "Gadget-O-Matic"},
|
||||
{1205, "Scarlet Scorpion"},
|
||||
{1206, "Hogwarts Express"},
|
||||
{1208, "Steam Warrior"},
|
||||
{1207, "Soaring Steam Plane"},
|
||||
{1209, "Enchanted Car"},
|
||||
{1210, "Shark Sub"},
|
||||
{1211, "Monstrous Mouth"},
|
||||
{1212, "IMF Scrambler"},
|
||||
{1213, "Shock Cycle"},
|
||||
{1214, "IMF Covert Jet"},
|
||||
{1215, "IMF Sports Car"},
|
||||
{1216, "IMF Tank"},
|
||||
{1217, "IMF Splorer"},
|
||||
{1218, "Sonic Speedster"},
|
||||
{1219, "Blue Typhoon"},
|
||||
{1220, "Moto Bug"},
|
||||
{1221, "The Tornado"},
|
||||
{1222, "Crabmeat"},
|
||||
{1223, "Eggcatcher"},
|
||||
{1224, "K.I.T.T."},
|
||||
{1225, "Goliath Armored Semi"},
|
||||
{1226, "K.I.T.T. Jet"},
|
||||
{1227, "Police Helicopter"},
|
||||
{1228, "Police Hovercraft"},
|
||||
{1229, "Police Plane"},
|
||||
{1230, "Bionic Steed"},
|
||||
{1231, "Bat-Raptor"},
|
||||
{1232, "Ultrabat"},
|
||||
{1233, "Batwing"},
|
||||
{1234, "The Black Thunder"},
|
||||
{1235, "Bat-Tank"},
|
||||
{1236, "Skeleton Organ"},
|
||||
{1237, "Skeleton Jukebox"},
|
||||
{1238, "Skele-Turkey"},
|
||||
{1239, "One-Eyed Willy's Pirate Ship"},
|
||||
{1240, "Fanged Fortune"},
|
||||
{1241, "Inferno Cannon"},
|
||||
{1242, "Buckbeak"},
|
||||
{1243, "Giant Owl"},
|
||||
{1244, "Fierce Falcon"},
|
||||
{1245, "Saturn's Sandworm"},
|
||||
{1247, "Haunted Vacuum"},
|
||||
{1246, "Spooky Spider"},
|
||||
{1248, "PPG Smartphone"},
|
||||
{1249, "PPG Hotline"},
|
||||
{1250, "Powerpuff Mag-Net"},
|
||||
{1253, "Mega Blast Bot"},
|
||||
{1251, "Ka-Pow Cannon"},
|
||||
{1252, "Slammin' Guitar"},
|
||||
{1254, "Octi"},
|
||||
{1255, "Super Skunk"},
|
||||
{1256, "Sonic Squid"},
|
||||
{1257, "T-Car"},
|
||||
{1258, "T-Forklift"},
|
||||
{1259, "T-Plane"},
|
||||
{1260, "Spellbook of Azarath"},
|
||||
{1261, "Raven Wings"},
|
||||
{1262, "Giant Hand"},
|
||||
{1263, "Titan Robot"},
|
||||
{1264, "T-Rocket"},
|
||||
{1265, "Robot Retriever"}};
|
||||
|
||||
minifig_creator_dialog::minifig_creator_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Figure Creator"));
|
||||
setObjectName("figure_creator");
|
||||
setMinimumSize(QSize(500, 150));
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
QComboBox* combo_figlist = new QComboBox();
|
||||
QStringList filterlist;
|
||||
|
||||
for (const auto& [figure, figure_name] : list_minifigs)
|
||||
{
|
||||
QString name = QString::fromStdString(figure_name);
|
||||
combo_figlist->addItem(name, QVariant(figure));
|
||||
filterlist << std::move(name);
|
||||
}
|
||||
|
||||
combo_figlist->addItem(tr("--Unknown--"), QVariant(0xFFFF));
|
||||
combo_figlist->setEditable(true);
|
||||
combo_figlist->setInsertPolicy(QComboBox::NoInsert);
|
||||
combo_figlist->model()->sort(0, Qt::AscendingOrder);
|
||||
|
||||
QCompleter* co_compl = new QCompleter(filterlist, this);
|
||||
co_compl->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
co_compl->setCompletionMode(QCompleter::PopupCompletion);
|
||||
co_compl->setFilterMode(Qt::MatchContains);
|
||||
combo_figlist->setCompleter(co_compl);
|
||||
|
||||
vbox_panel->addWidget(combo_figlist);
|
||||
|
||||
QFrame* line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
vbox_panel->addWidget(line);
|
||||
|
||||
QHBoxLayout* hbox_number = new QHBoxLayout();
|
||||
QLabel* label_number = new QLabel(tr("Figure Number:"));
|
||||
QLineEdit* edit_number = new QLineEdit(QString::fromStdString(std::to_string(0)));
|
||||
QRegularExpressionValidator* rxv = new QRegularExpressionValidator(QRegularExpression("\\d*"), this);
|
||||
edit_number->setValidator(rxv);
|
||||
hbox_number->addWidget(label_number);
|
||||
hbox_number->addWidget(edit_number);
|
||||
vbox_panel->addLayout(hbox_number);
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QPushButton* btn_create = new QPushButton(tr("Create"), this);
|
||||
QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this);
|
||||
hbox_buttons->addStretch();
|
||||
hbox_buttons->addWidget(btn_create);
|
||||
hbox_buttons->addWidget(btn_cancel);
|
||||
vbox_panel->addLayout(hbox_buttons);
|
||||
|
||||
setLayout(vbox_panel);
|
||||
|
||||
connect(combo_figlist, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index)
|
||||
{
|
||||
const u16 fig_info = combo_figlist->itemData(index).toUInt();
|
||||
if (fig_info != 0xFFFF)
|
||||
{
|
||||
edit_number->setText(QString::number(fig_info));
|
||||
}
|
||||
});
|
||||
|
||||
connect(btn_create, &QAbstractButton::clicked, this, [=, this]()
|
||||
{
|
||||
bool ok_num = false;
|
||||
const u16 fig_num = edit_number->text().toUInt(&ok_num) & 0xFFFF;
|
||||
if (!ok_num)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Error converting value"), tr("Figure number entered is invalid!"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
const auto found_figure = list_minifigs.find(fig_num);
|
||||
if (found_figure != list_minifigs.cend())
|
||||
{
|
||||
s_last_figure_path += QString::fromStdString(found_figure->second + ".bin");
|
||||
}
|
||||
else
|
||||
{
|
||||
s_last_figure_path += QString("Unknown(%1).bin").arg(fig_num);
|
||||
}
|
||||
|
||||
m_file_path = QFileDialog::getSaveFileName(this, tr("Create Figure File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;"));
|
||||
if (m_file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fs::file dim_file(m_file_path.toStdString(), fs::read + fs::write + fs::create);
|
||||
if (!dim_file)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to create minifig file!"), tr("Failed to create minifig file:\n%1").arg(m_file_path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 0x2D * 0x04> file_data{};
|
||||
g_dimensionstoypad.create_blank_character(file_data, fig_num);
|
||||
|
||||
dim_file.write(file_data.data(), file_data.size());
|
||||
dim_file.close();
|
||||
|
||||
s_last_figure_path = QFileInfo(m_file_path).absolutePath() + "/";
|
||||
accept();
|
||||
});
|
||||
|
||||
connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject);
|
||||
|
||||
connect(co_compl, QOverload<const QString&>::of(&QCompleter::activated), [=](const QString& text)
|
||||
{
|
||||
combo_figlist->setCurrentIndex(combo_figlist->findText(text));
|
||||
});
|
||||
}
|
||||
|
||||
QString minifig_creator_dialog::get_file_path() const
|
||||
{
|
||||
return m_file_path;
|
||||
}
|
||||
|
||||
minifig_move_dialog::minifig_move_dialog(QWidget* parent, u8 old_index)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Figure Mover"));
|
||||
setObjectName("figure_mover");
|
||||
setMinimumSize(QSize(500, 150));
|
||||
|
||||
auto* grid_panel = new QGridLayout();
|
||||
|
||||
add_minifig_position(grid_panel, 0, 0, 0, old_index);
|
||||
grid_panel->addWidget(new QLabel(tr("")), 0, 1);
|
||||
add_minifig_position(grid_panel, 1, 0, 2, old_index);
|
||||
grid_panel->addWidget(new QLabel(tr(""), this), 0, 3);
|
||||
add_minifig_position(grid_panel, 2, 0, 4, old_index);
|
||||
|
||||
add_minifig_position(grid_panel, 3, 1, 0, old_index);
|
||||
add_minifig_position(grid_panel, 4, 1, 1, old_index);
|
||||
grid_panel->addWidget(new QLabel(tr("")), 1, 2);
|
||||
add_minifig_position(grid_panel, 5, 1, 3, old_index);
|
||||
add_minifig_position(grid_panel, 6, 1, 4, old_index);
|
||||
|
||||
setLayout(grid_panel);
|
||||
}
|
||||
|
||||
void minifig_move_dialog::add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
|
||||
auto* vbox_panel = new QVBoxLayout();
|
||||
|
||||
if (figure_slots[index])
|
||||
{
|
||||
const auto found_figure = list_minifigs.find(figure_slots[index].value());
|
||||
if (found_figure != list_minifigs.cend())
|
||||
{
|
||||
vbox_panel->addWidget(new QLabel(tr(found_figure->second.c_str())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vbox_panel->addWidget(new QLabel(tr("None")));
|
||||
}
|
||||
auto* btn_move = new QPushButton(tr("Move Here"), this);
|
||||
if (old_index == index)
|
||||
{
|
||||
btn_move->setText(tr("Pick up and Place"));
|
||||
}
|
||||
vbox_panel->addWidget(btn_move);
|
||||
connect(btn_move, &QAbstractButton::clicked, this, [this, index]
|
||||
{
|
||||
m_index = index;
|
||||
m_pad = index == 1 ? 1 :
|
||||
index == 0 || index == 3 || index == 4 ? 2 :
|
||||
3;
|
||||
accept();
|
||||
});
|
||||
|
||||
grid_panel->addLayout(vbox_panel, row, column);
|
||||
}
|
||||
|
||||
u8 minifig_move_dialog::get_new_pad() const
|
||||
{
|
||||
return m_pad;
|
||||
}
|
||||
|
||||
u8 minifig_move_dialog::get_new_index() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
dimensions_dialog::dimensions_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Dimensions Manager"));
|
||||
setObjectName("dimensions_manager");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setMinimumSize(QSize(800, 200));
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
QVBoxLayout* vbox_group = new QVBoxLayout();
|
||||
QHBoxLayout* hbox_group_1 = new QHBoxLayout();
|
||||
QHBoxLayout* hbox_group_2 = new QHBoxLayout();
|
||||
|
||||
QGroupBox* group_figures = new QGroupBox(tr("Active Dimensions Figures:"));
|
||||
|
||||
add_minifig_slot(hbox_group_1, 2, 0);
|
||||
hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
|
||||
add_minifig_slot(hbox_group_1, 1, 1);
|
||||
hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
|
||||
add_minifig_slot(hbox_group_1, 3, 2);
|
||||
|
||||
add_minifig_slot(hbox_group_2, 2, 3);
|
||||
add_minifig_slot(hbox_group_2, 2, 4);
|
||||
hbox_group_2->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
|
||||
add_minifig_slot(hbox_group_2, 3, 5);
|
||||
add_minifig_slot(hbox_group_2, 3, 6);
|
||||
|
||||
vbox_group->addLayout(hbox_group_1);
|
||||
vbox_group->addSpacerItem(new QSpacerItem(0, 20, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
|
||||
vbox_group->addLayout(hbox_group_2);
|
||||
group_figures->setLayout(vbox_group);
|
||||
vbox_panel->addWidget(group_figures);
|
||||
setLayout(vbox_panel);
|
||||
}
|
||||
|
||||
dimensions_dialog::~dimensions_dialog()
|
||||
{
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
dimensions_dialog* dimensions_dialog::get_dlg(QWidget* parent)
|
||||
{
|
||||
if (inst == nullptr)
|
||||
inst = new dimensions_dialog(parent);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void dimensions_dialog::add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
|
||||
QVBoxLayout* vbox_layout = new QVBoxLayout();
|
||||
|
||||
QHBoxLayout* hbox_name_move = new QHBoxLayout();
|
||||
QHBoxLayout* hbox_actions = new QHBoxLayout();
|
||||
|
||||
QPushButton* clear_btn = new QPushButton(tr("Clear"));
|
||||
QPushButton* create_btn = new QPushButton(tr("Create"));
|
||||
QPushButton* load_btn = new QPushButton(tr("Load"));
|
||||
QPushButton* move_btn = new QPushButton(tr("Move"));
|
||||
|
||||
m_edit_figures[index] = new QLineEdit();
|
||||
m_edit_figures[index]->setEnabled(false);
|
||||
if (figure_slots[index])
|
||||
{
|
||||
const auto found_figure = list_minifigs.find(figure_slots[index].value());
|
||||
if (found_figure != list_minifigs.cend())
|
||||
{
|
||||
m_edit_figures[index]->setText(QString::fromStdString(found_figure->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_edit_figures[index]->setText(tr("Unknown Figure"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_edit_figures[index]->setText(tr("None"));
|
||||
}
|
||||
|
||||
connect(clear_btn, &QAbstractButton::clicked, this, [this, pad, index]
|
||||
{
|
||||
clear_figure(pad, index);
|
||||
});
|
||||
connect(create_btn, &QAbstractButton::clicked, this, [this, pad, index]
|
||||
{
|
||||
create_figure(pad, index);
|
||||
});
|
||||
connect(load_btn, &QAbstractButton::clicked, this, [this, pad, index]
|
||||
{
|
||||
load_figure(pad, index);
|
||||
});
|
||||
connect(move_btn, &QAbstractButton::clicked, this, [this, pad, index]
|
||||
{
|
||||
if (figure_slots[index])
|
||||
{
|
||||
move_figure(pad, index);
|
||||
}
|
||||
});
|
||||
|
||||
hbox_name_move->addWidget(m_edit_figures[index]);
|
||||
hbox_name_move->addWidget(move_btn);
|
||||
hbox_actions->addWidget(clear_btn);
|
||||
hbox_actions->addWidget(create_btn);
|
||||
hbox_actions->addWidget(load_btn);
|
||||
|
||||
vbox_layout->addLayout(hbox_name_move);
|
||||
vbox_layout->addLayout(hbox_actions);
|
||||
|
||||
layout->addLayout(vbox_layout);
|
||||
}
|
||||
|
||||
void dimensions_dialog::clear_figure(u8 pad, u8 index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
|
||||
if (figure_slots[index])
|
||||
{
|
||||
g_dimensionstoypad.remove_figure(pad, index, true, true);
|
||||
figure_slots[index] = std::nullopt;
|
||||
m_edit_figures[index]->setText(tr("None"));
|
||||
}
|
||||
}
|
||||
|
||||
void dimensions_dialog::create_figure(u8 pad, u8 index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
minifig_creator_dialog create_dlg(this);
|
||||
if (create_dlg.exec() == Accepted)
|
||||
{
|
||||
load_figure_path(pad, index, create_dlg.get_file_path());
|
||||
}
|
||||
}
|
||||
|
||||
void dimensions_dialog::load_figure(u8 pad, u8 index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Dimensions File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;"));
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_last_figure_path = QFileInfo(file_path).absolutePath() + "/";
|
||||
|
||||
load_figure_path(pad, index, file_path);
|
||||
}
|
||||
|
||||
void dimensions_dialog::move_figure(u8 pad, u8 index)
|
||||
{
|
||||
ensure(index < figure_slots.size());
|
||||
minifig_move_dialog move_dlg(this, index);
|
||||
g_dimensionstoypad.temp_remove(index);
|
||||
if (move_dlg.exec() == Accepted)
|
||||
{
|
||||
g_dimensionstoypad.move_figure(move_dlg.get_new_pad(), move_dlg.get_new_index(), pad, index);
|
||||
if (index != move_dlg.get_new_index())
|
||||
{
|
||||
figure_slots[move_dlg.get_new_index()] = figure_slots[index];
|
||||
m_edit_figures[move_dlg.get_new_index()]->setText(m_edit_figures[index]->text());
|
||||
figure_slots[index] = std::nullopt;
|
||||
m_edit_figures[index]->setText(tr("None"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dimensionstoypad.cancel_remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
void dimensions_dialog::load_figure_path(u8 pad, u8 index, const QString& path)
|
||||
{
|
||||
fs::file dim_file(path.toStdString(), fs::read + fs::write + fs::lock);
|
||||
if (!dim_file)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to open the figure file!"), tr("Failed to open the figure file(%1)!\nFile may already be in use on the base.").arg(path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 0x2D * 0x04> data;
|
||||
if (dim_file.read(data.data(), data.size()) != data.size())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to read the figure file!"), tr("Failed to read the figure file(%1)!\nFile was too small.").arg(path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
clear_figure(pad, index);
|
||||
|
||||
const u32 fig_num = g_dimensionstoypad.load_figure(data, std::move(dim_file), pad, index, true);
|
||||
|
||||
figure_slots[index] = fig_num;
|
||||
const auto name = list_minifigs.find(fig_num);
|
||||
if (name != list_minifigs.cend())
|
||||
{
|
||||
m_edit_figures[index]->setText(QString::fromStdString(name->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto blank_name = list_tokens.find(fig_num);
|
||||
if (blank_name != list_tokens.cend())
|
||||
{
|
||||
m_edit_figures[index]->setText(QString::fromStdString(blank_name->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_edit_figures[index]->setText("Blank Tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QGridLayout>
|
||||
|
||||
class minifig_creator_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit minifig_creator_dialog(QWidget* parent);
|
||||
QString get_file_path() const;
|
||||
|
||||
protected:
|
||||
QString m_file_path;
|
||||
};
|
||||
|
||||
class minifig_move_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit minifig_move_dialog(QWidget* parent, u8 old_index);
|
||||
u8 get_new_pad() const;
|
||||
u8 get_new_index() const;
|
||||
|
||||
protected:
|
||||
u8 m_pad = 0;
|
||||
u8 m_index = 0;
|
||||
|
||||
private:
|
||||
void add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index);
|
||||
};
|
||||
|
||||
class dimensions_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit dimensions_dialog(QWidget* parent);
|
||||
~dimensions_dialog();
|
||||
static dimensions_dialog* get_dlg(QWidget* parent);
|
||||
|
||||
dimensions_dialog(dimensions_dialog const&) = delete;
|
||||
void operator=(dimensions_dialog const&) = delete;
|
||||
|
||||
protected:
|
||||
void clear_figure(u8 pad, u8 index);
|
||||
void create_figure(u8 pad, u8 index);
|
||||
void load_figure(u8 pad, u8 index);
|
||||
void move_figure(u8 pad, u8 index);
|
||||
void load_figure_path(u8 pad, u8 index, const QString& path);
|
||||
|
||||
protected:
|
||||
std::array<QLineEdit*, 7> m_edit_figures{};
|
||||
|
||||
private:
|
||||
void add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index);
|
||||
static dimensions_dialog* inst;
|
||||
};
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#include "display_sleep_control.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static IOPMAssertionID s_pm_assertion = kIOPMNullAssertionID;
|
||||
|
||||
#elif defined(HAVE_QTDBUS)
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QDBusMessage>
|
||||
#include <QDBusReply>
|
||||
#include "util/types.hpp"
|
||||
static u32 s_dbus_cookie = 0;
|
||||
#endif
|
||||
|
||||
bool display_sleep_control_supported()
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
return true;
|
||||
#elif defined(HAVE_QTDBUS)
|
||||
for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"})
|
||||
{
|
||||
QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus());
|
||||
if (interface.isValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void enable_display_sleep(bool enabled)
|
||||
{
|
||||
if (!display_sleep_control_supported())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetThreadExecutionState(enabled ? ES_CONTINUOUS : (ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED));
|
||||
#elif defined(__APPLE__)
|
||||
if (enabled && s_pm_assertion != kIOPMNullAssertionID)
|
||||
{
|
||||
IOPMAssertionRelease(s_pm_assertion);
|
||||
s_pm_assertion = kIOPMNullAssertionID;
|
||||
}
|
||||
else if (!enabled)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
// Necessary as some of those values are macro using old casts
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Game running"), &s_pm_assertion);
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
#elif defined(HAVE_QTDBUS)
|
||||
if (enabled && s_dbus_cookie != 0)
|
||||
{
|
||||
for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"})
|
||||
{
|
||||
QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus());
|
||||
if (interface.isValid())
|
||||
{
|
||||
interface.call("UnInhibit", s_dbus_cookie);
|
||||
break;
|
||||
}
|
||||
}
|
||||
s_dbus_cookie = 0;
|
||||
}
|
||||
else if (!enabled)
|
||||
{
|
||||
for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"})
|
||||
{
|
||||
QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus());
|
||||
if (interface.isValid())
|
||||
{
|
||||
QDBusReply<u32> reply = interface.call("Inhibit", "rpcs3", "Game running");
|
||||
if (reply.isValid())
|
||||
{
|
||||
s_dbus_cookie = reply.value();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
bool display_sleep_control_supported();
|
||||
void enable_display_sleep(bool enabled);
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
#include <QApplication>
|
||||
#include <QThread>
|
||||
|
||||
#include "downloader.h"
|
||||
#include "curl_handle.h"
|
||||
#include "progress_dialog.h"
|
||||
|
||||
#include "util/logs.hpp"
|
||||
|
||||
LOG_CHANNEL(network_log, "NET");
|
||||
|
||||
usz curl_write_cb_compat(char* ptr, usz /*size*/, usz nmemb, void* userdata)
|
||||
{
|
||||
downloader* download = static_cast<downloader*>(userdata);
|
||||
return download->update_buffer(ptr, nmemb);
|
||||
}
|
||||
|
||||
downloader::downloader(QWidget* parent)
|
||||
: QObject(parent), m_parent(parent), m_curl(new rpcs3::curl::curl_handle())
|
||||
{
|
||||
}
|
||||
|
||||
downloader::~downloader()
|
||||
{
|
||||
if (m_thread && m_thread->isRunning())
|
||||
{
|
||||
m_curl_abort = true;
|
||||
m_thread->wait();
|
||||
}
|
||||
}
|
||||
|
||||
void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size)
|
||||
{
|
||||
network_log.notice("Starting download from URL: %s", url);
|
||||
|
||||
if (m_thread)
|
||||
{
|
||||
if (m_thread->isRunning())
|
||||
{
|
||||
m_curl_abort = true;
|
||||
m_thread->wait();
|
||||
}
|
||||
m_thread->deleteLater();
|
||||
}
|
||||
|
||||
m_keep_progress_dialog_open = keep_progress_dialog_open;
|
||||
m_curl_buf.clear();
|
||||
m_curl_abort = false;
|
||||
|
||||
CURLcode err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, url.c_str());
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_URL, %s) error: %s", url, curl_easy_strerror(err));
|
||||
|
||||
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat);
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_WRITEFUNCTION, curl_write_cb_compat) error: %s", curl_easy_strerror(err));
|
||||
|
||||
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this);
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_WRITEDATA) error: %s", curl_easy_strerror(err));
|
||||
|
||||
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, follow_location ? 1 : 0);
|
||||
if (err != CURLE_OK)
|
||||
network_log.error("curl_easy_setopt(CURLOPT_FOLLOWLOCATION, %d) error: %s", follow_location, curl_easy_strerror(err));
|
||||
|
||||
m_thread = QThread::create([this]
|
||||
{
|
||||
// Reset error buffer before we call curl_easy_perform
|
||||
m_curl->reset_error_buffer();
|
||||
|
||||
const CURLcode result = curl_easy_perform(m_curl->get_curl());
|
||||
m_curl_success = result == CURLE_OK;
|
||||
|
||||
if (!m_curl_success && !m_curl_abort)
|
||||
{
|
||||
const std::string error = fmt::format("curl_easy_perform(): %s", m_curl->get_verbose_error(result));
|
||||
network_log.error("%s", error);
|
||||
Q_EMIT signal_download_error(QString::fromStdString(error));
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_thread, &QThread::finished, this, [this]()
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
network_log.notice("Download aborted");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_keep_progress_dialog_open || !m_curl_success)
|
||||
{
|
||||
close_progress_dialog();
|
||||
}
|
||||
|
||||
if (m_curl_success)
|
||||
{
|
||||
network_log.notice("Download finished");
|
||||
Q_EMIT signal_download_finished(m_curl_buf);
|
||||
}
|
||||
});
|
||||
|
||||
// The downloader's signals are expected to be disconnected and customized before start is called.
|
||||
// Therefore we need to (re)connect its signal(s) here and not in the constructor.
|
||||
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update);
|
||||
|
||||
if (show_progress_dialog)
|
||||
{
|
||||
const int maximum = expected_size > 0 ? expected_size : 100;
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setWindowTitle(progress_dialog_title);
|
||||
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
||||
m_progress_dialog->SetRange(0, maximum);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progress_dialog = new progress_dialog(progress_dialog_title, tr("Please wait..."), tr("Abort"), 0, maximum, true, m_parent);
|
||||
m_progress_dialog->setAutoReset(false);
|
||||
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
||||
m_progress_dialog->show();
|
||||
|
||||
// Handle abort
|
||||
connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]()
|
||||
{
|
||||
m_curl_abort = true;
|
||||
m_progress_dialog = nullptr; // The progress dialog deletes itself on close
|
||||
Q_EMIT signal_download_canceled();
|
||||
});
|
||||
connect(m_progress_dialog, &QProgressDialog::finished, this, [this]()
|
||||
{
|
||||
m_progress_dialog = nullptr; // The progress dialog deletes itself on close
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
m_thread->setObjectName("Download Thread");
|
||||
m_thread->setParent(this);
|
||||
m_thread->start();
|
||||
}
|
||||
|
||||
void downloader::update_progress_dialog(const QString& title) const
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->setWindowTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
void downloader::close_progress_dialog()
|
||||
{
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->accept();
|
||||
m_progress_dialog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
progress_dialog* downloader::get_progress_dialog() const
|
||||
{
|
||||
return m_progress_dialog;
|
||||
}
|
||||
|
||||
usz downloader::update_buffer(char* data, usz size)
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto old_size = m_curl_buf.size();
|
||||
const auto new_size = old_size + size;
|
||||
m_curl_buf.resize(static_cast<int>(new_size));
|
||||
memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
|
||||
int max = 0;
|
||||
|
||||
if (m_actual_download_size < 0)
|
||||
{
|
||||
if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0)
|
||||
{
|
||||
max = static_cast<int>(m_actual_download_size);
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT signal_buffer_update(static_cast<int>(new_size), max);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void downloader::handle_buffer_update(int size, int max) const
|
||||
{
|
||||
if (m_curl_abort)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_progress_dialog)
|
||||
{
|
||||
m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum());
|
||||
m_progress_dialog->SetValue(size);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
namespace curl
|
||||
{
|
||||
class curl_handle;
|
||||
}
|
||||
} // namespace rpcs3
|
||||
|
||||
class progress_dialog;
|
||||
|
||||
class downloader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit downloader(QWidget* parent = nullptr);
|
||||
~downloader();
|
||||
|
||||
void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1);
|
||||
usz update_buffer(char* data, usz size);
|
||||
|
||||
void update_progress_dialog(const QString& title) const;
|
||||
void close_progress_dialog();
|
||||
|
||||
progress_dialog* get_progress_dialog() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void handle_buffer_update(int size, int max) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void signal_download_error(const QString& error);
|
||||
void signal_download_finished(const QByteArray& data);
|
||||
void signal_download_canceled();
|
||||
void signal_buffer_update(int size, int max);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
||||
std::unique_ptr<rpcs3::curl::curl_handle> m_curl;
|
||||
QByteArray m_curl_buf;
|
||||
atomic_t<bool> m_curl_abort = false;
|
||||
atomic_t<bool> m_curl_success = false;
|
||||
double m_actual_download_size = -1.0;
|
||||
|
||||
progress_dialog* m_progress_dialog = nullptr;
|
||||
atomic_t<bool> m_keep_progress_dialog_open = false;
|
||||
QString m_progress_dialog_title;
|
||||
|
||||
QThread* m_thread = nullptr;
|
||||
};
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
#include "elf_memory_dumping_dialog.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#include "qt_utils.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QCoreApplication>
|
||||
#include <QFontDatabase>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
Q_DECLARE_METATYPE(spu_memory_segment_dump_data);
|
||||
|
||||
elf_memory_dumping_dialog::elf_memory_dumping_dialog(u32 ppu_debugger_addr, std::shared_ptr<gui_settings> _gui_settings, QWidget* parent)
|
||||
: QDialog(parent), m_gui_settings(std::move(_gui_settings))
|
||||
{
|
||||
setWindowTitle(tr("SPU ELF Dumper"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
m_seg_list = new QListWidget();
|
||||
|
||||
// Font
|
||||
const int pSize = 10;
|
||||
QFont mono = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
mono.setPointSize(pSize);
|
||||
|
||||
m_seg_list->setMinimumWidth(gui::utils::get_label_width(tr("PPU Address: 0x00000000, LS Address: 0x00000, Segment Size: 0x00000, Flags: 0x0")));
|
||||
|
||||
// Address expression input
|
||||
auto make_hex_edit = [this, mono](u32 max_digits)
|
||||
{
|
||||
QLineEdit* le = new QLineEdit();
|
||||
le->setFont(mono);
|
||||
le->setMaxLength(max_digits + 2);
|
||||
le->setPlaceholderText("0x" + QStringLiteral("0").repeated(max_digits));
|
||||
le->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^(0[xX])?0*[a-fA-F0-9]{0,%1}$").arg(max_digits)), this));
|
||||
return le;
|
||||
};
|
||||
|
||||
m_segment_size_input = make_hex_edit(5);
|
||||
m_ppu_address_input = make_hex_edit(8);
|
||||
m_ls_address_input = make_hex_edit(5);
|
||||
m_segment_flags_input = make_hex_edit(1);
|
||||
m_segment_flags_input->setText("0x7"); // READ WRITE EXEC
|
||||
m_ppu_address_input->setText(QStringLiteral("0x%1").arg(ppu_debugger_addr & -0x10000, 1, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors.
|
||||
|
||||
QPushButton* add_segment_button = new QPushButton(QStringLiteral("+"));
|
||||
add_segment_button->setToolTip(tr("Add new segment"));
|
||||
add_segment_button->setFixedWidth(add_segment_button->sizeHint().height()); // Make button square
|
||||
connect(add_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::add_new_segment);
|
||||
|
||||
QPushButton* remove_segment_button = new QPushButton(QStringLiteral("-"));
|
||||
remove_segment_button->setToolTip(tr("Remove segment"));
|
||||
remove_segment_button->setFixedWidth(remove_segment_button->sizeHint().height()); // Make button square
|
||||
remove_segment_button->setEnabled(false);
|
||||
connect(remove_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::remove_segment);
|
||||
|
||||
QPushButton* save_to_file = new QPushButton(tr("Save To ELF"));
|
||||
save_to_file->setToolTip(tr("Save To An ELF file"));
|
||||
connect(save_to_file, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::save_to_file);
|
||||
|
||||
QHBoxLayout* hbox_input = new QHBoxLayout;
|
||||
hbox_input->addWidget(new QLabel(tr("Segment Size:")));
|
||||
hbox_input->addWidget(m_segment_size_input);
|
||||
hbox_input->addSpacing(5);
|
||||
|
||||
hbox_input->addWidget(new QLabel(tr("PPU Address:")));
|
||||
hbox_input->addWidget(m_ppu_address_input);
|
||||
hbox_input->addSpacing(5);
|
||||
hbox_input->addWidget(new QLabel(tr("LS Address:")));
|
||||
hbox_input->addWidget(m_ls_address_input);
|
||||
hbox_input->addSpacing(5);
|
||||
hbox_input->addWidget(new QLabel(tr("Flags:")));
|
||||
hbox_input->addWidget(m_segment_flags_input);
|
||||
|
||||
QHBoxLayout* hbox_save_and_edit = new QHBoxLayout;
|
||||
hbox_save_and_edit->addStretch(2);
|
||||
hbox_save_and_edit->addWidget(add_segment_button);
|
||||
hbox_save_and_edit->addSpacing(4);
|
||||
hbox_save_and_edit->addWidget(remove_segment_button);
|
||||
hbox_save_and_edit->addSpacing(4);
|
||||
hbox_save_and_edit->addWidget(save_to_file);
|
||||
|
||||
QVBoxLayout* vbox = new QVBoxLayout();
|
||||
vbox->addLayout(hbox_input);
|
||||
vbox->addSpacing(5);
|
||||
vbox->addWidget(m_seg_list);
|
||||
vbox->addSpacing(5);
|
||||
vbox->addLayout(hbox_save_and_edit);
|
||||
|
||||
setLayout(vbox);
|
||||
|
||||
connect(m_seg_list, &QListWidget::currentRowChanged, this, [this, remove_segment_button](int row)
|
||||
{
|
||||
remove_segment_button->setEnabled(row >= 0 && m_seg_list->item(row));
|
||||
});
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
void elf_memory_dumping_dialog::add_new_segment()
|
||||
{
|
||||
QStringList errors;
|
||||
auto interpret = [&](QString text, QString error_field) -> u32
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// Parse expression (or at least used to, was nuked to remove the need for QtJsEngine)
|
||||
const QString fixed_expression = QRegularExpression(QRegularExpression::anchoredPattern("a .*|^[A-Fa-f0-9]+$")).match(text).hasMatch() ? "0x" + text : text;
|
||||
const u32 res = static_cast<u32>(fixed_expression.toULong(&ok, 16));
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
errors << error_field;
|
||||
return umax;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
spu_memory_segment_dump_data data{};
|
||||
data.segment_size = interpret(m_segment_size_input->text(), tr("Segment Size"));
|
||||
data.src_addr = vm::get_super_ptr(interpret(m_ppu_address_input->text(), tr("PPU Address")));
|
||||
data.ls_addr = interpret(m_ls_address_input->text(), tr("LS Address"));
|
||||
data.flags = interpret(m_segment_flags_input->text(), tr("Segment Flags"));
|
||||
|
||||
if (!errors.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed To Add Segment"), tr("Segment parameters are incorrect:\n%1").arg(errors.join('\n')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.segment_size % 4)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment size must be 4 bytes aligned."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.segment_size + data.ls_addr > SPU_LS_SIZE || data.segment_size == 0 || data.segment_size % 4)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment range is invalid."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vm::check_addr(vm::try_get_addr(data.src_addr).first, vm::page_readable, data.segment_size))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed To Add Segment"), tr("PPU address range is not accessible."));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_seg_list->count(); ++i)
|
||||
{
|
||||
ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert<spu_memory_segment_dump_data>());
|
||||
const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value<spu_memory_segment_dump_data>();
|
||||
|
||||
const auto stored_max = seg_stored.src_addr + seg_stored.segment_size;
|
||||
const auto data_max = data.src_addr + data.segment_size;
|
||||
|
||||
if (seg_stored.src_addr < data_max && data.src_addr < stored_max)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment overlaps with previous SPU segment(s)\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 5, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list);
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(data));
|
||||
m_seg_list->setCurrentItem(item);
|
||||
}
|
||||
|
||||
void elf_memory_dumping_dialog::remove_segment()
|
||||
{
|
||||
const int row = m_seg_list->currentRow();
|
||||
if (row >= 0)
|
||||
{
|
||||
QListWidgetItem* item = m_seg_list->takeItem(row);
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void elf_memory_dumping_dialog::save_to_file()
|
||||
{
|
||||
std::vector<spu_memory_segment_dump_data> segs;
|
||||
segs.reserve(m_seg_list->count());
|
||||
|
||||
for (int i = 0; i < m_seg_list->count(); ++i)
|
||||
{
|
||||
ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert<spu_memory_segment_dump_data>());
|
||||
const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value<spu_memory_segment_dump_data>();
|
||||
segs.emplace_back(seg_stored);
|
||||
}
|
||||
|
||||
if (segs.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QString path_last_elf = m_gui_settings->GetValue(gui::fd_save_elf).toString();
|
||||
|
||||
const QString qpath = QFileDialog::getSaveFileName(this, tr("Capture"), path_last_elf, "SPU ELF (*.elf)");
|
||||
const std::string path = qpath.toStdString();
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
const auto result = spu_thread::capture_memory_as_elf({segs.data(), segs.size()}).save();
|
||||
|
||||
if (!result.empty() && fs::write_file(path, fs::rewrite, result))
|
||||
{
|
||||
gui_log.success("Saved ELF at %s", path);
|
||||
m_gui_settings->SetValue(gui::fd_save_elf, qpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Save Failure"), tr("Failed to save SPU ELF."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "gui_settings.h"
|
||||
|
||||
#include <QListWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class elf_memory_dumping_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit elf_memory_dumping_dialog(u32 ppu_debugger_pc, std::shared_ptr<gui_settings> _gui_settings, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void add_new_segment();
|
||||
void remove_segment();
|
||||
void save_to_file();
|
||||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
// UI variables needed in higher scope
|
||||
QListWidget* m_seg_list = nullptr;
|
||||
|
||||
QLineEdit* m_ls_address_input = nullptr;
|
||||
QLineEdit* m_segment_size_input = nullptr;
|
||||
QLineEdit* m_ppu_address_input = nullptr;
|
||||
QLineEdit* m_segment_flags_input = nullptr;
|
||||
};
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include "microphone_creator.h"
|
||||
#include "midi_creator.h"
|
||||
#include "render_creator.h"
|
||||
#include "emu_settings_type.h"
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QStringList>
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
#include <QDateTimeEdit>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
class _base;
|
||||
}
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
class emu_settings : public QObject
|
||||
{
|
||||
/** A settings class for Emulator specific settings. This class is a refactored version of the wx version. It is much nicer
|
||||
*
|
||||
*/
|
||||
Q_OBJECT
|
||||
public:
|
||||
std::set<emu_settings_type> m_broken_types; // list of broken settings
|
||||
|
||||
/** Creates a settings object which reads in the config.yml file at rpcs3/bin/%path%/config.yml
|
||||
* Settings are only written when SaveSettings is called.
|
||||
*/
|
||||
emu_settings();
|
||||
|
||||
bool Init();
|
||||
|
||||
/** Connects a combo box with the target settings type*/
|
||||
void EnhanceComboBox(QComboBox* combobox, emu_settings_type type, bool is_ranged = false, bool use_max = false, int max = 0, bool sorted = false, bool strict = true);
|
||||
|
||||
/** Connects a check box with the target settings type*/
|
||||
void EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type);
|
||||
|
||||
/** Connects a date time edit box with the target settings type*/
|
||||
void EnhanceDateTimeEdit(QDateTimeEdit* date_time_edit, emu_settings_type type, const QString& format, bool use_calendar, bool as_offset_from_now, int offset_update_time = 0);
|
||||
|
||||
/** Connects a slider with the target settings type*/
|
||||
void EnhanceSlider(QSlider* slider, emu_settings_type type);
|
||||
|
||||
/** Connects an integer spin box with the target settings type*/
|
||||
void EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = "");
|
||||
|
||||
/** Connects a double spin box with the target settings type*/
|
||||
void EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = "");
|
||||
|
||||
/** Connects a line edit with the target settings type*/
|
||||
void EnhanceLineEdit(QLineEdit* edit, emu_settings_type type);
|
||||
|
||||
/** Connects a button group with the target settings type*/
|
||||
void EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type);
|
||||
|
||||
std::vector<std::string> GetLibrariesControl();
|
||||
void SaveSelectedLibraries(const std::vector<std::string>& libs);
|
||||
|
||||
/** Returns the valid options for a given setting.*/
|
||||
static std::vector<std::string> GetSettingOptions(emu_settings_type type);
|
||||
|
||||
/** Returns the valid options for a given setting.*/
|
||||
static QStringList GetQStringSettingOptions(emu_settings_type type);
|
||||
|
||||
/** Returns the default value of the setting type.*/
|
||||
std::string GetSettingDefault(emu_settings_type type) const;
|
||||
|
||||
/** Returns the value of the setting type.*/
|
||||
std::string GetSetting(emu_settings_type type) const;
|
||||
|
||||
/** Sets the setting type to a given value.*/
|
||||
void SetSetting(emu_settings_type type, const std::string& val) const;
|
||||
|
||||
/** Try to find the settings type for a given string.*/
|
||||
emu_settings_type FindSettingsType(const cfg::_base* node) const;
|
||||
|
||||
/** Gets all the renderer info for gpu settings.*/
|
||||
render_creator* m_render_creator = nullptr;
|
||||
|
||||
/** Gets a list of all the microphones available.*/
|
||||
microphone_creator m_microphone_creator;
|
||||
|
||||
/** Gets a list of all the midi devices available.*/
|
||||
midi_creator m_midi_creator;
|
||||
|
||||
/** Loads the settings from path.*/
|
||||
void LoadSettings(const std::string& title_id = "", bool create_config_from_global = true);
|
||||
|
||||
/** Fixes all registered invalid settings after asking the user for permission.*/
|
||||
void OpenCorrectionDialog(QWidget* parent = Q_NULLPTR);
|
||||
|
||||
/** Get a localized and therefore freely adjustable version of the string used in config.yml.*/
|
||||
QString GetLocalizedSetting(const QString& original, emu_settings_type type, int index, bool strict) const;
|
||||
|
||||
/** Get a localized and therefore freely adjustable version of the string used in config.yml.*/
|
||||
std::string GetLocalizedSetting(const std::string& original, emu_settings_type type, int index, bool strict) const;
|
||||
|
||||
/** Get a localized and therefore freely adjustable version of the string used in config.yml.*/
|
||||
std::string GetLocalizedSetting(const cfg::_base* node, u32 index) const;
|
||||
|
||||
/** Validates the settings and logs unused entries or cleans up the yaml*/
|
||||
bool ValidateSettings(bool cleanup);
|
||||
|
||||
/** Resets the current settings to the global default. This includes all connected widgets. */
|
||||
void RestoreDefaults();
|
||||
|
||||
Q_SIGNALS:
|
||||
void RestoreDefaultsSignal();
|
||||
|
||||
public Q_SLOTS:
|
||||
/** Writes the unsaved settings to file. Used in settings dialog on accept.*/
|
||||
void SaveSettings() const;
|
||||
|
||||
private:
|
||||
YAML::Node m_default_settings; // The default settings as a YAML node.
|
||||
YAML::Node m_current_settings; // The current settings as a YAML node.
|
||||
std::string m_title_id;
|
||||
};
|
||||
|
|
@ -1,414 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// Node location
|
||||
using cfg_location = std::vector<const char*>;
|
||||
|
||||
enum class emu_settings_type
|
||||
{
|
||||
// Core
|
||||
PPUDecoder,
|
||||
SPUDecoder,
|
||||
HookStaticFuncs,
|
||||
ThreadSchedulerMode,
|
||||
SPULoopDetection,
|
||||
PreferredSPUThreads,
|
||||
PPUDebug,
|
||||
SPUDebug,
|
||||
MFCDebug,
|
||||
MaxLLVMThreads,
|
||||
LLVMPrecompilation,
|
||||
EnableTSX,
|
||||
AccurateSpuDMA,
|
||||
AccurateClineStores,
|
||||
AccurateRSXAccess,
|
||||
FIFOAccuracy,
|
||||
XFloatAccuracy,
|
||||
AccuratePPU128Loop,
|
||||
MFCCommandsShuffling,
|
||||
NumPPUThreads,
|
||||
SetDAZandFTZ,
|
||||
SPUBlockSize,
|
||||
SPUCache,
|
||||
DebugConsoleMode,
|
||||
SilenceAllLogs,
|
||||
SuspendEmulationSavestateMode,
|
||||
CompatibleEmulationSavestateMode,
|
||||
StartSavestatePaused,
|
||||
MaxSPURSThreads,
|
||||
SleepTimersAccuracy,
|
||||
ClocksScale,
|
||||
PerformanceReport,
|
||||
FullWidthAVX512,
|
||||
PPUNJFixup,
|
||||
AccurateDFMA,
|
||||
AccuratePPUSAT,
|
||||
AccuratePPUNJ,
|
||||
FixupPPUVNAN,
|
||||
AccuratePPUVNAN,
|
||||
AccuratePPUFPCC,
|
||||
MaxPreemptCount,
|
||||
SPUProfiler,
|
||||
DisableSpinOptimization,
|
||||
|
||||
// Graphics
|
||||
Renderer,
|
||||
Resolution,
|
||||
AspectRatio,
|
||||
FrameLimit,
|
||||
MSAA,
|
||||
LogShaderPrograms,
|
||||
WriteDepthBuffer,
|
||||
WriteColorBuffers,
|
||||
ReadColorBuffers,
|
||||
ReadDepthBuffer,
|
||||
HandleRSXTiledMemory,
|
||||
VSync,
|
||||
DebugOutput,
|
||||
DebugOverlay,
|
||||
RenderdocCompatibility,
|
||||
GPUTextureScaling,
|
||||
StretchToDisplayArea,
|
||||
VulkanAdapter,
|
||||
ForceHighpZ,
|
||||
StrictRenderingMode,
|
||||
DisableVertexCache,
|
||||
DisableOcclusionQueries,
|
||||
DisableVideoOutput,
|
||||
DisableFIFOReordering,
|
||||
StrictTextureFlushing,
|
||||
ShaderPrecisionQuality,
|
||||
StereoRenderMode,
|
||||
AnisotropicFilterOverride,
|
||||
TextureLodBias,
|
||||
ResolutionScale,
|
||||
MinimumScalableDimension,
|
||||
FsrSharpeningStrength,
|
||||
ExclusiveFullscreenMode,
|
||||
ForceCPUBlitEmulation,
|
||||
DisableOnDiskShaderCache,
|
||||
DisableVulkanMemAllocator,
|
||||
ShaderMode,
|
||||
ShaderCompilerNumThreads,
|
||||
MultithreadedRSX,
|
||||
VBlankRate,
|
||||
VBlankNTSCFixup,
|
||||
RelaxedZCULL,
|
||||
PreciseZCULL,
|
||||
DriverWakeUpDelay,
|
||||
VulkanAsyncTextureUploads,
|
||||
VulkanAsyncSchedulerDriver,
|
||||
AllowHostGPULabels,
|
||||
DisableMSLFastMath,
|
||||
OutputScalingMode,
|
||||
ForceHwMSAAResolve,
|
||||
DisableAsyncHostMM,
|
||||
|
||||
// Performance Overlay
|
||||
PerfOverlayEnabled,
|
||||
PerfOverlayFramerateGraphEnabled,
|
||||
PerfOverlayFrametimeGraphEnabled,
|
||||
PerfOverlayFramerateDatapoints,
|
||||
PerfOverlayFrametimeDatapoints,
|
||||
PerfOverlayDetailLevel,
|
||||
PerfOverlayFramerateDetailLevel,
|
||||
PerfOverlayFrametimeDetailLevel,
|
||||
PerfOverlayPosition,
|
||||
PerfOverlayUpdateInterval,
|
||||
PerfOverlayFontSize,
|
||||
PerfOverlayOpacity,
|
||||
PerfOverlayMarginX,
|
||||
PerfOverlayMarginY,
|
||||
PerfOverlayCenterX,
|
||||
PerfOverlayCenterY,
|
||||
|
||||
// Shader Loading Dialog
|
||||
ShaderLoadBgEnabled,
|
||||
ShaderLoadBgDarkening,
|
||||
ShaderLoadBgBlur,
|
||||
|
||||
// Audio
|
||||
AudioRenderer,
|
||||
DumpToFile,
|
||||
ConvertTo16Bit,
|
||||
AudioFormat,
|
||||
AudioFormats,
|
||||
AudioProvider,
|
||||
AudioAvport,
|
||||
AudioDevice,
|
||||
AudioChannelLayout,
|
||||
MasterVolume,
|
||||
EnableBuffering,
|
||||
AudioBufferDuration,
|
||||
EnableTimeStretching,
|
||||
TimeStretchingThreshold,
|
||||
MicrophoneType,
|
||||
MicrophoneDevices,
|
||||
MusicHandler,
|
||||
|
||||
// Input / Output
|
||||
BackgroundInput,
|
||||
ShowMoveCursor,
|
||||
LockOvlIptToP1,
|
||||
PadHandlerMode,
|
||||
PadConnection,
|
||||
KeyboardHandler,
|
||||
MouseHandler,
|
||||
Camera,
|
||||
CameraType,
|
||||
CameraFlip,
|
||||
CameraID,
|
||||
Move,
|
||||
Buzz,
|
||||
Turntable,
|
||||
GHLtar,
|
||||
MidiDevices,
|
||||
SDLMappings,
|
||||
IoDebugOverlay,
|
||||
|
||||
// Misc
|
||||
ExitRPCS3OnFinish,
|
||||
StartOnBoot,
|
||||
PauseOnFocusLoss,
|
||||
StartGameFullscreen,
|
||||
PreventDisplaySleep,
|
||||
ShowTrophyPopups,
|
||||
ShowRpcnPopups,
|
||||
UseNativeInterface,
|
||||
ShowShaderCompilationHint,
|
||||
ShowPPUCompilationHint,
|
||||
ShowAutosaveAutoloadHint,
|
||||
ShowPressureIntensityToggleHint,
|
||||
ShowAnalogLimiterToggleHint,
|
||||
ShowMouseAndKeyboardToggleHint,
|
||||
WindowTitleFormat,
|
||||
PauseDuringHomeMenu,
|
||||
|
||||
// Network
|
||||
InternetStatus,
|
||||
DNSAddress,
|
||||
IpSwapList,
|
||||
PSNStatus,
|
||||
BindAddress,
|
||||
EnableUpnp,
|
||||
PSNCountry,
|
||||
|
||||
// System
|
||||
LicenseArea,
|
||||
Language,
|
||||
KeyboardType,
|
||||
EnterButtonAssignment,
|
||||
EnableHostRoot,
|
||||
EmptyHdd0Tmp,
|
||||
LimitCacheSize,
|
||||
MaximumCacheSize,
|
||||
ConsoleTimeOffset,
|
||||
};
|
||||
|
||||
/** A helper map that keeps track of where a given setting type is located*/
|
||||
inline static const std::map<emu_settings_type, cfg_location> settings_location =
|
||||
{
|
||||
// Core Tab
|
||||
{emu_settings_type::PPUDecoder, {"Core", "PPU Decoder"}},
|
||||
{emu_settings_type::SPUDecoder, {"Core", "SPU Decoder"}},
|
||||
{emu_settings_type::HookStaticFuncs, {"Core", "Hook static functions"}},
|
||||
{emu_settings_type::ThreadSchedulerMode, {"Core", "Thread Scheduler Mode"}},
|
||||
{emu_settings_type::SPULoopDetection, {"Core", "SPU loop detection"}},
|
||||
{emu_settings_type::PreferredSPUThreads, {"Core", "Preferred SPU Threads"}},
|
||||
{emu_settings_type::PPUDebug, {"Core", "PPU Debug"}},
|
||||
{emu_settings_type::SPUDebug, {"Core", "SPU Debug"}},
|
||||
{emu_settings_type::MFCDebug, {"Core", "MFC Debug"}},
|
||||
{emu_settings_type::MaxLLVMThreads, {"Core", "Max LLVM Compile Threads"}},
|
||||
{emu_settings_type::LLVMPrecompilation, {"Core", "LLVM Precompilation"}},
|
||||
{emu_settings_type::EnableTSX, {"Core", "Enable TSX"}},
|
||||
{emu_settings_type::AccurateSpuDMA, {"Core", "Accurate SPU DMA"}},
|
||||
{emu_settings_type::AccurateClineStores, {"Core", "Accurate Cache Line Stores"}},
|
||||
{emu_settings_type::AccurateRSXAccess, {"Core", "Accurate RSX reservation access"}},
|
||||
{emu_settings_type::FIFOAccuracy, {"Core", "RSX FIFO Accuracy"}},
|
||||
{emu_settings_type::XFloatAccuracy, {"Core", "XFloat Accuracy"}},
|
||||
{emu_settings_type::MFCCommandsShuffling, {"Core", "MFC Commands Shuffling Limit"}},
|
||||
{emu_settings_type::SetDAZandFTZ, {"Core", "Set DAZ and FTZ"}},
|
||||
{emu_settings_type::SPUBlockSize, {"Core", "SPU Block Size"}},
|
||||
{emu_settings_type::SPUCache, {"Core", "SPU Cache"}},
|
||||
{emu_settings_type::DebugConsoleMode, {"Core", "Debug Console Mode"}},
|
||||
{emu_settings_type::MaxSPURSThreads, {"Core", "Max SPURS Threads"}},
|
||||
{emu_settings_type::SleepTimersAccuracy, {"Core", "Sleep Timers Accuracy"}},
|
||||
{emu_settings_type::ClocksScale, {"Core", "Clocks scale"}},
|
||||
{emu_settings_type::AccuratePPU128Loop, {"Core", "Accurate PPU 128-byte Reservation Op Max Length"}},
|
||||
{emu_settings_type::PerformanceReport, {"Core", "Enable Performance Report"}},
|
||||
{emu_settings_type::FullWidthAVX512, {"Core", "Full Width AVX-512"}},
|
||||
{emu_settings_type::NumPPUThreads, {"Core", "PPU Threads"}},
|
||||
{emu_settings_type::PPUNJFixup, {"Core", "PPU LLVM Java Mode Handling"}},
|
||||
{emu_settings_type::AccurateDFMA, {"Core", "Use Accurate DFMA"}},
|
||||
{emu_settings_type::AccuratePPUSAT, {"Core", "PPU Set Saturation Bit"}},
|
||||
{emu_settings_type::AccuratePPUNJ, {"Core", "PPU Accurate Non-Java Mode"}},
|
||||
{emu_settings_type::FixupPPUVNAN, {"Core", "PPU Fixup Vector NaN Values"}},
|
||||
{emu_settings_type::AccuratePPUVNAN, {"Core", "PPU Accurate Vector NaN Values"}},
|
||||
{emu_settings_type::AccuratePPUFPCC, {"Core", "PPU Set FPCC Bits"}},
|
||||
{emu_settings_type::MaxPreemptCount, {"Core", "Max CPU Preempt Count"}},
|
||||
{emu_settings_type::SPUProfiler, {"Core", "SPU Profiler"}},
|
||||
{emu_settings_type::DisableSpinOptimization, {"Core", "Disable SPU GETLLAR Spin Optimization"}},
|
||||
|
||||
// Graphics Tab
|
||||
{emu_settings_type::Renderer, {"Video", "Renderer"}},
|
||||
{emu_settings_type::Resolution, {"Video", "Resolution"}},
|
||||
{emu_settings_type::AspectRatio, {"Video", "Aspect ratio"}},
|
||||
{emu_settings_type::FrameLimit, {"Video", "Frame limit"}},
|
||||
{emu_settings_type::MSAA, {"Video", "MSAA"}},
|
||||
{emu_settings_type::LogShaderPrograms, {"Video", "Log shader programs"}},
|
||||
{emu_settings_type::WriteDepthBuffer, {"Video", "Write Depth Buffer"}},
|
||||
{emu_settings_type::WriteColorBuffers, {"Video", "Write Color Buffers"}},
|
||||
{emu_settings_type::ReadColorBuffers, {"Video", "Read Color Buffers"}},
|
||||
{emu_settings_type::ReadDepthBuffer, {"Video", "Read Depth Buffer"}},
|
||||
{emu_settings_type::HandleRSXTiledMemory, {"Video", "Handle RSX Memory Tiling"}},
|
||||
{emu_settings_type::VSync, {"Video", "VSync"}},
|
||||
{emu_settings_type::DebugOutput, {"Video", "Debug output"}},
|
||||
{emu_settings_type::DebugOverlay, {"Video", "Debug overlay"}},
|
||||
{emu_settings_type::RenderdocCompatibility, {"Video", "Renderdoc Compatibility Mode"}},
|
||||
{emu_settings_type::GPUTextureScaling, {"Video", "Use GPU texture scaling"}},
|
||||
{emu_settings_type::StretchToDisplayArea, {"Video", "Stretch To Display Area"}},
|
||||
{emu_settings_type::ForceHighpZ, {"Video", "Force High Precision Z buffer"}},
|
||||
{emu_settings_type::StrictRenderingMode, {"Video", "Strict Rendering Mode"}},
|
||||
{emu_settings_type::DisableVertexCache, {"Video", "Disable Vertex Cache"}},
|
||||
{emu_settings_type::DisableOcclusionQueries, {"Video", "Disable ZCull Occlusion Queries"}},
|
||||
{emu_settings_type::DisableVideoOutput, {"Video", "Disable Video Output"}},
|
||||
{emu_settings_type::DisableFIFOReordering, {"Video", "Disable FIFO Reordering"}},
|
||||
{emu_settings_type::StereoRenderMode, {"Video", "3D Display Mode"}},
|
||||
{emu_settings_type::StrictTextureFlushing, {"Video", "Strict Texture Flushing"}},
|
||||
{emu_settings_type::ForceCPUBlitEmulation, {"Video", "Force CPU Blit"}},
|
||||
{emu_settings_type::DisableOnDiskShaderCache, {"Video", "Disable On-Disk Shader Cache"}},
|
||||
{emu_settings_type::DisableVulkanMemAllocator, {"Video", "Disable Vulkan Memory Allocator"}},
|
||||
{emu_settings_type::ShaderMode, {"Video", "Shader Mode"}},
|
||||
{emu_settings_type::ShaderCompilerNumThreads, {"Video", "Shader Compiler Threads"}},
|
||||
{emu_settings_type::ShaderPrecisionQuality, {"Video", "Shader Precision"}},
|
||||
{emu_settings_type::MultithreadedRSX, {"Video", "Multithreaded RSX"}},
|
||||
{emu_settings_type::RelaxedZCULL, {"Video", "Relaxed ZCULL Sync"}},
|
||||
{emu_settings_type::PreciseZCULL, {"Video", "Accurate ZCULL stats"}},
|
||||
{emu_settings_type::AnisotropicFilterOverride, {"Video", "Anisotropic Filter Override"}},
|
||||
{emu_settings_type::TextureLodBias, {"Video", "Texture LOD Bias Addend"}},
|
||||
{emu_settings_type::ResolutionScale, {"Video", "Resolution Scale"}},
|
||||
{emu_settings_type::MinimumScalableDimension, {"Video", "Minimum Scalable Dimension"}},
|
||||
{emu_settings_type::VulkanAdapter, {"Video", "Vulkan", "Adapter"}},
|
||||
{emu_settings_type::VBlankRate, {"Video", "Vblank Rate"}},
|
||||
{emu_settings_type::VBlankNTSCFixup, {"Video", "Vblank NTSC Fixup"}},
|
||||
{emu_settings_type::DriverWakeUpDelay, {"Video", "Driver Wake-Up Delay"}},
|
||||
{emu_settings_type::AllowHostGPULabels, {"Video", "Allow Host GPU Labels"}},
|
||||
{emu_settings_type::DisableMSLFastMath, {"Video", "Disable MSL Fast Math"}},
|
||||
{emu_settings_type::OutputScalingMode, {"Video", "Output Scaling Mode"}},
|
||||
{emu_settings_type::ForceHwMSAAResolve, {"Video", "Force Hardware MSAA Resolve"}},
|
||||
{emu_settings_type::DisableAsyncHostMM, {"Video", "Disable Asynchronous Memory Manager"}},
|
||||
|
||||
// Vulkan
|
||||
{emu_settings_type::VulkanAsyncTextureUploads, {"Video", "Vulkan", "Asynchronous Texture Streaming 2"}},
|
||||
{emu_settings_type::VulkanAsyncSchedulerDriver, {"Video", "Vulkan", "Asynchronous Queue Scheduler"}},
|
||||
{emu_settings_type::FsrSharpeningStrength, {"Video", "Vulkan", "FidelityFX CAS Sharpening Intensity"}},
|
||||
{emu_settings_type::ExclusiveFullscreenMode, {"Video", "Vulkan", "Exclusive Fullscreen Mode"}},
|
||||
|
||||
// Performance Overlay
|
||||
{emu_settings_type::PerfOverlayEnabled, {"Video", "Performance Overlay", "Enabled"}},
|
||||
{emu_settings_type::PerfOverlayFramerateGraphEnabled, {"Video", "Performance Overlay", "Enable Framerate Graph"}},
|
||||
{emu_settings_type::PerfOverlayFrametimeGraphEnabled, {"Video", "Performance Overlay", "Enable Frametime Graph"}},
|
||||
{emu_settings_type::PerfOverlayFramerateDatapoints, {"Video", "Performance Overlay", "Framerate datapoints"}},
|
||||
{emu_settings_type::PerfOverlayFrametimeDatapoints, {"Video", "Performance Overlay", "Frametime datapoints"}},
|
||||
{emu_settings_type::PerfOverlayDetailLevel, {"Video", "Performance Overlay", "Detail level"}},
|
||||
{emu_settings_type::PerfOverlayFramerateDetailLevel, {"Video", "Performance Overlay", "Framerate graph detail level"}},
|
||||
{emu_settings_type::PerfOverlayFrametimeDetailLevel, {"Video", "Performance Overlay", "Frametime graph detail level"}},
|
||||
{emu_settings_type::PerfOverlayPosition, {"Video", "Performance Overlay", "Position"}},
|
||||
{emu_settings_type::PerfOverlayUpdateInterval, {"Video", "Performance Overlay", "Metrics update interval (ms)"}},
|
||||
{emu_settings_type::PerfOverlayFontSize, {"Video", "Performance Overlay", "Font size (px)"}},
|
||||
{emu_settings_type::PerfOverlayOpacity, {"Video", "Performance Overlay", "Opacity (%)"}},
|
||||
{emu_settings_type::PerfOverlayMarginX, {"Video", "Performance Overlay", "Horizontal Margin (px)"}},
|
||||
{emu_settings_type::PerfOverlayMarginY, {"Video", "Performance Overlay", "Vertical Margin (px)"}},
|
||||
{emu_settings_type::PerfOverlayCenterX, {"Video", "Performance Overlay", "Center Horizontally"}},
|
||||
{emu_settings_type::PerfOverlayCenterY, {"Video", "Performance Overlay", "Center Vertically"}},
|
||||
|
||||
// Shader Loading Dialog
|
||||
{emu_settings_type::ShaderLoadBgEnabled, {"Video", "Shader Loading Dialog", "Allow custom background"}},
|
||||
{emu_settings_type::ShaderLoadBgDarkening, {"Video", "Shader Loading Dialog", "Darkening effect strength"}},
|
||||
{emu_settings_type::ShaderLoadBgBlur, {"Video", "Shader Loading Dialog", "Blur effect strength"}},
|
||||
|
||||
// Audio
|
||||
{emu_settings_type::AudioRenderer, {"Audio", "Renderer"}},
|
||||
{emu_settings_type::DumpToFile, {"Audio", "Dump to file"}},
|
||||
{emu_settings_type::ConvertTo16Bit, {"Audio", "Convert to 16 bit"}},
|
||||
{emu_settings_type::AudioFormat, {"Audio", "Audio Format"}},
|
||||
{emu_settings_type::AudioFormats, {"Audio", "Audio Formats"}},
|
||||
{emu_settings_type::AudioProvider, {"Audio", "Audio Provider"}},
|
||||
{emu_settings_type::AudioAvport, {"Audio", "RSXAudio Avport"}},
|
||||
{emu_settings_type::AudioDevice, {"Audio", "Audio Device"}},
|
||||
{emu_settings_type::AudioChannelLayout, {"Audio", "Audio Channel Layout"}},
|
||||
{emu_settings_type::MasterVolume, {"Audio", "Master Volume"}},
|
||||
{emu_settings_type::EnableBuffering, {"Audio", "Enable Buffering"}},
|
||||
{emu_settings_type::AudioBufferDuration, {"Audio", "Desired Audio Buffer Duration"}},
|
||||
{emu_settings_type::EnableTimeStretching, {"Audio", "Enable Time Stretching"}},
|
||||
{emu_settings_type::TimeStretchingThreshold, {"Audio", "Time Stretching Threshold"}},
|
||||
{emu_settings_type::MicrophoneType, {"Audio", "Microphone Type"}},
|
||||
{emu_settings_type::MicrophoneDevices, {"Audio", "Microphone Devices"}},
|
||||
{emu_settings_type::MusicHandler, {"Audio", "Music Handler"}},
|
||||
|
||||
// Input / Output
|
||||
{emu_settings_type::BackgroundInput, {"Input/Output", "Background input enabled"}},
|
||||
{emu_settings_type::ShowMoveCursor, {"Input/Output", "Show move cursor"}},
|
||||
{emu_settings_type::LockOvlIptToP1, {"Input/Output", "Lock overlay input to player one"}},
|
||||
{emu_settings_type::PadHandlerMode, {"Input/Output", "Pad handler mode"}},
|
||||
{emu_settings_type::PadConnection, {"Input/Output", "Keep pads connected"}},
|
||||
{emu_settings_type::KeyboardHandler, {"Input/Output", "Keyboard"}},
|
||||
{emu_settings_type::MouseHandler, {"Input/Output", "Mouse"}},
|
||||
{emu_settings_type::Camera, {"Input/Output", "Camera"}},
|
||||
{emu_settings_type::CameraType, {"Input/Output", "Camera type"}},
|
||||
{emu_settings_type::CameraFlip, {"Input/Output", "Camera flip"}},
|
||||
{emu_settings_type::CameraID, {"Input/Output", "Camera ID"}},
|
||||
{emu_settings_type::Move, {"Input/Output", "Move"}},
|
||||
{emu_settings_type::Buzz, {"Input/Output", "Buzz emulated controller"}},
|
||||
{emu_settings_type::Turntable, {"Input/Output", "Turntable emulated controller"}},
|
||||
{emu_settings_type::GHLtar, {"Input/Output", "GHLtar emulated controller"}},
|
||||
{emu_settings_type::MidiDevices, {"Input/Output", "Emulated Midi devices"}},
|
||||
{emu_settings_type::SDLMappings, {"Input/Output", "Load SDL GameController Mappings"}},
|
||||
{emu_settings_type::IoDebugOverlay, {"Input/Output", "IO Debug overlay"}},
|
||||
|
||||
// Misc
|
||||
{emu_settings_type::ExitRPCS3OnFinish, {"Miscellaneous", "Exit RPCS3 when process finishes"}},
|
||||
{emu_settings_type::StartOnBoot, {"Miscellaneous", "Automatically start games after boot"}},
|
||||
{emu_settings_type::PauseOnFocusLoss, {"Miscellaneous", "Pause emulation on RPCS3 focus loss"}},
|
||||
{emu_settings_type::StartGameFullscreen, {"Miscellaneous", "Start games in fullscreen mode"}},
|
||||
{emu_settings_type::PreventDisplaySleep, {"Miscellaneous", "Prevent display sleep while running games"}},
|
||||
{emu_settings_type::ShowTrophyPopups, {"Miscellaneous", "Show trophy popups"}},
|
||||
{emu_settings_type::ShowRpcnPopups, {"Miscellaneous", "Show RPCN popups"}},
|
||||
{emu_settings_type::UseNativeInterface, {"Miscellaneous", "Use native user interface"}},
|
||||
{emu_settings_type::ShowShaderCompilationHint, {"Miscellaneous", "Show shader compilation hint"}},
|
||||
{emu_settings_type::ShowPPUCompilationHint, {"Miscellaneous", "Show PPU compilation hint"}},
|
||||
{emu_settings_type::ShowAutosaveAutoloadHint, {"Miscellaneous", "Show autosave/autoload hint"}},
|
||||
{emu_settings_type::ShowPressureIntensityToggleHint, {"Miscellaneous", "Show pressure intensity toggle hint"}},
|
||||
{emu_settings_type::ShowAnalogLimiterToggleHint, {"Miscellaneous", "Show analog limiter toggle hint"}},
|
||||
{emu_settings_type::ShowMouseAndKeyboardToggleHint, {"Miscellaneous", "Show mouse and keyboard toggle hint"}},
|
||||
{emu_settings_type::SilenceAllLogs, {"Miscellaneous", "Silence All Logs"}},
|
||||
{emu_settings_type::WindowTitleFormat, {"Miscellaneous", "Window Title Format"}},
|
||||
{emu_settings_type::PauseDuringHomeMenu, {"Miscellaneous", "Pause Emulation During Home Menu"}},
|
||||
|
||||
// Networking
|
||||
{emu_settings_type::InternetStatus, {"Net", "Internet enabled"}},
|
||||
{emu_settings_type::DNSAddress, {"Net", "DNS address"}},
|
||||
{emu_settings_type::IpSwapList, {"Net", "IP swap list"}},
|
||||
{emu_settings_type::PSNStatus, {"Net", "PSN status"}},
|
||||
{emu_settings_type::BindAddress, {"Net", "Bind address"}},
|
||||
{emu_settings_type::EnableUpnp, {"Net", "UPNP Enabled"}},
|
||||
{emu_settings_type::PSNCountry, {"Net", "PSN Country"}},
|
||||
|
||||
// System
|
||||
{emu_settings_type::LicenseArea, {"System", "License Area"}},
|
||||
{emu_settings_type::Language, {"System", "Language"}},
|
||||
{emu_settings_type::KeyboardType, {"System", "Keyboard Type"}},
|
||||
{emu_settings_type::EnterButtonAssignment, {"System", "Enter button assignment"}},
|
||||
{emu_settings_type::EnableHostRoot, {"VFS", "Enable /host_root/"}},
|
||||
{emu_settings_type::EmptyHdd0Tmp, {"VFS", "Empty /dev_hdd0/tmp/"}},
|
||||
{emu_settings_type::LimitCacheSize, {"VFS", "Limit disk cache size"}},
|
||||
{emu_settings_type::MaximumCacheSize, {"VFS", "Disk cache maximum size (MB)"}},
|
||||
{emu_settings_type::ConsoleTimeOffset, {"System", "Console time offset (s)"}},
|
||||
|
||||
// Savestates
|
||||
{emu_settings_type::SuspendEmulationSavestateMode, {"Savestate", "Suspend Emulation Savestate Mode"}},
|
||||
{emu_settings_type::CompatibleEmulationSavestateMode, {"Savestate", "Compatible Savestate Mode"}},
|
||||
{emu_settings_type::StartSavestatePaused, {"Savestate", "Start Paused"}},
|
||||
};
|
||||
|
|
@ -1,609 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "emulated_pad_settings_dialog.h"
|
||||
#include "localized_emu.h"
|
||||
#include "Input/raw_mouse_config.h"
|
||||
#include "Emu/Io/mouse_config.h"
|
||||
#include "Emu/Io/buzz_config.h"
|
||||
#include "Emu/Io/gem_config.h"
|
||||
#include "Emu/Io/ghltar_config.h"
|
||||
#include "Emu/Io/guncon3_config.h"
|
||||
#include "Emu/Io/topshotelite_config.h"
|
||||
#include "Emu/Io/topshotfearmaster_config.h"
|
||||
#include "Emu/Io/turntable_config.h"
|
||||
#include "Emu/Io/usio_config.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
enum button_role
|
||||
{
|
||||
button = Qt::UserRole,
|
||||
emulated_button
|
||||
};
|
||||
|
||||
emulated_pad_settings_dialog::emulated_pad_settings_dialog(pad_type type, QWidget* parent)
|
||||
: QDialog(parent), m_type(type)
|
||||
{
|
||||
setObjectName("emulated_pad_settings_dialog");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAttribute(Qt::WA_StyledBackground);
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* v_layout = new QVBoxLayout(this);
|
||||
|
||||
QTabWidget* tabs = new QTabWidget();
|
||||
tabs->setUsesScrollButtons(false);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(this);
|
||||
buttons->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::clicked, this, [this, buttons](QAbstractButton* button)
|
||||
{
|
||||
if (button == buttons->button(QDialogButtonBox::Apply))
|
||||
{
|
||||
save_config();
|
||||
}
|
||||
else if (button == buttons->button(QDialogButtonBox::Save))
|
||||
{
|
||||
save_config();
|
||||
accept();
|
||||
}
|
||||
else if (button == buttons->button(QDialogButtonBox::RestoreDefaults))
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all buttons of all players?")) != QMessageBox::Yes)
|
||||
return;
|
||||
reset_config();
|
||||
}
|
||||
else if (button == buttons->button(QDialogButtonBox::Cancel))
|
||||
{
|
||||
// Restore config
|
||||
load_config();
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
load_config();
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case emulated_pad_settings_dialog::pad_type::buzz:
|
||||
setWindowTitle(tr("Configure Emulated Buzz"));
|
||||
add_tabs<buzz_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::turntable:
|
||||
setWindowTitle(tr("Configure Emulated Turntable"));
|
||||
add_tabs<turntable_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ghltar:
|
||||
setWindowTitle(tr("Configure Emulated GHLtar"));
|
||||
add_tabs<ghltar_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::usio:
|
||||
setWindowTitle(tr("Configure Emulated USIO"));
|
||||
add_tabs<usio_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::gem:
|
||||
setWindowTitle(tr("Configure Emulated PS Move (Real)"));
|
||||
add_tabs<gem_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ds3gem:
|
||||
setWindowTitle(tr("Configure Emulated PS Move (Fake)"));
|
||||
add_tabs<gem_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::mousegem:
|
||||
setWindowTitle(tr("Configure Emulated PS Move (Mouse)"));
|
||||
add_tabs<gem_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::guncon3:
|
||||
setWindowTitle(tr("Configure Emulated GunCon 3"));
|
||||
add_tabs<guncon3_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotelite:
|
||||
setWindowTitle(tr("Configure Emulated Top Shot Elite"));
|
||||
add_tabs<topshotelite_btn>(tabs);
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotfearmaster:
|
||||
setWindowTitle(tr("Configure Emulated Top Shot Fearmaster"));
|
||||
add_tabs<topshotfearmaster_btn>(tabs);
|
||||
break;
|
||||
}
|
||||
|
||||
v_layout->addWidget(tabs);
|
||||
v_layout->addWidget(buttons);
|
||||
setLayout(v_layout);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs)
|
||||
{
|
||||
ensure(!!tabs);
|
||||
|
||||
std::set<int> ignored_values;
|
||||
|
||||
const auto remove_value = [&ignored_values](int value)
|
||||
{
|
||||
ignored_values.insert(static_cast<int>(value));
|
||||
};
|
||||
|
||||
usz players = 0;
|
||||
switch (m_type)
|
||||
{
|
||||
case pad_type::buzz:
|
||||
players = g_cfg_buzz.players.size();
|
||||
break;
|
||||
case pad_type::turntable:
|
||||
players = g_cfg_turntable.players.size();
|
||||
break;
|
||||
case pad_type::ghltar:
|
||||
players = g_cfg_ghltar.players.size();
|
||||
break;
|
||||
case pad_type::usio:
|
||||
players = g_cfg_usio.players.size();
|
||||
break;
|
||||
case pad_type::gem:
|
||||
players = g_cfg_gem_real.players.size();
|
||||
|
||||
// Ignore combo, x and y axis
|
||||
remove_value(static_cast<int>(gem_btn::x_axis));
|
||||
remove_value(static_cast<int>(gem_btn::y_axis));
|
||||
for (int i = static_cast<int>(gem_btn::combo_begin); i <= static_cast<int>(gem_btn::combo_end); i++)
|
||||
{
|
||||
remove_value(i);
|
||||
}
|
||||
break;
|
||||
case pad_type::ds3gem:
|
||||
players = g_cfg_gem_fake.players.size();
|
||||
|
||||
// Ignore combo
|
||||
for (int i = static_cast<int>(gem_btn::combo_begin); i <= static_cast<int>(gem_btn::combo_end); i++)
|
||||
{
|
||||
remove_value(i);
|
||||
}
|
||||
break;
|
||||
case pad_type::mousegem:
|
||||
players = g_cfg_gem_mouse.players.size();
|
||||
|
||||
// Ignore x and y axis
|
||||
remove_value(static_cast<int>(gem_btn::x_axis));
|
||||
remove_value(static_cast<int>(gem_btn::y_axis));
|
||||
break;
|
||||
case pad_type::guncon3:
|
||||
players = g_cfg_guncon3.players.size();
|
||||
break;
|
||||
case pad_type::topshotelite:
|
||||
players = g_cfg_topshotelite.players.size();
|
||||
break;
|
||||
case pad_type::topshotfearmaster:
|
||||
players = g_cfg_topshotfearmaster.players.size();
|
||||
break;
|
||||
}
|
||||
|
||||
constexpr u32 max_items_per_column = 6;
|
||||
const int count = static_cast<int>(T::count) - static_cast<int>(ignored_values.size());
|
||||
int rows = count;
|
||||
|
||||
for (u32 cols = 1; utils::aligned_div(static_cast<u32>(count), cols) > max_items_per_column;)
|
||||
{
|
||||
rows = utils::aligned_div(static_cast<u32>(count), ++cols);
|
||||
}
|
||||
|
||||
m_combos.resize(players);
|
||||
|
||||
const bool show_mouse_legend = m_type == pad_type::mousegem;
|
||||
|
||||
if (show_mouse_legend)
|
||||
{
|
||||
if (!g_cfg_mouse.load())
|
||||
{
|
||||
cfg_log.notice("Could not restore mouse config. Using defaults.");
|
||||
}
|
||||
|
||||
if (!g_cfg_raw_mouse.load())
|
||||
{
|
||||
cfg_log.notice("Could not restore raw mouse config. Using defaults.");
|
||||
}
|
||||
}
|
||||
|
||||
for (usz player = 0; player < players; player++)
|
||||
{
|
||||
// Create grid with all buttons
|
||||
QGridLayout* grid_layout = new QGridLayout(this);
|
||||
|
||||
for (int i = 0, row = 0, col = 0; i < static_cast<int>(T::count); i++)
|
||||
{
|
||||
if (ignored_values.contains(i))
|
||||
continue;
|
||||
|
||||
const T id = static_cast<T>(i);
|
||||
const QString name = QString::fromStdString(fmt::format("%s", id));
|
||||
|
||||
QHBoxLayout* h_layout = new QHBoxLayout(this);
|
||||
QGroupBox* gb = new QGroupBox(name, this);
|
||||
QComboBox* combo = new QComboBox;
|
||||
|
||||
if constexpr (std::is_same_v<T, gem_btn>)
|
||||
{
|
||||
const gem_btn btn = static_cast<gem_btn>(i);
|
||||
if (btn >= gem_btn::combo_begin && btn <= gem_btn::combo_end)
|
||||
{
|
||||
gb->setToolTip(tr("Press the \"Combo\" button in combination with any of the other combo buttons to trigger their related PS Move button.\n"
|
||||
"This can be useful if your device does not have enough regular buttons."));
|
||||
}
|
||||
}
|
||||
|
||||
// Add empty value
|
||||
combo->addItem("");
|
||||
const int index = combo->findText("");
|
||||
combo->setItemData(index, static_cast<int>(pad_button::pad_button_max_enum), button_role::button);
|
||||
combo->setItemData(index, i, button_role::emulated_button);
|
||||
|
||||
if (m_type != pad_type::mousegem)
|
||||
{
|
||||
for (int p = 0; p < static_cast<int>(pad_button::pad_button_max_enum); p++)
|
||||
{
|
||||
const QString translated = localized_emu::translated_pad_button(static_cast<pad_button>(p));
|
||||
combo->addItem(translated);
|
||||
const int index = combo->findText(translated);
|
||||
combo->setItemData(index, p, button_role::button);
|
||||
combo->setItemData(index, i, button_role::emulated_button);
|
||||
}
|
||||
}
|
||||
|
||||
if (std::is_same_v<T, guncon3_btn> || std::is_same_v<T, topshotelite_btn> || std::is_same_v<T, topshotfearmaster_btn> || m_type == pad_type::mousegem)
|
||||
{
|
||||
for (int p = static_cast<int>(pad_button::mouse_button_1); p <= static_cast<int>(pad_button::mouse_button_8); p++)
|
||||
{
|
||||
const QString translated = localized_emu::translated_pad_button(static_cast<pad_button>(p));
|
||||
combo->addItem(translated);
|
||||
const int index = combo->findText(translated);
|
||||
combo->setItemData(index, p, button_role::button);
|
||||
combo->setItemData(index, i, button_role::emulated_button);
|
||||
}
|
||||
}
|
||||
|
||||
pad_button saved_btn_id = pad_button::pad_button_max_enum;
|
||||
switch (m_type)
|
||||
{
|
||||
case pad_type::buzz:
|
||||
saved_btn_id = ::at32(g_cfg_buzz.players, player)->get_pad_button(static_cast<buzz_btn>(id));
|
||||
break;
|
||||
case pad_type::turntable:
|
||||
saved_btn_id = ::at32(g_cfg_turntable.players, player)->get_pad_button(static_cast<turntable_btn>(id));
|
||||
break;
|
||||
case pad_type::ghltar:
|
||||
saved_btn_id = ::at32(g_cfg_ghltar.players, player)->get_pad_button(static_cast<ghltar_btn>(id));
|
||||
break;
|
||||
case pad_type::usio:
|
||||
saved_btn_id = ::at32(g_cfg_usio.players, player)->get_pad_button(static_cast<usio_btn>(id));
|
||||
break;
|
||||
case pad_type::gem:
|
||||
saved_btn_id = ::at32(g_cfg_gem_real.players, player)->get_pad_button(static_cast<gem_btn>(id));
|
||||
break;
|
||||
case pad_type::ds3gem:
|
||||
saved_btn_id = ::at32(g_cfg_gem_fake.players, player)->get_pad_button(static_cast<gem_btn>(id));
|
||||
break;
|
||||
case pad_type::mousegem:
|
||||
saved_btn_id = ::at32(g_cfg_gem_mouse.players, player)->get_pad_button(static_cast<gem_btn>(id));
|
||||
break;
|
||||
case pad_type::guncon3:
|
||||
saved_btn_id = ::at32(g_cfg_guncon3.players, player)->get_pad_button(static_cast<guncon3_btn>(id));
|
||||
break;
|
||||
case pad_type::topshotelite:
|
||||
saved_btn_id = ::at32(g_cfg_topshotelite.players, player)->get_pad_button(static_cast<topshotelite_btn>(id));
|
||||
break;
|
||||
case pad_type::topshotfearmaster:
|
||||
saved_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->get_pad_button(static_cast<topshotfearmaster_btn>(id));
|
||||
break;
|
||||
}
|
||||
|
||||
combo->setCurrentIndex(combo->findData(static_cast<int>(saved_btn_id)));
|
||||
|
||||
connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this, player, id, combo](int index)
|
||||
{
|
||||
if (index < 0 || !combo)
|
||||
return;
|
||||
|
||||
const QVariant data = combo->itemData(index, button_role::button);
|
||||
if (!data.isValid() || !data.canConvert<int>())
|
||||
return;
|
||||
|
||||
const pad_button btn_id = static_cast<pad_button>(data.toInt());
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case pad_type::buzz:
|
||||
::at32(g_cfg_buzz.players, player)->set_button(static_cast<buzz_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::turntable:
|
||||
::at32(g_cfg_turntable.players, player)->set_button(static_cast<turntable_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::ghltar:
|
||||
::at32(g_cfg_ghltar.players, player)->set_button(static_cast<ghltar_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::usio:
|
||||
::at32(g_cfg_usio.players, player)->set_button(static_cast<usio_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::gem:
|
||||
::at32(g_cfg_gem_real.players, player)->set_button(static_cast<gem_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::ds3gem:
|
||||
::at32(g_cfg_gem_fake.players, player)->set_button(static_cast<gem_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::mousegem:
|
||||
::at32(g_cfg_gem_mouse.players, player)->set_button(static_cast<gem_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::guncon3:
|
||||
::at32(g_cfg_guncon3.players, player)->set_button(static_cast<guncon3_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::topshotelite:
|
||||
::at32(g_cfg_topshotelite.players, player)->set_button(static_cast<topshotelite_btn>(id), btn_id);
|
||||
break;
|
||||
case pad_type::topshotfearmaster:
|
||||
::at32(g_cfg_topshotfearmaster.players, player)->set_button(static_cast<topshotfearmaster_btn>(id), btn_id);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (row >= rows)
|
||||
{
|
||||
row = 0;
|
||||
col++;
|
||||
}
|
||||
|
||||
::at32(m_combos, player).push_back(combo);
|
||||
h_layout->addWidget(combo);
|
||||
gb->setLayout(h_layout);
|
||||
grid_layout->addWidget(gb, row, col);
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
QVBoxLayout* v_layout = new QVBoxLayout(this);
|
||||
|
||||
// Create a legend of the current mouse settings
|
||||
if (show_mouse_legend)
|
||||
{
|
||||
QHBoxLayout* legend_layout = new QHBoxLayout(this);
|
||||
if (player == 0)
|
||||
{
|
||||
std::string basic_mouse_settings;
|
||||
fmt::append(basic_mouse_settings, "1: %s\n", g_cfg_mouse.mouse_button_1.to_string());
|
||||
fmt::append(basic_mouse_settings, "2: %s\n", g_cfg_mouse.mouse_button_2.to_string());
|
||||
fmt::append(basic_mouse_settings, "3: %s\n", g_cfg_mouse.mouse_button_3.to_string());
|
||||
fmt::append(basic_mouse_settings, "4: %s\n", g_cfg_mouse.mouse_button_4.to_string());
|
||||
fmt::append(basic_mouse_settings, "5: %s\n", g_cfg_mouse.mouse_button_5.to_string());
|
||||
fmt::append(basic_mouse_settings, "6: %s\n", g_cfg_mouse.mouse_button_6.to_string());
|
||||
fmt::append(basic_mouse_settings, "7: %s\n", g_cfg_mouse.mouse_button_7.to_string());
|
||||
fmt::append(basic_mouse_settings, "8: %s", g_cfg_mouse.mouse_button_8.to_string());
|
||||
|
||||
QGroupBox* gb_legend_basic = new QGroupBox(tr("Current Basic Mouse Config"), this);
|
||||
QVBoxLayout* gb_legend_basic_layout = new QVBoxLayout(this);
|
||||
gb_legend_basic_layout->addWidget(new QLabel(QString::fromStdString(basic_mouse_settings), this));
|
||||
gb_legend_basic->setLayout(gb_legend_basic_layout);
|
||||
legend_layout->addWidget(gb_legend_basic);
|
||||
}
|
||||
{
|
||||
std::string raw_mouse_settings;
|
||||
const auto& raw_cfg = *ensure(::at32(g_cfg_raw_mouse.players, player));
|
||||
fmt::append(raw_mouse_settings, "1: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_1.to_string()));
|
||||
fmt::append(raw_mouse_settings, "2: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_2.to_string()));
|
||||
fmt::append(raw_mouse_settings, "3: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_3.to_string()));
|
||||
fmt::append(raw_mouse_settings, "4: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_4.to_string()));
|
||||
fmt::append(raw_mouse_settings, "5: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_5.to_string()));
|
||||
fmt::append(raw_mouse_settings, "6: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_6.to_string()));
|
||||
fmt::append(raw_mouse_settings, "7: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_7.to_string()));
|
||||
fmt::append(raw_mouse_settings, "8: %s", raw_mouse_config::get_button_name(raw_cfg.mouse_button_8.to_string()));
|
||||
|
||||
QGroupBox* gb_legend_raw = new QGroupBox(tr("Current Raw Mouse Config"), this);
|
||||
QVBoxLayout* gb_legend_raw_layout = new QVBoxLayout(this);
|
||||
gb_legend_raw_layout->addWidget(new QLabel(QString::fromStdString(raw_mouse_settings), this));
|
||||
gb_legend_raw->setLayout(gb_legend_raw_layout);
|
||||
legend_layout->addWidget(gb_legend_raw);
|
||||
}
|
||||
v_layout->addLayout(legend_layout);
|
||||
}
|
||||
|
||||
v_layout->addLayout(grid_layout);
|
||||
|
||||
QWidget* widget = new QWidget(this);
|
||||
widget->setLayout(v_layout);
|
||||
|
||||
tabs->addTab(widget, tr("Player %0").arg(player + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void emulated_pad_settings_dialog::load_config()
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case emulated_pad_settings_dialog::pad_type::buzz:
|
||||
if (!g_cfg_buzz.load())
|
||||
{
|
||||
cfg_log.notice("Could not load buzz config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::turntable:
|
||||
if (!g_cfg_turntable.load())
|
||||
{
|
||||
cfg_log.notice("Could not load turntable config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ghltar:
|
||||
if (!g_cfg_ghltar.load())
|
||||
{
|
||||
cfg_log.notice("Could not load ghltar config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::usio:
|
||||
if (!g_cfg_usio.load())
|
||||
{
|
||||
cfg_log.notice("Could not load usio config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::gem:
|
||||
if (!g_cfg_gem_real.load())
|
||||
{
|
||||
cfg_log.notice("Could not load gem config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ds3gem:
|
||||
if (!g_cfg_gem_fake.load())
|
||||
{
|
||||
cfg_log.notice("Could not load fake gem config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::mousegem:
|
||||
if (!g_cfg_gem_mouse.load())
|
||||
{
|
||||
cfg_log.notice("Could not load mouse gem config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::guncon3:
|
||||
if (!g_cfg_guncon3.load())
|
||||
{
|
||||
cfg_log.notice("Could not load guncon3 config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotelite:
|
||||
if (!g_cfg_topshotelite.load())
|
||||
{
|
||||
cfg_log.notice("Could not load topshotelite config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotfearmaster:
|
||||
if (!g_cfg_topshotfearmaster.load())
|
||||
{
|
||||
cfg_log.notice("Could not load topshotfearmaster config. Using defaults.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void emulated_pad_settings_dialog::save_config()
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case emulated_pad_settings_dialog::pad_type::buzz:
|
||||
g_cfg_buzz.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::turntable:
|
||||
g_cfg_turntable.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ghltar:
|
||||
g_cfg_ghltar.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::usio:
|
||||
g_cfg_usio.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::gem:
|
||||
g_cfg_gem_real.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ds3gem:
|
||||
g_cfg_gem_fake.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::mousegem:
|
||||
g_cfg_gem_mouse.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::guncon3:
|
||||
g_cfg_guncon3.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotelite:
|
||||
g_cfg_topshotelite.save();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotfearmaster:
|
||||
g_cfg_topshotfearmaster.save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void emulated_pad_settings_dialog::reset_config()
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case emulated_pad_settings_dialog::pad_type::buzz:
|
||||
g_cfg_buzz.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::turntable:
|
||||
g_cfg_turntable.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ghltar:
|
||||
g_cfg_ghltar.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::usio:
|
||||
g_cfg_usio.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::gem:
|
||||
g_cfg_gem_real.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::ds3gem:
|
||||
g_cfg_gem_fake.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::mousegem:
|
||||
g_cfg_gem_mouse.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::guncon3:
|
||||
g_cfg_guncon3.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotelite:
|
||||
g_cfg_topshotelite.from_default();
|
||||
break;
|
||||
case emulated_pad_settings_dialog::pad_type::topshotfearmaster:
|
||||
g_cfg_topshotfearmaster.from_default();
|
||||
break;
|
||||
}
|
||||
|
||||
for (usz player = 0; player < m_combos.size(); player++)
|
||||
{
|
||||
for (QComboBox* combo : m_combos.at(player))
|
||||
{
|
||||
if (!combo)
|
||||
continue;
|
||||
|
||||
const QVariant data = combo->itemData(0, button_role::emulated_button);
|
||||
if (!data.isValid() || !data.canConvert<int>())
|
||||
continue;
|
||||
|
||||
pad_button def_btn_id = pad_button::pad_button_max_enum;
|
||||
switch (m_type)
|
||||
{
|
||||
case pad_type::buzz:
|
||||
def_btn_id = ::at32(g_cfg_buzz.players, player)->default_pad_button(static_cast<buzz_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::turntable:
|
||||
def_btn_id = ::at32(g_cfg_turntable.players, player)->default_pad_button(static_cast<turntable_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::ghltar:
|
||||
def_btn_id = ::at32(g_cfg_ghltar.players, player)->default_pad_button(static_cast<ghltar_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::usio:
|
||||
def_btn_id = ::at32(g_cfg_usio.players, player)->default_pad_button(static_cast<usio_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::gem:
|
||||
def_btn_id = ::at32(g_cfg_gem_real.players, player)->default_pad_button(static_cast<gem_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::ds3gem:
|
||||
def_btn_id = ::at32(g_cfg_gem_fake.players, player)->default_pad_button(static_cast<gem_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::mousegem:
|
||||
def_btn_id = ::at32(g_cfg_gem_mouse.players, player)->default_pad_button(static_cast<gem_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::guncon3:
|
||||
def_btn_id = ::at32(g_cfg_guncon3.players, player)->default_pad_button(static_cast<guncon3_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::topshotelite:
|
||||
def_btn_id = ::at32(g_cfg_topshotelite.players, player)->default_pad_button(static_cast<topshotelite_btn>(data.toInt()));
|
||||
break;
|
||||
case pad_type::topshotfearmaster:
|
||||
def_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->default_pad_button(static_cast<topshotfearmaster_btn>(data.toInt()));
|
||||
break;
|
||||
}
|
||||
|
||||
combo->setCurrentIndex(combo->findData(static_cast<int>(def_btn_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QTabWidget>
|
||||
|
||||
#include <vector>
|
||||
|
||||
class emulated_pad_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class pad_type
|
||||
{
|
||||
buzz,
|
||||
turntable,
|
||||
ghltar,
|
||||
usio,
|
||||
gem,
|
||||
ds3gem,
|
||||
mousegem,
|
||||
guncon3,
|
||||
topshotelite,
|
||||
topshotfearmaster,
|
||||
};
|
||||
|
||||
emulated_pad_settings_dialog(pad_type type, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void add_tabs(QTabWidget* tabs);
|
||||
|
||||
void load_config();
|
||||
void save_config();
|
||||
void reset_config();
|
||||
|
||||
pad_type m_type;
|
||||
|
||||
std::vector<std::vector<QComboBox*>> m_combos;
|
||||
};
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#include "fatal_error_dialog.h"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QTextDocument>
|
||||
#include <QIcon>
|
||||
|
||||
const QString document_with_help_text = R"(
|
||||
<style>
|
||||
p {white-space: nowrap;}
|
||||
</style>
|
||||
<p>
|
||||
%1<br>
|
||||
%2<br>
|
||||
<a href='https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support'>https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support</a><br>
|
||||
%3<br>
|
||||
</p>
|
||||
)";
|
||||
|
||||
const QString document_without_help_text = R"(
|
||||
<style>
|
||||
p {white-space: nowrap;}
|
||||
</style>
|
||||
<p>
|
||||
%1<br>
|
||||
</p>
|
||||
)";
|
||||
|
||||
fatal_error_dialog::fatal_error_dialog(std::string_view text, bool is_html, bool include_help_text) : QMessageBox()
|
||||
{
|
||||
const QString qstr = QString::fromUtf8(text.data(), text.size());
|
||||
const QString msg = is_html ? qstr : Qt::convertFromPlainText(qstr);
|
||||
|
||||
QString document_body;
|
||||
if (include_help_text) [[likely]]
|
||||
{
|
||||
document_body = document_with_help_text
|
||||
.arg(msg)
|
||||
.arg(tr("HOW TO REPORT ERRORS:"))
|
||||
.arg(tr("Please, don't send incorrect reports. Thanks for understanding."));
|
||||
}
|
||||
else
|
||||
{
|
||||
document_body = document_without_help_text.arg(msg);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
setWindowIcon(QIcon(":/rpcs3.ico"));
|
||||
#endif
|
||||
setWindowTitle(tr("RPCS3: Fatal Error"));
|
||||
setIcon(QMessageBox::Icon::Critical);
|
||||
setTextFormat(Qt::TextFormat::RichText);
|
||||
setText(document_body);
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||