Compare commits

...

74 Commits

Author SHA1 Message Date
chaoticgd
52f03c900f Debugger: Improve logic for applying custom font sizes 2025-09-25 18:26:20 -04:00
lightningterror
74be344ce6 GS/Debug: Add log for CopyRect if rect is empty. 2025-09-26 00:10:19 +02:00
Ty
14574ef4eb CI: Update Flatpak builder action version 2025-09-25 18:09:29 -04:00
TheTechnician27
fe565afff0 GameList: Prioritize file title over serial for covers 2025-09-25 18:08:50 -04:00
TellowKrinkle
5fcd0f94c2 GS:Capture: Allow selecting hardware device 2025-09-25 18:07:37 -04:00
TheTechnician27
e22609ea29 QtHost: Fix verbose status toggle 2025-09-25 18:00:22 -04:00
chaoticgd
1d068ffde9 Patch: Clear R5900 recompiler cache after updating dynamic patches 2025-09-25 11:08:01 -04:00
TJnotJT
fe133b3c0c GS: Remove unused PRMODE register from dump. 2025-09-25 03:55:06 +02:00
TJnotJT
eb42ce3343 GS: Add prim class name in vertex dump for better readability. 2025-09-25 03:55:06 +02:00
TJnotJT
c50c24e3c9 GS: Adjust spacing in vertex dump for better readability.
Space between each primitive in the vertex list.

Extra space for negative X, Y, U, V coordinates.

Avoid double blank lines after vertex list.
2025-09-25 03:55:06 +02:00
TJnotJT
ab85d759b0 GS: Add fog coefficient to the vertex dump. 2025-09-25 03:55:06 +02:00
lightningterror
2ef2adf517 GS/HW: Abort copy if rect is empty. 2025-09-25 03:42:25 +02:00
chaoticgd
c13e23ab68 Debugger: Fix some use-after-free bugs in the disassembly view 2025-09-25 03:16:49 +02:00
chaoticgd
ec57e9c178 Debugger: Fix some user-after-free race conditions in the memory view 2025-09-25 03:16:49 +02:00
chaoticgd
b5a2fe3223 Debugger: Fix a dormant use-after-free bug in the breakpoint model 2025-09-25 03:16:49 +02:00
chaoticgd
88d378d293 Qt: Fix some possible use-after-free bugs related to input recording 2025-09-25 03:16:49 +02:00
chaoticgd
c79e6ecc2c Qt: Fix use-after-free race condition in achievement login dialog 2025-09-25 03:16:49 +02:00
PCSX2 Bot
a46ee17537 [ci skip] Qt: Update Base Translation. 2025-09-25 03:16:31 +02:00
JordanTheToaster
b6dbffa251 3rdparty: Update ImGui to 1.92.3 2025-09-25 03:14:19 +02:00
JordanTheToaster
4af1f7846d Deps: Update libjpegturbo to 3.1.2 2025-09-25 03:14:19 +02:00
JordanTheToaster
f5e706f753 Deps: Update plutovg to 1.3.1 2025-09-25 03:14:19 +02:00
JordanTheToaster
2dcf602666 Deps: Update FreeType to 2.14.1 2025-09-25 03:14:19 +02:00
JordanTheToaster
42be070727 Deps: Update harfbuzz to 11.5.0 2025-09-25 03:14:19 +02:00
TheTechnician27
98ded09177 ImGui: Add contingent for empty binding in save state select menu 2025-09-24 15:30:05 +02:00
JordanTheToaster
24cb93d7c5 3rdparty: Update rcheevos to commit 0a7508d 2025-09-24 15:19:10 +02:00
Vermaaaaaa
535ebdbc8c Qt/FSUI: Added FR #13128 Show Titles Option 2025-09-24 15:18:06 +02:00
TJnotJT
42a6076967 GS: Ignore output on RC1/2 when the data is not used. 2025-09-24 15:12:22 +02:00
pgert
fb27549bed GameDB: Correct Pro Evolution Soccer 2014 region.
SLES-55675 has Greek (and Italian) in menu, not German (and Italian).
2025-09-24 15:11:24 +02:00
TJnotJT
6720c9ef83 GS/SW: Misc code cleanup. 2025-09-23 15:22:07 +02:00
PCSX2 Bot
b7c135586e [ci skip] Qt: Update Base Translation. 2025-09-21 20:08:05 -04:00
KamFretoZ
f47b55ce42 Qt: Cleanup leftover menu 2025-09-21 18:09:28 -04:00
KamFretoZ
a635796b12 Qt: Add option to fill custom background 2025-09-21 18:09:28 -04:00
KamFretoZ
fbbd11bc18 Qt: Move the settings to the interface section 2025-09-21 18:09:28 -04:00
KamFretoZ
4134dd015d Qt: Support APNG backgrounds
Co-Authored-By: TheLastRar <TheLastRar@users.noreply.github.com>
2025-09-21 18:09:28 -04:00
KamFretoZ
b0dedcc590 Deps: Add additional libwebp libraries
Needed for animated WEBPs to work on Windows.

Co-Authored-By: TheLastRar <TheLastRar@users.noreply.github.com>
2025-09-21 18:09:28 -04:00
KamFretoZ
f5ce81d72c Qt: Add Custom background support
Qt: Make sure custom background aren't active when game list isn't shown

To save on CPU Power and be more efficient

Co-Authored-By: TheLastRar <TheLastRar@users.noreply.github.com>
2025-09-21 18:09:28 -04:00
RedPanda4552
9810d2923c Pad: De-duplicate analog light/lock attributes, add public getters 2025-09-21 16:47:40 -04:00
PCSX2 Bot
92ede270ce [ci skip] Qt: Update Base Translation. 2025-09-20 02:32:20 +02:00
lightningterror
427096dc29 GS/HW: Adjust texture copies perfmon info for CopyRect.
If we are optimizing out copies we don't need to increment the perf monitor info.

Also remove duplicate tex copies perf monitor info in tc as we already add it in CopyRect function.
2025-09-20 02:27:50 +02:00
lightningterror
a7f948c00f GS: Backport and unify CopyRect optimizations to avoid redundant copies.
Moved the shared code in GSDevice where it can be shared between renderers.

Backported optimizations to DX11/GL/Metal:
Source is cleared, destination is a render target, and it's a full copy
we can clear it instead.

Source is cleared, destination is a render target, destination is cleared,
if it's the same colour and rect, we can just avoid this entirely.
2025-09-20 02:27:50 +02:00
TellowKrinkle
4afa5b8409 GS:SW: Use accurate fog equation 2025-09-19 23:56:03 +02:00
JordanTheToaster
7f285b2164 VMManager: Warn when integer scaling is enabled 2025-09-19 23:52:17 +02:00
JordanTheToaster
9dac7825d7 VMManager: Warn when disabling dithering 2025-09-19 23:52:17 +02:00
JordanTheToaster
305d0e81d6 Qt/FSUI: Adjust renderer nomenclature 2025-09-19 23:52:17 +02:00
JordanTheToaster
56f3d6ea09 VMManager: Warn when using Force 32bit dithering 2025-09-19 23:52:17 +02:00
PCSX2 Bot
edc04fb8e3 [ci skip] Qt: Update Base Translation. 2025-09-17 20:08:33 -04:00
JordanTheToaster
a72f78fd79 Config: Increase space in GS config struct 2025-09-17 14:43:46 +02:00
PCSX2 Bot
aadd0fd65e [ci skip] Qt: Update Base Translation. 2025-09-17 09:40:02 +02:00
SternXD
e2bc80a96f FSUI: Fix regression allowing Save State load in Hardcore Mode via ESC menu 2025-09-16 09:43:39 -04:00
TJnotJT
704e531c1f GS: Make sure transfers are dumped between consecutive vsyncs. 2025-09-16 14:02:10 +02:00
TJnotJT
203981182d GS: Make privileged registers dumping YAML. 2025-09-16 14:02:10 +02:00
TJnotJT
14b67e3ac3 GS: Make context dump YAML. Add comments to output. 2025-09-16 14:02:10 +02:00
TJnotJT
f83e11892b GS: Add dumping of transfer bitmaps. 2025-09-16 14:02:10 +02:00
TJnotJT
a8c549baee GS: Refactor duplicated debugging code in HW/SW renderer.
Specifically, code for dumping the draw information (vertices, registers, transfers).
2025-09-16 14:02:10 +02:00
TJnotJT
13142dd31d GS: Add memory transfers dumping as a text file.
In addition to vertices and registers, dumps the memory transfers that occurred just before the draw. Helps with debugging.
2025-09-16 14:02:10 +02:00
TJnotJT
ed5c364603 GS: YAML dumping of vertices. 2025-09-16 14:02:10 +02:00
PCSX2 Bot
0ccf7d2e10 [ci skip] Qt: Update Base Translation. 2025-09-15 18:22:33 +02:00
PCSX2 Bot
ebb0dc7cc5 [ci skip] PAD: Update to latest controller database. 2025-09-15 18:22:20 +02:00
TheTechnician27
52ebebc739 Toolbar: Add 'Hotkeys' action 2025-09-14 13:28:14 -04:00
dependabot[bot]
c8141261f2 actions: Bump the ci-deps group with 3 updates (#13244)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-14 12:15:08 -04:00
KamFretoZ
9c7750b85d OSD: Add Accessories type indicator to USB Input OSD 2025-09-14 12:10:19 -04:00
chaoticgd
bf10b55aa1 PINE: Use the correct naming convention for static globals 2025-09-13 14:45:10 -04:00
chaoticgd
43d9ea99b0 PINE: Don't crash with SIGPIPE if the client dies while writing 2025-09-13 14:45:10 -04:00
Ziemas
37f28e95b6 Core: Calculate EE/IOPmemSize based on structs 2025-09-11 12:45:12 +02:00
Ziemas
1870193615 Core: Fix EEmemSize
It's got desynced from the actual size of the EEVM_MemoryAllocMess
struct at some point.
2025-09-11 12:45:12 +02:00
lightningterror
189374d19c GS: Bump shader cache version.
Forgot to re include it when I reset the commit.
2025-09-09 20:32:25 +02:00
lightningterror
ac04695edd GS/shaders: Fix false positive warning for pow being negative.
Replaced pow with exp2 log2.
2025-09-09 20:29:02 +02:00
lightningterror
93bf9db0b4 GS/shaders: Fix types mismatch for ps_convert_rgb5a1_8i shader.
Fixes shader compile errors and warnings on mesa, also synch vk and dx shaders for consistency.
2025-09-09 20:29:02 +02:00
PCSX2 Bot
9219a1a38b [ci skip] PAD: Update to latest controller database. 2025-09-08 18:15:01 +02:00
PCSX2 Bot
1a28d6f0d1 [ci skip] Qt: Update Base Translation. 2025-09-08 18:14:48 +02:00
JordanTheToaster
aca1b4478e Deps: Update SDL3 to v3.2.22 2025-09-07 10:30:26 -04:00
TheTechnician27
9a794f7aaa CDVD: Add message for attempting to remove a disc when no disc exists 2025-09-07 10:29:55 -04:00
TheTechnician27
6e65558d42 Hotkeys: Better organize hotkeys page 2025-09-07 10:28:52 -04:00
JordanTheToaster
74936f49e0 GameDB: Deadly Strike fixes 2025-09-02 14:50:37 +02:00
165 changed files with 6963 additions and 3617 deletions

View File

@@ -93,7 +93,7 @@ jobs:
- name: Build Flatpak (beta)
if: ${{ inputs.stableBuild == false || inputs.stableBuild == 'false' }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flatpak-builder@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
@@ -109,7 +109,7 @@ jobs:
- name: Build Flatpak (stable)
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flatpak-builder@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false

View File

@@ -68,7 +68,7 @@ jobs:
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
- name: Create a GitHub Release (Manual)
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
if: steps.tag_version.outputs.new_tag && github.event_name == 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -77,7 +77,7 @@ jobs:
tag_name: ${{ steps.tag_version.outputs.new_tag }}
- name: Create a GitHub Release (Push)
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
if: steps.tag_version.outputs.new_tag && github.event_name != 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -203,7 +203,7 @@ jobs:
echo "TAG_VAL=${TAG_VAL}"
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: 22

View File

@@ -0,0 +1,60 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bace076..bfb1c66 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,6 +21,10 @@ endif ()
find_package(Qt${APNG_QT_VERSION} REQUIRED COMPONENTS Core Gui)
+set(CMAKE_FIND_FRAMEWORK NEVER)
+find_package(PNG 1.6.40 REQUIRED)
+find_package(ZLIB REQUIRED)
+
add_subdirectory(src)
if(APNG_TESTS)
diff --git a/cmake/FindZLib.cmake b/cmake/FindZLib.cmake
deleted file mode 100644
index f8e9220..0000000
--- a/cmake/FindZLib.cmake
+++ /dev/null
@@ -1 +0,0 @@
-add_library(ZLIB::ZLIB ALIAS zlibstatic) # use our zlib
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 697df95..0e89371 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,2 +1 @@
-add_subdirectory(3rdparty EXCLUDE_FROM_ALL)
add_subdirectory(plugins)
diff --git a/src/plugins/imageformats/apng/CMakeLists.txt b/src/plugins/imageformats/apng/CMakeLists.txt
index e1b3fd9..72164fb 100644
--- a/src/plugins/imageformats/apng/CMakeLists.txt
+++ b/src/plugins/imageformats/apng/CMakeLists.txt
@@ -14,13 +14,10 @@ target_sources(ApngImagePlugin PRIVATE ${APNG_SOURCES})
target_link_libraries(ApngImagePlugin PRIVATE
Qt${APNG_QT_VERSION}::Core
Qt${APNG_QT_VERSION}::Gui
- png_static
- zlibstatic
+ PNG::PNG
+ ZLIB::ZLIB
)
-get_target_property(_png_include png_static INCLUDE_DIRECTORIES)
-target_include_directories(ApngImagePlugin PRIVATE ${_png_include})
-
target_compile_definitions(ApngImagePlugin PRIVATE
QT_DEPRECATED_WARNINGS
QT_ASCII_CAST_WARNINGS
@@ -31,3 +28,10 @@ set_target_properties(ApngImagePlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/imageformats"
LIBRARY_OUTPUT_NAME qapng
)
+
+install(TARGETS ApngImagePlugin DESTINATION "plugins/imageformats")
+
+if(WIN32 AND MSVC)
+ set_target_properties(ApngImagePlugin PROPERTIES DEBUG_POSTFIX d)
+ install(FILES $<TARGET_PDB_FILE:ApngImagePlugin> DESTINATION "plugins/imageformats" OPTIONAL)
+endif()

View File

@@ -14,18 +14,19 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
FREETYPE=2.14.1
HARFBUZZ=11.5.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.1
LIBJPEGTURBO=3.1.2
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.20
SDL=SDL3-3.2.22
QT=6.9.2
QTAPNG=1.3.0
LZ4=1.10.0
ZSTD=1.5.7
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -37,13 +38,14 @@ mkdir -p deps-build
cd deps-build
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67 $SDL.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
44be9c9ecfe04129c4dea0a7e1b36ad476c9cc07c292016ac98e7b41514f2440 qtbase-everywhere-src-$QT.tar.xz
@@ -52,12 +54,13 @@ d984cab8f26334aa1c15e5b8f0cd9f1b7c0c1289fe0b68c1c84ab469b75605a5 qtsvg-everywhe
d8b7f7e8e970cc0b975205fd6d5832ea917ef3e751df69b97439c1cddd67a489 qttools-everywhere-src-$QT.tar.xz
c73bb6281ed365c0f954f4b1b6e1b13e1b3fefd94854f46fcd9a412f641f7ed6 qttranslations-everywhere-src-$QT.tar.xz
cad79806565568f12f9983fed69219416abcee9d5deef4abdfcf94aa2eef7781 qtwayland-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
4b08587d782f6858e6cb815b455fd7238f45190a57094857a3123883ecb595eb plutovg-$PLUTOVG.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -67,6 +70,7 @@ curl -L \
-O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \
-O "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/$LIBJPEGTURBO/libjpeg-turbo-$LIBJPEGTURBO.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://download.sourceforge.net/libpng-apng/libpng-$LIBPNG-apng.patch.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://github.com/lz4/lz4/releases/download/v$LZ4/lz4-$LZ4.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
@@ -77,6 +81,7 @@ curl -L \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtwayland-everywhere-src-$QT.tar.xz" \
-o "QtApng-$QTAPNG.tar.gz" "https://github.com/jurplel/QtApng/archive/refs/tags/$QTAPNG.tar.gz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
@@ -99,7 +104,9 @@ cd ..
echo "Building libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
@@ -250,6 +257,16 @@ cmake --build . --parallel
ninja install
cd ../../
echo "Building Qt APNG..."
rm -fr "QtApng-$QTAPNG"
tar xf "QtApng-$QTAPNG.tar.gz"
cd "QtApng-$QTAPNG"
patch -p1 < "$SCRIPTDIR/../common/qtapng-cmake.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"

View File

@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.2.20.tar.gz",
"sha256": "467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67"
"url": "https://libsdl.org/release/SDL3-3.2.22.tar.gz",
"sha256": "f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc"
}
],
"cleanup": [

View File

@@ -13,8 +13,8 @@
{
"type": "git",
"url": "https://github.com/sammycage/plutovg.git",
"tag": "v1.3.0",
"commit": "1596f459d6796b37f3f6d610ce598de2403350b5"
"tag": "v1.3.1",
"commit": "57f82ac9ffc98fb47833f1661583b347b2a08dd3"
}
],
"cleanup": [

View File

@@ -0,0 +1,46 @@
{
"name": "libpng",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DPNG_TESTS=OFF",
"-DPNG_STATIC=OFF",
"-DPNG_SHARED=ON",
"-DPNG_TOOLS=OFF"
],
"build-options": {
"strip": true
},
"sources": [
{
"type": "archive",
"url": "https://downloads.sourceforge.net/project/libpng/libpng16/1.6.50/libpng-1.6.50.tar.xz",
"sha256": "4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307"
},
{
"type": "file",
"url": "https://download.sourceforge.net/libpng-apng/libpng-1.6.50-apng.patch.gz",
"dest-filename": "libpng-1.6.50-apng.patch.gz",
"sha256": "687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165"
},
{
"type": "shell",
"commands":
[
"gunzip -f libpng-1.6.50-apng.patch.gz",
"patch -p1 < \"libpng-1.6.50-apng.patch\""
]
}
],
"cleanup": [
"/bin",
"/include",
"/lib/*.a",
"/lib/*.la",
"/lib/cmake",
"/lib/libpng",
"/lib/pkgconfig",
"/share/man"
]
}

View File

@@ -0,0 +1,29 @@
{
"name": "qtapng",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DCMAKE_PREFIX_PATH=\"${FLATPAK_DEST}\""
],
"build-options": {
"strip": true
},
"sources": [
{
"type": "git",
"url": "https://github.com/jurplel/QtApng.git",
"tag": "1.3.0",
"commit": "bd15516b281204e90ecd5b80b00d1274b062f5fc"
},
{
"type": "patch",
"path": "../../../common/qtapng-cmake.patch"
}
],
"cleanup": [
"/plugins"
],
"post-install": [
"mv ${FLATPAK_DEST}/plugins/* ${FLATPAK_DEST}/bin/"
]
}

View File

@@ -32,6 +32,8 @@
"modules/23-kddockwidgets.json",
"modules/24-plutovg.json",
"modules/25-plutosvg.json",
"modules/26-libpng.json",
"modules/27-qtapng.json",
{
"name": "pcsx2",
"buildsystem": "cmake-ninja",
@@ -45,6 +47,7 @@
"cxxflags": "",
"cxxflags-override": true,
"config-opts": [
"-DCMAKE_PREFIX_PATH=\"${FLATPAK_DEST}\"",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm18/bin/clang",

37
.github/workflows/scripts/macos/build-dependencies-universal.sh vendored Executable file → Normal file
View File

@@ -38,19 +38,20 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.20
FREETYPE=2.14.1
HARFBUZZ=11.5.0
SDL=SDL3-3.2.22
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.1
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
MOLTENVK=1.2.9
QT=6.7.3
QTAPNG=1.3.0
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -77,14 +78,15 @@ CMAKE_ARCH_ARM64=-DCMAKE_OSX_ARCHITECTURES="arm64"
CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67 $SDL.tar.gz
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
@@ -92,12 +94,13 @@ f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
4b08587d782f6858e6cb815b455fd7238f45190a57094857a3123883ecb595eb plutovg-$PLUTOVG.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -108,6 +111,7 @@ curl -C - -L \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-O "https://github.com/lz4/lz4/releases/download/v$LZ4/lz4-$LZ4.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://download.sourceforge.net/libpng-apng/libpng-$LIBPNG-apng.patch.gz" \
-O "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/$LIBJPEGTURBO/libjpeg-turbo-$LIBJPEGTURBO.tar.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
@@ -117,6 +121,7 @@ curl -C - -L \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-o "QtApng-$QTAPNG.tar.gz" "https://github.com/jurplel/QtApng/archive/refs/tags/$QTAPNG.tar.gz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
@@ -200,7 +205,9 @@ cd ..
echo "Installing libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
make -C build "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -DPNG_ARM_NEON=on -B build-arm64
@@ -369,6 +376,16 @@ make "-j$NPROCS"
make install
cd ../..
echo "Building Qt APNG..."
rm -fr "QtApng-$QTAPNG"
tar xf "QtApng-$QTAPNG.tar.gz"
cd "QtApng-$QTAPNG"
patch -p1 < "$SCRIPTDIR/../common/qtapng-cmake.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -B build
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"

View File

@@ -20,19 +20,20 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.20
FREETYPE=2.14.1
HARFBUZZ=11.5.0
SDL=SDL3-3.2.22
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.1
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
MOLTENVK=1.2.9
QT=6.7.3
QTAPNG=1.3.0
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -57,14 +58,15 @@ CMAKE_COMMON=(
)
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67 $SDL.tar.gz
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
@@ -72,12 +74,13 @@ f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
4b08587d782f6858e6cb815b455fd7238f45190a57094857a3123883ecb595eb plutovg-$PLUTOVG.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -88,6 +91,7 @@ curl -L \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-O "https://github.com/lz4/lz4/releases/download/v$LZ4/lz4-$LZ4.tar.gz" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://download.sourceforge.net/libpng-apng/libpng-$LIBPNG-apng.patch.gz" \
-O "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/$LIBJPEGTURBO/libjpeg-turbo-$LIBJPEGTURBO.tar.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
@@ -97,6 +101,7 @@ curl -L \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-o "QtApng-$QTAPNG.tar.gz" "https://github.com/jurplel/QtApng/archive/refs/tags/$QTAPNG.tar.gz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
@@ -156,7 +161,9 @@ cd ..
echo "Installing libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
make -C build "-j$NPROCS"
make -C build install
@@ -331,6 +338,16 @@ make "-j$NPROCS"
make install
cd ../..
echo "Building Qt APNG..."
rm -fr "QtApng-$QTAPNG"
tar xf "QtApng-$QTAPNG.tar.gz"
cd "QtApng-$QTAPNG"
patch -p1 < "$SCRIPTDIR/../common/qtapng-cmake.patch"
cmake "${CMAKE_COMMON[@]}" -B build
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"

View File

@@ -42,20 +42,22 @@ echo INSTALLDIR=%INSTALLDIR%
cd "%BUILDDIR%"
set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.1
set FREETYPE=2.14.1
set HARFBUZZ=11.5.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.20
set SDL=SDL3-3.2.22
set QT=6.9.2
set LIBPNGLONG=1.6.50
set QTMINOR=6.9
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.7
set KDDOCKWIDGETS=2.2.3
set PLUTOVG=1.3.0
set PLUTOVG=1.3.1
set PLUTOSVG=0.0.7
set SHADERC=2025.3
@@ -63,22 +65,24 @@ set SHADERC_GLSLANG=efd24d75bcbc55620e759f6bf42c45a32abac5f8
set SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 850cb5e38e21106c0abba86c5b73f8f74b9a32d7725505901d081080b0d3f0b3 || goto error
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip a7c2412e8fce884d98f67a0c79edf7f0b922adc9cbc177cb4fd4eeeca3fcdbe2 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" d76454913ea6f5f38856fbf00578d8e39daf842887f3396c95608414680250f7 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 1ba8e5b48f3b4d47d2de7121529d448532200fa36d9ed21f93909f6eb03f61cb || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 5153e6b3603a253e6f86dc0b1eb5b80d1dce849ceef628369942587e86582cbb || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 615184f756d91ce416f2cf883bb67fd4262651417c2e40c4d681c8641a48263e || goto error
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 77d2425458bca62c16b1ed49ed02de4c4114a113781bd94c1961b273bdca00fb || goto error
@@ -107,7 +111,10 @@ cd .. || goto error
echo Building libpng...
rmdir /S /Q "lpng%LIBPNG%"
%SEVENZIP% x "lpng%LIBPNG%.zip" || goto error
rem apng not in released libpng yet
%SEVENZIP% x "lpng%LIBPNG%-apng.patch.gz" -aoa || goto error
cd "lpng%LIBPNG%" || goto error
%PATCH% -p1 < "../libpng-%LIBPNGLONG%-apng.patch" || goto error
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
@@ -246,6 +253,22 @@ cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
if %DEBUG%==1 (
set QTAPNGBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=Release -DCMAKE_DEFAULT_CONFIGS=all -G "Ninja Multi-Config"
) else (
set QTAPNGBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
)
echo Building Qt APNG...
rmdir /S /Q "QtApng-%QTAPNG%"
%SEVENZIP% x "QtApng-%QTAPNG%.zip" || goto error
cd "QtApng-%QTAPNG%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\qtapng-cmake.patch" || goto error
cmake -B build -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" %FORCEPDB% %QTAPNGBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
if %DEBUG%==1 (
set KDDOCKWIDGETSBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=Release -DCMAKE_DEFAULT_CONFIGS=all -G "Ninja Multi-Config"
) else (

View File

@@ -40,20 +40,22 @@ set "PATH=%PATH%;%INSTALLDIR%\bin"
cd "%BUILDDIR%"
set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.1
set FREETYPE=2.14.1
set HARFBUZZ=11.5.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.20
set SDL=SDL3-3.2.22
set LIBPNGLONG=1.6.50
set QT=6.9.2
set QTMINOR=6.9
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.7
set KDDOCKWIDGETS=2.2.3
set PLUTOVG=1.3.0
set PLUTOVG=1.3.1
set PLUTOSVG=0.0.7
set SHADERC=2025.3
@@ -61,22 +63,24 @@ set SHADERC_GLSLANG=efd24d75bcbc55620e759f6bf42c45a32abac5f8
set SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 850cb5e38e21106c0abba86c5b73f8f74b9a32d7725505901d081080b0d3f0b3 || goto error
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip a7c2412e8fce884d98f67a0c79edf7f0b922adc9cbc177cb4fd4eeeca3fcdbe2 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" d76454913ea6f5f38856fbf00578d8e39daf842887f3396c95608414680250f7 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 1ba8e5b48f3b4d47d2de7121529d448532200fa36d9ed21f93909f6eb03f61cb || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 5153e6b3603a253e6f86dc0b1eb5b80d1dce849ceef628369942587e86582cbb || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 615184f756d91ce416f2cf883bb67fd4262651417c2e40c4d681c8641a48263e || goto error
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 77d2425458bca62c16b1ed49ed02de4c4114a113781bd94c1961b273bdca00fb || goto error
@@ -104,7 +108,10 @@ cd .. || goto error
echo Building libpng...
rmdir /S /Q "lpng%LIBPNG%"
%SEVENZIP% x "lpng%LIBPNG%.zip" || goto error
rem apng not in released libpng yet
%SEVENZIP% x "lpng%LIBPNG%-apng.patch.gz" -aoa || goto error
cd "lpng%LIBPNG%" || goto error
%PATCH% -p1 < "../libpng-%LIBPNGLONG%-apng.patch" || goto error
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
@@ -247,6 +254,22 @@ cmake --build . --parallel || goto error
ninja install || goto error
cd ..\.. || goto error
if %DEBUG%==1 (
set QTAPNGBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=Release -DCMAKE_DEFAULT_CONFIGS=all -G "Ninja Multi-Config"
) else (
set QTAPNGBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
)
echo Building Qt APNG...
rmdir /S /Q "QtApng-%QTAPNG%"
%SEVENZIP% x "QtApng-%QTAPNG%.zip" || goto error
cd "QtApng-%QTAPNG%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\qtapng-cmake.patch" || goto error
cmake -B build -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" %FORCEPDB% %QTAPNGBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
cd .. || goto error
if %DEBUG%==1 (
set KDDOCKWIDGETSBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=Release -DCMAKE_DEFAULT_CONFIGS=all -G "Ninja Multi-Config"
) else (

View File

@@ -8,7 +8,7 @@ jobs:
if: github.repository == 'PCSX2/pcsx2'
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -35,6 +35,100 @@ HOW TO UPDATE?
and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
- Please report any issue!
-----------------------------------------------------------------------
VERSION 1.92.3 (Released 2025-09-17)
-----------------------------------------------------------------------
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.3
Other Changes:
- Fonts: fixed merging a font and specifying a font target in DstFont
that's not the last added font (regression in 1.92). (#8912)
- Fonts: fixed an assertion failure when a rectangle entry has been reused
1024 times (e.g. due to constant change of font size). (#8906) [@cfillion]
- Clipper, Tables: added ImGuiListClipperFlags_NoSetTableRowCounters as a way to
disable the assumption that 1 clipper item == 1 table row, which breaks when
e.g. using clipper with ItemsHeight=1 in order to clip in pixel units. (#8886)
- Scrollbar, Style: added configurable style.ScrollbarPadding value and corresponding
ImGuiStyleVar_ScrollbarPadding enum, instead of an hard-coded computed default. (#8895)
- Nav: fixed Ctrl+Tab window appearing as empty when the sole active and focused
window has the ImGuiWindowFlags_NoNavFocus flag. (#8914)
- Nav: fixed a crash that could occur when opening a popup following the processing
of a global shortcut while no windows were focused.
- Bullet: fixed tessellation which looked out of place in very large sizes.
- InputText: added ImGuiInputTextFlags_WordWrap flag to word-wrap multi-line buffers.
(#3237, #952, #1062, #7363). Current caveats:
- This is marked as beta because not being tested enough.
Please report any incorrect cursor movement, selection behavior etc. bug to #3237.
- Wrapping style is not ideal. Wrapping of long words/sections (e.g. words
larger than total available width) may be particularly unpleasing.
- Wrapping width needs to always account for the possibility of a vertical scrollbar.
- It is currently much slower than regular text fields:
- Ballpark estimate of cost on my 2019 desktop PC:
For a 100 KB text buffer: +~0.3 ms/+~1.0 ms (Optimized vs Debug builds).
- The CPU cost is very roughly proportional to text length, so a 10 KB buffer
should cost about ten times less.
- InputText, InputInt, InputFloat: fixed an issue where using Escape to revert
would not write back the reverted value during the IsItemDeactivatedAfterEdit()
frame if the provided input buffer doesn't store temporary edits.
(regression in 1.91.7) (#8915, #8273)
- InputText: fixed an issue where using Escape with ImGuiInputTextFlags_EscapeClearsAll
would not write back the cleared value during the IsItemDeactivatedAfterEdit()
frame if the provided input buffer doesn't store temporary edits. (#8915, #8273)
- InputText: allow passing an empty string with buf_size==0. (#8907)
In theory the buffer size should always account for a zero-terminator, but idioms
such as using InputTextMultiline() with ImGuiInputTextFlags_ReadOnly to display
a text blob are facilitated by allowing this.
- InputText: refactored internals to simplify and optimizing rendering of selection.
Very large selection (e.g. 1 MB) now take less overhead.
- InputText: revert a change in 1.79 where pressing Down or PageDown on the last line
of a multi-line buffer without a trailing carriage return would keep the cursor
unmoved. We revert back to move to the end of line in this situation.
- InputText: fixed pressing End (without Shift) in a multi-line selection from
mistakenly moving cursor based on selection start.
- Focus, InputText: fixed an issue where SetKeyboardFocusHere() did not work
on InputTextMultiline() fields with ImGuiInputTextFlags_AllowTabInput, since
they normally inhibit activation to allow tabbing through multiple items. (#8928)
- Selectable: added ImGuiSelectableFlags_SelectOnNav to auto-select an item when
moved into, unless Ctrl is held. (automatic when in a BeginMultiSelect() block).
- TabBar: fixed an issue were forcefully selecting a tab using internal API would
be ignored on first/appearing frame before tabs are submitted (#8929, #6681)
- DrawList: fixed CloneOutput() unnecessarily taking a copy of the ImDrawListSharedData
pointer, which could to issue when deleting the cloned list. (#8894, #1860)
- DrawList: made AddCallback() assert when passing a null callback.
- Debug Tools: ID Stack Tool: fixed using fixed-size buffers preventing long identifiers
from being displayed in the tool. (#8905, #4631)
- Debug Tools: ID Stack Tool: when ### is used, uncontributing prefix before the ###
is now skipped. (#8904, #4631)
- Debug Tools: ID Stack Tool: added option to hex-encode non-ASCII characters in
output path. (#8904, #4631)
- Debug Tools: ID Stack Tool: fixed a crash when using PushOverrideID(0) during
a query. (#8937, #4631)
- Debug Tools: Fixed assertion failure when opening a combo box while using
io.ConfigDebugBeginReturnValueOnce/ConfigDebugBeginReturnValueLoop. (#8931) [@harrymander]
- Demo: tweaked ShowFontSelector() and ShowStyleSelector() to update selection
while navigating and to not close popup automatically.
- CI: Updates Windows CI to use a more recent VulkanSDK. (#8925, #8778) [@yaz0r]
- Examples: Android: Android+OpenGL3: update Gradle project (#8888, #8878) [@scribam]
- Examples: GLFW+OpenGL2, GLFW+Vulkan, GLFW+Metal, Win32+Vulkan: Fixed not applying
content scale consistently with other examples. (#8921, #8756)
- Backends: GLFW: distinguish X11 vs Wayland to fix various scaling issues.
(#8920, #8921) [@TheBrokenRail, @pthom, @ocornut]
- window/monitor content scales are always reported as 1.0 on Wayland.
- framebuffer scales are always reported as 1.0 on X11.
- Backends: SDL2: window/monitor content scales are always reported as 1.0 on Wayland.
(#8920, #8921) [@TheBrokenRail, @pthom, @ocornut]
- Backends: SDL3: use SDL_GetWindowDisplayScale() on Mac to obtain DisplayFrameBufferScale,
fixing incorrect values during resolution changes e.g. going fullscreen.
(#8703, #4414) [@jclounge]
- Backends: SDL_GPU: Added ImGui_ImplSDLGPU3_InitInfo::SwapchainComposition and
PresentMode to configure how secondary viewports are created. Currently only used
multi-viewport mode. (#8892) [@PTSVU]
- Backends: Vulkan: added ImGui_ImplVulkan_CreateMainPipeline() to recreate pipeline
without reinitializing backend. (#8110, #8111) [@SuperRonan]
-----------------------------------------------------------------------
VERSION 1.92.2b (Released 2025-08-13)
-----------------------------------------------------------------------
@@ -50,7 +144,7 @@ Changes:
leak between items when the window cannot be moved.
- Backends: Allegro5: Fixed texture format setup which didn't work on all
setups/drivers. (#8770, #8465)
- Backends: Allegro5: Added ImGui_ImplAllegro5_SetDisplay() function to
- Backends: Allegro5: Added ImGui_ImplAllegro5_SetDisplay() function to
change current ALLEGRO_DISPLAY, as Allegro applications often need to do that.
- Backends: Allegro5: Fixed missing support for ImGuiKey_PrintScreen
under Windows, as raw Allegro 5 does not receive it.
@@ -312,7 +406,7 @@ Breaking changes:
to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this)
However you may set TexMinWidth = TexMaxWidth for the same effect.
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on
ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself.
ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself.
An assert will trigger if you don't.
- Fonts: obsoleted ImGui::SetWindowFontScale() which is not useful anymore. Prefer using
PushFont(NULL, style.FontSizeBase * factor) or to manipulate other scaling factors.
@@ -1094,7 +1188,7 @@ Breaking changes:
allows casting any pointer/integer type without warning:
- May warn: ImGui::Image((void*)MyTextureData, ...);
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
- Note that you can always define ImTextureID to be your own high-level structures
(with dedicated constructors and extra render parameters) if you like.
- IO: moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
@@ -4464,7 +4558,7 @@ Breaking Changes:
- ShowTestWindow() -> use ShowDemoWindow()
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (headers)
// Help:
@@ -28,8 +28,8 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.92.2b"
#define IMGUI_VERSION_NUM 19222
#define IMGUI_VERSION "1.92.3"
#define IMGUI_VERSION_NUM 19230
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
@@ -100,8 +100,10 @@ Index of this file:
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different
// location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...))
// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different
// location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...),
// and only works when using Code Analysis, rather than just normal compiling).
// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis)
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1)))
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
@@ -231,6 +233,7 @@ typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Internal, do not use!
typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas
typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags
@@ -246,6 +249,7 @@ typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: f
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items
typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
typedef int ImGuiListClipperFlags; // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiMultiSelectFlags; // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
@@ -1053,8 +1057,8 @@ namespace ImGui
// Inputs Utilities: Shortcut Testing & Routing [BETA]
// - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
// ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments)
// ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments)
// ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments
// ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments
// only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.
// - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.
// Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut.
@@ -1182,7 +1186,7 @@ enum ImGuiWindowFlags_
};
// Flags for ImGui::BeginChild()
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.)
// About using AutoResizeX/AutoResizeY flags:
// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
@@ -1261,6 +1265,15 @@ enum ImGuiInputTextFlags_
ImGuiInputTextFlags_CallbackResize = 1 << 22, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
// Multi-line Word-Wrapping [BETA]
// - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237.
// - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing.
// - Wrapping width needs to always account for the possibility of a vertical scrollbar.
// - It is much slower than regular text fields.
// Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build).
// The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less.
ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultine(): word-wrap lines that are too long.
// Obsolete names
//ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior
};
@@ -1337,6 +1350,7 @@ enum ImGuiSelectableFlags_
ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text
ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one
ImGuiSelectableFlags_Highlight = 1 << 5, // Make the item be displayed as if it is hovered
ImGuiSelectableFlags_SelectOnNav = 1 << 6, // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0
@@ -1805,6 +1819,7 @@ enum ImGuiStyleVar_
ImGuiStyleVar_CellPadding, // ImVec2 CellPadding
ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
ImGuiStyleVar_GrabRounding, // float GrabRounding
ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize
@@ -2271,6 +2286,7 @@ struct ImGuiStyle
float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar.
float ScrollbarRounding; // Radius of grab corners for scrollbar.
float ScrollbarPadding; // Padding of scrollbar grab within its frame (same for both axises).
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
@@ -2428,7 +2444,7 @@ struct ImGuiIO
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version.
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
// Tools to detect code submitting items with conflicting/duplicate IDs
@@ -2531,7 +2547,7 @@ struct ImGuiIO
bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)
// Other state maintained from data above + IO function calls
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame()
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -2608,10 +2624,10 @@ struct ImGuiInputTextCallbackData
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always]
int CursorPos; // // Read-write // [Completion,History,Always]
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection)
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection
int SelectionEnd; // // Read-write // [Completion,History,Always]
// Helper functions for text manipulation.
@@ -2780,6 +2796,13 @@ struct ImGuiStorage
#endif
};
// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)
enum ImGuiListClipperFlags_
{
ImGuiListClipperFlags_None = 0,
ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0, // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row.
};
// Helper: Manually clip large list of items.
// If you have lots evenly spaced items and you have random access to the list, you can perform coarse
// clipping based on visibility to only submit items that are in view.
@@ -2810,6 +2833,7 @@ struct ImGuiListClipper
double StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed
double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows.
void* TempData; // [Internal] Internal data
ImGuiListClipperFlags Flags; // [Internal] Flags, currently not yet well exposed.
// items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need)
// items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
@@ -3120,7 +3144,7 @@ struct ImDrawCmd
// Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
// Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used!
inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID
inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID)
};
// Vertex layout
@@ -3316,7 +3340,7 @@ struct ImDrawList
// Advanced: Miscellaneous
IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer.
IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead.
// Advanced: Channels
// - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)
@@ -3617,7 +3641,7 @@ struct ImFontAtlas
// - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
// - The pitch is always = Width * BytesPerPixels (1 or 4)
// - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste).
// - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:
// - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.
// - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.
@@ -3732,7 +3756,7 @@ struct ImFontAtlas
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.X
#endif
//unsigned int FontBuilderFlags; // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags.
//int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height)
//int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
//typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.X
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
@@ -3821,10 +3845,10 @@ struct ImFont
// 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
// 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
IMGUI_API ImFontBaked* GetFontBaked(float font_size, float density = -1.0f); // Get or create baked data for given size
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);
IMGUI_API const char* CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false);
IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }
#endif

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@@ -77,8 +77,8 @@ Index of this file:
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
#endif
@@ -193,6 +193,7 @@ enum ImGuiLocKey : int; // -> enum ImGuiLocKey // E
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
// Flags
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx()
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow()
@@ -335,6 +336,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_PRIu64 "llu"
#define IM_PRIX64 "llX"
#endif
#define IM_TEXTUREID_TO_U64(_TEXID) ((ImU64)(intptr_t)(_TEXID))
//-----------------------------------------------------------------------------
// [SECTION] Generic helpers
@@ -366,6 +368,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
// Helpers: Hashing
IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);
IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);
IMGUI_API const char* ImHashSkipUncontributingPrefix(const char* label);
// Helpers: Sorting
#ifndef ImQsort
@@ -427,6 +430,18 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons
IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point.
IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
enum ImDrawTextFlags_
{
ImDrawTextFlags_None = 0,
ImDrawTextFlags_CpuFineClip = 1 << 0, // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()
ImDrawTextFlags_WrapKeepBlanks = 1 << 1,
ImDrawTextFlags_StopOnNewLine = 1 << 2,
};
IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags);
IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0);
IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line
// Helpers: File System
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
@@ -590,6 +605,7 @@ struct IMGUI_API ImRect
void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); }
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; }
};
// Helper: ImBitArray
@@ -788,13 +804,13 @@ struct ImChunkStream
// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
struct ImGuiTextIndex
{
ImVector<int> LineOffsets;
ImVector<int> Offsets;
int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)
void clear() { LineOffsets.clear(); EndOffset = 0; }
int size() { return LineOffsets.Size; }
const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; }
const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); }
void clear() { Offsets.clear(); EndOffset = 0; }
int size() { return Offsets.Size; }
const char* get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); }
const char* get_line_end(const char* base, int n) { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); }
void append(const char* base, int old_size, int new_size);
};
@@ -1050,7 +1066,6 @@ enum ImGuiSelectableFlagsPrivate_
{
// NB: need to be in sync with last value of ImGuiSelectableFlags_
ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20,
ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API.
ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release)
ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release)
ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)
@@ -1218,11 +1233,15 @@ struct IMGUI_API ImGuiInputTextState
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack
int BufCapacity; // end-user buffer capacity (include zero terminator)
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
int LineCount; // last line count (solely for debugging)
float WrapWidth; // word-wrapping width
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame
bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
int ReloadSelectionStart;
int ReloadSelectionEnd;
@@ -1232,6 +1251,7 @@ struct IMGUI_API ImGuiInputTextState
void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
void OnCharPressed(unsigned int c);
float GetPreferredOffsetX() const;
// Cursor & Selection
void CursorAnimReset();
@@ -1650,8 +1670,9 @@ enum ImGuiActivateFlags_
ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.
ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.
ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request
ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing)
ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function.
ImGuiActivateFlags_FromFocusApi = 1 << 5, // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi)
};
// Early work-in-progress API for ScrollToItem()
@@ -2094,10 +2115,10 @@ struct ImGuiStackLevelInfo
ImGuiID ID;
ImS8 QueryFrameCount; // >= 1: Query in progress
bool QuerySuccess; // Obtained result from DebugHookIdInfo()
ImGuiDataType DataType : 8;
char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed.
ImS8 DataType; // ImGuiDataType
int DescOffset; // -1 or offset into parent's ResultPathsBuf
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); DescOffset = -1; }
};
// State for ID Stack tool queries
@@ -2105,13 +2126,16 @@ struct ImGuiIDStackTool
{
int LastActiveFrame;
int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
ImGuiID QueryId; // ID to query details for
ImGuiID QueryMainId; // ID to query details for
ImVector<ImGuiStackLevelInfo> Results;
bool CopyToClipboardOnCtrlC;
bool QueryHookActive; // Used to disambiguate the case where DebugHookIdInfoId == 0 which is valid.
bool OptHexEncodeNonAsciiChars;
bool OptCopyToClipboardOnCtrlC;
float CopyToClipboardLastTime;
ImGuiTextBuffer ResultPathBuf;
ImGuiTextBuffer ResultPathsBuf;
ImGuiTextBuffer ResultTempBuf;
ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; }
};
//-----------------------------------------------------------------------------
@@ -2193,7 +2217,7 @@ struct ImGuiContext
// Item/widgets state and tracking information
ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID DebugHookIdInfoId; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id
@@ -2398,6 +2422,7 @@ struct ImGuiContext
// Widget state
ImGuiInputTextState InputTextState;
ImGuiTextIndex InputTextLineIndex; // Temporary storage
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFontBaked InputTextPasswordFontBackupBaked;
ImFontFlags InputTextPasswordFontBackupFlags;
@@ -3226,6 +3251,7 @@ namespace ImGui
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
IMGUI_API void PushMultiItemsWidths(int components, float width_full);
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min);
IMGUI_API void CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end);
// Parameter stacks (shared)
IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);
@@ -3491,6 +3517,8 @@ namespace ImGui
// Tab Bars
inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }
IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id);
IMGUI_API void TabBarRemove(ImGuiTabBar* tab_bar);
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order);
@@ -3732,12 +3760,12 @@ inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { re
inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_IndexMask_ (0x0007FFFF) // 20-bits signed: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
#define ImFontAtlasRectId_GenerationShift_ (20)
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; }
inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_IndexMask_); }
inline unsigned int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
// User are returned ImFontAtlasRectId values which are meant to be persistent.
@@ -3747,7 +3775,7 @@ inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx)
struct ImFontAtlasRectEntry
{
int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int IsUsed : 1;
};

View File

@@ -181,10 +181,10 @@
//
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
@@ -260,7 +260,7 @@
//
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
//
//
@@ -427,7 +427,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -437,6 +437,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
r.x0 = r.x1 = 0;
r.ymin = r.ymax = 0;
r.num_chars = 0;
*out_side_on_line = 0;
// search rows to find one that straddles 'y'
while (i < n) {
@@ -456,7 +457,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
// below all text, return 'after' last character
if (i >= n)
{
*out_side_on_line = 1;
return n;
}
// check if it's before the beginning of the line
if (x < r.x0)
@@ -469,6 +473,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) {
*out_side_on_line = (k == 0) ? 0 : 1;
if (x < prev_x+w/2)
return k+i;
else
@@ -480,6 +485,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
}
// if the last character is a newline, return that. otherwise return 'after' the last character
*out_side_on_line = 1;
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
return i+r.num_chars-1;
else
@@ -491,6 +497,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
{
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
int side_on_line;
if( state->single_line )
{
StbTexteditRow r;
@@ -498,16 +505,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
y = r.ymin;
}
state->cursor = stb_text_locate_coord(str, x, y);
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
state->select_start = state->cursor;
state->select_end = state->cursor;
state->has_preferred_x = 0;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
int side_on_line;
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
@@ -521,8 +530,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
if (state->select_start == state->select_end)
state->select_start = state->cursor;
p = stb_text_locate_coord(str, x, y);
p = stb_text_locate_coord(str, x, y, &side_on_line);
state->cursor = state->select_end = p;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
}
/////////////////////////////////////////////////////////////////////////////
@@ -572,6 +582,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
if (n < i + r.num_chars)
break;
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
break;
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
break; // [DEAR IMGUI]
prev_start = i;
@@ -668,6 +680,35 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
}
}
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
#ifndef STB_TEXTEDIT_MOVELINESTART
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
if (state->single_line)
return 0;
while (cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
cursor = prev;
}
return cursor;
}
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
#endif
#ifndef STB_TEXTEDIT_MOVELINEEND
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->single_line)
return n;
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
return cursor;
}
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
@@ -921,8 +962,8 @@ retry:
// [DEAR IMGUI]
// going down while being on the last line shouldn't bring us to that line end
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
break;
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
// break;
// now find character position down a row
state->cursor = start;
@@ -943,6 +984,8 @@ retry:
}
stb_textedit_clamp(str, state);
if (state->cursor == find.first_char + find.length)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1;
state->preferred_x = goal_x;
@@ -1007,6 +1050,10 @@ retry:
}
stb_textedit_clamp(str, state);
if (state->cursor == find.first_char)
str->LastMoveDirectionLR = ImGuiDir_Right;
else if (state->cursor == find.prev_first)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1;
state->preferred_x = goal_x;
@@ -1024,7 +1071,7 @@ retry:
prev_scan = prev;
}
find.first_char = find.prev_first;
find.prev_first = prev_scan;
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
}
break;
}
@@ -1098,10 +1145,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->has_preferred_x = 0;
break;
@@ -1109,13 +1153,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2:
#endif
case STB_TEXTEDIT_K_LINEEND: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_move_to_last(str, state);
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->has_preferred_x = 0;
break;
}
@@ -1126,10 +1166,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state);
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1138,13 +1175,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
#endif
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state);
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (main code and documentation)
// Help:
@@ -444,7 +444,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
- Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.
- Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese().
- Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.
- Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.
- Fonts: obsoleted ImFont::Scale which is not useful anymore.
- Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things:
@@ -526,7 +526,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
- May warn: ImGui::Image((void*)MyTextureData, ...);
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
- note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
- 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
- drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
@@ -813,7 +813,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
- ShowTestWindow() -> use ShowDemoWindow()
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
@@ -1413,6 +1413,7 @@ ImGuiStyle::ImGuiStyle()
ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axises)
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
@@ -1482,6 +1483,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor)
ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
ScrollbarPadding = ImTrunc(ScrollbarPadding * scale_factor);
GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
GrabRounding = ImTrunc(GrabRounding * scale_factor);
LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
@@ -2064,7 +2066,7 @@ void ImStrncpy(char* dst, const char* src, size_t count)
if (count < 1)
return;
if (count > 1)
strncpy(dst, src, count - 1);
strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array
dst[count - 1] = 0;
}
@@ -2385,6 +2387,17 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
return ~crc;
}
// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
// FIXME-OPT: This is not designed to be optimal. Use with care.
const char* ImHashSkipUncontributingPrefix(const char* label)
{
const char* result = label;
while (unsigned char c = *label++)
if (c == '#' && label[0] == '#' && label[1] == '#')
result = label - 1;
return result;
}
//-----------------------------------------------------------------------------
// [SECTION] MISC HELPERS/UTILITIES (File functions)
//-----------------------------------------------------------------------------
@@ -3061,19 +3074,21 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
va_end(args_copy);
}
IM_MSVC_RUNTIME_CHECKS_OFF
void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
{
IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
if (old_size == new_size)
return;
if (EndOffset == 0 || base[EndOffset - 1] == '\n')
LineOffsets.push_back(EndOffset);
Offsets.push_back(EndOffset);
const char* base_end = base + new_size;
for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; )
if (++p < base_end) // Don't push a trailing offset on last \n
LineOffsets.push_back((int)(intptr_t)(p - base));
Offsets.push_back((int)(intptr_t)(p - base));
EndOffset = ImMax(EndOffset, new_size);
}
IM_MSVC_RUNTIME_CHECKS_RESTORE
//-----------------------------------------------------------------------------
// [SECTION] ImGuiListClipper
@@ -3111,7 +3126,7 @@ static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>&
}
}
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height)
{
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
@@ -3129,10 +3144,13 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_
{
if (table->IsInsideRow)
ImGui::TableEndRow(table);
table->RowPosY2 = window->DC.CursorPos.y;
const int row_increase = (int)((off_y / line_height) + 0.5f);
table->CurrentRow += row_increase;
table->RowBgColorCounter += row_increase;
if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886.
{
table->CurrentRow += row_increase;
table->RowBgColorCounter += row_increase;
}
table->RowPosY2 = window->DC.CursorPos.y;
}
}
@@ -3215,7 +3233,7 @@ void ImGuiListClipper::SeekCursorForItem(int item_n)
// - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
// - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight);
}
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
@@ -3314,7 +3332,6 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
// Add visible range
float min_y = window->ClipRect.Min.y;
float max_y = window->ClipRect.Max.y;
@@ -3333,6 +3350,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
}
// Add main visible range
const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
@@ -3398,6 +3416,13 @@ bool ImGuiListClipper::Step()
return ret;
}
// Generic helper, equivalent to old ImGui::CalcListClipping() but statelesss
void ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end)
{
*out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0);
*out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start);
}
//-----------------------------------------------------------------------------
// [SECTION] STYLING
//-----------------------------------------------------------------------------
@@ -3503,6 +3528,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] =
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize
@@ -4043,7 +4069,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
WheelingWindowReleaseTimer = 0.0f;
DebugDrawIdConflictsId = 0;
DebugHookIdInfo = 0;
DebugHookIdInfoId = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdPreviousFrameItemCount = 0;
HoveredIdAllowOverlap = false;
@@ -4357,6 +4383,7 @@ void ImGui::Shutdown()
g.ClipboardHandlerData.clear();
g.MenusIdSubmittedThisFrame.clear();
g.InputTextState.ClearFreeMemory();
g.InputTextLineIndex.clear();
g.InputTextDeactivatedState.ClearFreeMemory();
g.SettingsWindows.clear();
@@ -4472,6 +4499,7 @@ void ImGui::GcCompactTransientMiscBuffers()
ImGuiContext& g = *GImGui;
g.ItemFlagsStack.clear();
g.GroupStack.clear();
g.InputTextLineIndex.clear();
g.MultiSelectTempDataStacked = 0;
g.MultiSelectTempData.clear_destruct();
TableGcCompactSettings();
@@ -7099,10 +7127,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
// Close button
if (has_close_button)
{
ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;
if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
*p_open = false;
g.CurrentItemFlags &= ~ImGuiItemFlags_NoFocus;
g.CurrentItemFlags = backup_item_flags;
}
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
@@ -7311,7 +7340,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
// Process SetNextWindow***() calls
// (FIXME: Consider splitting the HasXXX flags into X/Y components
// (FIXME: Consider splitting the HasXXX flags into X/Y components)
bool window_pos_set_by_api = false;
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)
@@ -8445,7 +8474,7 @@ void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& s
}
// Content size = inner scrollable rectangle, padded with WindowPadding.
// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
// SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
void ImGui::SetNextWindowContentSize(const ImVec2& size)
{
ImGuiContext& g = *GImGui;
@@ -8951,7 +8980,7 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *Ctx;
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
#endif
return id;
@@ -8963,7 +8992,7 @@ ImGuiID ImGuiWindow::GetID(const void* ptr)
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *Ctx;
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
#endif
return id;
@@ -8975,7 +9004,7 @@ ImGuiID ImGuiWindow::GetID(int n)
ImGuiID id = ImHashData(&n, sizeof(n), seed);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *Ctx;
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
#endif
return id;
@@ -9038,7 +9067,7 @@ void ImGui::PushOverrideID(ImGuiID id)
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
#endif
window->IDStack.push_back(id);
@@ -9052,7 +9081,7 @@ ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
#endif
return id;
@@ -9063,7 +9092,7 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
ImGuiID id = ImHashData(&n, sizeof(n), seed);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
if (g.DebugHookIdInfoId == id)
DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
#endif
return id;
@@ -13384,6 +13413,9 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL)
source = ImGuiInputSource_Mouse;
// Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
if (source == ImGuiInputSource_Mouse)
{
@@ -13399,11 +13431,11 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
ImRect ref_rect;
if (activated_shortcut)
ref_rect = g.LastItemData.NavRect;
else
else if (window != NULL)
ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
// Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
{
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
ref_rect.Translate(window->Scroll - next_scroll);
@@ -13914,6 +13946,8 @@ void ImGui::NavMoveRequestApplyResult()
{
g.NavNextActivateId = result->ID;
g.NavNextActivateFlags = ImGuiActivateFlags_None;
if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)
g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi;
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
}
@@ -14235,7 +14269,7 @@ static void ImGui::NavUpdateWindowing()
g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad;
}
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{
if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad)
g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
@@ -15256,13 +15290,9 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
{
ImGuiContext& g = *GImGui;
// Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
if (g.IO.ConfigDebugIniSettings == false)
{
// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
// Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
if (const char* p = strstr(name, "###"))
name = p;
}
name = ImHashSkipUncontributingPrefix(name);
const size_t name_len = ImStrlen(name);
// Allocate chunk
@@ -15743,6 +15773,7 @@ static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImG
// - DebugNodeWindowSettings() [Internal]
// - DebugNodeWindowsList() [Internal]
// - DebugNodeWindowsListByBeginStackParent() [Internal]
// - ShowFontSelector()
//-----------------------------------------------------------------------------
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
@@ -15940,12 +15971,12 @@ static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTex
return buf;
}
static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, const ImDrawCmd* cmd)
static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref)
{
char* buf_end = buf + buf_size;
if (cmd->TexRef._TexData != NULL)
buf += ImFormatString(buf, buf_end - buf, "#%03d: ", cmd->TexRef._TexData->UniqueID);
return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), cmd->TexRef.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID()
if (tex_ref._TexData != NULL)
buf += ImFormatString(buf, buf_end - buf, "#%03d: ", tex_ref._TexData->UniqueID);
return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), tex_ref.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID()
}
#ifdef IMGUI_ENABLE_FREETYPE
@@ -16133,7 +16164,7 @@ void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRe
char texid_desc[30];
Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors);
Text("TexID = %s, BackendUserData = %p", FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->TexID), tex->BackendUserData);
Text("TexID = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->GetTexRef()), tex->BackendUserData);
TreePop();
}
PopID();
@@ -16834,7 +16865,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con
}
char texid_desc[30];
FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd);
FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TexRef);
char buf[300];
ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
@@ -17150,7 +17181,7 @@ void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)
if (glyph->PackId >= 0)
{
ImTextureRect* r = ImFontAtlasPackGetRect(font->ContainerAtlas, glyph->PackId);
Text("PackId: %d (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
}
Text("SourceIdx: %d", glyph->SourceIdx);
}
@@ -17284,7 +17315,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
}
const ImVec2* pr = window->NavPreferredScoringPosRel;
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
BulletText("NavPreferredScoringPosRel[%d] = (%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
@@ -17630,20 +17661,22 @@ void ImGui::UpdateDebugToolStackQueries()
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
// Clear hook when id stack tool is not visible
g.DebugHookIdInfo = 0;
g.DebugHookIdInfoId = 0;
tool->QueryHookActive = false;
if (g.FrameCount != tool->LastActiveFrame + 1)
return;
// Update queries. The steps are: -1: query Stack, >= 0: query each stack item
// We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
if (tool->QueryId != query_id)
const ImGuiID query_main_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
if (tool->QueryMainId != query_main_id)
{
tool->QueryId = query_id;
tool->QueryMainId = query_main_id;
tool->StackLevel = -1;
tool->Results.resize(0);
tool->ResultPathsBuf.resize(0);
}
if (query_id == 0)
if (query_main_id == 0)
return;
// Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
@@ -17655,11 +17688,15 @@ void ImGui::UpdateDebugToolStackQueries()
// Update hook
stack_level = tool->StackLevel;
if (stack_level == -1)
g.DebugHookIdInfo = query_id;
if (stack_level >= 0 && stack_level < tool->Results.Size)
{
g.DebugHookIdInfo = tool->Results[stack_level].ID;
g.DebugHookIdInfoId = query_main_id;
tool->QueryHookActive = true;
}
else if (stack_level >= 0 && stack_level < tool->Results.Size)
{
g.DebugHookIdInfoId = tool->Results[stack_level].ID;
tool->Results[stack_level].QueryFrameCount++;
tool->QueryHookActive = true;
}
}
@@ -17669,11 +17706,17 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
if (tool->QueryHookActive == false)
{
IM_ASSERT(id == 0);
return;
}
// Step 0: stack query
// This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
if (tool->StackLevel == -1)
{
IM_ASSERT(tool->Results.Size == 0);
tool->StackLevel++;
tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
for (int n = 0; n < window->IDStack.Size + 1; n++)
@@ -17688,42 +17731,48 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat
ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
switch (data_type)
if (info->DescOffset == -1)
{
case ImGuiDataType_S32:
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
break;
case ImGuiDataType_String:
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);
break;
case ImGuiDataType_Pointer:
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
break;
case ImGuiDataType_ID:
if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
return;
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
break;
default:
IM_ASSERT(0);
const char* result = NULL;
const char* result_end = NULL;
switch (data_type)
{
case ImGuiDataType_S32:
ImFormatStringToTempBuffer(&result, &result_end, "%d", (int)(intptr_t)data_id);
break;
case ImGuiDataType_String:
ImFormatStringToTempBuffer(&result, &result_end, "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);
break;
case ImGuiDataType_Pointer:
ImFormatStringToTempBuffer(&result, &result_end, "(void*)0x%p", data_id);
break;
case ImGuiDataType_ID:
// PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
ImFormatStringToTempBuffer(&result, &result_end, "0x%08X [override]", id);
break;
default:
IM_ASSERT(0);
}
info->DescOffset = tool->ResultPathsBuf.size();
tool->ResultPathsBuf.append(result, result_end + 1); // Include zero terminator
}
info->QuerySuccess = true;
info->DataType = data_type;
info->DataType = (ImS8)data_type;
}
static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
{
ImGuiStackLevelInfo* info = &tool->Results[n];
ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", ImHashSkipUncontributingPrefix(window->Name));
if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", ImHashSkipUncontributingPrefix(&tool->ResultPathsBuf.Buf[info->DescOffset]));
if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
return (*buf = 0);
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", ImHashSkipUncontributingPrefix(label));
#endif
return ImFormatString(buf, buf_size, "???");
}
@@ -17744,38 +17793,47 @@ void ImGui::ShowIDStackToolWindow(bool* p_open)
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
// Build and display path
tool->ResultPathBuf.resize(0);
tool->ResultTempBuf.resize(0);
for (int stack_n = 0; stack_n < tool->Results.Size; stack_n++)
{
char level_desc[256];
StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
tool->ResultPathBuf.append(stack_n == 0 ? "//" : "/");
for (int n = 0; level_desc[n]; n++)
tool->ResultTempBuf.append(stack_n == 0 ? "//" : "/");
for (const char* p = level_desc; *p != 0; )
{
if (level_desc[n] == '/')
tool->ResultPathBuf.append("\\");
tool->ResultPathBuf.append(level_desc + n, level_desc + n + 1);
unsigned int c;
const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL);
if (c == '/')
tool->ResultTempBuf.append("\\");
if (c < 256 || !tool->OptHexEncodeNonAsciiChars)
tool->ResultTempBuf.append(p, p_next);
else for (; p < p_next; p++)
tool->ResultTempBuf.appendf("\\x%02x", (unsigned char)*p);
p = p_next;
}
}
Text("0x%08X", tool->QueryId);
Text("0x%08X", tool->QueryMainId);
SameLine();
MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
// CTRL+C to copy path
const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);
Checkbox("Hex-encode non-ASCII", &tool->OptHexEncodeNonAsciiChars);
SameLine();
PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); Checkbox("Ctrl+C: copy path", &tool->CopyToClipboardOnCtrlC); PopStyleVar();
Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC);
PopStyleVar();
SameLine();
TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
{
tool->CopyToClipboardLastTime = (float)g.Time;
SetClipboardText(tool->ResultPathBuf.c_str());
SetClipboardText(tool->ResultTempBuf.c_str());
}
Text("- Path \"%s\"", tool->ResultPathBuf.c_str());
Text("- Path \"%s\"", tool->ResultTempBuf.c_str());
#ifdef IMGUI_ENABLE_TEST_ENGINE
Text("- Label \"%s\"", tool->QueryId ? ImGuiTestEngine_FindItemDebugLabel(&g, tool->QueryId) : "");
Text("- Label \"%s\"", tool->QueryMainId ? ImGuiTestEngine_FindItemDebugLabel(&g, tool->QueryMainId) : "");
#endif
Separator();
@@ -17842,7 +17900,7 @@ void ImGui::ShowFontSelector(const char* label)
for (ImFont* font : io.Fonts->Fonts)
{
PushID((void*)font);
if (Selectable(font->GetDebugName(), font == font_current))
if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav))
io.FontDefault = font;
if (font == font_current)
SetItemDefaultFocus();

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (demo code)
// Help:
@@ -3229,7 +3229,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f);
//ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
}
ImGuiListClipper clipper;
@@ -3708,6 +3708,8 @@ static void DemoWindowWidgetsTextInput()
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)");
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h");
ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.");
ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
@@ -3855,10 +3857,13 @@ static void DemoWindowWidgetsTextInput()
// For this demo we are using ImVector as a string container.
// Note that because we need to store a terminating zero character, our size/capacity are 1 more
// than usually reported by a typical string class.
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
static ImVector<char> my_str;
if (my_str.empty())
my_str.push_back(0);
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
ImGui::TreePop();
}
@@ -5182,7 +5187,7 @@ static void DemoWindowPopups()
// Typical use for regular windows:
// bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
// Typical use for popups:
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); }
// With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
// This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
@@ -5768,7 +5773,7 @@ static void DemoWindowTables()
ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
ImGui::Checkbox("Display headers", &display_headers);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
PopStyleCompact();
if (ImGui::BeginTable("table1", 3, flags))
@@ -7261,7 +7266,7 @@ static void DemoWindowTables()
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
ImGui::TreePop();
}
@@ -8224,22 +8229,34 @@ void ImGui::ShowAboutWindow(bool* p_open)
//-----------------------------------------------------------------------------
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
// Here we use the simplified Combo() api that packs items into a single literal string.
// Useful for quick combo boxes where the choices are known locally.
bool ImGui::ShowStyleSelector(const char* label)
{
// FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.
// So we keep track of last active one among our limited selection.
static int style_idx = -1;
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
const char* style_names[] = { "Dark", "Light", "Classic" };
bool ret = false;
if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_ARRAYSIZE(style_names)) ? style_names[style_idx] : ""))
{
switch (style_idx)
for (int n = 0; n < IM_ARRAYSIZE(style_names); n++)
{
case 0: ImGui::StyleColorsDark(); break;
case 1: ImGui::StyleColorsLight(); break;
case 2: ImGui::StyleColorsClassic(); break;
if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav))
{
style_idx = n;
ret = true;
switch (style_idx)
{
case 0: ImGui::StyleColorsDark(); break;
case 1: ImGui::StyleColorsLight(); break;
case 2: ImGui::StyleColorsClassic(); break;
}
}
else if (style_idx == n)
ImGui::SetItemDefaultFocus();
}
return true;
ImGui::EndCombo();
}
return false;
return ret;
}
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
@@ -8325,7 +8342,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
SeparatorText("Borders");
@@ -8339,9 +8355,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Scrollbar");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f");
SeparatorText("Tabs");
SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
@@ -9285,10 +9305,9 @@ static void ShowExampleAppLayout(bool* p_open)
ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
for (int i = 0; i < 100; i++)
{
// FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
char label[128];
sprintf(label, "MyObject %d", i);
if (ImGui::Selectable(label, selected == i))
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav))
selected = i;
}
ImGui::EndChild();
@@ -9640,7 +9659,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
if (ImGui::GetIO().KeyShift)
{
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
ImVec2 avail_size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (drawing and font code)
/*
@@ -48,7 +48,7 @@ Index of this file:
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif
// Clang/GCC warnings with -Weverything
@@ -478,9 +478,10 @@ void ImDrawList::_ClearFreeMemory()
_Splitter.ClearFreeMemory();
}
// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club
ImDrawList* ImDrawList::CloneOutput() const
{
ImDrawList* dst = IM_NEW(ImDrawList(_Data));
ImDrawList* dst = IM_NEW(ImDrawList(NULL));
dst->CmdBuffer = CmdBuffer;
dst->IdxBuffer = IdxBuffer;
dst->VtxBuffer = VtxBuffer;
@@ -517,6 +518,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t use
{
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
IM_ASSERT(callback != NULL);
IM_ASSERT(curr_cmd->UserCallback == NULL);
if (curr_cmd->ElemCount != 0)
{
@@ -1717,7 +1719,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32
clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
}
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL);
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
}
void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
@@ -3025,7 +3027,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
else
{
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
font = Fonts.back();
font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();
}
// Add to list
@@ -3096,6 +3098,7 @@ static const char* GetDefaultCompressedFontDataTTF(int* out_size);
#endif
// Load embedded ProggyClean.ttf at size 13, disable oversampling
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
{
#ifndef IMGUI_DISABLE_DEFAULT_FONT
@@ -3114,9 +3117,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
int ttf_compressed_size = 0;
const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size);
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg, glyph_ranges);
return font;
return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);
#else
IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
IM_UNUSED(font_cfg_template);
@@ -4293,6 +4294,8 @@ void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
index_entry->IsUsed = false;
index_entry->TargetIndex = builder->RectsIndexFreeListStart;
index_entry->Generation++;
if (index_entry->Generation == 0)
index_entry->Generation++; // Keep non-zero on overflow
const int pack_padding = atlas->TexGlyphPadding;
builder->RectsIndexFreeListStart = index_idx;
@@ -4516,16 +4519,16 @@ void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)
if (tex->Status == ImTextureStatus_WantCreate)
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
else if (tex->Status == ImTextureStatus_WantDestroy)
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, tex->TexID, tex->BackendUserData);
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, IM_TEXTUREID_TO_U64(tex->TexID), tex->BackendUserData);
else if (tex->Status == ImTextureStatus_WantUpdates)
{
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData);
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
for (const ImTextureRect& r : tex->Updates)
{
IM_UNUSED(r);
IM_ASSERT(r.x >= 0 && r.y >= 0);
IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert.
//IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData);
//IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
}
}
}
@@ -5338,10 +5341,11 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
}
// Trim trailing space and find beginning of next line
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags)
{
while (text < text_end && ImCharIsBlankA(*text))
text++;
if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0)
while (text < text_end && ImCharIsBlankA(*text))
text++;
if (*text == '\n')
text++;
return text;
@@ -5350,7 +5354,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)
{
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
@@ -5364,7 +5368,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
ImFontBaked* baked = GetFontBaked(size);
ImFontBaked* baked = font->GetFontBaked(size);
const float scale = size / baked->Size;
float line_width = 0.0f;
@@ -5390,12 +5394,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
if (c < 32)
{
if (c == '\n')
{
line_width = word_width = blank_width = 0.0f;
inside_word = true;
s = next_s;
continue;
}
return s; // Direct return, skip "Wrap_width is too small to fit anything" path.
if (c == '\r')
{
s = next_s;
@@ -5430,6 +5429,8 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
{
prev_word_end = word_end;
line_width += word_width + blank_width;
if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width)
prev_word_end = s;
word_width = blank_width = 0.0f;
}
@@ -5456,14 +5457,21 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
return s;
}
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining)
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
{
return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);
}
ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
{
if (!text_end)
text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
if (!text_end_display)
text_end_display = text_end;
ImFontBaked* baked = font->GetFontBaked(size);
const float line_height = size;
ImFontBaked* baked = GetFontBaked(size);
const float scale = size / baked->Size;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@@ -5472,13 +5480,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
const char* word_wrap_eol = NULL;
const char* s = text_begin;
while (s < text_end)
while (s < text_end_display)
{
// Word-wrapping
if (word_wrap_enabled)
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - line_width);
word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags);
if (s >= word_wrap_eol)
{
@@ -5486,8 +5495,10 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size.x = line_width;
text_size.y += line_height;
line_width = 0.0f;
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
if (flags & ImDrawTextFlags_StopOnNewLine)
break;
word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue;
}
}
@@ -5500,18 +5511,17 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
else
s += ImTextCharFromUtf8(&c, s, text_end);
if (c < 32)
if (c == '\n')
{
if (c == '\n')
{
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
continue;
}
if (c == '\r')
continue;
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
if (flags & ImDrawTextFlags_StopOnNewLine)
break;
continue;
}
if (c == '\r')
continue;
// Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
@@ -5531,15 +5541,23 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
if (text_size.x < line_width)
text_size.x = line_width;
if (line_width > 0 || text_size.y == 0.0f)
if (out_offset != NULL)
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
text_size.y += line_height;
if (remaining)
*remaining = s;
if (out_remaining != NULL)
*out_remaining = s;
return text_size;
}
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)
{
return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)
{
@@ -5580,7 +5598,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip)
// DO NOT CALL DIRECTLY THIS WILL CHANGE WIDLY IN 2025-2025. Use ImDrawList::AddText().
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
{
// Align to be pixel perfect
begin:
@@ -5610,8 +5629,8 @@ begin:
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition().
// If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPosition(size, s, line_end ? line_end : text_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end);
s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags);
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags);
}
else
{
@@ -5646,6 +5665,7 @@ begin:
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_index = draw_list->_VtxCurrentIdx;
const int cmd_count = draw_list->CmdBuffer.Size;
const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL;
@@ -5656,7 +5676,7 @@ begin:
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - (x - origin_x));
word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags);
if (s >= word_wrap_eol)
{
@@ -5665,7 +5685,7 @@ begin:
if (y > clip_rect.w)
break; // break out of main loop
word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
continue;
}
}
@@ -5833,8 +5853,9 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
{
// FIXME-OPT: This should be baked in font.
draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
// FIXME-OPT: This should be baked in font now that it's easier.
float font_size = draw_list->_Data->FontSize;
draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold
}
void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz)
@@ -6117,6 +6138,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
//-----------------------------------------------------------------------------
#ifndef IMGUI_DISABLE_DEFAULT_FONT

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (tables and columns code)
/*
@@ -437,7 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
table->InnerWindow->SkipItems = false;
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
if (instance_no == 0)
{
table->HasScrollbarYPrev = table->HasScrollbarYCurr;
@@ -2054,10 +2054,11 @@ void ImGui::TableEndRow(ImGuiTable* table)
}
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
// get the new cursor position.
// - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
// end of row and get the new cursor position.
if (unfreeze_rows_request)
{
IM_ASSERT(table->FreezeRowsRequest > 0);
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
@@ -3942,7 +3943,7 @@ void ImGui::TableSettingsAddSettingsHandler()
// - TableGcCompactSettings() [Internal]
//-------------------------------------------------------------------------
// Remove Table (currently only used by TestEngine)
// Remove Table data (currently only used by TestEngine)
void ImGui::TableRemove(ImGuiTable* table)
{
//IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.92.2b
// dear imgui, v1.92.3
// (widgets code)
/*
@@ -135,8 +135,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
// For InputTextEx()
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);
//-------------------------------------------------------------------------
// [SECTION] Widgets: Text, etc.
@@ -1026,7 +1025,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
const bool allow_interaction = (alpha >= 1.0f);
ImRect bb = bb_frame;
bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
float padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5f));
bb.Expand(-padding);
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
@@ -2031,7 +2031,8 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
if (!ret)
{
EndPopup();
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated.
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
return false;
}
g.BeginComboDepth++;
@@ -3910,9 +3911,6 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f
// - InputText()
// - InputTextWithHint()
// - InputTextMultiline()
// - InputTextGetCharInfo() [Internal]
// - InputTextReindexLines() [Internal]
// - InputTextReindexLinesRange() [Internal]
// - InputTextEx() [Internal]
// - DebugNodeInputTextState() [Internal]
//-------------------------------------------------------------------------
@@ -3939,75 +3937,11 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
}
// This is only used in the path where the multiline widget is inactive.
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
{
int line_count = 0;
const char* s = text_begin;
while (true)
{
const char* s_eol = strchr(s, '\n');
line_count++;
if (s_eol == NULL)
{
s = s + ImStrlen(s);
break;
}
s = s_eol + 1;
}
*out_text_end = s;
return line_count;
}
// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA()
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
{
ImGuiContext& g = *ctx;
//ImFont* font = g.Font;
ImFontBaked* baked = g.FontBaked;
const float line_height = g.FontSize;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
const char* s = text_begin;
while (s < text_end)
{
unsigned int c = (unsigned int)*s;
if (c < 0x80)
s += 1;
else
s += ImTextCharFromUtf8(&c, s, text_end);
if (c == '\n')
{
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
if (stop_on_new_line)
break;
continue;
}
if (c == '\r')
continue;
line_width += baked->GetCharAdvance((ImWchar)c) * scale;
}
if (text_size.x < line_width)
text_size.x = line_width;
if (out_offset)
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
text_size.y += line_height;
if (remaining)
*remaining = s;
return text_size;
ImGuiInputTextState* obj = &g.InputTextState;
return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags);
}
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
@@ -4018,14 +3952,14 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx >= 0 && idx <= obj->TextLen); return obj->TextSrc[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }
static char STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
const char* text = obj->TextSrc;
const char* text_remaining = NULL;
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true);
const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks);
r->x0 = 0.0f;
r->x1 = size.x;
r->baseline_y_delta = size.y;
@@ -4127,6 +4061,75 @@ static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
// Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping.
static int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
{
if (state->single_line)
return 0;
if (obj->WrapWidth > 0.0f)
{
ImGuiContext& g = *obj->Ctx;
const char* p_cursor = obj->TextSrc + cursor;
const char* p_bol = ImStrbol(p_cursor, obj->TextSrc);
const char* p = p_bol;
const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
while (p >= p_bol)
{
const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below)
return (int)(p_bol - obj->TextSrc);
if (p_eol == p_cursor && obj->TextA[cursor] != '\n' && obj->LastMoveDirectionLR == ImGuiDir_Left)
return (int)(p_bol - obj->TextSrc);
if (p_eol >= p_cursor)
return (int)(p - obj->TextSrc);
p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
}
}
// Regular handler, same as stb_textedit_move_line_start()
while (cursor > 0)
{
int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor);
if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE)
break;
cursor = prev_cursor;
}
return cursor;
}
static int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
{
int n = STB_TEXTEDIT_STRINGLEN(obj);
if (state->single_line)
return n;
if (obj->WrapWidth > 0.0f)
{
ImGuiContext& g = *obj->Ctx;
const char* p_cursor = obj->TextSrc + cursor;
const char* p = ImStrbol(p_cursor, obj->TextSrc);
const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
while (p < text_end)
{
const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
cursor = (int)(p_eol - obj->TextSrc);
if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle
break;
if (p_eol > p_cursor)
return cursor;
p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
}
}
// Regular handler, same as stb_textedit_move_line_end()
while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE)
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor);
return cursor;
}
#define STB_TEXTEDIT_MOVELINESTART STB_TEXTEDIT_MOVELINESTART_IMPL
#define STB_TEXTEDIT_MOVELINEEND STB_TEXTEDIT_MOVELINEEND_IMPL
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
{
// Offset remaining text (+ copy zero terminator)
@@ -4230,6 +4233,11 @@ void ImGuiInputTextState::OnKeyPressed(int key)
stb_textedit_key(this, Stb, key);
CursorFollow = true;
CursorAnimReset();
const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT);
if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT)
LastMoveDirectionLR = ImGuiDir_Left;
else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT)
LastMoveDirectionLR = ImGuiDir_Right;
}
void ImGuiInputTextState::OnCharPressed(unsigned int c)
@@ -4251,6 +4259,7 @@ void ImGuiInputTextState::ClearSelection() { Stb->select_start
int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; }
int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; }
int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; }
float ImGuiInputTextState::GetPreferredOffsetX() const { return Stb->has_preferred_x ? Stb->preferred_x : -1; }
void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
void ImGuiInputTextState::ReloadUserBufAndSelectAll() { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
@@ -4400,7 +4409,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im
if (c == '.' || c == ',')
c = c_decimal_point;
// Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
// Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
// While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may
// scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))
@@ -4505,6 +4514,97 @@ void ImGui::InputTextDeactivateHook(ImGuiID id)
}
}
static int* ImLowerBound(int* in_begin, int* in_end, int v)
{
int* in_p = in_begin;
for (size_t count = (size_t)(in_end - in_p); count > 0; )
{
size_t count2 = count >> 1;
int* mid = in_p + count2;
if (*mid < v)
{
in_p = ++mid;
count -= count2 + 1;
}
else
{
count = count2;
}
}
return in_p;
}
// FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool?
// 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive)
static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end)
{
ImGuiContext& g = *GImGui;
int size = 0;
const char* s;
if (flags & ImGuiInputTextFlags_WordWrap)
{
for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s)
{
if (size++ <= max_output_buffer_size)
line_index->Offsets.push_back((int)(s - buf));
s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
}
}
else if (buf_end != NULL)
{
for (s = buf; s < buf_end; s = s ? s + 1 : buf_end)
{
if (size++ <= max_output_buffer_size)
line_index->Offsets.push_back((int)(s - buf));
s = (const char*)ImMemchr(s, '\n', buf_end - s);
}
}
else
{
const char* s_eol;
for (s = buf; ; s = s_eol + 1)
{
if (size++ <= max_output_buffer_size)
line_index->Offsets.push_back((int)(s - buf));
if ((s_eol = strchr(s, '\n')) != NULL)
continue;
s += strlen(s);
break;
}
}
if (out_buf_end != NULL)
*out_buf_end = buf_end = s;
if (size == 0)
{
line_index->Offsets.push_back(0);
size++;
}
if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size)
{
line_index->Offsets.push_back((int)(buf_end - buf));
size++;
}
return size;
}
static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n)
{
const char* cursor_ptr = buf + cursor_n;
int* it_begin = line_index->Offsets.begin();
int* it_end = line_index->Offsets.end();
const int* it = ImLowerBound(it_begin, it_end, cursor_n);
if (it > it_begin)
if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0))
it--;
const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it);
const char* line_start = line_index->get_line_begin(buf, line_no);
ImVec2 offset;
offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;
offset.y = (line_no + 1) * g.FontSize;
return offset;
}
// Edit a string of text
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
@@ -4522,7 +4622,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
IM_ASSERT(buf != NULL && buf_size >= 0);
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline will not work with left-trimming
IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline))); // Multiline does not not work with left-trimming
IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0); // WordWrap does not work with Password mode.
IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0); // WordWrap does not work in single-line mode.
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
@@ -4556,8 +4658,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
item_data_backup = g.LastItemData;
window->DC.CursorPos = backup_pos;
// Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.
if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput))
// Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.
if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && !(g.NavActivateFlags & ImGuiActivateFlags_FromFocusApi) && (flags & ImGuiInputTextFlags_AllowTabInput))
g.NavActivateId = 0;
// Prevent NavActivate reactivating in BeginChild() when we are already active.
@@ -4613,6 +4715,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_resizable)
IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
// Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches.
// Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible.
const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0;
float wrap_width = 0.0f;
if (is_wordwrap)
wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize));
const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard)));
const bool user_clicked = hovered && io.MouseClicked[0];
@@ -4652,7 +4761,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Take a copy of the initial buffer value.
// From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
const int buf_len = (int)ImStrlen(buf);
IM_ASSERT(buf_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?");
IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && "Is your input buffer properly zero-terminated?");
state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
@@ -4765,6 +4874,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_password && !is_displaying_hint)
PushPasswordFont();
// Word-wrapping: attempt to keep cursor in view while resizing frame/parent
// FIXME-WORDWRAP: It would be better to preserve same relative offset.
if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width)
{
state->CursorCenterY = true;
state->WrapWidth = wrap_width;
render_cursor = true;
}
// Process mouse inputs and character inputs
if (g.ActiveId == id)
{
@@ -4772,6 +4890,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->Edited = false;
state->BufCapacity = buf_size;
state->Flags = flags;
state->WrapWidth = wrap_width;
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
// Down the line we should have a cleaner library-wide concept of Selected vs Active.
@@ -4809,9 +4928,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// Triple-click: Select line
const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n';
state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start.
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);
state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);
state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);
state->WrapWidth = wrap_width;
if (!is_eol && is_multiline)
{
ImSwap(state->Stb->select_start, state->Stb->select_end);
@@ -4963,7 +5084,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
if (buf[0] != 0)
if (state->TextA.Data[0] != 0)
{
revert_edit = true;
}
@@ -5055,14 +5176,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
// Clear input
IM_ASSERT(buf[0] != 0);
IM_ASSERT(state->TextA.Data[0] != 0);
apply_new_text = "";
apply_new_text_length = 0;
value_changed = true;
IMSTB_TEXTEDIT_CHARTYPE empty_string;
char empty_string = 0;
stb_textedit_replace(state, state->Stb, &empty_string, 0);
}
else if (strcmp(buf, state->TextToRevertTo.Data) != 0)
else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0)
{
apply_new_text = state->TextToRevertTo.Data;
apply_new_text_length = state->TextToRevertTo.Size - 1;
@@ -5229,9 +5350,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
}
const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size
ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
ImVec2 text_size(0.0f, 0.0f);
ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size
if (is_multiline)
clip_rect.ClipWith(draw_window->ClipRect);
// Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
// without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
@@ -5256,15 +5379,42 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
buf_display = hint;
buf_display_end = hint + ImStrlen(hint);
}
else
{
if (render_cursor || render_selection || g.ActiveId == id)
buf_display_end = buf_display + state->TextLen; //-V595
else if (is_multiline && !is_wordwrap)
buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path.
else
buf_display_end = buf_display + ImStrlen(buf_display);
}
// Calculate visibility
int line_visible_n0 = 0, line_visible_n1 = 1;
if (is_multiline)
CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
// Build line index for easy data access (makes code below simpler and faster)
ImGuiTextIndex* line_index = &g.InputTextLineIndex;
line_index->Offsets.resize(0);
int line_count = 1;
if (is_multiline)
line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, (render_cursor && state && state->CursorFollow) ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end);
line_index->EndOffset = (int)(buf_display_end - buf_display);
line_visible_n1 = ImMin(line_visible_n1, line_count);
// Store text height (we don't need width)
text_size = ImVec2(inner_size.x, line_count * g.FontSize);
//GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255));
// Calculate blinking cursor position
const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f);
ImVec2 draw_scroll;
// Render text. We currently only render selection when the widget is active or while scrolling.
// FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive.
const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
if (render_cursor || render_selection)
{
IM_ASSERT(state != NULL);
if (!is_displaying_hint)
buf_display_end = buf_display + state->TextLen;
// Render text (with cursor and selection)
// This is going to be messy. We need to:
// - Display the text (this alone can be more easily clipped)
@@ -5272,48 +5422,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// - Measure text height (for scrollbar)
// We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
const char* text_begin = buf_display;
const char* text_end = text_begin + state->TextLen;
ImVec2 cursor_offset, select_start_offset;
{
// Find lines numbers straddling cursor and selection min position
int cursor_line_no = render_cursor ? -1 : -1000;
int selmin_line_no = render_selection ? -1 : -1000;
const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL;
const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL;
// Count lines and find line number for cursor and selection ends
int line_count = 1;
if (is_multiline)
{
for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
{
if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; }
if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; }
line_count++;
}
}
if (cursor_line_no == -1)
cursor_line_no = line_count;
if (selmin_line_no == -1)
selmin_line_no = line_count;
// Calculate 2d position by finding the beginning of the line and measuring distance
cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x;
cursor_offset.y = cursor_line_no * g.FontSize;
if (selmin_line_no >= 0)
{
select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x;
select_start_offset.y = selmin_line_no * g.FontSize;
}
// Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
if (is_multiline)
text_size = ImVec2(inner_size.x, line_count * g.FontSize);
}
IM_ASSERT(state != NULL);
state->LineCount = line_count;
// Scroll
float new_scroll_y = scroll_y;
if (render_cursor && state->CursorFollow)
{
// Horizontal scroll in chunks of quarter width
@@ -5328,7 +5441,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
else
{
state->Scroll.y = 0.0f;
state->Scroll.x = 0.0f;
}
// Vertical scroll
@@ -5336,103 +5449,105 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// Test if cursor is vertically visible
if (cursor_offset.y - g.FontSize < scroll_y)
scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
draw_window->Scroll.y = scroll_y;
new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
}
state->CursorFollow = false;
}
if (state->CursorCenterY)
{
if (is_multiline)
new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y);
state->CursorCenterY = false;
render_cursor = false;
}
if (new_scroll_y != scroll_y)
{
const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
draw_window->Scroll.y = scroll_y;
CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
line_visible_n1 = ImMin(line_visible_n1, line_count);
}
// Draw selection
const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
draw_scroll.x = state->Scroll.x;
if (render_selection)
{
const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
const float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
const float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll;
for (const char* p = text_selected_begin; p < text_selected_end; )
const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end);
const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end);
for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++)
{
if (rect_pos.y > clip_rect.w + g.FontSize)
break;
if (rect_pos.y < clip_rect.y)
{
p = (const char*)ImMemchr((void*)p, '\n', text_selected_end - p);
p = p ? p + 1 : text_selected_end;
}
else
{
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
rect_pos.x = draw_pos.x - draw_scroll.x;
}
rect_pos.y += g.FontSize;
}
}
const char* p = line_index->get_line_begin(buf_display, line_n);
const char* p_eol = line_index->get_line_end(buf_display, line_n);
const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\n');
if (p_eol_is_wrap)
p_eol++;
const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;
const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;
// We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash.
// FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it.
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
{
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
}
float rect_width = 0.0f;
if (line_selected_begin < line_selected_end)
rect_width += CalcTextSize(line_selected_begin, line_selected_end).x;
if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap)
rect_width += bg_eol_width; // So we can see selected empty lines
if (rect_width == 0.0f)
continue;
// Draw blinking cursor
if (render_cursor)
{
state->CursorAnim += io.DeltaTime;
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
// This is required for some backends (SDL3) to start emitting character/text inputs.
// As per #6341, make sure we don't set that on the deactivating frame.
if (!is_readonly && g.ActiveId == id)
{
ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data->WantVisible = true;
ime_data->WantTextInput = true;
ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
ime_data->InputLineHeight = g.FontSize;
ime_data->ViewportId = window->Viewport->ID;
ImRect rect;
rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x;
rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize;
rect.Max.x = rect.Min.x + rect_width;
rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize;
rect.Min.y -= bg_offy_up;
rect.ClipWith(clip_rect);
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
}
}
}
else
// Find render position for right alignment (single-line only)
if (g.ActiveId != id && flags & ImGuiInputTextFlags_ElideLeft)
draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
//draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive?
// Render text
if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1))
g.Font->RenderText(draw_window->DrawList, g.FontSize,
draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize),
text_col, clip_rect.AsVec4(),
line_index->get_line_begin(buf_display, line_visible_n0),
line_index->get_line_end(buf_display, line_visible_n1 - 1),
wrap_width, ImDrawTextFlags_WrapKeepBlanks);
// Render blinking cursor
if (render_cursor)
{
// Render text only (no selection, no cursor)
if (is_multiline)
text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width
else if (!is_displaying_hint && g.ActiveId == id)
buf_display_end = buf_display + state->TextLen;
else if (!is_displaying_hint)
buf_display_end = buf_display + ImStrlen(buf_display);
state->CursorAnim += io.DeltaTime;
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
// This is required for some backends (SDL3) to start emitting character/text inputs.
// As per #6341, make sure we don't set that on the deactivating frame.
if (!is_readonly && g.ActiveId == id)
{
// Find render position for right alignment
if (flags & ImGuiInputTextFlags_ElideLeft)
draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);
ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data->WantVisible = true;
ime_data->WantTextInput = true;
ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
ime_data->InputLineHeight = g.FontSize;
ime_data->ViewportId = window->Viewport->ID;
}
}
@@ -5488,8 +5603,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
ImStb::StbUndoState* undo_state = &stb_state->undostate;
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID);
Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("BufCapacity: %d", state->BufCapacity);
Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state->TextLen, stb_state->cursor,
(state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? " (L)" : " (R)") : "",
stb_state->select_start, stb_state->select_end);
Text("BufCapacity: %d, LineCount: %d", state->BufCapacity, state->LineCount);
Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
@@ -7217,6 +7334,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
bool auto_selected = false;
// Multi-selection support (footer)
if (is_multi_select)
@@ -7233,8 +7351,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
// - (2) usage will fail with clipped items
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
if (g.NavJustMovedToId == id)
selected = pressed = true;
if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0)
selected = pressed = auto_selected = true;
}
// Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad
@@ -7285,7 +7403,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);
// Automatically close popups
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
CloseCurrentPopup();
if (disabled_item && !disabled_global)
@@ -8363,7 +8481,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
// - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass.
// - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code.
// - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling
// left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.)
// left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.
// FIXME-OPT: For each block of consecutive SetRange request:
// - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage.
// - rewrite sorted storage a single time.
@@ -9078,7 +9196,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
{
// Menu inside a regular/vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
@@ -9285,7 +9403,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
{
// Menu item inside a vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
@@ -9424,6 +9542,19 @@ static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)
return ImGuiPtrOrIndex(tab_bar);
}
ImGuiTabBar* ImGui::TabBarFindByID(ImGuiID id)
{
ImGuiContext& g = *GImGui;
return g.TabBars.GetByKey(id);
}
// Remove TabBar data (currently only used by TestEngine)
void ImGui::TabBarRemove(ImGuiTabBar* tab_bar)
{
ImGuiContext& g = *GImGui;
g.TabBars.Remove(tab_bar->ID, tab_bar);
}
bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
{
ImGuiContext& g = *GImGui;
@@ -9762,7 +9893,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
tab_bar->TabsNames.Buf.resize(0);
// If we have lost the selected tab, select the next most recently active one
if (found_selected_tab_id == false)
const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
if (found_selected_tab_id == false && !tab_bar_appearing)
tab_bar->SelectedTabId = 0;
if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;

View File

@@ -185,7 +185,24 @@
#define ICON_PF_KEYBOARD "\xE2\x90\xBD"
#define ICON_PF_MOUSE "\xE2\x90\xBE"
#define ICON_PF_MOUSE_AND_KEYBOARD "\xE2\x90\xBF"
#define ICON_PF_DUALSHOCK2 "\xE2\x91\x81"
#define ICON_PF_DUALSHOCK2_SLASH "\xE2\x91\x82"
#define ICON_PF_GUITAR "\xE2\x91\x83"
#define ICON_PF_STEERING_WHEEL_ALT "\xE2\x91\x84"
#define ICON_PF_SEGA_SEAMIC "\xE2\x91\x85"
#define ICON_PF_JOGCON "\xE2\x91\x86"
#define ICON_PF_BUZZ_CONTROLLER "\xE2\x91\x87"
#define ICON_PF_GAMETRAK_DEVICE "\xE2\x91\x88"
#define ICON_PF_DJ_HERO_TURNTABLE "\xE2\x91\x89"
#define ICON_PF_REALPLAY_BOWLING "\xE2\x91\x8A"
#define ICON_PF_NEGCON "\xE2\x91\x8B"
#define ICON_PF_REZ_VIBRATOR "\xE2\x91\x8C"
#define ICON_PF_EYETOY_WEBCAM "\xE2\x91\x8D"
#define ICON_PF_SINGSTAR_MIC "\xE2\x91\x8E"
#define ICON_PF_GUNCON2 "\xE2\x91\x8F"
#define ICON_PF_HEADSET "\xE2\x91\x90"
#define ICON_PF_KEYBOARDMANIA "\xE2\x91\x91"
#define ICON_PF_PRINTER "\xE2\x91\x92"
#define ICON_PF_F1 "\xE2\x91\xA0"
#define ICON_PF_F2 "\xE2\x91\xA1"
#define ICON_PF_F3 "\xE2\x91\xA2"
@@ -362,6 +379,7 @@
#define ICON_PF_HEARTBEAT_MAG "\xE2\x8D\xBE"
#define ICON_PF_MONITOR_CODE "\xE2\x8D\xBF"
#define ICON_PF_SIXTY_CIRCLE "\xE2\x8E\x80"
#define ICON_PF_VIDEO_CAMERA "\xE2\x8E\x81"
#define ICON_PF_SPEAKER_ALT "\xE2\x8D\xA7"
#define ICON_PF_THUNDERBOLT "\xE2\x8D\x9C"
#define ICON_PF_BACKWARD "\xE2\x8F\x8C"

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,6 @@ add_library(rcheevos
include/rc_hash.h
include/rc_runtime.h
include/rc_runtime_types.h
include/rc_url.h
include/rc_util.h
src/rapi/rc_api_common.c
src/rapi/rc_api_common.h
@@ -47,5 +46,5 @@ add_library(rcheevos
target_include_directories(rcheevos PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_include_directories(rcheevos INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_compile_definitions(rcheevos PRIVATE "RC_DISABLE_LUA=1" "RCHEEVOS_URL_SSL=1" "RC_NO_THREADS=1" "RC_HASH_NO_DISC" "RC_HASH_NO_ENCRYPTED" "RC_HASH_NO_ROM" "RC_HASH_NO_ZIP")
target_compile_definitions(rcheevos PRIVATE "RC_NO_THREADS=1" "RC_HASH_NO_DISC" "RC_HASH_NO_ENCRYPTED" "RC_HASH_NO_ROM" "RC_HASH_NO_ZIP")

View File

@@ -186,6 +186,37 @@ RC_EXPORT int RC_CCONV rc_api_process_update_leaderboard_response(rc_api_update_
RC_EXPORT int RC_CCONV rc_api_process_update_leaderboard_server_response(rc_api_update_leaderboard_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_update_leaderboard_response(rc_api_update_leaderboard_response_t* response);
/* --- Update Rich Presence --- */
/**
* API parameters for an update rich presence request.
*/
typedef struct rc_api_update_rich_presence_request_t {
/* The username of the developer */
const char* username;
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
uint32_t game_id;
/* The script for the rich_presence */
const char* script;
}
rc_api_update_rich_presence_request_t;
/**
* Response data for an update rich presence request.
*/
typedef struct rc_api_update_rich_presence_response_t {
/* Common server-provided response information */
rc_api_response_t response;
}
rc_api_update_rich_presence_response_t;
RC_EXPORT int RC_CCONV rc_api_init_update_rich_presence_request(rc_api_request_t* request, const rc_api_update_rich_presence_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_init_update_rich_presence_request_hosted(rc_api_request_t* request, const rc_api_update_rich_presence_request_t* api_params, const rc_api_host_t* host);
RC_EXPORT int RC_CCONV rc_api_process_update_rich_presence_server_response(rc_api_update_rich_presence_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_update_rich_presence_response(rc_api_update_rich_presence_response_t* response);
/* --- Fetch Badge Range --- */
/**

View File

@@ -211,6 +211,10 @@ typedef struct rc_client_user_game_summary_t {
uint32_t points_core;
uint32_t points_unlocked;
/* minimum version: 12.1 */
time_t beaten_time;
time_t completed_time;
} rc_client_user_game_summary_t;
/**
@@ -358,6 +362,8 @@ typedef struct rc_client_subset_t {
RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client_t* client, uint32_t subset_id);
RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary);
/*****************************************************************************\
| Fetch Game Hashes |
\*****************************************************************************/
@@ -582,7 +588,7 @@ enum {
RC_EXPORT rc_client_leaderboard_list_t* RC_CCONV rc_client_create_leaderboard_list(rc_client_t* client, int grouping);
/**
* Destroys a list allocated by rc_client_get_leaderboard_list.
* Destroys a list allocated by rc_client_create_leaderboard_list.
*/
RC_EXPORT void RC_CCONV rc_client_destroy_leaderboard_list(rc_client_leaderboard_list_t* list);

View File

@@ -37,7 +37,7 @@
* #endif
*/
#ifdef __cplusplus
#if defined(__cplusplus) && !defined(CXX_BUILD)
#define RC_BEGIN_C_DECLS extern "C" {
#define RC_END_C_DECLS }
#else

View File

@@ -154,6 +154,7 @@ typedef struct rc_hash_iterator {
uint8_t consoles[12];
int index;
const char* path;
void* userdata;
rc_hash_callbacks_t callbacks;
} rc_hash_iterator_t;

View File

@@ -1,36 +0,0 @@
#ifndef RC_URL_H
#define RC_URL_H
#include "rc_export.h"
#include <stddef.h>
RC_BEGIN_C_DECLS
RC_EXPORT int RC_CCONV rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore, const char* game_hash);
RC_EXPORT int RC_CCONV rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
RC_EXPORT int RC_CCONV rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count);
RC_EXPORT int RC_CCONV rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count);
RC_EXPORT int RC_CCONV rc_url_get_gameid(char* buffer, size_t size, const char* hash);
RC_EXPORT int RC_CCONV rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
RC_EXPORT int RC_CCONV rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name);
RC_EXPORT int RC_CCONV rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password);
RC_EXPORT int RC_CCONV rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token);
RC_EXPORT int RC_CCONV rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore);
RC_EXPORT int RC_CCONV rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
RC_EXPORT int RC_CCONV rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
RC_END_C_DECLS
#endif /* RC_URL_H */

View File

@@ -273,17 +273,17 @@ static int rc_json_extract_html_error(rc_api_response_t* response, const rc_api_
iterator.json = server_response->body;
iterator.end = server_response->body + server_response->body_length;
/* if the title contains an HTTP status code(i.e "404 Not Found"), return the title */
/* assume the title contains the most appropriate message to display to the user */
if (rc_json_find_substring(&iterator, "<title>")) {
const char* title_start = iterator.json + 7;
if (isdigit((int)*title_start) && rc_json_find_substring(&iterator, "</title>")) {
if (rc_json_find_substring(&iterator, "</title>")) {
response->error_message = rc_buffer_strncpy(&response->buffer, title_start, iterator.json - title_start);
response->succeeded = 0;
return RC_INVALID_JSON;
}
}
/* title not found, or did not start with an error code, return the first line of the response */
/* title not found, return the first line of the response (up to 200 characters) */
iterator.json = server_response->body;
while (iterator.json < iterator.end && *iterator.json != '\n' &&

View File

@@ -100,7 +100,7 @@ int rc_api_process_fetch_code_notes_server_response(rc_api_fetch_code_notes_resp
if (!rc_json_get_required_string(&address_str, &response->response, &note_fields[0], "Address"))
return RC_MISSING_VALUE;
note->address = (unsigned)strtol(address_str, NULL, 16);
note->address = (uint32_t)strtoul(address_str, NULL, 16);
if (!rc_json_get_required_string(&note->note, &response->response, &note_fields[2], "Note"))
return RC_MISSING_VALUE;
@@ -419,6 +419,60 @@ void rc_api_destroy_update_leaderboard_response(rc_api_update_leaderboard_respon
rc_buffer_destroy(&response->response.buffer);
}
/* --- Update Rich Presence --- */
int rc_api_init_update_rich_presence_request(rc_api_request_t* request, const rc_api_update_rich_presence_request_t* api_params) {
return rc_api_init_update_rich_presence_request_hosted(request, api_params, &g_host);
}
int rc_api_init_update_rich_presence_request_hosted(rc_api_request_t* request,
const rc_api_update_rich_presence_request_t* api_params,
const rc_api_host_t* host) {
rc_api_url_builder_t builder;
rc_api_url_build_dorequest_url(request, host);
if (api_params->game_id == 0)
return RC_INVALID_STATE;
if (!api_params->script)
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 128);
if (!rc_api_url_build_dorequest(&builder, "submitrichpresence", api_params->username, api_params->api_token))
return builder.result;
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
rc_url_builder_append_str_param(&builder, "d", api_params->script);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
return builder.result;
}
int rc_api_process_update_rich_presence_server_response(rc_api_update_rich_presence_response_t* response, const rc_api_server_response_t* server_response) {
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("Code"),
};
memset(response, 0, sizeof(*response));
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK || !response->response.succeeded)
return result;
return RC_OK;
}
void rc_api_destroy_update_rich_presence_response(rc_api_update_rich_presence_response_t* response) {
rc_buffer_destroy(&response->response.buffer);
}
/* --- Fetch Badge Range --- */
int rc_api_init_fetch_badge_range_request(rc_api_request_t* request, const rc_api_fetch_badge_range_request_t* api_params) {

View File

@@ -905,11 +905,22 @@ int rc_client_user_get_image_url(const rc_client_user_t* user, char buffer[], si
return rc_client_get_image_url(buffer, buffer_size, RC_IMAGE_TYPE_USER, user->display_name);
}
static void rc_client_subset_get_user_game_summary(const rc_client_subset_info_t* subset,
rc_client_user_game_summary_t* summary, const uint8_t unlock_bit)
static void rc_client_subset_get_user_game_summary(const rc_client_t* client,
const rc_client_subset_info_t* subset, rc_client_user_game_summary_t* summary)
{
rc_client_achievement_info_t* achievement = subset->achievements;
rc_client_achievement_info_t* stop = achievement + subset->public_.num_achievements;
time_t last_unlock_time = 0;
time_t last_progression_time = 0;
time_t first_win_time = 0;
int num_progression_achievements = 0;
int num_win_achievements = 0;
int num_unlocked_progression_achievements = 0;
const uint8_t unlock_bit = (client->state.hardcore) ?
RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE : RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE;
rc_mutex_lock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */
for (; achievement < stop; ++achievement) {
switch (achievement->public_.category) {
case RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE:
@@ -919,10 +930,28 @@ static void rc_client_subset_get_user_game_summary(const rc_client_subset_info_t
if (achievement->public_.unlocked & unlock_bit) {
++summary->num_unlocked_achievements;
summary->points_unlocked += achievement->public_.points;
if (achievement->public_.unlock_time > last_unlock_time)
last_unlock_time = achievement->public_.unlock_time;
if (achievement->public_.type == RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION) {
++num_unlocked_progression_achievements;
if (achievement->public_.unlock_time > last_progression_time)
last_progression_time = achievement->public_.unlock_time;
}
else if (achievement->public_.type == RC_CLIENT_ACHIEVEMENT_TYPE_WIN) {
if (first_win_time == 0 || achievement->public_.unlock_time < first_win_time)
first_win_time = achievement->public_.unlock_time;
}
}
if (achievement->public_.bucket == RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED) {
if (achievement->public_.type == RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION)
++num_progression_achievements;
else if (achievement->public_.type == RC_CLIENT_ACHIEVEMENT_TYPE_WIN)
++num_win_achievements;
if (achievement->public_.bucket == RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED)
++summary->num_unsupported_achievements;
}
break;
@@ -934,13 +963,18 @@ static void rc_client_subset_get_user_game_summary(const rc_client_subset_info_t
continue;
}
}
rc_mutex_unlock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */
if (summary->num_unlocked_achievements == summary->num_core_achievements)
summary->completed_time = last_unlock_time;
if ((first_win_time || num_win_achievements == 0) && num_unlocked_progression_achievements == num_progression_achievements)
summary->beaten_time = (first_win_time > last_progression_time) ? first_win_time : last_progression_time;
}
void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_game_summary_t* summary)
{
const uint8_t unlock_bit = (client->state.hardcore) ?
RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE : RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE;
if (!summary)
return;
@@ -949,20 +983,47 @@ void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_g
return;
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
if (client->state.external_client && client->state.external_client->get_user_game_summary) {
client->state.external_client->get_user_game_summary(summary);
if (client->state.external_client) {
if (client->state.external_client->get_user_game_summary_v5) {
client->state.external_client->get_user_game_summary_v5(summary);
return;
}
if (client->state.external_client->get_user_game_summary) {
client->state.external_client->get_user_game_summary(summary);
return;
}
}
#endif
if (rc_client_is_game_loaded(client))
rc_client_subset_get_user_game_summary(client, client->game->subsets, summary);
}
void rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary)
{
if (!summary)
return;
memset(summary, 0, sizeof(*summary));
if (!client || !subset_id)
return;
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
if (client->state.external_client && client->state.external_client->get_user_subset_summary) {
client->state.external_client->get_user_subset_summary(subset_id, summary);
return;
}
#endif
if (!rc_client_is_game_loaded(client))
return;
rc_mutex_lock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */
rc_client_subset_get_user_game_summary(client->game->subsets, summary, unlock_bit);
rc_mutex_unlock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */
if (rc_client_is_game_loaded(client)) {
const rc_client_subset_info_t* subset = client->game->subsets;
for (; subset; subset = subset->next) {
if (subset->public_.id == subset_id) {
rc_client_subset_get_user_game_summary(client, subset, summary);
break;
}
}
}
}
typedef struct rc_client_fetch_all_user_progress_callback_data_t {
@@ -1376,29 +1437,34 @@ static uint32_t rc_client_subset_toggle_hardcore_achievements(rc_client_subset_i
break;
}
}
else if (achievement->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE ||
achievement->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE) {
/* if it's active despite being unlocked, and we're in encore mode, leave it active */
if (client->state.encore_mode) {
++active_count;
continue;
}
achievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED;
else {
achievement->public_.unlock_time = (active_bit == RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE) ?
achievement->unlock_time_hardcore : achievement->unlock_time_softcore;
achievement->unlock_time_hardcore : achievement->unlock_time_softcore;
if (achievement->trigger && achievement->trigger->state == RC_TRIGGER_STATE_PRIMED) {
rc_client_event_t client_event;
memset(&client_event, 0, sizeof(client_event));
client_event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE;
client_event.achievement = &achievement->public_;
client->callbacks.event_handler(&client_event, client);
if (achievement->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE ||
achievement->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE) {
/* if it's active despite being unlocked, and we're in encore mode, leave it active */
if (client->state.encore_mode) {
++active_count;
continue;
}
/* switch to inactive */
achievement->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED;
if (achievement->trigger && rc_trigger_state_active(achievement->trigger->state)) {
/* hide any active challenge indicators */
if (achievement->trigger->state == RC_TRIGGER_STATE_PRIMED) {
rc_client_event_t client_event;
memset(&client_event, 0, sizeof(client_event));
client_event.type = RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE;
client_event.achievement = &achievement->public_;
client->callbacks.event_handler(&client_event, client);
}
achievement->trigger->state = RC_TRIGGER_STATE_TRIGGERED;
}
}
if (achievement->trigger && rc_trigger_state_active(achievement->trigger->state))
achievement->trigger->state = RC_TRIGGER_STATE_TRIGGERED;
}
}
@@ -1728,7 +1794,9 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
if (load_state->hash->hash[0] != '[') {
if (load_state->client->state.spectator_mode != RC_CLIENT_SPECTATOR_MODE_LOCKED) {
/* schedule the periodic ping */
rc_client_scheduled_callback_data_t* callback_data = rc_buffer_alloc(&load_state->game->buffer, sizeof(rc_client_scheduled_callback_data_t));
rc_client_scheduled_callback_data_t* callback_data = (rc_client_scheduled_callback_data_t*)
rc_buffer_alloc(&load_state->game->buffer, sizeof(rc_client_scheduled_callback_data_t));
memset(callback_data, 0, sizeof(*callback_data));
callback_data->callback = rc_client_ping;
callback_data->related_id = load_state->game->public_.id;
@@ -1768,7 +1836,9 @@ static void rc_client_dispatch_activate_game(struct rc_client_scheduled_callback
static void rc_client_queue_activate_game(rc_client_load_state_t* load_state)
{
rc_client_scheduled_callback_data_t* scheduled_callback_data = calloc(1, sizeof(rc_client_scheduled_callback_data_t));
rc_client_scheduled_callback_data_t* scheduled_callback_data =
(rc_client_scheduled_callback_data_t*)calloc(1, sizeof(rc_client_scheduled_callback_data_t));
if (!scheduled_callback_data) {
rc_client_load_error(load_state, RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY));
return;
@@ -1805,7 +1875,7 @@ static void rc_client_start_session_callback(const rc_api_server_response_t* ser
outstanding_requests = rc_client_end_load_state(load_state);
if (error_message) {
rc_client_load_error(callback_data, result, error_message);
rc_client_load_error(load_state, result, error_message);
}
else if (outstanding_requests < 0) {
/* previous load state was aborted, load_state was free'd */
@@ -1818,7 +1888,7 @@ static void rc_client_start_session_callback(const rc_api_server_response_t* ser
(rc_api_start_session_response_t*)malloc(sizeof(rc_api_start_session_response_t));
if (!load_state->start_session_response) {
rc_client_load_error(callback_data, RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY));
rc_client_load_error(load_state, RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY));
}
else {
/* safer to parse the response again than to try to copy it */
@@ -1911,7 +1981,7 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state,
/* allocate the achievement array */
size = sizeof(rc_client_achievement_info_t) * num_achievements;
achievement = achievements = rc_buffer_alloc(buffer, size);
achievement = achievements = (rc_client_achievement_info_t*)rc_buffer_alloc(buffer, size);
memset(achievements, 0, size);
/* copy the achievement data */
@@ -1960,7 +2030,7 @@ static void rc_client_copy_achievements(rc_client_load_state_t* load_state,
achievement->public_.bucket = RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED;
}
else {
rc_buffer_consume(buffer, preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset);
rc_buffer_consume(buffer, (const uint8_t*)preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset);
}
rc_destroy_preparse_state(&preparse);
@@ -2053,7 +2123,7 @@ static void rc_client_copy_leaderboards(rc_client_load_state_t* load_state,
/* allocate the achievement array */
size = sizeof(rc_client_leaderboard_info_t) * num_leaderboards;
buffer = &load_state->game->buffer;
leaderboard = leaderboards = rc_buffer_alloc(buffer, size);
leaderboard = leaderboards = (rc_client_leaderboard_info_t*)rc_buffer_alloc(buffer, size);
memset(leaderboards, 0, size);
/* copy the achievement data */
@@ -2103,7 +2173,7 @@ static void rc_client_copy_leaderboards(rc_client_load_state_t* load_state,
leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_DISABLED;
}
else {
rc_buffer_consume(buffer, preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset);
rc_buffer_consume(buffer, (const uint8_t*)preparse.parse.buffer, (uint8_t*)preparse.parse.buffer + preparse.parse.offset);
}
rc_destroy_preparse_state(&preparse);
@@ -2618,7 +2688,7 @@ rc_client_game_hash_t* rc_client_find_game_hash(rc_client_t* client, const char*
}
if (!game_hash) {
game_hash = rc_buffer_alloc(&client->state.buffer, sizeof(rc_client_game_hash_t));
game_hash = (rc_client_game_hash_t*)rc_buffer_alloc(&client->state.buffer, sizeof(rc_client_game_hash_t));
memset(game_hash, 0, sizeof(*game_hash));
snprintf(game_hash->hash, sizeof(game_hash->hash), "%s", hash);
game_hash->game_id = RC_CLIENT_UNKNOWN_GAME_ID;
@@ -2796,16 +2866,14 @@ rc_hash_iterator_t* rc_client_get_load_state_hash_iterator(rc_client_t* client)
static void rc_client_log_hash_message_verbose(const char* message, const rc_hash_iterator_t* iterator)
{
rc_client_load_state_t unused;
rc_client_load_state_t* load_state = (rc_client_load_state_t*)(((uint8_t*)iterator) - RC_OFFSETOF(unused, hash_iterator));
const rc_client_load_state_t* load_state = (const rc_client_load_state_t*)iterator->userdata;
if (load_state->client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO)
rc_client_log_message(load_state->client, message);
}
static void rc_client_log_hash_message_error(const char* message, const rc_hash_iterator_t* iterator)
{
rc_client_load_state_t unused;
rc_client_load_state_t* load_state = (rc_client_load_state_t*)(((uint8_t*)iterator) - RC_OFFSETOF(unused, hash_iterator));
const rc_client_load_state_t* load_state = (const rc_client_load_state_t*)iterator->userdata;
if (load_state->client->state.log_level >= RC_CLIENT_LOG_LEVEL_ERROR)
rc_client_log_message(load_state->client, message);
}
@@ -2875,6 +2943,7 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl
/* initialize the iterator */
rc_hash_initialize_iterator(&load_state->hash_iterator, file_path, data, data_size);
rc_hash_merge_callbacks(&load_state->hash_iterator, &client->callbacks.hash);
load_state->hash_iterator.userdata = load_state;
if (!load_state->hash_iterator.callbacks.verbose_message)
load_state->hash_iterator.callbacks.verbose_message = rc_client_log_hash_message_verbose;
@@ -3114,7 +3183,8 @@ static rc_client_async_handle_t* rc_client_begin_change_media_internal(rc_client
rc_api_request_t request;
int result;
if (game_hash->game_id != RC_CLIENT_UNKNOWN_GAME_ID) {
if (game_hash->game_id != RC_CLIENT_UNKNOWN_GAME_ID || /* previously looked up */
game_hash->hash[0] == '[') { /* internal use - don't try to look up */
rc_client_change_media_internal(client, game_hash, callback, callback_userdata);
return NULL;
}
@@ -3207,7 +3277,7 @@ static rc_client_game_info_t* rc_client_check_pending_media(rc_client_t* client,
}
/* still waiting for game data - don't call callback - it's queued */
if (pending_media)
if (pending_media)
return NULL;
return game;
@@ -3364,7 +3434,7 @@ const rc_client_game_t* rc_client_get_game_info(const rc_client_t* client)
if (client->state.external_client->get_game_info)
return rc_client_external_convert_v1_game(client, client->state.external_client->get_game_info());
}
}
#endif
return client->game ? &client->game->public_ : NULL;
@@ -4130,7 +4200,7 @@ static void rc_client_award_achievement_callback(const rc_api_server_response_t*
}
static void rc_client_award_achievement_server_call(rc_client_award_achievement_callback_data_t* ach_data)
{
{
rc_api_award_achievement_request_t api_params;
rc_api_request_t request;
int result;
@@ -4284,7 +4354,7 @@ const rc_client_leaderboard_t* rc_client_get_leaderboard_info(const rc_client_t*
if (leaderboard != NULL)
return &leaderboard->public_;
}
return NULL;
}
@@ -4368,7 +4438,7 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli
};
if (!client)
return calloc(1, sizeof(rc_client_leaderboard_list_t));
return (rc_client_leaderboard_list_t*)calloc(1, list_size);
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
if (client->state.external_client && client->state.external_client->create_leaderboard_list)
@@ -4376,7 +4446,7 @@ rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* cli
#endif
if (!client->game)
return calloc(1, sizeof(rc_client_leaderboard_list_t));
return (rc_client_leaderboard_list_t*)calloc(1, list_size);
memset(&bucket_counts, 0, sizeof(bucket_counts));

View File

@@ -35,6 +35,7 @@ typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_load_subse
uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata);
typedef const rc_client_game_t* (RC_CCONV *rc_client_external_get_game_info_func_t)(void);
typedef const rc_client_subset_t* (RC_CCONV *rc_client_external_get_subset_info_func_t)(uint32_t subset_id);
typedef void (RC_CCONV* rc_client_external_get_user_subset_summary_func_t)(uint32_t subset_id, rc_client_user_game_summary_t* summary);
typedef void (RC_CCONV *rc_client_external_get_user_game_summary_func_t)(rc_client_user_game_summary_t* summary);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_change_media_func_t)(rc_client_t* client, const char* file_path,
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
@@ -139,9 +140,13 @@ typedef struct rc_client_external_t
/* VERSION 4 */
rc_client_external_set_int_func_t set_allow_background_memory_reads;
/* VERSION 5 */
rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5;
rc_client_external_get_user_subset_summary_func_t get_user_subset_summary;
} rc_client_external_t;
#define RC_CLIENT_EXTERNAL_VERSION 4
#define RC_CLIENT_EXTERNAL_VERSION 5
void rc_client_add_game_hash(rc_client_t* client, const char* hash, uint32_t game_id);
void rc_client_load_unknown_game(rc_client_t* client, const char* hash);

View File

@@ -144,6 +144,28 @@ typedef struct v3_rc_client_achievement_list_info_t {
rc_client_destroy_achievement_list_func_t destroy_func;
} v3_rc_client_achievement_list_info_t;
/* user_game_summary */
typedef struct v1_rc_client_user_game_summary_t {
uint32_t num_core_achievements;
uint32_t num_unofficial_achievements;
uint32_t num_unlocked_achievements;
uint32_t num_unsupported_achievements;
uint32_t points_core;
uint32_t points_unlocked;
} v1_rc_client_user_game_summary_t;
typedef struct v5_rc_client_user_game_summary_t {
uint32_t num_core_achievements;
uint32_t num_unofficial_achievements;
uint32_t num_unlocked_achievements;
uint32_t num_unsupported_achievements;
uint32_t points_core;
uint32_t points_unlocked;
time_t beaten_time;
time_t completed_time;
} v5_rc_client_user_game_summary_t;
RC_END_C_DECLS
#endif /* RC_CLIENT_EXTERNAL_CONVERSIONS_H */

View File

@@ -78,6 +78,7 @@ static const rc_disallowed_setting_t _rc_disallowed_fbneo_settings[] = {
};
static const rc_disallowed_setting_t _rc_disallowed_fceumm_settings[] = {
{ "fceumm_game_genie", "!disabled" },
{ "fceumm_region", ",PAL,Dendy" },
{ NULL, NULL }
};

View File

@@ -343,8 +343,12 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
if (parse->buffer) {
classification = rc_classify_condition(&condition);
if (classification == RC_CONDITION_CLASSIFICATION_COMBINING) {
if (combining_classification == RC_CONDITION_CLASSIFICATION_COMBINING)
combining_classification = rc_find_next_classification(&(*memaddr)[1]); /* skip over '_' */
if (combining_classification == RC_CONDITION_CLASSIFICATION_COMBINING) {
if (**memaddr == '_')
combining_classification = rc_find_next_classification(&(*memaddr)[1]); /* skip over '_' */
else
combining_classification = RC_CONDITION_CLASSIFICATION_OTHER;
}
classification = combining_classification;
}

View File

@@ -971,9 +971,10 @@ static const rc_memory_regions_t rc_memory_regions_wasm4 = { _rc_memory_regions_
/* https://wiibrew.org/wiki/Memory_map */
static const rc_memory_region_t _rc_memory_regions_wii[] = {
{ 0x00000000U, 0x017FFFFF, 0x80000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x01800000U, 0x057FFFFF, 0x90000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
{ 0x01800000U, 0x0FFFFFFF, 0x81800000U, RC_MEMORY_TYPE_UNUSED, "Unused" },
{ 0x10000000U, 0x13FFFFFF, 0x90000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_wii = { _rc_memory_regions_wii, 2 };
static const rc_memory_regions_t rc_memory_regions_wii = { _rc_memory_regions_wii, 3 };
/* ===== WonderSwan ===== */
/* http://daifukkat.su/docs/wsman/#ovr_memmap */

View File

@@ -328,7 +328,7 @@ static float rc_build_float(uint32_t mantissa_bits, int32_t exponent, int sign)
if (mantissa_bits == 0) {
/* infinity */
#ifdef INFINITY /* INFINITY and NAN #defines require C99 */
dbl = INFINITY;
dbl = (double)INFINITY;
#else
dbl = -log(0.0);
#endif

View File

@@ -380,9 +380,6 @@ int rc_operand_is_float_memref(const rc_operand_t* self) {
if (!rc_operand_is_memref(self))
return 0;
if (self->type == RC_OPERAND_RECALL)
return rc_memsize_is_float(self->memref_access_type);
if (self->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
const rc_modified_memref_t* memref = (const rc_modified_memref_t*)self->value.memref;
if (memref->modifier_type != RC_OPERATOR_INDIRECT_READ)
@@ -434,6 +431,9 @@ int rc_operand_is_float(const rc_operand_t* self) {
if (self->type == RC_OPERAND_FP)
return 1;
if (self->type == RC_OPERAND_RECALL)
return rc_memsize_is_float(self->size);
return rc_operand_is_float_memref(self);
}

View File

@@ -174,8 +174,8 @@ static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t*
case RC_OPERATOR_SUB:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand);
if (value > op_max)
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
if (value >= op_max)
return value - op_max;
return 0xFFFFFFFF;
@@ -351,11 +351,11 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
snprintf(result, result_size, "Condition %d: Using pointer from previous frame", index);
return 0;
}
if (rc_operand_is_float(&cond->operand1) || rc_operand_is_float(&cond->operand2)) {
if (rc_operand_is_float(operand1) || rc_operand_is_float(&cond->operand2)) {
snprintf(result, result_size, "Condition %d: Using non-integer value in AddAddress calcuation", index);
return 0;
}
if (rc_operand_type_is_transform(cond->operand1.type)) {
if (rc_operand_type_is_transform(operand1->type) && cond->oper != RC_OPERATOR_MULT) {
snprintf(result, result_size, "Condition %d: Using transformed value in AddAddress calcuation", index);
return 0;
}
@@ -721,7 +721,7 @@ static int rc_validate_comparison_overlap(int comparison1, uint32_t value1, int
}
static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, const rc_condset_t* compare_conditions,
const char* prefix, const char* compare_prefix, char result[], const size_t result_size)
int has_hits, const char* prefix, const char* compare_prefix, char result[], const size_t result_size)
{
int comparison1, comparison2;
uint32_t value1, value2;
@@ -895,7 +895,15 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co
{
/* only ever report the redundancy on the non-ResetIf condition. The ResetIf is allowed to
* fire when the non-ResetIf condition is not true. */
continue;
if (has_hits)
continue;
}
else if (condition->type == RC_CONDITION_RESET_IF && compare_condition->type != RC_CONDITION_RESET_IF)
{
/* if the ResetIf condition is more restrictive than the non-ResetIf condition,
and there aren't any hits to clear, ignore it */
if (has_hits)
continue;
}
else if (compare_condition->type == RC_CONDITION_MEASURED_IF || condition->type == RC_CONDITION_MEASURED_IF)
{
@@ -947,12 +955,21 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
{
const rc_condset_t* alt;
int index;
int has_hits = (trigger->requirement && trigger->requirement->num_hittarget_conditions > 0);
if (!has_hits) {
for (alt = trigger->alternative; alt; alt = alt->next) {
if (alt->num_hittarget_conditions > 0) {
has_hits = 1;
break;
}
}
}
if (!trigger->alternative) {
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address))
return 0;
return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, "", "", result, result_size);
return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, has_hits, "", "", result, result_size);
}
snprintf(result, result_size, "Core ");
@@ -960,7 +977,7 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
return 0;
/* compare core to itself */
if (!rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, "Core", "Core", result, result_size))
if (!rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, has_hits, "Core", "Core", result, result_size))
return 0;
index = 1;
@@ -972,15 +989,15 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
/* compare alt to itself */
snprintf(altname, sizeof(altname), "Alt%d", index);
if (!rc_validate_conflicting_conditions(alt, alt, altname, altname, result, result_size))
if (!rc_validate_conflicting_conditions(alt, alt, has_hits, altname, altname, result, result_size))
return 0;
/* compare alt to core */
if (!rc_validate_conflicting_conditions(trigger->requirement, alt, "Core", altname, result, result_size))
if (!rc_validate_conflicting_conditions(trigger->requirement, alt, has_hits, "Core", altname, result, result_size))
return 0;
/* compare core to alt */
if (!rc_validate_conflicting_conditions(alt, trigger->requirement, altname, "Core", result, result_size))
if (!rc_validate_conflicting_conditions(alt, trigger->requirement, has_hits, altname, "Core", result, result_size))
return 0;
}

View File

@@ -48,8 +48,9 @@ static void rc_alloc_helper_variable_memref_value(rc_richpresence_display_part_t
condset = value->conditions;
if (condset && !condset->next) {
/* single value - if it's only "measured" and "indirect" conditions, we can simplify to a memref */
if (condset->num_measured_conditions &&
/* single value - if it's a single Measured clause (including any AddSource/AddAddress helpers), we can
* simplify to a memref. If there are supporting clauses like MeasuredIf or ResetIf, we can't */
if (condset->num_measured_conditions == 1 &&
!condset->num_pause_conditions && !condset->num_reset_conditions &&
!condset->num_other_conditions && !condset->num_hittarget_conditions) {
rc_condition_t* condition = condset->conditions;

View File

@@ -36,7 +36,7 @@ rc_runtime_t* rc_runtime_alloc(void) {
rc_runtime_event_handler_t unused = &rc_runtime_natvis_helper;
(void)unused;
self = malloc(sizeof(rc_runtime_t));
self = (rc_runtime_t*)malloc(sizeof(rc_runtime_t));
if (self) {
rc_runtime_init(self);
@@ -307,7 +307,7 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* mema
rc_lboard_t* lboard;
rc_preparse_state_t preparse;
rc_runtime_lboard_t* runtime_lboard;
int size;
int32_t size;
uint32_t i;
(void)unused_L;
@@ -797,7 +797,7 @@ void rc_runtime_invalidate_address(rc_runtime_t* self, uint32_t address) {
} while (memref_list);
}
void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
rc_runtime_validate_address_t validate_handler) {
int num_invalid = 0;
rc_memref_list_t* memref_list = &self->memrefs->memrefs;

View File

@@ -920,7 +920,7 @@ uint32_t rc_runtime_progress_size(const rc_runtime_t* runtime, void* unused_L)
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, void* unused_L)
{
return rc_runtime_serialize_progress_sized(buffer, 0xFFFFFFFF, runtime, unused_L);
return rc_runtime_serialize_progress_sized((uint8_t*)buffer, 0xFFFFFFFF, runtime, unused_L);
}
int rc_runtime_serialize_progress_sized(uint8_t* buffer, uint32_t buffer_size, const rc_runtime_t* runtime, void* unused_L)

View File

@@ -61,27 +61,41 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
char buffer[64] = "A:";
const char* buffer_ptr;
char* ptr;
char c;
int done;
/* convert legacy format into condset */
next_clause = &self->conditions;
do {
num_measured_conditions = 0;
/* count the number of joiners and add one to determine the number of clauses. */
done = 0;
num_measured_conditions = 1;
buffer_ptr = *memaddr;
while ((c = *buffer_ptr++) && c != '$') {
if (c == '_') {
++num_measured_conditions;
buffer[0] = 'A'; /* reset to AddSource */
do {
switch (*buffer_ptr++) {
case '_': /* add next */
++num_measured_conditions;
buffer[0] = 'A'; /* reset to AddSource */
break;
case '*': /* multiply */
if (*buffer_ptr == '-') {
/* multiplication by a negative number will convert to SubSource */
++buffer_ptr;
buffer[0] = 'B';
}
break;
case '\0': /* end of string */
case '$': /* maximum of */
case ':': /* end of leaderboard clause */
case ')': /* end of rich presence macro */
done = 1;
break;
default: /* assume everything else is valid - bad stuff will be filtered out later */
break;
}
else if (c == '*' && *buffer_ptr == '-') {
/* multiplication by a negative number will convert to SubSource */
++buffer_ptr;
buffer[0] = 'B';
}
}
} while (!done);
/* if last condition is SubSource, we'll need to add a dummy condition for the Measured */
if (buffer[0] == 'B')

View File

@@ -480,6 +480,7 @@ static int rc_hash_file_from_buffer(char hash[33], uint32_t console_id, const rc
rc_hash_iterator_t buffered_file_iterator;
memset(&buffered_file_iterator, 0, sizeof(buffered_file_iterator));
memcpy(&buffered_file_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
buffered_file_iterator.userdata = iterator->userdata;
buffered_file_iterator.callbacks.filereader.open = rc_file_open_buffered_file;
buffered_file_iterator.callbacks.filereader.close = rc_file_close_buffered_file;
@@ -650,6 +651,7 @@ int rc_hash_buffered_file(char hash[33], uint32_t console_id, const rc_hash_iter
rc_hash_iterator_t buffer_iterator;
memset(&buffer_iterator, 0, sizeof(buffer_iterator));
memcpy(&buffer_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
buffer_iterator.userdata = iterator->userdata;
buffer_iterator.path = iterator->path;
buffer_iterator.buffer = buffer;
buffer_iterator.buffer_size = (size_t)size;
@@ -773,6 +775,7 @@ static int rc_hash_generate_from_playlist(char hash[33], uint32_t console_id, co
memset(&first_file_iterator, 0, sizeof(first_file_iterator));
memcpy(&first_file_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
first_file_iterator.userdata = iterator->userdata;
first_file_iterator.path = disc_path; /* rc_hash_destory_iterator will free */
result = rc_hash_from_file(hash, console_id, &first_file_iterator);
@@ -1293,7 +1296,8 @@ static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator,
}
/* find the handler for the extension */
handler = bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
handler = (const rc_hash_iterator_ext_handler_entry_t*)
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
if (handler) {
handler->handler(iterator, handler->data);
} else {

View File

@@ -21758,6 +21758,8 @@ SLES-52954:
SLES-52955:
name: "Deadly Strike"
region: "PAL-E"
roundModes:
eeDivRoundMode: 3 # Fixes grid like pattern.
SLES-52956:
name: "Action Girlz Racing"
region: "PAL-E"
@@ -30296,7 +30298,7 @@ SLES-55674:
region: "PAL-F-G"
SLES-55675:
name: "Pro Evolution Soccer 2014"
region: "PAL-G-I"
region: "PAL-GR-I"
SLES-55676:
name: "Pro Evolution Soccer 2014"
region: "PAL-P-S"
@@ -38928,6 +38930,8 @@ SLPM-62459:
name-en: "Simple 2000 Series Vol. 16 - Sengoku vs. Gendai"
region: "NTSC-J"
compat: 5
roundModes:
eeDivRoundMode: 3 # Fixes grid like pattern.
SLPM-62460:
name: "SuperLite2000 シミュレーション 箱庭鉄道 ~ブルートレイン・特急編~"
name-sort: "すーぱーらいと 2000 しみゅれーしょん はこにわてつどう ぶるーとれいんとっきゅうへん"

Binary file not shown.

View File

@@ -191,6 +191,7 @@
0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,
03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows,
030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,
03000000250900000218000000000000,Elecom PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000411200004450000000000000,Elecom U1012,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,
030000006e0500000320000000000000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,
030000006e0500000e20000000000000,Elecom U3912T,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,
@@ -252,6 +253,7 @@
03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f025000021c1000010010000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -478,6 +480,7 @@
030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,
03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows,
03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000ec110000e1a7000000000000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Windows,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,
@@ -535,7 +538,6 @@
03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000250900000118000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000250900000218000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,
030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,
030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
@@ -681,6 +683,7 @@
03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,
03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
030000009d0d00001130000000000000,Sanwa PlayStation Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows,
03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -841,6 +844,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,
030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows,
03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000073500000400000000000000,Zenaim Arcade Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -1018,6 +1022,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000ec110000e1a7000001010000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Mac OS X,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
@@ -1290,6 +1295,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,
030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,
03000000632500007a05000001020000,Cosmic Byte Ares Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,
03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
@@ -1311,6 +1317,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
03000000373500000b10000019010000,GameSir Cyclone 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1503,8 +1510,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,
060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,
03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,
03000000ec110000e1a7000010010000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux,
060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
@@ -1656,7 +1663,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f0000c100000072056800,Retro Bit Legacy16,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:+a4,misc1:b11,rightshoulder:b10,righttrigger:+a5,start:b6,x:b3,y:b2,platform:Linux,
03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,
0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,
190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
@@ -1692,11 +1698,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
03000000632500002305000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002605000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000632500007505000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000632500007505000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000bc2000000055000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,

View File

@@ -305,64 +305,64 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
uint2 pos = uint2(input.p.xy);
// Collapse separate R G B A areas into their base pixel
uint2 column = (pos & ~uint2(0u, 3u)) / uint2(1,2);
uint2 column = (pos & ~uint2(0u, 3u)) / uint2(1u, 2u);
uint2 subcolumn = (pos & uint2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
uint PSM = uint(DOFFSET);
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32) != 0)
if ((pos.x & 32u) != 0u)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64) != 0)
if ((pos.x & 64u) != 0u)
{
column.x -= 32;
column.x -= 32u;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
if ((pos.y & 32u) != 0u)
{
column.y -= 16;
column.x += 32;
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96) != 0)
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
column.x ^= 32u;
column.y ^= 32u;
}
}
@@ -371,10 +371,10 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
// Compensate for potentially differing page pitch.
uint SBW = uint(EMODA);
uint DBW = uint(EMODC);
uint2 block_xy = coord / uint2(64,64);
uint block_num = (block_xy.y * (DBW / 128)) + block_xy.x;
uint2 block_offset = uint2((block_num % (SBW / 64)) * 64, (block_num / (SBW / 64)) * 64);
coord = (coord % uint2(64, 64)) + block_offset;
uint2 block_xy = coord / uint2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uint2 block_offset = uint2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uint2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
@@ -394,18 +394,16 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = (float)(((green << 5) | red) & 0xFF) / 255.0f;
output.c = (float4)(sel0);
output.c = (float4)(((float)(((green << 5) | red) & 0xFFu)) / 255.0f);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = (float)((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
output.c = (float4)(sel0);
output.c = (float4)(((float)((alpha | (blue << 2) | (green >> 3)) & 0xFFu)) / 255.0f);
}
return output;
}

View File

@@ -37,7 +37,7 @@ float4 ContrastSaturationBrightness(float4 color) // Ported to HLSL
float3 conColor = lerp(AvgLumin, satColor, con);
float3 csb = conColor;
csb = pow(csb, 1.0 / gam);
csb = exp2(log2(csb) * (1.0 / gam));
color.rgb = csb;
return color;
}

View File

@@ -258,62 +258,62 @@ void ps_convert_rgb5a1_8i()
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1u, 2u);
uvec2 subcolumn = (pos & uvec2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32) != 0)
if ((pos.x & 32u) != 0u)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64) != 0)
if ((pos.x & 64u) != 0u)
{
column.x -= 32;
column.x -= 32u;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
if ((pos.y & 32u) != 0u)
{
column.y -= 16;
column.x += 32;
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96) != 0)
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
column.x ^= 32u;
column.y ^= 32u;
}
}
uvec2 coord = column | subcolumn;
@@ -342,18 +342,16 @@ void ps_convert_rgb5a1_8i()
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFF) / 255.0f;
SV_Target0 = vec4(sel0);
SV_Target0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
SV_Target0 = vec4(sel0);
SV_Target0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
}
}
#endif

View File

@@ -332,62 +332,62 @@ void ps_convert_rgb5a1_8i()
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1u, 2u);
uvec2 subcolumn = (pos & uvec2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32) != 0)
if ((pos.x & 32u) != 0u)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64) != 0)
if ((pos.x & 64u) != 0u)
{
column.x -= 32;
column.x -= 32u;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
if ((pos.y & 32u) != 0u)
{
column.y -= 16;
column.x += 32;
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96) != 0)
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16;
column.y ^= 8;
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
column.x ^= 32u;
column.y ^= 32u;
}
}
uvec2 coord = column | subcolumn;
@@ -410,23 +410,22 @@ void ps_convert_rgb5a1_8i()
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
o_col0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
o_col0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
}
}
#endif

View File

@@ -16,6 +16,8 @@
<DepsDLLs Include="$(DepsBinDir)libpng16.dll" />
<DepsDLLs Include="$(DepsBinDir)libsharpyuv.dll" />
<DepsDLLs Include="$(DepsBinDir)libwebp.dll" />
<DepsDLLs Include="$(DepsBinDir)libwebpdemux.dll" />
<DepsDLLs Include="$(DepsBinDir)libwebpmux.dll" />
<DepsDLLs Include="$(DepsBinDir)lz4.dll" />
<DepsDLLs Include="$(DepsBinDir)SDL3.dll" />
<DepsDLLs Include="$(DepsBinDir)shaderc_shared.dll" />

View File

@@ -473,8 +473,8 @@ static void PrintCommandLineHelp(const char* progname)
std::fprintf(stderr, " -help: Displays this information and exits.\n");
std::fprintf(stderr, " -version: Displays version information and exits.\n");
std::fprintf(stderr, " -dumpdir <dir>: Frame dump directory (will be dumped as filename_frameN.png).\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices), respectively, per draw. Generates lots of data.\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i|tr]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices, transfers (list)), transfers (images), respectively, per draw. Generates lots of data.\n");
std::fprintf(stderr, " -dumprange N[,L,B]: Start dumping from draw N (base 0), stops after L draws, and only "
"those draws that are multiples of B (intersection of -dumprange and -dumprangef used)."
"Defaults to 0,-1,1 (all draws). Only used if -dump used.\n");
@@ -558,6 +558,8 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveAlpha", true);
if (str.find("i") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveInfo", true);
if (str.find("tr") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveTransferImages", true);
continue;
}
else if (CHECK_ARG_PARAM("-dumprange"))

View File

@@ -12,6 +12,7 @@
#include "DebugTools/DisassemblyManager.h"
#include "common/Console.h"
#include <QtCore/QPointer>
#include <QtWidgets/QMessageBox>
#include <algorithm>
@@ -538,15 +539,20 @@ bool BreakpointModel::insertBreakpointRows(int row, int count, std::vector<Break
void BreakpointModel::refreshData()
{
Host::RunOnCPUThread([this]() mutable {
const QPointer<BreakpointModel> model(this);
const BreakPointCpu cpu_type = m_cpu.getCpuType();
Host::RunOnCPUThread([model, cpu_type]() mutable {
std::vector<BreakpointMemcheck> all_breakpoints;
std::ranges::move(CBreakPoints::GetBreakpoints(m_cpu.getCpuType(), false), std::back_inserter(all_breakpoints));
std::ranges::move(CBreakPoints::GetMemChecks(m_cpu.getCpuType()), std::back_inserter(all_breakpoints));
std::ranges::move(CBreakPoints::GetBreakpoints(cpu_type, false), std::back_inserter(all_breakpoints));
std::ranges::move(CBreakPoints::GetMemChecks(cpu_type), std::back_inserter(all_breakpoints));
QtHost::RunOnUIThread([this, breakpoints = std::move(all_breakpoints)]() mutable {
beginResetModel();
m_breakpoints = std::move(breakpoints);
endResetModel();
QtHost::RunOnUIThread([model, breakpoints = std::move(all_breakpoints)]() mutable {
if (!model)
return;
model->beginResetModel();
model->m_breakpoints = std::move(breakpoints);
model->endResetModel();
});
});
}

View File

@@ -203,11 +203,6 @@ void DebuggerView::updateStyleSheet()
#endif
}
// HACK: Make the font size smaller without applying a stylesheet to the
// whole window (which would impact performance).
if (g_debugger_window)
stylesheet += QString("font-size: %1pt;").arg(g_debugger_window->fontSize());
setStyleSheet(stylesheet);
}

View File

@@ -179,9 +179,9 @@ void DebuggerWindow::clearToolBarState()
void DebuggerWindow::setupFonts()
{
m_font_size = Host::GetBaseIntSettingValue("Debugger/UserInterface", "FontSize", DEFAULT_FONT_SIZE);
m_font_size = Host::GetBaseIntSettingValue("Debugger/UserInterface", "FontSize", QApplication::font().pointSize());
if (m_font_size < MINIMUM_FONT_SIZE || m_font_size > MAXIMUM_FONT_SIZE)
m_font_size = DEFAULT_FONT_SIZE;
m_font_size = QApplication::font().pointSize();
m_ui.actionIncreaseFontSize->setShortcuts(QKeySequence::ZoomIn);
connect(m_ui.actionIncreaseFontSize, &QAction::triggered, this, [this]() {
@@ -208,7 +208,7 @@ void DebuggerWindow::setupFonts()
});
connect(m_ui.actionResetFontSize, &QAction::triggered, this, [this]() {
m_font_size = DEFAULT_FONT_SIZE;
m_font_size = QApplication::font().pointSize();
updateFontActions();
updateTheme();
@@ -222,7 +222,7 @@ void DebuggerWindow::updateFontActions()
{
m_ui.actionIncreaseFontSize->setEnabled(m_font_size < MAXIMUM_FONT_SIZE);
m_ui.actionDecreaseFontSize->setEnabled(m_font_size > MINIMUM_FONT_SIZE);
m_ui.actionResetFontSize->setEnabled(m_font_size != DEFAULT_FONT_SIZE);
m_ui.actionResetFontSize->setEnabled(m_font_size != QApplication::font().pointSize());
}
void DebuggerWindow::saveFontSize()
@@ -239,15 +239,13 @@ int DebuggerWindow::fontSize()
void DebuggerWindow::updateTheme()
{
// TODO: Migrate away from stylesheets to improve performance.
if (m_font_size != DEFAULT_FONT_SIZE)
{
int size = m_font_size + QApplication::font().pointSize() - DEFAULT_FONT_SIZE;
setStyleSheet(QString("* { font-size: %1pt; } QTabBar { font-size: %2pt; }").arg(size).arg(size + 1));
}
else
{
setStyleSheet(QString("font-size: %1pt;").arg(m_font_size));
// HACK: Improve performance for the default font size setting. It seems we
// need to call setStyleSheet twice here otherwise some widgets do not
// update properly.
if (m_font_size == QApplication::font().pointSize())
setStyleSheet(QString());
}
dockManager().updateTheme();
}

View File

@@ -73,7 +73,6 @@ private:
QTimer* m_refresh_timer = nullptr;
int m_font_size;
static const constexpr int DEFAULT_FONT_SIZE = 10;
static const constexpr int MINIMUM_FONT_SIZE = 5;
static const constexpr int MAXIMUM_FONT_SIZE = 30;
};

View File

@@ -126,47 +126,49 @@ void DisassemblyView::contextAssembleInstruction()
u32 encodedInstruction;
std::string errorText;
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), &cpu(), m_selectedAddressStart, encodedInstruction, errorText);
if (!valid)
{
QMessageBox::warning(this, tr("Assemble Error"), QString::fromStdString(errorText));
return;
}
else
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu(), val = encodedInstruction] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, val);
}
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
}
setInstructions(m_selectedAddressStart, m_selectedAddressEnd, encodedInstruction);
}
void DisassemblyView::contextNoopInstruction()
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] {
for (u32 i = start; i <= end; i += 4)
{
this->m_nopedInstructions.insert({i, cpu->read32(i)});
cpu->write32(i, 0x00);
}
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
setInstructions(m_selectedAddressStart, m_selectedAddressEnd, 0);
}
void DisassemblyView::contextRestoreInstruction()
{
Host::RunOnCPUThread([this, start = m_selectedAddressStart, end = m_selectedAddressEnd, cpu = &cpu()] {
for (u32 i = start; i <= end; i += 4)
const u32 start = m_selectedAddressStart;
const u32 count = (m_selectedAddressEnd - start) / 4 + 1;
std::vector<std::optional<u32>> original_instructions;
original_instructions.reserve(count);
for (u32 i = 0; i < count; i++)
{
const u32 address = start + i * 4;
const auto instruction = m_nopedInstructions.find(address);
if (instruction != m_nopedInstructions.end())
{
if (this->m_nopedInstructions.find(i) != this->m_nopedInstructions.end())
{
cpu->write32(i, this->m_nopedInstructions[i]);
this->m_nopedInstructions.erase(i);
}
original_instructions.emplace_back(instruction->second);
m_nopedInstructions.erase(instruction);
}
else
{
original_instructions.emplace_back(std::nullopt);
}
}
const QPointer<DisassemblyView> view(this);
Host::RunOnCPUThread([view, start, count, original_instructions = std::move(original_instructions), cpu = &cpu()] {
for (u32 i = 0; i < count; i++)
{
u32 address = start + i * 4;
if (original_instructions[i].has_value())
cpu->write32(address, *original_instructions[i]);
}
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
@@ -294,11 +296,21 @@ void DisassemblyView::contextStubFunction()
FunctionInfo function = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
u32 address = function.address.valid() ? function.address.value : m_selectedAddressStart;
Host::RunOnCPUThread([this, address, cpu = &cpu()] {
this->m_stubbedFunctions.insert({address, {cpu->read32(address), cpu->read32(address + 4)}});
const QPointer<DisassemblyView> view(this);
Host::RunOnCPUThread([view, address, cpu = &cpu()] {
const u32 first_instruction = cpu->read32(address);
const u32 second_instruction = cpu->read32(address + 4);
cpu->write32(address, 0x03E00008); // jr ra
cpu->write32(address + 4, 0x00000000); // nop
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
QtHost::RunOnUIThread([view, address, first_instruction, second_instruction]() {
if (!view)
return;
view->m_stubbedFunctions.emplace(address, std::make_tuple(first_instruction, second_instruction));
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
});
}
@@ -314,11 +326,12 @@ void DisassemblyView::contextRestoreFunction()
auto stub = m_stubbedFunctions.find(address);
if (stub != m_stubbedFunctions.end())
{
Host::RunOnCPUThread([this, address, cpu = &cpu(), stub] {
auto [first_instruction, second_instruction] = stub->second;
const auto [first_instruction, second_instruction] = stub->second;
m_stubbedFunctions.erase(stub);
Host::RunOnCPUThread([address, cpu = &cpu(), first_instruction, second_instruction] {
cpu->write32(address, first_instruction);
cpu->write32(address + 4, second_instruction);
this->m_stubbedFunctions.erase(address);
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
}
@@ -974,18 +987,47 @@ void DisassemblyView::toggleBreakpoint(u32 address)
if (!cpu().isAlive())
return;
QPointer<DisassemblyView> disassembly_widget(this);
Host::RunOnCPUThread([cpu = &cpu(), address, disassembly_widget] {
const QPointer<DisassemblyView> view(this);
Host::RunOnCPUThread([view, cpu = &cpu(), address] {
if (!CBreakPoints::IsAddressBreakPoint(cpu->getCpuType(), address))
CBreakPoints::AddBreakPoint(cpu->getCpuType(), address);
else
CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), address);
QtHost::RunOnUIThread([cpu, disassembly_widget]() {
QtHost::RunOnUIThread([view, cpu]() {
BreakpointModel::getInstance(*cpu)->refreshData();
if (disassembly_widget)
disassembly_widget->repaint();
if (view)
view->repaint();
});
});
}
void DisassemblyView::setInstructions(u32 start, u32 end, u32 value)
{
const u32 count = (end - start) / 4 + 1;
const QPointer<DisassemblyView> view(this);
Host::RunOnCPUThread([view, start, count, cpu = &cpu(), value] {
std::vector<u32> original_instructions;
original_instructions.reserve(count);
for (u32 i = 0; i < count; i++)
{
const u32 address = start + i * 4;
original_instructions.emplace_back(cpu->read32(address));
cpu->write32(address, value);
}
QtHost::RunOnUIThread([view, start, count, original_instructions = std::move(original_instructions)]() {
if (!view)
return;
for (u32 i = 0; i < count; i++)
{
const u32 address = start + i * 4;
view->m_nopedInstructions.emplace(address, original_instructions[i]);
}
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
});
});
}

View File

@@ -92,6 +92,7 @@ private:
};
QString FetchSelectionInfo(SelectionInfo selInfo);
void setInstructions(u32 start, u32 end, u32 value);
bool AddressCanRestore(u32 start, u32 end);
bool FunctionCanRestore(u32 address);
};

View File

@@ -8,6 +8,7 @@
#include "QtHost.h"
#include "QtUtils.h"
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtGui/QActionGroup>
#include <QtGui/QClipboard>
#include <QtGui/QMouseEvent>
@@ -246,9 +247,16 @@ void MemoryViewTable::InsertIntoSelectedHexView(u8 value, DebugInterface& cpu)
u8 newVal = value << (selectedNibbleHI ? 4 : 0);
curVal |= newVal;
Host::RunOnCPUThread([this, address = selectedAddress, &cpu, val = curVal] {
const QPointer<MemoryViewTable> table(this);
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = curVal] {
cpu.write8(address, val);
QtHost::RunOnUIThread([this] { parent->update(); });
QtHost::RunOnUIThread([table] {
if (!table)
return;
table->parent->update();
});
});
}
@@ -261,46 +269,57 @@ void MemoryViewTable::InsertAtCurrentSelection(const QString& text, DebugInterfa
// This approach prevents one from pasting on a nibble boundary, but that is almost always
// user error, and we don't have an undo function in this view, so best to stay conservative.
QByteArray input = selectedText ? text.toUtf8() : QByteArray::fromHex(text.toUtf8());
Host::RunOnCPUThread([this, address = selectedAddress, &cpu, inBytes = input] {
const QPointer<MemoryViewTable> table(this);
const MemoryViewType display_type = displayType;
const bool little_endian = littleEndian;
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, input, display_type, little_endian] {
u32 currAddr = address;
for (int i = 0; i < inBytes.size(); i++)
for (int i = 0; i < input.size(); i++)
{
cpu.write8(currAddr, inBytes[i]);
currAddr = nextAddress(currAddr);
QtHost::RunOnUIThread([this] { parent->update(); });
cpu.write8(currAddr, input[i]);
currAddr = nextAddress(currAddr, address, display_type, little_endian);
}
QtHost::RunOnUIThread([this, inBytes] { UpdateSelectedAddress(selectedAddress + inBytes.size()); parent->update(); });
u32 end_address = address + input.size();
QtHost::RunOnUIThread([table, end_address] {
if (!table)
return;
table->UpdateSelectedAddress(end_address);
table->parent->update();
});
});
}
u32 MemoryViewTable::nextAddress(u32 addr)
u32 MemoryViewTable::nextAddress(u32 addr, u32 selected_address, MemoryViewType display_type, bool little_endian)
{
if (!littleEndian)
if (!little_endian)
{
return addr + 1;
}
else
{
if (selectedAddress % static_cast<s32>(displayType) == 0)
return addr + (static_cast<s32>(displayType) * 2 - 1);
if (selected_address % static_cast<s32>(display_type) == 0)
return addr + (static_cast<s32>(display_type) * 2 - 1);
else
return addr - 1;
}
}
u32 MemoryViewTable::prevAddress(u32 addr)
u32 MemoryViewTable::prevAddress(u32 addr, u32 selected_address, MemoryViewType display_type, bool little_endian)
{
if (!littleEndian)
if (!little_endian)
{
return addr - 1;
}
else
{
// It works
if ((addr & (static_cast<u32>(displayType) - 1)) == (static_cast<u32>(displayType) - 1))
return addr - (static_cast<s32>(displayType) * 2 - 1);
if ((addr & (static_cast<u32>(display_type) - 1)) == (static_cast<u32>(display_type) - 1))
return addr - (static_cast<s32>(display_type) * 2 - 1);
else
return selectedAddress + 1;
return selected_address + 1;
}
}
@@ -358,9 +377,17 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar, DebugInterface& cpu)
{
if (keyCharIsText || (!keychar.isNonCharacter() && keychar.category() != QChar::Other_Control))
{
Host::RunOnCPUThread([this, address = selectedAddress, &cpu, val = keychar.toLatin1()] {
const QPointer<MemoryViewTable> table(this);
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = keychar.toLatin1()] {
cpu.write8(address, val);
QtHost::RunOnUIThread([this] { UpdateSelectedAddress(selectedAddress + 1); parent->update(); });
QtHost::RunOnUIThread([table, address]() {
if (!table)
return;
table->UpdateSelectedAddress(address + 1);
table->parent->update();
});
});
pressHandled = true;
}
@@ -369,12 +396,22 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar, DebugInterface& cpu)
{
case Qt::Key::Key_Backspace:
case Qt::Key::Key_Escape:
Host::RunOnCPUThread([this, address = selectedAddress, &cpu] {
{
const QPointer<MemoryViewTable> table(this);
Host::RunOnCPUThread([table, address = selectedAddress, &cpu] {
cpu.write8(address, 0);
QtHost::RunOnUIThread([this] {BackwardSelection(); parent->update(); });
QtHost::RunOnUIThread([table] {
if (!table)
return;
table->BackwardSelection();
table->parent->update();
});
});
pressHandled = true;
break;
}
case Qt::Key::Key_Right:
ForwardSelection();
pressHandled = true;

View File

@@ -26,8 +26,10 @@ enum class MemoryViewType
DWORD = 8,
};
class MemoryViewTable
class MemoryViewTable : public QObject
{
Q_OBJECT
private:
QWidget* parent;
MemoryViewType displayType = MemoryViewType::BYTE;
bool littleEndian = true;
@@ -60,8 +62,8 @@ class MemoryViewTable
}
}
u32 nextAddress(u32 addr);
u32 prevAddress(u32 addr);
static u32 nextAddress(u32 addr, u32 selected_address, MemoryViewType display_type, bool little_endian);
static u32 prevAddress(u32 addr, u32 selected_address, MemoryViewType display_type, bool little_endian);
public:
MemoryViewTable(QWidget* parent)

View File

@@ -12,16 +12,21 @@
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
#include "fmt/format.h"
#include <QtCore/QSortFilterProxyModel>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtGui/QPixmapCache>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QApplication>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMenu>
#include <QtWidgets/QScrollBar>
@@ -284,6 +289,7 @@ void GameListWidget::initialize()
m_empty_ui.supportedFormats->setText(qApp->translate("GameListWidget", SUPPORTED_FORMATS_STRING));
connect(m_empty_ui.addGameDirectory, &QPushButton::clicked, this, [this]() { emit addGameDirectoryRequested(); });
connect(m_empty_ui.scanForNewGames, &QPushButton::clicked, this, [this]() { refresh(false); });
connect(qApp, &QGuiApplication::applicationStateChanged, this, [this]() { GameListWidget::updateCustomBackgroundState(); });
m_ui.stack->insertWidget(2, m_empty_widget);
if (Host::GetBaseBoolSettingValue("UI", "GameListGridView", false))
@@ -295,6 +301,135 @@ void GameListWidget::initialize()
updateToolbar();
resizeTableViewColumnsToFit();
setCustomBackground();
}
static void resizeAndPadImage(QImage* image, int expected_width, int expected_height, bool fill_with_top_left, bool expand_to_fill)
{
const qreal dpr = image->devicePixelRatio();
const int dpr_expected_width = static_cast<int>(static_cast<qreal>(expected_width) * dpr);
const int dpr_expected_height = static_cast<int>(static_cast<qreal>(expected_height) * dpr);
if (image->width() == dpr_expected_width && image->height() == dpr_expected_height)
return;
// Resize
if (((static_cast<float>(image->width()) / static_cast<float>(image->height())) >=
(static_cast<float>(dpr_expected_width) / static_cast<float>(dpr_expected_height))) != expand_to_fill)
{
*image = image->scaledToWidth(dpr_expected_width, Qt::SmoothTransformation);
}
else
{
*image = image->scaledToHeight(dpr_expected_height, Qt::SmoothTransformation);
}
if (image->width() == dpr_expected_width && image->height() == dpr_expected_height)
return;
// Padding
int xoffs = 0;
int yoffs = 0;
const int image_width = image->width();
const int image_height = image->height();
if ((image_width < dpr_expected_width) != expand_to_fill)
xoffs = static_cast<int>(static_cast<qreal>((dpr_expected_width - image_width) / 2) / dpr);
if ((image_height < dpr_expected_height) != expand_to_fill)
yoffs = static_cast<int>(static_cast<qreal>((dpr_expected_height - image_height) / 2) / dpr);
QImage padded_image(dpr_expected_width, dpr_expected_height, QImage::Format_ARGB32);
padded_image.setDevicePixelRatio(dpr);
if (fill_with_top_left)
padded_image.fill(image->pixel(0, 0));
else
padded_image.fill(Qt::transparent);
// Painting
QPainter painter;
const float opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity");
if (painter.begin(&padded_image))
{
painter.setOpacity((static_cast<float>(opacity / 100.0f))); // Qt expects the range to be from 0.0 to 1.0
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.drawImage(xoffs, yoffs, *image);
painter.end();
}
*image = std::move(padded_image);
}
void GameListWidget::setCustomBackground(bool force_refresh)
{
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
bool enabled = Host::GetBaseBoolSettingValue("UI", "GameListBackgroundEnabled");
bool fill = Host::GetBaseBoolSettingValue("UI", "GameListBackgroundFill");
// Cleanup old animation if it still exists on gamelist
if (m_background_movie != nullptr)
{
delete m_background_movie;
m_background_movie = nullptr;
}
if (!Path::IsAbsolute(path))
path = Path::Combine(EmuFolders::DataRoot, path);
// Only try to create background both if path are valid and custom background are enabled
if ((!path.empty() && FileSystem::FileExists(path.c_str())) && enabled)
{
QMovie* new_movie;
if (Path::GetExtension(path) == "png")
// Use apng plugin
new_movie = new QMovie(QString::fromStdString(path), "apng", this);
else
new_movie = new QMovie(QString::fromStdString(path), QByteArray(), this);
if (new_movie->isValid())
m_background_movie = new_movie;
else
{
Console.Warning("Failed to load background movie from: %s", path.c_str());
delete new_movie;
}
}
// If there is no valid background then reset fallback to UI state
if (!m_background_movie)
{
m_ui.stack->setPalette(QApplication::palette());
m_table_view->setAlternatingRowColors(true);
return;
}
// Background is valid, connect the signals and start animation in gamelist
connect(m_background_movie, &QMovie::frameChanged, this, [this, fill]() { processBackgroundFrames(fill); });
updateCustomBackgroundState(force_refresh);
m_table_view->setAlternatingRowColors(false);
}
void GameListWidget::updateCustomBackgroundState(bool force_start)
{
if (m_background_movie)
{
if ((isVisible() && (isActiveWindow() || force_start)) && qGuiApp->applicationState() == Qt::ApplicationActive)
m_background_movie->start();
else
m_background_movie->stop();
}
}
void GameListWidget::processBackgroundFrames(bool fill_area)
{
QImage img = m_background_movie->currentImage();
img.setDevicePixelRatio(devicePixelRatioF());
const int widget_width = m_ui.stack->width();
const int widget_height = m_ui.stack->height();
resizeAndPadImage(&img, widget_width, widget_height, false, fill_area);
QPalette new_palette(m_ui.stack->palette());
new_palette.setBrush(QPalette::Base, img);
m_ui.stack->setPalette(new_palette);
}
bool GameListWidget::isShowingGameList() const
@@ -555,11 +690,24 @@ void GameListWidget::updateToolbar()
m_ui.gridScale->setEnabled(grid_view);
}
void GameListWidget::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
updateCustomBackgroundState();
}
void GameListWidget::hideEvent(QHideEvent* event)
{
QWidget::hideEvent(event);
updateCustomBackgroundState();
}
void GameListWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
resizeTableViewColumnsToFit();
m_model->updateCacheSize(width(), height());
setCustomBackground();
}
bool GameListWidget::event(QEvent* event)

View File

@@ -8,6 +8,7 @@
#include "pcsx2/GameList.h"
#include <QtGui/QMovie>
#include <QtWidgets/QListView>
#include <QtWidgets/QTableView>
@@ -48,6 +49,9 @@ public:
void refresh(bool invalidate_cache);
void cancelRefresh();
void reloadThemeSpecificImages();
void setCustomBackground(bool force = false);
void updateCustomBackgroundState(bool force_start = false);
void processBackgroundFrames(bool fill_area);
bool isShowingGameList() const;
bool isShowingGameGrid() const;
@@ -92,6 +96,8 @@ public Q_SLOTS:
void refreshGridCovers();
protected:
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
bool event(QEvent* event) override;
@@ -115,4 +121,6 @@ private:
Ui::EmptyGameListWidget m_empty_ui;
GameListRefreshThread* m_refresh_thread = nullptr;
QMovie* m_background_movie = nullptr;
};

View File

@@ -154,7 +154,7 @@ void MainWindow::initialize()
});
});
// The cocoa backing isn't initialized yet, delay this until stuff is set up with a `RunOnUIThread` call
QtHost::RunOnUIThread([this]{
QtHost::RunOnUIThread([this] {
CocoaTools::MarkHelpMenu(m_ui.menuHelp->toNSMenu());
});
#endif
@@ -339,6 +339,8 @@ void MainWindow::connectSignals()
connect(m_ui.actionToolbarSettings, &QAction::triggered, this, &MainWindow::onSettingsTriggeredFromToolbar);
connect(m_ui.actionToolbarControllerSettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsWindow::Category::GlobalSettings); });
connect(m_ui.actionToolbarHotkeySettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsWindow::Category::HotkeySettings); });
connect(m_ui.actionToolbarScreenshot, &QAction::triggered, this, &MainWindow::onScreenshotActionTriggered);
connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close);
connect(m_ui.actionScreenshot, &QAction::triggered, this, &MainWindow::onScreenshotActionTriggered);
@@ -1854,15 +1856,15 @@ void MainWindow::onInputRecNewActionTriggered()
if (result == QDialog::Accepted)
{
Host::RunOnCPUThread(
[&, filePath = dlg.getFilePath(), fromSavestate = dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE,
[filePath = dlg.getFilePath(), fromSavestate = dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE,
authorName = dlg.getAuthorName()]() {
if (g_InputRecording.create(filePath, fromSavestate, authorName))
{
QtHost::RunOnUIThread([&]() {
m_ui.actionInputRecNew->setEnabled(false);
m_ui.actionInputRecStop->setEnabled(true);
m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
m_ui.actionToolbarReset->setEnabled(!g_InputRecording.isTypeSavestate());
QtHost::RunOnUIThread([]() {
g_main_window->m_ui.actionInputRecNew->setEnabled(false);
g_main_window->m_ui.actionInputRecStop->setEnabled(true);
g_main_window->m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
g_main_window->m_ui.actionToolbarReset->setEnabled(!g_InputRecording.isTypeSavestate());
});
}
else
@@ -1914,14 +1916,14 @@ void MainWindow::onInputRecPlayActionTriggered()
Host::RunOnCPUThread([]() { g_InputRecording.stop(); });
m_ui.actionInputRecStop->setEnabled(false);
}
Host::RunOnCPUThread([&, filename = QDir::toNativeSeparators(fileNames.first()).toStdString()]() {
Host::RunOnCPUThread([filename = QDir::toNativeSeparators(fileNames.first()).toStdString()]() {
if (g_InputRecording.play(filename))
{
QtHost::RunOnUIThread([&]() {
m_ui.actionInputRecNew->setEnabled(false);
m_ui.actionInputRecStop->setEnabled(true);
m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
m_ui.actionToolbarReset->setEnabled(!g_InputRecording.isTypeSavestate());
QtHost::RunOnUIThread([]() {
g_main_window->m_ui.actionInputRecNew->setEnabled(false);
g_main_window->m_ui.actionInputRecStop->setEnabled(true);
g_main_window->m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
g_main_window->m_ui.actionToolbarReset->setEnabled(!g_InputRecording.isTypeSavestate());
});
}
else
@@ -1938,13 +1940,13 @@ void MainWindow::onInputRecStopActionTriggered()
{
if (g_InputRecording.isActive())
{
Host::RunOnCPUThread([&]() {
Host::RunOnCPUThread([]() {
g_InputRecording.stop();
QtHost::RunOnUIThread([&]() {
m_ui.actionInputRecNew->setEnabled(true);
m_ui.actionInputRecStop->setEnabled(false);
m_ui.actionReset->setEnabled(true);
m_ui.actionToolbarReset->setEnabled(true);
QtHost::RunOnUIThread([]() {
g_main_window->m_ui.actionInputRecNew->setEnabled(true);
g_main_window->m_ui.actionInputRecStop->setEnabled(false);
g_main_window->m_ui.actionReset->setEnabled(true);
g_main_window->m_ui.actionToolbarReset->setEnabled(true);
});
});
}
@@ -2146,6 +2148,9 @@ void MainWindow::changeEvent(QEvent* event)
QtHost::SetIconThemeFromStyle();
reloadThemeSpecificImages();
}
if (event->type() == QEvent::ActivationChange)
m_game_list_widget->updateCustomBackgroundState();
}
static QString getFilenameFromMimeData(const QMimeData* md)
@@ -2221,7 +2226,7 @@ bool MainWindow::startFile(const QString& filename)
const auto lock = pauseAndLockVM();
if (QMessageBox::Yes != QMessageBox::question(this, tr("Confirm Reset"),
tr("The new ELF cannot be loaded without resetting the virtual machine. Do you want to reset the virtual machine now?")))
tr("The new ELF cannot be loaded without resetting the virtual machine. Do you want to reset the virtual machine now?")))
{
return true;
}
@@ -2692,6 +2697,7 @@ SettingsWindow* MainWindow::getSettingsWindow()
m_settings_window = new SettingsWindow();
connect(m_settings_window->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this, &MainWindow::onThemeChanged);
connect(m_settings_window->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::languageChanged, this, &MainWindow::onLanguageChanged);
connect(m_settings_window->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::backgroundChanged, m_game_list_widget, [this] { m_game_list_widget->setCustomBackground(true); });
connect(m_settings_window->getGameListSettingsWidget(), &GameListSettingsWidget::preferEnglishGameListChanged, this, [] {
g_main_window->m_game_list_widget->refreshGridCovers();
});
@@ -2941,7 +2947,7 @@ void MainWindow::openScreenshotsFolderForGame(const GameList::Entry* entry)
}
const QFileInfo fi(QString::fromStdString(game_dir));
QtUtils::OpenURL(this, QUrl::fromLocalFile(fi.absoluteFilePath()));
QtUtils::OpenURL(this, QUrl::fromLocalFile(fi.absoluteFilePath()));
}
std::optional<bool> MainWindow::promptForResumeState(const QString& save_state_path)

View File

@@ -132,7 +132,7 @@
</property>
<widget class="QMenu" name="menuDebugSwitchRenderer">
<property name="title">
<string>&amp;Switch Renderer</string>
<string>&amp;Switch Graphics API</string>
</property>
<property name="icon">
<iconset theme="brush-line"/>
@@ -266,6 +266,7 @@
<addaction name="separator"/>
<addaction name="actionToolbarSettings"/>
<addaction name="actionToolbarControllerSettings"/>
<addaction name="actionToolbarHotkeySettings"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionStartFile">
@@ -458,6 +459,14 @@
<string>&amp;Hotkeys</string>
</property>
</action>
<action name="actionToolbarHotkeySettings">
<property name="icon">
<iconset theme="keyboard-line"/>
</property>
<property name="text">
<string comment="In Toolbar">Hotkeys</string>
</property>
</action>
<action name="actionGraphicsSettings">
<property name="icon">
<iconset theme="image-fill"/>

View File

@@ -991,30 +991,33 @@ void Host::OnGameChanged(const std::string& title, const std::string& elf_overri
void EmuThread::updatePerformanceMetrics(bool force)
{
if (m_verbose_status && VMManager::HasValidVM())
if (VMManager::HasValidVM())
{
std::string gs_stat_str;
GSgetTitleStats(gs_stat_str);
QString gs_stat;
if (THREAD_VU1)
if (m_verbose_status)
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | VU: %5% | GS: %6%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
}
else
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | GS: %5%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
std::string gs_stat_str;
GSgetTitleStats(gs_stat_str);
if (THREAD_VU1)
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | VU: %5% | GS: %6%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
}
else
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | GS: %5%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
}
}
QMetaObject::invokeMethod(g_main_window->getStatusVerboseWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat));
@@ -2333,11 +2336,13 @@ bool QtHost::RunSetupWizard()
return true;
}
class PCSX2MainApplication : public QApplication {
class PCSX2MainApplication : public QApplication
{
public:
using QApplication::QApplication;
bool event(QEvent* event) override {
bool event(QEvent* event) override
{
if (event->type() == QEvent::FileOpen)
{
QFileOpenEvent* open = static_cast<QFileOpenEvent*>(event);

View File

@@ -9,6 +9,7 @@
#include "common/Error.h"
#include <QtCore/QPointer>
#include <QtWidgets/QMessageBox>
AchievementLoginDialog::AchievementLoginDialog(QWidget* parent, Achievements::LoginRequestReason reason)
@@ -44,11 +45,18 @@ void AchievementLoginDialog::loginClicked()
m_ui.status->setText(tr("Logging in..."));
enableUI(false);
Host::RunOnCPUThread([this, username = std::move(username), password = std::move(password)]() {
const QPointer<AchievementLoginDialog> dialog(this);
Host::RunOnCPUThread([dialog, username = std::move(username), password = std::move(password)]() {
Error error;
const bool result = Achievements::Login(username.c_str(), password.c_str(), &error);
const QString message = QString::fromStdString(error.GetDescription());
QMetaObject::invokeMethod(this, "processLoginResult", Qt::QueuedConnection, Q_ARG(bool, result), Q_ARG(const QString&, message));
QtHost::RunOnUIThread([dialog, result, message = std::move(message)]() {
if (!dialog)
return;
dialog->processLoginResult(result, message);
});
});
}

View File

@@ -71,6 +71,13 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="saveTransferImages">
<property name="text">
<string>Save Transfer Image Data</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">

View File

@@ -109,6 +109,7 @@ DebugSettingsWidget::DebugSettingsWidget(SettingsWindow* settings_dialog, QWidge
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveDepth, "EmuCore/GS", "SaveDepth", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveAlpha, "EmuCore/GS", "SaveAlpha", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveInfo, "EmuCore/GS", "SaveInfo", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_gs.saveTransferImages, "EmuCore/GS", "SaveTransferImages", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveDrawStart, "EmuCore/GS", "SaveDrawStart", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveDrawCount, "EmuCore/GS", "SaveDrawCount", 5000);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_gs.saveFrameStart, "EmuCore/GS", "SaveFrameStart", 0);
@@ -213,6 +214,7 @@ void DebugSettingsWidget::onDrawDumpingChanged()
m_gs.saveDepth->setEnabled(enabled);
m_gs.saveAlpha->setEnabled(enabled);
m_gs.saveInfo->setEnabled(enabled);
m_gs.saveTransferImages->setEnabled(enabled);
m_gs.saveDrawStart->setEnabled(enabled);
m_gs.saveDrawCount->setEnabled(enabled);
m_gs.saveFrameStart->setEnabled(enabled);

View File

@@ -23,7 +23,7 @@
<item row="0" column="0">
<widget class="QLabel" name="rendererText">
<property name="text">
<string>Renderer:</string>
<string>Graphics API:</string>
</property>
</widget>
</item>

View File

@@ -40,7 +40,7 @@ static constexpr RendererInfo s_renderer_info[] = {
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Metal"), GSRendererType::Metal},
#endif
//: Graphics backend/engine type (refers to emulating the GS in software, on the CPU). Translate accordingly.
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Software"), GSRendererType::SW},
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Software Renderer"), GSRendererType::SW},
//: Null here means that this is a graphics backend that will show nothing.
{QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Null"), GSRendererType::Null},
};
@@ -635,7 +635,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_fixes.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
tr("Flushes all targets in the texture cache back to local memory when shutting down. Can prevent lost visuals when saving "
"state or switching renderers, but can also cause graphical corruption."));
"state or switching graphics APIs, but can also cause graphical corruption."));
dialog()->registerWidgetHelp(m_fixes.estimateTextureRegion, tr("Estimate Texture Region"), tr("Unchecked"),
tr("Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games)."));
@@ -828,7 +828,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_advanced.useBlitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"),
//: Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit
tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 "
"renderer. This usually results in slower performance, but may be required for some "
"graphics API. This usually results in slower performance, but may be required for some "
"streaming applications, or to uncap framerates on some systems."));
dialog()->registerWidgetHelp(m_advanced.exclusiveFullscreenControl, tr("Allow Exclusive Fullscreen"), tr("Automatic (Default)"),

View File

@@ -53,16 +53,24 @@ void HotkeySettingsWidget::createButtons()
auto iter = m_categories.find(category);
if (iter == m_categories.end())
{
// Top line
QLabel* top_line = new QLabel(m_container);
top_line->setFrameShape(QFrame::HLine);
top_line->setFixedHeight(12);
m_layout->addWidget(top_line);
// Category label
QLabel* label = new QLabel(category, m_container);
QFont label_font(label->font());
label_font.setPointSizeF(14.0f);
label->setFont(label_font);
m_layout->addWidget(label);
QLabel* line = new QLabel(m_container);
line->setFrameShape(QFrame::HLine);
line->setFixedHeight(4);
m_layout->addWidget(line);
// Bottom line
QLabel* bottom_line = new QLabel(m_container);
bottom_line->setFrameShape(QFrame::HLine);
bottom_line->setFixedHeight(12);
m_layout->addWidget(bottom_line);
QGridLayout* layout = new QGridLayout();
layout->setContentsMargins(0, 0, 0, 0);

View File

@@ -4,11 +4,15 @@
#include "InterfaceSettingsWidget.h"
#include "AutoUpdaterDialog.h"
#include "Common.h"
#include "Host.h"
#include "MainWindow.h"
#include "SettingWidgetBinder.h"
#include "SettingsWindow.h"
#include "QtHost.h"
static const char* IMAGE_FILE_FILTER = QT_TRANSLATE_NOOP(GameListWidget,
"Supported Image Types (*.bmp *.gif *.jpg *.jpeg *.png *.webp)");
const char* InterfaceSettingsWidget::THEME_NAMES[] = {
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Native"),
//: Ignore what Crowdin says in this string about "[Light]/[Dark]" being untouchable here, these are not variables in this case and must be translated.
@@ -105,6 +109,13 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
QtHost::GetDefaultThemeName(), "InterfaceSettingsWidget");
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); });
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.backgroundOpacity, "UI", "GameListBackgroundOpacity", 100);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.backgroundFill, "UI", "GameListBackgroundFill", false);
connect(m_ui.backgroundBrowse, &QPushButton::clicked, [this]() { onSetGameListBackgroundTriggered(); });
connect(m_ui.backgroundReset, &QPushButton::clicked, [this]() { onClearGameListBackgroundTriggered(); });
connect(m_ui.backgroundOpacity, &QSpinBox::valueChanged, [this]() { emit backgroundChanged(); });
connect(m_ui.backgroundFill, &QCheckBox::checkStateChanged, [this]() {emit backgroundChanged(); });
populateLanguages();
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.language, "UI", "Language", QtHost::GetDefaultLanguage());
connect(m_ui.language, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit languageChanged(); });
@@ -139,8 +150,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
if (dialog()->isPerGameSettings())
{
// language/theme doesn't make sense to have in per-game settings
m_ui.verticalLayout->removeWidget(m_ui.preferencesGroup);
m_ui.preferencesGroup->hide();
m_ui.verticalLayout->removeWidget(m_ui.appearancesGroup);
m_ui.appearancesGroup->hide();
// start paused doesn't make sense, because settings are applied after ELF load.
m_ui.pauseOnStart->setEnabled(false);
@@ -185,6 +196,19 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
dialog()->registerWidgetHelp(
m_ui.startFullscreenUI, tr("Start Big Picture Mode"), tr("Unchecked"),
tr("Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches."));
dialog()->registerWidgetHelp(
m_ui.backgroundBrowse, tr("Game List Background"), tr("None"),
tr("Enable an animated / static background on the game list (where you launch your games).<br>"
"This background is only visible in the library and will be hidden once a game is launched. It will also be paused when it's not in focus."));
dialog()->registerWidgetHelp(
m_ui.backgroundReset, tr("Disable/Reset Game List Background"), tr("None"),
tr("Disable and reset the currently applied game list background."));
dialog()->registerWidgetHelp(
m_ui.backgroundOpacity, tr("Game List Background Opacity"), tr("100%"),
tr("Sets the opacity of the custom background."));
dialog()->registerWidgetHelp(
m_ui.backgroundFill, tr("Fill Image"), tr("Unchecked"),
tr("Expand the image to fill all available background area."));
onRenderToSeparateWindowChanged();
}
@@ -201,3 +225,30 @@ void InterfaceSettingsWidget::populateLanguages()
for (const std::pair<QString, QString>& it : QtHost::GetAvailableLanguageList())
m_ui.language->addItem(it.first, it.second);
}
void InterfaceSettingsWidget::onSetGameListBackgroundTriggered()
{
const QString path = QDir::toNativeSeparators(
QFileDialog::getOpenFileName(this, tr("Select Background Image"), QString(), IMAGE_FILE_FILTER));
if (path.isEmpty())
return;
std::string relative_path = Path::MakeRelative(QDir::toNativeSeparators(path).toStdString(), EmuFolders::DataRoot);
Host::SetBaseBoolSettingValue("UI", "GameListBackgroundEnabled", true);
Host::SetBaseStringSettingValue("UI", "GameListBackgroundPath", relative_path.c_str());
if (!Host::ContainsBaseSettingValue("UI", "GameListBackgroundOpacity"))
Host::SetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 100.0f);
Host::CommitBaseSettingChanges();
emit backgroundChanged();
}
void InterfaceSettingsWidget::onClearGameListBackgroundTriggered()
{
Host::SetBaseBoolSettingValue("UI", "GameListBackgroundEnabled", false);
Host::RemoveBaseSettingValue("UI", "GameListBackgroundPath");
Host::CommitBaseSettingChanges();
emit backgroundChanged();
}

View File

@@ -18,9 +18,12 @@ public:
Q_SIGNALS:
void themeChanged();
void languageChanged();
void backgroundChanged();
private Q_SLOTS:
void onRenderToSeparateWindowChanged();
void onSetGameListBackgroundTriggered();
void onClearGameListBackgroundTriggered();
private:
void populateLanguages();

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>608</height>
<width>725</width>
<height>617</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -140,11 +140,14 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="preferencesGroup">
<widget class="QGroupBox" name="appearancesGroup">
<property name="title">
<string>Preferences</string>
<string>Appearance</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QComboBox" name="theme"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="languageLabel">
<property name="text">
@@ -162,8 +165,87 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="theme"/>
<item row="2" column="0">
<widget class="QLabel" name="backgroundLabel">
<property name="text">
<string>Game List Background:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="backgroundPathLayout">
<item>
<widget class="QPushButton" name="backgroundBrowse">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="backgroundReset">
<property name="text">
<string>Reset</string>
</property>
<property name="icon">
<iconset theme="trash-fill"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="backgroundOpacityLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Opacity:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="backgroundOpacity">
<property name="wrapping">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::PlusMinus</enum>
</property>
<property name="showGroupSeparator" stdset="0">
<bool>false</bool>
</property>
<property name="suffix">
<string notr="true">%</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="backgroundFill">
<property name="text">
<string>Fill Image</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@@ -174,13 +256,6 @@
<string>Automatic Updater</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QLabel" name="autoUpdateCurrentVersion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="updaterChannelLabel">
<property name="text">
@@ -188,6 +263,16 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="autoUpdateCurrentVersion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="autoUpdateTag"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="currentVersionLabel">
<property name="text">
@@ -195,9 +280,6 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="autoUpdateTag"/>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="automaticUpdaterCheckGroup" stretch="1,0">
<item>
@@ -222,7 +304,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

File diff suppressed because it is too large Load Diff

View File

@@ -1294,7 +1294,7 @@ function(setup_main_executable target)
# Copy dependency libraries.
set(DEPS_BINDIR "${CMAKE_SOURCE_DIR}/deps/bin")
set(DEPS_TO_COPY freetype.dll harfbuzz.dll jpeg62.dll libpng16.dll libsharpyuv.dll libwebp.dll lz4.dll SDL3.dll shaderc_shared.dll zlib1.dll zstd.dll plutovg.dll plutosvg.dll)
set(DEPS_TO_COPY freetype.dll harfbuzz.dll jpeg62.dll libpng16.dll libsharpyuv.dll libwebp.dll libwebpdemux.dll libwebpmux.dll lz4.dll SDL3.dll shaderc_shared.dll zlib1.dll zstd.dll plutovg.dll plutosvg.dll)
set(DEPS_TO_COPY
$<IF:$<CONFIG:Debug>,kddockwidgets-qt6d.dll,kddockwidgets-qt6.dll>
${DEPS_TO_COPY}

View File

@@ -711,7 +711,7 @@ struct Pcsx2Config
union
{
u64 bitset;
u64 bitset[2];
struct
{
@@ -774,6 +774,7 @@ struct Pcsx2Config
SaveDepth : 1,
SaveAlpha : 1,
SaveInfo : 1,
SaveTransferImages : 1,
DumpReplaceableTextures : 1,
DumpReplaceableMipmaps : 1,
DumpTexturesWithFMVActive : 1,

View File

@@ -491,11 +491,27 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
}
}
if (GSConfig.EnableVideoCaptureParameters)
{
res = wrap_av_dict_parse_string(&s_video_codec_arguments, GSConfig.VideoCaptureParameters.c_str(), "=", ":", 0);
if (res < 0)
{
LogAVError(res, "av_dict_parse_string() for video failed: ");
InternalEndCapture(lock);
return false;
}
}
if (hwconfig)
{
Console.WriteLn(Color_StrongGreen, fmt::format("Trying to use {} hardware device for video encoding.",
wrap_av_hwdevice_get_type_name(hwconfig->device_type)));
res = wrap_av_hwdevice_ctx_create(&s_video_hw_context, hwconfig->device_type, nullptr, nullptr, 0);
const char* device = nullptr;
if (AVDictionaryEntry* dev = wrap_av_dict_get(s_video_codec_arguments, "vaapi_device", nullptr, 0))
device = dev->value;
if (AVDictionaryEntry* dev = wrap_av_dict_get(s_video_codec_arguments, "hwaccel_device", nullptr, 0))
device = dev->value;
Console.WriteLnFmt(Color_StrongGreen, "Trying to use {}:{} for video encoding.",
wrap_av_hwdevice_get_type_name(hwconfig->device_type), device ? device : "default");
res = wrap_av_hwdevice_ctx_create(&s_video_hw_context, hwconfig->device_type, device, nullptr, 0);
if (res < 0)
{
LogAVError(res, "av_hwdevice_ctx_create() failed: ");
@@ -539,17 +555,6 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
}
}
if (GSConfig.EnableVideoCaptureParameters)
{
res = wrap_av_dict_parse_string(&s_video_codec_arguments, GSConfig.VideoCaptureParameters.c_str(), "=", ":", 0);
if (res < 0)
{
LogAVError(res, "av_dict_parse_string() for video failed: ");
InternalEndCapture(lock);
return false;
}
}
if (output_format->flags & AVFMT_GLOBALHEADER)
s_video_codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

View File

@@ -169,121 +169,123 @@ void GSDrawingContext::Dump(const std::string& filename)
if (!fp)
return;
// Warning: The indentation must be consistent with GSDrawingEnvironment::Dump().
fprintf(fp,
"XYOFFSET\n"
"\tOFX:%.4f\n"
"\tOFY:%.4f\n\n",
"XYOFFSET:\n"
" OFX: %.4f\n"
" OFY: %.4f\n\n",
XYOFFSET.OFX / 16.0f, XYOFFSET.OFY / 16.0f);
fprintf(fp,
"MIPTBP1\n"
"\tTBP1:0x%x\n"
"\tTBW1:%u\n"
"\tTBP2:0x%x\n"
"\tTBW2:%u\n"
"\tTBP3:0x%x\n"
"\tTBW3:%u\n\n",
"MIPTBP1:\n"
" TBP1: 0x%x\n"
" TBW1: %u\n"
" TBP2: 0x%x\n"
" TBW2: %u\n"
" TBP3: 0x%x\n"
" TBW3: %u\n\n",
static_cast<uint32_t>(MIPTBP1.TBP1), static_cast<uint32_t>(MIPTBP1.TBW1), static_cast<uint32_t>(MIPTBP1.TBP2),
static_cast<uint32_t>(MIPTBP1.TBW2), static_cast<uint32_t>(MIPTBP1.TBP3), static_cast<uint32_t>(MIPTBP1.TBW3));
fprintf(fp,
"MIPTBP2\n"
"\tTBP4:0x%x\n"
"\tTBW4:%u\n"
"\tTBP5:0x%x\n"
"\tTBW5:%u\n"
"\tTBP6:0x%x\n"
"\tTBW6:%u\n\n",
"MIPTBP2:\n"
" TBP4: 0x%x\n"
" TBW4: %u\n"
" TBP5: 0x%x\n"
" TBW5: %u\n"
" TBP6: 0x%x\n"
" TBW6: %u\n\n",
static_cast<uint32_t>(MIPTBP2.TBP4), static_cast<uint32_t>(MIPTBP2.TBW4), static_cast<uint32_t>(MIPTBP2.TBP5),
static_cast<uint32_t>(MIPTBP2.TBW5), static_cast<uint32_t>(MIPTBP2.TBP6), static_cast<uint32_t>(MIPTBP2.TBW6));
fprintf(fp,
"TEX0\n"
"\tTBP0:0x%x\n"
"\tTBW:%u\n"
"\tPSM:0x%x (%s)\n"
"\tTW:%u\n"
"\tTH:%u\n"
"\tTCC:%u\n"
"\tTFX:%u\n"
"\tCBP:0x%x\n"
"\tCPSM:0x%x (%s)\n"
"\tCSM:%u\n"
"\tCSA:%u\n"
"\tCLD:%u\n\n",
TEX0.TBP0, TEX0.TBW, TEX0.PSM, GSUtil::GetPSMName(TEX0.PSM), TEX0.TW, static_cast<uint32_t>(TEX0.TH), TEX0.TCC, TEX0.TFX, TEX0.CBP, TEX0.CPSM, GSUtil::GetPSMName(TEX0.CPSM), TEX0.CSM,TEX0.CSA, TEX0.CLD);
"TEX0:\n"
" TBP0: 0x%x\n"
" TBW: %u\n"
" PSM: 0x%x # %s\n"
" TW: %u\n"
" TH: %u\n"
" TCC: %u # %s\n"
" TFX: %u # %s\n"
" CBP: 0x%x\n"
" CPSM: 0x%x # %s\n"
" CSM: %u\n"
" CSA: %u\n"
" CLD: %u\n\n",
TEX0.TBP0, TEX0.TBW, TEX0.PSM, GSUtil::GetPSMName(TEX0.PSM), TEX0.TW, static_cast<uint32_t>(TEX0.TH), TEX0.TCC, GSUtil::GetTCCName(TEX0.TCC), TEX0.TFX, GSUtil::GetTFXName(TEX0.TFX), TEX0.CBP, TEX0.CPSM, GSUtil::GetPSMName(TEX0.CPSM), TEX0.CSM, TEX0.CSA, TEX0.CLD);
fprintf(fp,
"TEX1\n"
"\tLCM:%u\n"
"\tMXL:%u\n"
"\tMMAG:%u\n"
"\tMMIN:%u\n"
"\tMTBA:%u\n"
"\tL:%u\n"
"\tK:%d\n\n",
TEX1.LCM, TEX1.MXL, TEX1.MMAG, TEX1.MMIN, TEX1.MTBA, TEX1.L, TEX1.K);
"TEX1:\n"
" LCM: %u # %s\n"
" MXL: %u\n"
" MMAG: %u # %s\n"
" MMIN: %u # %s\n"
" MTBA: %u\n"
" L: %u\n"
" K: %.4f\n\n",
TEX1.LCM, GSUtil::GetLCMName(TEX1.LCM), TEX1.MXL, TEX1.MMAG, GSUtil::GetMMAGName(TEX1.MMAG), TEX1.MMIN, GSUtil::GetMMINName(TEX1.MMIN), TEX1.MTBA, TEX1.L, static_cast<float>((static_cast<int>(TEX1.K) ^ 0x800) - 0x800) / 16.0f);
fprintf(fp,
"CLAMP\n"
"\tWMS:%u (%s)\n"
"\tWMT:%u (%s)\n"
"\tMINU:%u\n"
"\tMAXU:%u\n"
"\tMAXV:%u\n"
"\tMINV:%u\n\n",
CLAMP.WMS, GSUtil::GetWMName(CLAMP.WMS), CLAMP.WMT,GSUtil::GetWMName(CLAMP.WMT), CLAMP.MINU, CLAMP.MAXU, CLAMP.MAXV, static_cast<uint32_t>(CLAMP.MINV));
"CLAMP:\n"
" WMS: %u # %s\n"
" WMT: %u # %s\n"
" MINU: %u\n"
" MAXU: %u\n"
" MINV: %u\n"
" MAXV: %u\n\n",
CLAMP.WMS, GSUtil::GetWMName(CLAMP.WMS), CLAMP.WMT,GSUtil::GetWMName(CLAMP.WMT), CLAMP.MINU, CLAMP.MAXU, static_cast<uint32_t>(CLAMP.MINV), CLAMP.MAXV);
fprintf(fp,
"SCISSOR\n"
"\tSCAX0:%u\n"
"\tSCAX1:%u\n"
"\tSCAY0:%u\n"
"\tSCAY1:%u\n\n",
"SCISSOR:\n"
" SCAX0: %u\n"
" SCAX1: %u\n"
" SCAY0: %u\n"
" SCAY1: %u\n\n",
SCISSOR.SCAX0, SCISSOR.SCAX1, SCISSOR.SCAY0, SCISSOR.SCAY1);
fprintf(fp,
"ALPHA\n"
"\tA:%u\n"
"\tB:%u\n"
"\tC:%u\n"
"\tD:%u\n"
"\tFIX:%u\n",
"ALPHA:\n"
" A: %u\n"
" B: %u\n"
" C: %u\n"
" D: %u\n"
" FIX: %u\n",
ALPHA.A, ALPHA.B, ALPHA.C, ALPHA.D, ALPHA.FIX);
const char* col[3] = {"Cs", "Cd", "0"};
const char* alpha[3] = {"As", "Ad", "Af"};
fprintf(fp, "\t=> (%s - %s) * %s + %s\n\n", col[ALPHA.A], col[ALPHA.B], alpha[ALPHA.C], col[ALPHA.D]);
constexpr const char* col[3] = {"Cs", "Cd", "0"};
constexpr const char* alpha[3] = {"As", "Ad", "Af"};
fprintf(fp, " # => (%s - %s) * %s + %s\n\n", col[ALPHA.A], col[ALPHA.B], alpha[ALPHA.C], col[ALPHA.D]);
fprintf(fp,
"TEST\n"
"\tATE:%u\n"
"\tATST:%u (%s)\n"
"\tAREF:%u\n"
"\tAFAIL:%u (%s)\n"
"\tDATE:%u\n"
"\tDATM:%u\n"
"\tZTE:%u\n"
"\tZTST:%u (%s)\n\n",
TEST.ATE, TEST.ATST, GSUtil::GetATSTName(TEST.ATST), TEST.AREF, TEST.AFAIL, GSUtil::GetAFAILName(TEST.AFAIL), TEST.DATE, TEST.DATM, TEST.ZTE, TEST.ZTST, GSUtil::GetZTSTName(TEST.ZTST));
"TEST:\n"
" ATE: %u\n"
" ATST: %u # %s\n"
" AREF: %u\n"
" AFAIL: %u # %s\n"
" DATE: %u\n"
" DATM: %u # %s\n"
" ZTE: %u\n"
" ZTST: %u # %s\n\n",
TEST.ATE, TEST.ATST, GSUtil::GetATSTName(TEST.ATST), TEST.AREF, TEST.AFAIL, GSUtil::GetAFAILName(TEST.AFAIL), TEST.DATE, TEST.DATM, GSUtil::GetDATMName(TEST.DATM), TEST.ZTE, TEST.ZTST, GSUtil::GetZTSTName(TEST.ZTST));
fprintf(fp,
"FBA\n"
"\tFBA:%u\n\n",
"FBA:\n"
" FBA: %u\n\n",
FBA.FBA);
fprintf(fp,
"FRAME\n"
"\tFBP (*32):0x%x\n"
"\tFBW:%u\n"
"\tPSM:0x%x (%s)\n"
"\tFBMSK:0x%x\n\n",
"FRAME:\n"
" FBP: 0x%x # (*32)\n"
" FBW: %u\n"
" PSM: 0x%x # %s\n"
" FBMSK: 0x%x\n\n",
FRAME.FBP * 32, FRAME.FBW, FRAME.PSM, GSUtil::GetPSMName(FRAME.PSM), FRAME.FBMSK);
fprintf(fp,
"ZBUF\n"
"\tZBP (*32):0x%x\n"
"\tPSM:0x%x (%s)\n"
"\tZMSK:%u\n\n",
"ZBUF:\n"
" ZBP: 0x%x # (*32)\n"
" PSM: 0x%x # %s\n"
" ZMSK: %u\n\n",
ZBUF.ZBP * 32, ZBUF.PSM, GSUtil::GetPSMName(ZBUF.PSM), ZBUF.ZMSK);
fclose(fp);

View File

@@ -35,112 +35,102 @@ void GSDrawingEnvironment::Dump(const std::string& filename) const
if (!fp)
return;
fprintf(fp, "PRIM\n"
"\tPRIM:%u (%s)\n"
"\tIIP:%u\n"
"\tTME:%u\n"
"\tFGE:%u\n"
"\tABE:%u\n"
"\tAA1:%u\n"
"\tFST:%u\n"
"\tCTXT:%u\n"
"\tFIX:%u\n\n",
// Warning: The indentation must be consistent with GSDrawingContext::Dump().
fprintf(fp, "PRIM:\n"
" PRIM: %u # %s\n"
" IIP: %u\n"
" TME: %u\n"
" FGE: %u\n"
" ABE: %u\n"
" AA1: %u\n"
" FST: %u\n"
" CTXT: %u\n"
" FIX: %u\n\n",
PRIM.PRIM, GSUtil::GetPrimName(PRIM.PRIM), PRIM.IIP, PRIM.TME, PRIM.FGE, PRIM.ABE, PRIM.AA1, PRIM.FST, PRIM.CTXT, PRIM.FIX);
fprintf(fp, "PRMODE (when AC=0)\n"
"\t_PRIM:%u (%s)\n"
"\tIIP:%u\n"
"\tTME:%u\n"
"\tFGE:%u\n"
"\tABE:%u\n"
"\tAA1:%u\n"
"\tFST:%u\n"
"\tCTXT:%u\n"
"\tFIX:%u\n\n",
PRMODE._PRIM, GSUtil::GetPrimName(PRMODE._PRIM), PRMODE.IIP, PRMODE.TME, PRMODE.FGE, PRMODE.ABE, PRMODE.AA1, PRMODE.FST, PRMODE.CTXT, PRMODE.FIX);
fprintf(fp, "PRMODECONT:\n"
" AC: %u # %s\n\n",
PRMODECONT.AC, GSUtil::GetACName(PRMODECONT.AC));
fprintf(fp, "PRMODECONT\n"
"\tAC:%u\n\n",
PRMODECONT.AC);
fprintf(fp, "TEXCLUT:\n"
" CBW: %u\n"
" COU: %u\n"
" COV: %u\n\n",
TEXCLUT.CBW, TEXCLUT.COU, TEXCLUT.COV);
fprintf(fp, "TEXCLUT\n"
"\tCOU:%u\n"
"\tCBW:%u\n"
"\tCOV:%u\n\n",
TEXCLUT.COU, TEXCLUT.CBW, TEXCLUT.COV);
fprintf(fp, "SCANMSK:\n"
" MSK: %u # %s\n\n",
SCANMSK.MSK, GSUtil::GetSCANMSKName(SCANMSK.MSK));
fprintf(fp, "SCANMSK\n"
"\tMSK:%u\n\n",
SCANMSK.MSK);
fprintf(fp, "TEXA\n"
"\tAEM:%u\n"
"\tTA0:%u\n"
"\tTA1:%u\n\n",
fprintf(fp, "TEXA:\n"
" AEM: %u\n"
" TA0: %u\n"
" TA1: %u\n\n",
TEXA.AEM, TEXA.TA0, TEXA.TA1);
fprintf(fp, "FOGCOL\n"
"\tFCG:%u\n"
"\tFCB:%u\n"
"\tFCR:%u\n\n",
fprintf(fp, "FOGCOL:\n"
" FCG: %u\n"
" FCB: %u\n"
" FCR: %u\n\n",
FOGCOL.FCG, FOGCOL.FCB, FOGCOL.FCR);
fprintf(fp, "DIMX\n"
"\tDM22:%d\n"
"\tDM23:%d\n"
"\tDM31:%d\n"
"\tDM02:%d\n"
"\tDM21:%d\n"
"\tDM12:%d\n"
"\tDM03:%d\n"
"\tDM01:%d\n"
"\tDM33:%d\n"
"\tDM30:%d\n"
"\tDM11:%d\n"
"\tDM10:%d\n"
"\tDM20:%d\n"
"\tDM32:%d\n"
"\tDM00:%d\n"
"\tDM13:%d\n\n",
fprintf(fp, "DIMX:\n"
" DM22: %d\n"
" DM23: %d\n"
" DM31: %d\n"
" DM02: %d\n"
" DM21: %d\n"
" DM12: %d\n"
" DM03: %d\n"
" DM01: %d\n"
" DM33: %d\n"
" DM30: %d\n"
" DM11: %d\n"
" DM10: %d\n"
" DM20: %d\n"
" DM32: %d\n"
" DM00: %d\n"
" DM13: %d\n\n",
DIMX.DM22, DIMX.DM23, DIMX.DM31, DIMX.DM02, DIMX.DM21, DIMX.DM12, DIMX.DM03, DIMX.DM01, DIMX.DM33, DIMX.DM30, DIMX.DM11, DIMX.DM10, DIMX.DM20, DIMX.DM32, DIMX.DM00, DIMX.DM13);
fprintf(fp, "DTHE\n"
"\tDTHE:%u\n\n",
fprintf(fp, "DTHE:\n"
" DTHE: %u\n\n",
DTHE.DTHE);
fprintf(fp, "COLCLAMP\n"
"\tCLAMP:%u\n\n",
fprintf(fp, "COLCLAMP:\n"
" CLAMP: %u\n\n",
COLCLAMP.CLAMP);
fprintf(fp, "PABE\n"
"\tPABE:%u\n\n",
fprintf(fp, "PABE:\n"
" PABE: %u\n\n",
PABE.PABE);
fprintf(fp, "BITBLTBUF\n"
"\tSBW:%u\n"
"\tSBP:0x%x\n"
"\tSPSM:%u (%s)\n"
"\tDBW:%u\n"
"\tDPSM:%u (%s)\n"
"\tDBP:0x%x\n\n",
fprintf(fp, "BITBLTBUF:\n"
" SBW: %u\n"
" SBP: 0x%x\n"
" SPSM: %u # %s\n"
" DBW: %u\n"
" DPSM: %u # %s\n"
" DBP: 0x%x\n\n",
BITBLTBUF.SBW, BITBLTBUF.SBP, BITBLTBUF.SPSM, GSUtil::GetPSMName(BITBLTBUF.SPSM), BITBLTBUF.DBW, BITBLTBUF.DPSM, GSUtil::GetPSMName(BITBLTBUF.DPSM), BITBLTBUF.DBP);
fprintf(fp, "TRXDIR\n"
"\tXDIR:%u\n\n",
fprintf(fp, "TRXDIR:\n"
" XDIR: %u\n\n",
TRXDIR.XDIR);
fprintf(fp, "TRXPOS\n"
"\tDIRY:%u\n"
"\tSSAY:%u\n"
"\tSSAX:%u\n"
"\tDIRX:%u\n"
"\tDSAX:%u\n"
"\tDSAY:%u\n\n",
fprintf(fp, "TRXPOS:\n"
" DIRY: %u\n"
" SSAY: %u\n"
" SSAX: %u\n"
" DIRX: %u\n"
" DSAX: %u\n"
" DSAY: %u\n\n",
TRXPOS.DIRY, TRXPOS.SSAY, TRXPOS.SSAX, TRXPOS.DIRX, TRXPOS.DSAX, TRXPOS.DSAY);
fprintf(fp, "TRXREG\n"
"\tRRH:%u\n"
"\tRRW:%u\n\n",
fprintf(fp, "TRXREG:\n"
" RRH: %u\n"
" RRW: %u\n\n",
TRXREG.RRH, TRXREG.RRW);
fclose(fp);

View File

@@ -651,7 +651,7 @@ void GSLocalMemory::ReadTexture(const GSOffset& off, const GSVector4i& r, u8* ds
//
void GSLocalMemory::SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int w, int h)
void GSLocalMemory::SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int w, int h, int x, int y)
{
int pitch = w * 4;
int size = pitch * h;
@@ -671,7 +671,7 @@ void GSLocalMemory::SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int
{
for (int i = 0; i < w; i++)
{
((u32*)p)[i] = (this->*rp)(i, j, TEX0.TBP0, TEX0.TBW);
((u32*)p)[i] = (this->*rp)(x + i, y + j, TEX0.TBP0, TEX0.TBW);
}
}

View File

@@ -1121,7 +1121,7 @@ public:
//
void SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int w, int h);
void SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int w, int h, int x = 0, int y = 0);
};
constexpr inline GSOffset GSOffset::fromKnownPSM(u32 bp, u32 bw, GS_PSM psm)

View File

@@ -15,6 +15,7 @@
#include <algorithm>
#include <cfloat>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <bit>
@@ -433,6 +434,34 @@ const char* GSState::GetFlushReasonString(GSFlushReason reason)
}
}
void GSState::DumpDrawInfo(bool dump_regs, bool dump_verts, bool dump_transfers)
{
std::string s;
// Dump Register state
if (dump_regs)
{
s = GetDrawDumpPath("%05d_context.txt", s_n);
m_draw_env->Dump(s);
m_context->Dump(s);
}
// Dump vertices
if (dump_verts)
{
s = GetDrawDumpPath("%05d_vertex.txt", s_n);
DumpVertices(s);
}
// Dump transfers
if (dump_transfers)
{
s = GetDrawDumpPath("%05d_transfers.txt", s_n);
DumpTransferList(s);
}
}
void GSState::DumpVertices(const std::string& filename)
{
std::ofstream file(filename);
@@ -440,108 +469,411 @@ void GSState::DumpVertices(const std::string& filename)
if (!file.is_open())
return;
file << "FLUSH REASON: " << GetFlushReasonString(m_state_flush_reason);
file.imbue(std::locale::classic()); // Disable integer separators.
if (m_state_flush_reason != GSFlushReason::CONTEXTCHANGE && m_dirty_gs_regs)
file << " AND POSSIBLE CONTEXT CHANGE";
constexpr const char* DEL = ", ";
constexpr const char* INDENT = " ";
constexpr const char* LIST_ITEM = "- ";
constexpr const char* OPEN_MAP = "{";
constexpr const char* CLOSE_MAP = "}";
constexpr int TRACE_INDEX_WIDTH = 10;
constexpr int XYUV_WIDTH = 10;
constexpr int Z_WIDTH = 10;
constexpr int RGBA_WIDTH = 3;
constexpr int SCI_FLOAT_WIDTH = 15;
constexpr int STQ_BITS_WIDTH = 10;
file << std::endl << std::endl;
const int n = GSUtil::GetClassVertexCount(m_vt.m_primclass);
const u32 count = m_index.tail;
GSVertex* buffer = &m_vertex.buff[0];
auto WriteVertexIndex = [&file](int index) {
file << std::left << std::dec << " # " << index;
};
const char* DEL = ", ";
auto WriteTraceIndex = [&file](const char* index) {
file << std::left << std::dec << std::setw(TRACE_INDEX_WIDTH) << std::setfill(' ') << index;
};
file << "VERTEX COORDS (XYZ)" << std::endl;
file << std::fixed << std::setprecision(4);
for (u32 i = 0; i < count; ++i)
{
file << "\t" << std::dec << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
auto WriteXYZ_vec = [&file](const GSVector4& v) {
file << std::dec << std::right << std::fixed;
file << "X: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << v.x << DEL;
file << "Y: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << v.y << DEL;
file << "Z: " << std::setw(Z_WIDTH) << std::setfill(' ') << static_cast<u32>(v.z);
};
const float x = (v.XYZ.X - (int)m_context->XYOFFSET.OFX) / 16.0f;
const float y = (v.XYZ.Y - (int)m_context->XYOFFSET.OFY) / 16.0f;
// Different handler because we have full precision on Z
auto WriteXYZ_vert = [this, &file](const GSVertex& v) {
const float x = (static_cast<int>(v.XYZ.X) - static_cast<int>(m_context->XYOFFSET.OFX)) / 16.0f;
const float y = (static_cast<int>(v.XYZ.Y) - static_cast<int>(m_context->XYOFFSET.OFY)) / 16.0f;
file << std::dec << std::right << std::fixed;
file << "X: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << x << DEL;
file << "Y: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << y << DEL;
file << "Z: " << std::setw(Z_WIDTH) << std::setfill(' ') << v.XYZ.Z;
};
file << x << DEL;
file << y << DEL;
file << v.XYZ.Z;
file << std::endl;
}
file << std::endl;
file << "VERTEX COLOR (RGBA)" << std::endl;
file << std::fixed << std::setprecision(6);
for (u32 i = 0; i < count; ++i)
{
file << "\t" << std::dec << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.G) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.B) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A) << DEL;
file << "FOG: " << std::setfill('0') << std::setw(3) << unsigned(v.FOG);
file << std::endl;
}
file << std::endl;
const bool use_uv = PRIM->FST;
const std::string qualifier = use_uv ? "UV" : "STQ";
file << "TEXTURE COORDS (" << qualifier << ")" << std::endl;;
for (u32 i = 0; i < count; ++i)
{
file << "\t" << "v" << std::dec << i << ": ";
const GSVertex v = buffer[m_index.buff[i]];
// note
// Yes, technically as far as the GS is concerned Q belongs
// to RGBAQ. However, the purpose of this dump is to print
// our data in a more human readable format and typically Q
// is associated with STQ.
if (use_uv)
auto WriteUV_vec = [this, &file](const GSVector4& v) {
file << std::right;
if (PRIM->FST)
{
const float uv_U = v.U / 16.0f;
const float uv_V = v.V / 16.0f;
file << uv_U << DEL << uv_V;
file << std::fixed;
file << "U: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << v.x << DEL;
file << "V: " << std::setprecision(4) << std::setw(XYUV_WIDTH) << std::setfill(' ') << v.y;
}
else
{
float x = (v.ST.S / v.RGBAQ.Q) * (1 << m_context->TEX0.TW);
float y = (v.ST.T / v.RGBAQ.Q) * (1 << m_context->TEX0.TH);
file << v.ST.S << "(" << std::hex << std::bit_cast<u32>(v.ST.S) << ")" << DEL << v.ST.T << "(" << std::hex << std::bit_cast<u32>(v.ST.T) << ")" << DEL << v.RGBAQ.Q << "(" << std::hex << std::bit_cast<u32>(v.RGBAQ.Q) << ") - " << x << "," << y;
file << std::defaultfloat;
file << "U: " << std::setw(SCI_FLOAT_WIDTH) << std::setfill(' ') << v.x << DEL;
file << "V: " << std::setw(SCI_FLOAT_WIDTH) << std::setfill(' ') << v.y;
}
};
auto WriteUV_vert = [this, WriteUV_vec](const GSVertex& v) {
GSVector4 vec;
if (PRIM->FST)
vec = GSVector4(v.U / 16.0f, v.V / 16.0f);
else
vec = GSVector4(
(v.ST.S / v.RGBAQ.Q) * (1 << m_context->TEX0.TW),
(v.ST.T / v.RGBAQ.Q) * (1 << m_context->TEX0.TH)
);
WriteUV_vec(vec);
};
auto WriteRGBA_vec = [&file](const GSVector4i& v) {
file << std::dec << std::right;
file << "R: " << std::setw(RGBA_WIDTH) << std::setfill(' ') << v.r << DEL;
file << "G: " << std::setw(RGBA_WIDTH) << std::setfill(' ') << v.g << DEL;
file << "B: " << std::setw(RGBA_WIDTH) << std::setfill(' ') << v.b << DEL;
file << "A: " << std::setw(RGBA_WIDTH) << std::setfill(' ') << v.a;
};
auto WriteRGBA_vert = [WriteRGBA_vec](const GSVertex& v) {
GSVector4i vec = GSVector4i(v.RGBAQ.R, v.RGBAQ.G, v.RGBAQ.B, v.RGBAQ.A);
WriteRGBA_vec(vec);
};
auto WriteF = [&file](const int f) {
file << "F: " << std::setw(RGBA_WIDTH) << std::setfill(' ') << f;
};
auto WriteSTQ_vec = [&file](const GSVector4& v) {
file << std::defaultfloat << std::right;
file << "S: " << std::setw(SCI_FLOAT_WIDTH) << std::setfill(' ') << v.x << DEL;
file << "T: " << std::setw(SCI_FLOAT_WIDTH) << std::setfill(' ') << v.y << DEL;
file << "Q: " << std::setw(SCI_FLOAT_WIDTH) << std::setfill(' ') << v.z;
};
auto WriteSTQ_bits = [&file](const GSVector4& v) {
file << std::hex << std::showbase << std::right;
file << "Si: " << std::setw(STQ_BITS_WIDTH) << std::setfill('0') << std::bit_cast<u32>(v.x) << DEL;
file << "Ti: " << std::setw(STQ_BITS_WIDTH) << std::setfill('0') << std::bit_cast<u32>(v.y) << DEL;
file << "Qi: " << std::setw(STQ_BITS_WIDTH) << std::setfill('0') << std::bit_cast<u32>(v.z);
};
auto WriteSTQ_vert = [&file, WriteSTQ_vec, WriteSTQ_bits](const GSVertex& v) {
GSVector4 vec = GSVector4(v.ST.S, v.ST.T, v.RGBAQ.Q, v.RGBAQ.Q);
WriteSTQ_vec(vec);
file << DEL;
WriteSTQ_bits(vec);
};
auto WriteBools = [&file](std::vector<const char*> names, std::vector<u32> values) {
for (int i = 0; i < static_cast<int>(names.size()); i++)
{
if (i > 0)
file << DEL;
file << names[i] << ": " << static_cast<bool>(values[i]);
}
};
// Dump flush reason
file << "flush_reason: \"" << GetFlushReasonString(m_state_flush_reason);
if (m_state_flush_reason != GSFlushReason::CONTEXTCHANGE && m_dirty_gs_regs)
file << " AND POSSIBLE CONTEXT CHANGE";
file << "\"" << std::endl;
file << std::endl;
// Dump vertices
file << "vertex: # " << GSUtil::GetPrimClassName(m_vt.m_primclass) << std::endl;
const u32 count = m_index.tail;
GSVertex* buffer = &m_vertex.buff[0];
for (u32 i = 0; i < count; ++i)
{
GSVertex v = buffer[m_index.buff[i]];
if ((n > 1) && (i > 0) && ((i % n) == 0))
file << std::endl;
file << INDENT << LIST_ITEM << OPEN_MAP;
WriteXYZ_vert(v);
if (PRIM->TME)
{
file << DEL;
WriteUV_vert(v);
}
file << DEL;
WriteRGBA_vert(v);
if (PRIM->FGE)
{
file << DEL;
WriteF(v.FOG);
}
file << CLOSE_MAP;
WriteVertexIndex(i);
file << std::endl;
}
file << std::endl;
file << "TRACER" << std::dec << std::endl;
// Dump extra info for STQ
if (PRIM->TME && !PRIM->FST)
{
file << "vertex_stq: # " << GSUtil::GetPrimClassName(m_vt.m_primclass) << std::endl;
for (u32 i = 0; i < count; ++i)
{
if ((n > 1) && (i > 0) && ((i % n) == 0))
file << std::endl;
GSVector4i v = m_vt.m_min.c;
file << "\tmin c (r,g,b,a): " << v.x << DEL << v.y << DEL << v.z << DEL << v.w << std::endl;
v = m_vt.m_max.c;
file << "\tmax c (r,g,b,a): " << v.x << DEL << v.y << DEL << v.z << DEL << v.w << std::endl;
file << INDENT << LIST_ITEM << OPEN_MAP;
WriteSTQ_vert(buffer[m_index.buff[i]]);
file << CLOSE_MAP;
GSVector4 v2 = m_vt.m_min.p;
file << "\tmin p (x,y,z,f): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << (u32)v2.w << std::endl;
v2 = m_vt.m_max.p;
file << "\tmax p (x,y,z,f): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << (u32)v2.w << std::endl;
WriteVertexIndex(i);
v2 = m_vt.m_min.t;
file << "\tmin t (u,v,q): " << v2.x << DEL << v2.y << DEL << v2.z << std::endl;
v2 = m_vt.m_max.t;
file << "\tmax t (u,v,q): " << v2.x << DEL << v2.y << DEL << v2.z << std::endl;
file << std::endl;
}
file << std::endl;
}
// Dump vertex trace
file << "vertex_trace:" << std::endl;
file << INDENT;
WriteTraceIndex("min_xyz: ");
file << OPEN_MAP;
WriteXYZ_vec(m_vt.m_min.p);
file << CLOSE_MAP << std::endl;
file << INDENT;
WriteTraceIndex("max_xyz: ");
file << OPEN_MAP;
WriteXYZ_vec(m_vt.m_max.p);
file << CLOSE_MAP << std::endl;
if (PRIM->TME)
{
if (PRIM->FST)
{
file << INDENT;
WriteTraceIndex("min_uv: ");
file << OPEN_MAP;
WriteUV_vec(m_vt.m_min.t);
file << CLOSE_MAP << std::endl;
file << INDENT;
WriteTraceIndex("max_uv: ");
file << OPEN_MAP;
WriteUV_vec(m_vt.m_max.t);
file << CLOSE_MAP << std::endl;
}
else
{
// Note: The vertex trace does not actually track the min/max of raw ST values
// hence the labels "min_uvq" and "max_uvq" are used instead of "min_stq" and "max_stq".
file << INDENT;
WriteTraceIndex("min_uvq: ");
file << OPEN_MAP;
WriteSTQ_vec(m_vt.m_min.t);
file << CLOSE_MAP << std::endl;
file << INDENT;
WriteTraceIndex("max_uvq: ");
file << OPEN_MAP;
WriteSTQ_vec(m_vt.m_max.t);
file << CLOSE_MAP << std::endl;
}
}
file << INDENT;
WriteTraceIndex("min_rgba: ");
file << OPEN_MAP;
WriteRGBA_vec(m_vt.m_min.c);
file << CLOSE_MAP << std::endl;
file << INDENT;
WriteTraceIndex("max_rgba: ");
file << OPEN_MAP;
WriteRGBA_vec(m_vt.m_max.c);
file << CLOSE_MAP << std::endl;
if (PRIM->FGE)
{
file << INDENT;
WriteTraceIndex("min_f: ");
file << OPEN_MAP;
WriteF(m_vt.m_min.p.w);
file << CLOSE_MAP << std::endl;
file << INDENT;
WriteTraceIndex("max_f: ");
file << OPEN_MAP;
WriteF(m_vt.m_max.p.w);
file << CLOSE_MAP << std::endl;
}
file << std::endl;
file << "\teq c (r,g,b,a): " << (m_vt.m_eq.r & 1) << DEL << (m_vt.m_eq.g & 1) << DEL << (m_vt.m_eq.b & 1) << DEL << (m_vt.m_eq.a & 1) << std::endl;
file << "\teq p (x,y,z,f): " << (m_vt.m_eq.x & 1) << DEL << (m_vt.m_eq.y & 1) << DEL << (m_vt.m_eq.z & 1) << DEL << (m_vt.m_eq.f & 1) << std::endl;
file << "\teq t (u,v,q) : " << (m_vt.m_eq.s & 1) << DEL << (m_vt.m_eq.t & 1) << DEL << (m_vt.m_eq.q & 1) << std::endl;
file.close();
file << INDENT;
WriteTraceIndex("eq_xyz: ");
file << OPEN_MAP;
WriteBools({"X", "Y", "Z"}, {m_vt.m_eq.x, m_vt.m_eq.y, m_vt.m_eq.z});
file << CLOSE_MAP << std::endl;
if (PRIM->TME)
{
if (PRIM->FST)
{
file << INDENT;
WriteTraceIndex("eq_uv: ");
file << OPEN_MAP;
WriteBools({"U", "V"}, {m_vt.m_eq.s, m_vt.m_eq.t});
file << CLOSE_MAP << std::endl;
}
else
{
// Note: The vertex trace does not actually track the min/max of raw ST values
// hence the labels "eq_uvq" is used instead of "eq_stq".
file << INDENT;
WriteTraceIndex("eq_uvq: ");
file << OPEN_MAP;
WriteBools({"U", "V", "Q"}, {m_vt.m_eq.s, m_vt.m_eq.t, m_vt.m_eq.q});
file << CLOSE_MAP << std::endl;
}
}
file << INDENT;
WriteTraceIndex("eq_rgba: ");
file << OPEN_MAP;
WriteBools({"R", "G", "B", "A"}, {m_vt.m_eq.r, m_vt.m_eq.g, m_vt.m_eq.b, m_vt.m_eq.a});
file << CLOSE_MAP << std::endl;
if (PRIM->FGE)
{
file << INDENT;
WriteTraceIndex("eq_f: ");
file << OPEN_MAP;
WriteBools({"F"}, {m_vt.m_eq.f});
file << CLOSE_MAP << std::endl;
}
}
void GSState::DumpTransferList(const std::string& filename)
{
// Only create the file if there are transfers to dump
std::optional<std::ofstream> file;
constexpr const char* LIST_ITEM = "- ";
constexpr const char* DEL = ", ";
constexpr const char* INDENT = " ";
constexpr const char* OPEN_MAP = "{";
constexpr const char* CLOSE_MAP = "}";
constexpr const char* COMMENT = " # ";
int n_dumped = 0; // Number of transfers dumped for this draw.
for (int i = 0; i < static_cast<int>(m_draw_transfers.size()); ++i)
{
if (m_draw_transfers[i].draw != s_n - 1)
continue; // skip transfers that did not start in the previous draw
if (!file.has_value())
{
file.emplace(filename);
if (!file->is_open())
return; // failed to open file
file->imbue(std::locale::classic()); // Disable integer separators.
}
const GSUploadQueue& transfer = m_draw_transfers[i];
if (n_dumped > 0)
(*file) << std::endl;
// EE->GS or GS->GS
(*file) << LIST_ITEM << "type: " << (transfer.ee_to_gs ? "EE_to_GS" : "GS_to_GS") << std::endl;
// Dump BITBLTBUF
(*file) << INDENT << "BITBLTBUF: " << OPEN_MAP;
if (!transfer.ee_to_gs)
{
// Transferring GS->GS so the source info is relevant
(*file) << "SBP: " << std::hex << std::showbase << transfer.blit.SBP << DEL <<
"SBW: " << std::dec << transfer.blit.SBW << DEL <<
"SPSM: " << std::hex << std::showbase << transfer.blit.SPSM << DEL;
}
(*file) << "DBP: " << std::hex << std::showbase << transfer.blit.DBP << DEL <<
"DBW: " << std::dec << transfer.blit.DBW << DEL <<
"DPSM: " << std::hex << std::showbase << transfer.blit.DPSM << CLOSE_MAP;
(*file) << COMMENT; // Write the human-readable PSM in comments
if (!transfer.ee_to_gs)
{
// Transferring GS->GS so the source info is relevant
(*file) << GSUtil::GetPSMName(transfer.blit.SPSM) << " -> ";
}
(*file) << GSUtil::GetPSMName(transfer.blit.DPSM) << std::endl;
// Dump rectangle
(*file) << INDENT << "rect: [" << std::dec << transfer.rect.x << DEL << transfer.rect.y << DEL <<
transfer.rect.z << DEL << transfer.rect.w << "]" << std::endl;
// Dump zero_clear
(*file) << INDENT << "zero_clear: " << (transfer.zero_clear ? "true" : "false") << std::endl;
n_dumped++;
}
}
void GSState::DumpTransferImages()
{
// Only create the file if there are transfers to dump
std::optional<std::ofstream> file;
int transfer_n = 0;
for (int i = 0; i < static_cast<int>(m_draw_transfers.size()); ++i)
{
if (m_draw_transfers[i].draw != s_n - 1)
continue; // skip transfers that did not start in the previous draw
const GSUploadQueue& transfer = m_draw_transfers[i];
std::string filename;
if (transfer.ee_to_gs)
{
// Transferring EE->GS then only the destination info is relevant.
filename = GetDrawDumpPath("%05d_transfer%02d_EE_to_GS_%03x_%d_%s_%d_%d_%d_%d.png",
s_n, transfer_n++, transfer.blit.DBP, transfer.blit.DBW, GSUtil::GetPSMName(transfer.blit.DPSM),
transfer.rect.x, transfer.rect.y, transfer.rect.z, transfer.rect.w);
}
else
{
// Transferring GS->GS then the source info is relevant.
filename = GetDrawDumpPath("%05d_transfer%02d_GS_to_GS_%03x_%d_%s_%03x_%d_%s_%d_%d_%d_%d.bmp",
s_n, transfer_n++, transfer.blit.SBP, transfer.blit.SBW, GSUtil::GetPSMName(transfer.blit.SPSM),
transfer.blit.DBP, transfer.blit.DBW, GSUtil::GetPSMName(transfer.blit.DPSM),
transfer.rect.x, transfer.rect.y, transfer.rect.z, transfer.rect.w);
}
m_mem.SaveBMP(filename, transfer.blit.DBP, transfer.blit.DBW, transfer.blit.DPSM,
transfer.rect.width(), transfer.rect.height(), transfer.rect.x, transfer.rect.y);
}
}
__inline void GSState::CheckFlushes()
@@ -1771,6 +2103,20 @@ void GSState::FlushPrim()
const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER);
m_quad_check_valid = false;
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
if (GSConfig.SaveInfo)
{
// Only dump registers/vertices if we are drawing.
// Always dump the transfers since these are relevant for debugging regardless of
// whether the draw is skipped or not.
DumpDrawInfo(!skip_draw, !skip_draw, true);
}
if (GSConfig.SaveTransferImages)
DumpTransferImages();
}
if (!skip_draw)
Draw();
@@ -2016,7 +2362,7 @@ void GSState::Write(const u8* mem, int len)
}
else
{
GSUploadQueue new_transfer = { blit, r, s_n, false };
const GSUploadQueue new_transfer = { blit, r, s_n, false, true };
m_draw_transfers.push_back(new_transfer);
}
@@ -2207,7 +2553,7 @@ void GSState::Move()
}
else
{
GSUploadQueue new_transfer = { m_env.BITBLTBUF, r, s_n, false };
const GSUploadQueue new_transfer = { m_env.BITBLTBUF, r, s_n, false, false };
m_draw_transfers.push_back(new_transfer);
}

View File

@@ -215,6 +215,7 @@ public:
GSVector4i rect;
int draw;
bool zero_clear;
bool ee_to_gs;
};
enum NoGapsType
@@ -441,7 +442,10 @@ public:
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
void DumpDrawInfo(bool dump_regs, bool dump_verts, bool dump_transfers);
void DumpVertices(const std::string& filename);
void DumpTransferList(const std::string& filename);
void DumpTransferImages();
bool TrianglesAreQuads(bool shuffle_check = false);
PRIM_OVERLAP PrimitiveOverlap();

View File

@@ -114,6 +114,68 @@ const char* GSUtil::GetPrimName(u32 prim)
return (prim < std::size(names)) ? names[prim] : "";
}
const char* GSUtil::GetPrimClassName(u32 primclass)
{
static constexpr const char* names[] = {
"POINT", "LINE", "TRIANGLE", "SPRITE", "INVALID"};
return (primclass < std::size(names)) ? names[primclass] : "";
}
const char* GSUtil::GetMMAGName(u32 mmag)
{
static constexpr const char* names[] = {"NEAREST", "LINEAR"};
return (mmag < std::size(names)) ? names[mmag] : "";
}
const char* GSUtil::GetMMINName(u32 mmin)
{
static constexpr const char* names[8] = {"NEAREST", "LINEAR", "NEAREST_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
"LINEAR_MIPMAP_NEAREST", "LINEAR_MIPMAP_LINEAR"};
return (mmin < std::size(names)) ? names[mmin] : "";
}
const char* GSUtil::GetMTBAName(u32 mtba)
{
static constexpr const char* names[] = {"MIPTBP1", "AUTO"};
return (mtba < std::size(names)) ? names[mtba] : "";
}
const char* GSUtil::GetLCMName(u32 lcm)
{
static constexpr const char* names[] = {"Formula", "K"};
return (lcm < std::size(names)) ? names[lcm] : "";
}
const char* GSUtil::GetSCANMSKName(u32 scanmsk)
{
static constexpr const char* names[] = {"Normal", "Reserved", "Even prohibited", "Odd prohibited"};
return (scanmsk < std::size(names)) ? names[scanmsk] : "";
}
const char* GSUtil::GetDATMName(u32 datm)
{
static constexpr const char* names[] = {"0 pass", "1 pass"};
return (datm < std::size(names)) ? names[datm] : "";
}
const char* GSUtil::GetTFXName(u32 tfx)
{
static constexpr const char* names[] = {"MODULATE", "DECAL", "HIGHLIGHT", "HIGHLIGHT2"};
return (tfx < std::size(names)) ? names[tfx] : "";
}
const char* GSUtil::GetTCCName(u32 tcc)
{
static constexpr const char* names[] = {"RGB", "RGBA"};
return (tcc < std::size(names)) ? names[tcc] : "";
}
const char* GSUtil::GetACName(u32 ac)
{
static constexpr const char* names[] = {"PRMODE", "PRIM"};
return (ac < std::size(names)) ? names[ac] : "";
}
const u32* GSUtil::HasSharedBitsPtr(u32 dpsm)
{
return s_maps.SharedBitsField[dpsm];

View File

@@ -15,6 +15,16 @@ public:
static const char* GetWMName(u32 wm);
static const char* GetZTSTName(u32 ztst);
static const char* GetPrimName(u32 prim);
static const char* GetPrimClassName(u32 primclass);
static const char* GetMMAGName(u32 mmag);
static const char* GetMMINName(u32 mmin);
static const char* GetMTBAName(u32 mtba);
static const char* GetLCMName(u32 lcm);
static const char* GetSCANMSKName(u32 scanmsk);
static const char* GetDATMName(u32 datm);
static const char* GetTFXName(u32 tfx);
static const char* GetTCCName(u32 tcc);
static const char* GetACName(u32 ac);
static const u32* HasSharedBitsPtr(u32 dpsm);
static bool HasSharedBits(u32 spsm, const u32* ptr);

View File

@@ -985,10 +985,6 @@ public:
__forceinline GSVector4i modulate16(const GSVector4i& f) const
{
// a * f << shift
if (shift == 0)
{
return mul16hrs(f);
}
return sll16<shift + 1>().mul16hs(f);
}

View File

@@ -958,10 +958,6 @@ public:
__forceinline GSVector4i modulate16(const GSVector4i& f) const
{
// a * f << shift
if (shift == 0)
{
return mul16hrs(f);
}
return sll16<shift + 1>().mul16hs(f);
}

View File

@@ -798,11 +798,6 @@ public:
{
// a * f << shift
if (shift == 0)
{
return mul16hrs(f);
}
return sll16<shift + 1>().mul16hs(f);
}

View File

@@ -397,6 +397,41 @@ void GSDevice::ClearDepth(GSTexture* t, float d)
t->SetClearDepth(d);
}
bool GSDevice::ProcessClearsBeforeCopy(GSTexture* sTex, GSTexture* dTex, const bool full_copy)
{
pxAssert(sTex->GetState() == GSTexture::State::Cleared && dTex->IsRenderTargetOrDepthStencil());
// Pass it forward if we're clearing the whole thing.
if (full_copy)
{
if (dTex->IsDepthStencil())
dTex->SetClearDepth(sTex->GetClearDepth());
else
dTex->SetClearColor(sTex->GetClearColor());
dTex->SetState(GSTexture::State::Cleared);
return true;
}
// Destination is cleared, if it's the same colour and rect, we can just avoid this entirely.
if (dTex->GetState() == GSTexture::State::Cleared)
{
if (dTex->IsDepthStencil())
{
if (dTex->GetClearDepth() == sTex->GetClearDepth())
return true;
}
else
{
if (dTex->GetClearColor() == sTex->GetClearColor())
return true;
}
}
return false;
}
void GSDevice::InvalidateRenderTarget(GSTexture* t)
{
t->SetState(GSTexture::State::Invalidated);

View File

@@ -1022,6 +1022,7 @@ public:
void ClearRenderTarget(GSTexture* t, u32 c);
void ClearDepth(GSTexture* t, float d);
bool ProcessClearsBeforeCopy(GSTexture* sTex, GSTexture* dTex, const bool full_copy);
void InvalidateRenderTarget(GSTexture* t);
virtual void PushDebugGroup(const char* fmt, ...) = 0;

View File

@@ -107,9 +107,20 @@ bool GSRenderer::Merge(int field)
}
else
{
if (PCRTCDisplays.PCRTCDisplays[0].enabled)
const bool use_rc1 =
PCRTCDisplays.PCRTCDisplays[0].enabled && // RC1 enabled.
(!(m_regs->PMODE.MMOD == 1 && m_regs->PMODE.ALP == 0) || // Blend RC1 with non-zero alpha.
(m_regs->PMODE.AMOD == 0) || // Use alpha of RC1.
(feedback_merge && m_regs->EXTBUF.FBIN == 0)); // Use RC1 for feedback merge.
const bool use_rc2 =
PCRTCDisplays.PCRTCDisplays[1].enabled && // RC2 enabled.
// Blending RC2 and not overwriting completely with RC1.
((m_regs->PMODE.SLBG == 0 && !(use_rc1 && m_regs->PMODE.MMOD == 1 && m_regs->PMODE.ALP == 255)) ||
(m_regs->PMODE.AMOD == 1) || // Use alpha of RC2.
(feedback_merge && m_regs->EXTBUF.FBIN == 1)); // Use RC2 for feedback merge.
if (use_rc1)
tex[0] = GetOutput(0, tex_scale[0], y_offset[0]);
if (PCRTCDisplays.PCRTCDisplays[1].enabled)
if (use_rc2)
tex[1] = GetOutput(1, tex_scale[1], y_offset[1]);
if (feedback_merge)
tex[2] = GetFeedbackOutput(tex_scale[2]);
@@ -569,9 +580,17 @@ void GSRenderer::EndPresentFrame()
void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
{
if (GSConfig.SaveInfo && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
DumpGSPrivRegs(*m_regs, GetDrawDumpPath("%05d_f%05lld_vsync_gs_reg.txt", s_n, g_perfmon.GetFrame()));
if (GSConfig.SaveInfo)
{
DumpGSPrivRegs(*m_regs, GetDrawDumpPath("%05d_f%05lld_vsync_gs_reg.txt", s_n, g_perfmon.GetFrame()));
DumpDrawInfo(false, false, true);
}
if (GSConfig.SaveTransferImages)
DumpTransferImages();
}
const int fb_sprite_blits = g_perfmon.GetDisplayFramebufferSpriteBlits();
@@ -1088,7 +1107,7 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
if (i == 1 && !r.PMODE.EN2)
continue;
std::fprintf(fp.get(), "DISPFB[%d] BP=%05x BW=%u PSM=%u DBX=%u DBY=%u\n",
std::fprintf(fp.get(), "DISPFB%d: { BP: 0x%05x, BW: %u, PSM: %u, DBX: %u, DBY: %u }\n",
i,
r.DISP[i].DISPFB.Block(),
r.DISP[i].DISPFB.FBW,
@@ -1096,7 +1115,7 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
r.DISP[i].DISPFB.DBX,
r.DISP[i].DISPFB.DBY);
std::fprintf(fp.get(), "DISPLAY[%d] DX=%u DY=%u DW=%u DH=%u MAGH=%u MAGV=%u\n",
std::fprintf(fp.get(), "DISPLAY%d: { DX: %u, DY: %u, DW: %u, DH: %u, MAGH: %u, MAGV: %u }\n",
i,
r.DISP[i].DISPLAY.DX,
r.DISP[i].DISPLAY.DY,
@@ -1106,7 +1125,7 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
r.DISP[i].DISPLAY.MAGV);
}
std::fprintf(fp.get(), "PMODE EN1=%u EN2=%u CRTMD=%u MMOD=%u AMOD=%u SLBG=%u ALP=%u\n",
std::fprintf(fp.get(), "PMODE: { EN1: %u, EN2: %u, CRTMD: %u, MMOD: %u, AMOD: %u, SLBG: %u, ALP: %u }\n",
r.PMODE.EN1,
r.PMODE.EN2,
r.PMODE.CRTMD,
@@ -1115,7 +1134,8 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
r.PMODE.SLBG,
r.PMODE.ALP);
std::fprintf(fp.get(), "SMODE1 CLKSEL=%u CMOD=%u EX=%u GCONT=%u LC=%u NVCK=%u PCK2=%u PEHS=%u PEVS=%u PHS=%u PRST=%u PVS=%u RC=%u SINT=%u SLCK=%u SLCK2=%u SPML=%u T1248=%u VCKSEL=%u VHP=%u XPCK=%u\n",
std::fprintf(fp.get(),
"SMODE1: { CLKSEL: %u, CMOD: %u, EX: %u, GCONT: %u, LC: %u, NVCK: %u, PCK2: %u, PEHS: %u, PEVS: %u, PHS: %u, PRST: %u, PVS: %u, RC: %u, SINT: %u, SLCK: %u, SLCK2: %u, SPML: %u, T1248: %u, VCKSEL: %u, VHP: %u, XPCK: %u }\n",
r.SMODE1.CLKSEL,
r.SMODE1.CMOD,
r.SMODE1.EX,
@@ -1138,24 +1158,24 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
r.SMODE1.VHP,
r.SMODE1.XPCK);
std::fprintf(fp.get(), "SMODE2 INT=%u FFMD=%u DPMS=%u\n",
std::fprintf(fp.get(), "SMODE2: { INT: %u, FFMD: %u, DPMS: %u }\n",
r.SMODE2.INT,
r.SMODE2.FFMD,
r.SMODE2.DPMS);
std::fprintf(fp.get(), "SRFSH %08x_%08x\n",
std::fprintf(fp.get(), "SRFSH: { U32_0: 0x%08x, U32_1: 0x%08x }\n",
r.SRFSH.U32[0],
r.SRFSH.U32[1]);
std::fprintf(fp.get(), "SYNCH1 %08x_%08x\n",
std::fprintf(fp.get(), "SYNCH1: { U32_0: 0x%08x, U32_1: 0x%08x }\n",
r.SYNCH1.U32[0],
r.SYNCH1.U32[1]);
std::fprintf(fp.get(), "SYNCH2 %08x_%08x\n",
std::fprintf(fp.get(), "SYNCH2: { U32_0: 0x%08x, U32_1: 0x%08x }\n",
r.SYNCH2.U32[0],
r.SYNCH2.U32[1]);
std::fprintf(fp.get(), "SYNCV VBP=%u VBPE=%u VDP=%u VFP=%u VFPE=%u VS=%u\n",
std::fprintf(fp.get(), "SYNCV: { VBP: %u, VBPE: %u, VDP: %u, VFP: %u, VFPE: %u, VS: %u }\n",
r.SYNCV.VBP,
r.SYNCV.VBPE,
r.SYNCV.VDP,
@@ -1163,21 +1183,21 @@ void DumpGSPrivRegs(const GSPrivRegSet& r, const std::string& filename)
r.SYNCV.VFPE,
r.SYNCV.VS);
std::fprintf(fp.get(), "CSR %08x_%08x\n",
std::fprintf(fp.get(), "CSR: { U32_0: 0x%08x, U32_1: 0x%08x }\n",
r.CSR.U32[0],
r.CSR.U32[1]);
std::fprintf(fp.get(), "BGCOLOR B=%u G=%u R=%u\n",
std::fprintf(fp.get(), "BGCOLOR: { B: %u, G: %u, R: %u }\n",
r.BGCOLOR.B,
r.BGCOLOR.G,
r.BGCOLOR.R);
std::fprintf(fp.get(), "EXTBUF BP=0x%x BW=%u FBIN=%u WFFMD=%u EMODA=%u EMODC=%u WDX=%u WDY=%u\n",
std::fprintf(fp.get(), "EXTBUF: { BP: 0x%05x, BW: %u, FBIN: %u, WFFMD: %u, EMODA: %u, EMODC: %u, WDX: %u, WDY: %u }\n",
r.EXTBUF.EXBP, r.EXTBUF.EXBW, r.EXTBUF.FBIN, r.EXTBUF.WFFMD,
r.EXTBUF.EMODA, r.EXTBUF.EMODC, r.EXTBUF.WDX, r.EXTBUF.WDY);
std::fprintf(fp.get(), "EXTDATA SX=%u SY=%u SMPH=%u SMPV=%u WW=%u WH=%u\n",
std::fprintf(fp.get(), "EXTDATA: { SX: %u, SY: %u, SMPH: %u, SMPV: %u, WW: %u, WH: %u }\n",
r.EXTDATA.SX, r.EXTDATA.SY, r.EXTDATA.SMPH, r.EXTDATA.SMPV, r.EXTDATA.WW, r.EXTDATA.WH);
std::fprintf(fp.get(), "EXTWRITE EN=%u\n", r.EXTWRITE.WRITE);
std::fprintf(fp.get(), "EXTWRITE: { EN: %u }\n", r.EXTWRITE.WRITE);
}

View File

@@ -409,7 +409,7 @@ GSRendererType D3D::GetPreferredRenderer()
return true;
Host::AddIconOSDMessage("VKDriverUnsupported", ICON_FA_TV, TRANSLATE_STR("GS",
"The Vulkan renderer was automatically selected, but no compatible devices were found.\n"
"The Vulkan graphics API was automatically selected, but no compatible devices were found.\n"
" You should update all graphics drivers in your system, including any integrated GPUs\n"
" to use the Vulkan renderer."), Host::OSD_WARNING_DURATION);
return false;

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GS.h"
#include "GS/GSGL.h"
#include "GSDevice11.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSExtra.h"
@@ -1229,22 +1230,48 @@ std::unique_ptr<GSDownloadTexture> GSDevice11::CreateDownloadTexture(u32 width,
void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
{
CommitClear(sTex);
CommitClear(dTex);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// DX11 doesn't support partial depth copy so we need to
// either pass a nullptr D3D11_BOX for a full depth copy or use CopyResource instead.
// Optimization: Use CopyResource for depth copies, it's faster than CopySubresourceRegion.
if (sTex->GetType() == GSTexture::Type::DepthStencil)
// Empty rect, abort copy.
if (r.rempty())
{
m_ctx->CopyResource(*static_cast<GSTexture11*>(dTex), *static_cast<GSTexture11*>(sTex));
GL_INS("D3D11: CopyRect rect empty.");
return;
}
D3D11_BOX box = {static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0U, static_cast<UINT>(r.right), static_cast<UINT>(r.bottom), 1U};
m_ctx->CopySubresourceRegion(*static_cast<GSTexture11*>(dTex), 0, destX, destY, 0, *static_cast<GSTexture11*>(sTex), 0, &box);
const GSVector4i dst_rect(0, 0, dTex->GetWidth(), dTex->GetHeight());
const bool full_draw_copy = sTex->IsDepthStencil() || dst_rect.eq(r);
// Source is cleared, if destination is a render target, we can carry the clear forward.
if (sTex->GetState() == GSTexture::State::Cleared)
{
if (dTex->IsRenderTargetOrDepthStencil() && ProcessClearsBeforeCopy(sTex, dTex, full_draw_copy))
return;
// Commit clear for the source texture.
CommitClear(sTex);
}
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Commit destination clear if partially overwritten (color only).
if (dTex->GetState() == GSTexture::State::Cleared && !full_draw_copy)
CommitClear(dTex);
// DX11 doesn't support partial depth copy so we need to
// either pass a nullptr D3D11_BOX for a full depth copy or use CopyResource instead.
// Optimization: Use CopyResource for depth copies or full rect color copies, it's faster than CopySubresourceRegion.
const GSVector4i src_rect(0, 0, sTex->GetWidth(), sTex->GetHeight());
const bool full_rt_copy = sTex->IsDepthStencil() || (destX == 0 && destY == 0 && r.eq(src_rect) && src_rect.eq(dst_rect));
if (full_rt_copy)
{
m_ctx->CopyResource(*static_cast<GSTexture11*>(dTex), *static_cast<GSTexture11*>(sTex));
}
else
{
const D3D11_BOX box = {static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0U, static_cast<UINT>(r.right), static_cast<UINT>(r.bottom), 1U};
m_ctx->CopySubresourceRegion(*static_cast<GSTexture11*>(dTex), 0, destX, destY, 0, *static_cast<GSTexture11*>(sTex), 0, &box);
}
dTex->SetState(GSTexture::State::Dirty);
}
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)

View File

@@ -1337,77 +1337,73 @@ std::unique_ptr<GSDownloadTexture> GSDevice12::CreateDownloadTexture(u32 width,
void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GSTexture12* const sTexVK = static_cast<GSTexture12*>(sTex);
GSTexture12* const dTexVK = static_cast<GSTexture12*>(dTex);
const GSVector4i dtex_rc(0, 0, dTexVK->GetWidth(), dTexVK->GetHeight());
if (sTexVK->GetState() == GSTexture::State::Cleared)
// Empty rect, abort copy.
if (r.rempty())
{
// source is cleared. if destination is a render target, we can carry the clear forward
if (dTexVK->IsRenderTargetOrDepthStencil())
GL_INS("D3D12: CopyRect rect empty.");
return;
}
GSTexture12* const sTex12 = static_cast<GSTexture12*>(sTex);
GSTexture12* const dTex12 = static_cast<GSTexture12*>(dTex);
const GSVector4i dst_rect(0, 0, dTex12->GetWidth(), dTex12->GetHeight());
const bool full_draw_copy = dst_rect.eq(r);
// Source is cleared, if destination is a render target, we can carry the clear forward.
if (sTex12->GetState() == GSTexture::State::Cleared)
{
if (dTex12->IsRenderTargetOrDepthStencil() && ProcessClearsBeforeCopy(sTex, dTex, full_draw_copy))
return;
// Do an attachment clear.
EndRenderPass();
dTex12->SetState(GSTexture::State::Dirty);
if (dTex12->GetType() != GSTexture::Type::DepthStencil)
{
if (dtex_rc.eq(r))
{
// pass it forward if we're clearing the whole thing
if (sTexVK->IsDepthStencil())
dTexVK->SetClearDepth(sTexVK->GetClearDepth());
else
dTexVK->SetClearColor(sTexVK->GetClearColor());
return;
}
else
{
// otherwise we need to do an attachment clear
EndRenderPass();
dTexVK->SetState(GSTexture::State::Dirty);
if (dTexVK->GetType() != GSTexture::Type::DepthStencil)
{
dTexVK->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
GetCommandList()->ClearRenderTargetView(
dTexVK->GetWriteDescriptor(), sTexVK->GetUNormClearColor().v, 0, nullptr);
}
else
{
dTexVK->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
GetCommandList()->ClearDepthStencilView(
dTexVK->GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, sTexVK->GetClearDepth(), 0, 0, nullptr);
}
return;
}
dTex12->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
GetCommandList()->ClearRenderTargetView(
dTex12->GetWriteDescriptor(), sTex12->GetUNormClearColor().v, 0, nullptr);
}
else
{
dTex12->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
GetCommandList()->ClearDepthStencilView(
dTex12->GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, sTex12->GetClearDepth(), 0, 0, nullptr);
}
return;
// commit the clear to the source first, then do normal copy
sTexVK->CommitClear();
sTex12->CommitClear();
}
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// if the destination has been cleared, and we're not overwriting the whole thing, commit the clear first
// (the area outside of where we're copying to)
if (dTexVK->GetState() == GSTexture::State::Cleared && !dtex_rc.eq(r))
dTexVK->CommitClear();
if (dTex12->GetState() == GSTexture::State::Cleared && !full_draw_copy)
dTex12->CommitClear();
EndRenderPass();
sTexVK->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE);
sTexVK->SetUseFenceCounter(GetCurrentFenceValue());
if (m_tfx_textures[0] && sTexVK->GetSRVDescriptor() == m_tfx_textures[0])
sTex12->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE);
sTex12->SetUseFenceCounter(GetCurrentFenceValue());
if (m_tfx_textures[0] && sTex12->GetSRVDescriptor() == m_tfx_textures[0])
PSSetShaderResource(0, nullptr, false);
dTexVK->TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST);
dTexVK->SetUseFenceCounter(GetCurrentFenceValue());
dTex12->TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST);
dTex12->SetUseFenceCounter(GetCurrentFenceValue());
D3D12_TEXTURE_COPY_LOCATION srcloc;
srcloc.pResource = sTexVK->GetResource();
srcloc.pResource = sTex12->GetResource();
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcloc.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dstloc;
dstloc.pResource = dTexVK->GetResource();
dstloc.pResource = dTex12->GetResource();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = 0;
@@ -1415,7 +1411,7 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
static_cast<UINT>(r.bottom), 1u};
GetCommandList()->CopyTextureRegion(&dstloc, destX, destY, 0, &srcloc, &srcbox);
dTexVK->SetState(GSTexture::State::Dirty);
dTex12->SetState(GSTexture::State::Dirty);
}
void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,

View File

@@ -2317,21 +2317,6 @@ void GSRendererHW::RoundSpriteOffset()
void GSRendererHW::Draw()
{
if (GSConfig.SaveInfo && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
std::string s;
// Dump Register state
s = GetDrawDumpPath("%05d_context.txt", s_n);
m_draw_env->Dump(s);
m_context->Dump(s);
// Dump vertices
s = GetDrawDumpPath("%05d_vertex.txt", s_n);
DumpVertices(s);
}
static u32 num_skipped_channel_shuffle_draws = 0;
// We mess with this state as an optimization, so take a copy and use that instead.

Some files were not shown because too many files have changed in this diff Show More