Compare commits

...

257 Commits

Author SHA1 Message Date
TJnotJT
be1af0cd0f GS: Allow dumping draw/frames stats. 2025-11-16 21:26:10 +01:00
TheTechnician27
6ab02e76f1 Docs: Remove Debugger.md 2025-11-15 14:55:28 -05:00
TheTechnician27
f87bc7d72b UI: Make 'Start fullscreen' option work for BPM and improve variable name
Co-authored-by: Geraldi Kusuma Arnanto <981538+aldee@users.noreply.github.com>
2025-11-15 13:37:21 -05:00
chaoticgd
086f4f11e1 Qt: Prevent entering/exiting fullscreen while the VM is locked 2025-11-15 13:31:23 -05:00
TJnotJT
6f54da6234 GS/HW/TC: Do not force temporary source creation for PSMT8 sources that request outside a target valid area. 2025-11-15 16:56:57 +01:00
TJnotJT
44f47f11b8 GS/HW/TC: Force a temporary source creation in edges cases.
Case: When looking up a source, we find a perfect BP hit for a target.
However, the requested area is outside the target's valid area.
Don't use the target direct and instead load from memory in a temporary source.

Co-authored-by: refraction
2025-11-15 16:56:57 +01:00
TJnotJT
b5a2d04b2e GS/HW/TC: Remove outside target PSM check in LookupSource.
Co-autored-by: refraction
2025-11-15 16:56:57 +01:00
TJnotJT
8508ebb7d3 GS/HW: Remove legacy code for changing RT from depth to color in TC invalidation.
Co-authored-by: refraction
2025-11-15 16:56:57 +01:00
lightningterror
3234e45f33 GS/HW: Also check if blend will update the rt.
If it doesn't and there is no depth buffer we can abort the draw(s) safely.
2025-11-15 16:54:12 +01:00
lightningterror
53d1320d83 GS/HW: Re check if RT is written after we know the source alpha. 2025-11-15 16:54:12 +01:00
TheTechnician27
9b545809be Docs: Remove debugger.txt and Debugger.pdf 2025-11-14 20:46:06 -05:00
chaoticgd
79400acf2a Build: Add VectorIntrin.h to precompiled header 2025-11-14 08:03:27 -05:00
PCSX2 Bot
3107c4103a [ci skip] Qt: Update Base Translation. 2025-11-12 19:06:36 -05:00
chaoticgd
68c88f692e Qt: Fix use-after-free in GameSummaryWidget 2025-11-12 18:51:57 -05:00
chaoticgd
df19b37d6d Qt: Fix game list deadlock when changing discs 2025-11-12 18:51:57 -05:00
chaoticgd
1b5c352566 Debugger: Improve symbol tree enum editing 2025-11-12 18:46:10 -05:00
PCSX2 Bot
bed6a9e4d4 [ci skip] Qt: Update Base Translation. 2025-11-12 01:03:38 +01:00
lightningterror
d602ad1d3e GS/HW: Purge no longer needed/replaced crc hacks GSC_ZettaiZetsumeiToshi2, GSC_SteambotChronicles. 2025-11-11 22:36:46 +01:00
refractionpcsx2
51c31347df GS/HW: Remove clamp hack for edge garbage.
Not seemingly required anymore, might be because NS is a thing.
2025-11-11 22:36:46 +01:00
refractionpcsx2
00876e7076 GameDB: Fixes for later IRem games 2025-11-11 22:36:46 +01:00
refractionpcsx2
47eb499893 GS/HW: Add CRC hack to cover IRem games to deal with depth deswizzle shuffle 2025-11-11 22:36:46 +01:00
refractionpcsx2
b8680c3139 GS/HW: Mask channel offsets in channel shuffle heuristic 2025-11-11 22:36:46 +01:00
refractionpcsx2
9b4e3b8f74 GS/HW: Adjust target lookup to better check on source match 2025-11-11 22:36:46 +01:00
refractionpcsx2
3e858167bc Build: Update gamedb documentation and linter 2025-11-11 21:19:02 +01:00
refractionpcsx2
44d66555cc GS/HW: Add options to maintain upscale in Native Scaling
This will adjust how the box filter samples to simulate bilinear when upscaling
2025-11-11 21:19:02 +01:00
refractionpcsx2
c5438ceca3 GS/HW: Fix up invalidly selected Tex in RT targets 2025-11-11 20:58:35 +01:00
JordanTheToaster
3e1927ae44 GameDB: Ore no Shita de Agake fixes 2025-11-11 14:59:04 +01:00
TheTechnician27
b688117002 RTC: Automatically detect timezone 2025-11-10 20:14:36 -06:00
dependabot[bot]
e62e6fb6c3 Bump softprops/action-gh-release in the ci-deps group
Bumps the ci-deps group with 1 update: [softprops/action-gh-release](https://github.com/softprops/action-gh-release).


Updates `softprops/action-gh-release` from 2.4.1 to 2.4.2
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](6da8fa9354...5be0e66d93)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: ci-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 19:28:32 -05:00
PCSX2 Bot
262e94e5d7 [ci skip] PAD: Update to latest controller database. 2025-11-10 20:01:35 +01:00
PCSX2 Bot
11a4b4e7ff [ci skip] Qt: Update Base Translation. 2025-11-10 01:07:34 +01:00
chaoticgd
a98cfcf28c CDVD: Synchronise access from different threads 2025-11-09 09:27:27 -05:00
PCSX2 Bot
d02f30ee62 [ci skip] Qt: Update Base Translation. 2025-11-08 18:23:15 -06:00
JordanTheToaster
c0bf01a646 MacOS: Patch Tahoe unclickable buttons 2025-11-08 08:31:46 -05:00
TheTechnician27
babb985e9e GS Hotkeys: Improve upscale and downscale hotkeys 2025-11-07 20:28:27 -05:00
PCSX2 Bot
e379c8317d [ci skip] Qt: Update Base Translation. 2025-11-07 03:49:56 +01:00
chaoticgd
5098277474 Debugger: Fix 1 pixel gap under layout tabs on non-Windows platforms 2025-11-06 09:32:29 -05:00
TheLastRar
4a94cb6cbd DEV9: Remove using namespace in TAPAdapter header 2025-11-05 16:44:10 -05:00
TheTechnician27
e245454b91 GameListWidget: Fix icon cell highlights and column widths 2025-11-05 16:43:28 -05:00
TheTechnician27
b003eadd2d VMManager: More helpful error message on no BIOS present 2025-11-03 12:52:28 -05:00
JordanTheToaster
a5984d8213 Flatpak: Update KDE runtime to 6.10 2025-11-03 12:49:35 -05:00
Stern
4dbd95b0bb labeler: Add OSD / ImGui labeler 2025-11-03 10:38:26 -05:00
JordanTheToaster
68803229da Core: Bump savestate version.
[SAVEVERSION+]
2025-11-02 20:19:38 -05:00
chaoticgd
b661a2a149 Deps: Update KDDockWidgets to 2.4.0 2025-11-02 15:02:25 -05:00
PCSX2 Bot
63cd355d7a [ci skip] Qt: Update Base Translation. 2025-11-02 14:43:04 -05:00
Ziemas
0f5ff68679 SPU: Remove KON delay/check key once per T 2025-11-02 14:36:54 -05:00
JordanTheToaster
bd74921926 Deps: Update SDL3 to v3.2.26 2025-11-01 11:32:32 -04:00
TheLastRar
78f83514f4 Deps: Force lib directory for libjpeg-turbo build 2025-11-01 11:31:20 -04:00
TheTechnician27
2c36259b88 DB: Remove some unsupported or nonexistent serials 2025-11-01 11:29:27 -04:00
TheTechnician27
32a0bed6af GameListModel: Touch up a couple functions 2025-11-01 11:28:59 -04:00
TheTechnician27
d415f8364c Snapshots: Clean up per-game snapshots folder code 2025-11-01 11:14:55 -04:00
Ty Lamontagne
7c768b6833 Qt: Mouse Lock: Add warning about mixed-resolution non 100% DPI configs 2025-11-01 10:21:54 -04:00
Ty Lamontagne
773f6968a4 Qt: Implement mouse locking when rendering to separate window 2025-11-01 10:21:54 -04:00
Ty Lamontagne
1021199512 Qt: Visually disable the mouse lock button on wayland 2025-11-01 10:21:54 -04:00
Ty Lamontagne
08ef9e2bd9 Qt: Make mouse screen locking DPI aware
Also removed some global mouse hook stuff. Don't ever want to use that anyways.
2025-11-01 10:21:54 -04:00
refractionpcsx2
6ba3f96f27 GS/MAD: Try to reconstruct centre line if previous version was weaved in motion 2025-11-01 09:29:09 -04:00
TheTechnician27
7d5b7bc3ce FUNDING.yml: Add Liberapay 2025-11-01 09:20:57 -04:00
TheTechnician27
0d43d30346 OSD: Fix uninitialized save state slot timestamp 2025-10-31 22:19:07 -04:00
PCSX2 Bot
da824b4e9e [ci skip] Qt: Update Base Translation. 2025-10-31 01:03:12 +01:00
chaoticgd
ed08b5f34e Memcard: Fix infinite loop caused by UB when YAML parsing fails 2025-10-30 14:02:55 -04:00
chaoticgd
0ce312c1c3 GameDB: Fix infinite loops caused by UB when YAML parsing fails 2025-10-30 14:02:55 -04:00
PCSX2 Bot
07bc2fa452 [ci skip] Qt: Update Base Translation. 2025-10-28 01:19:58 +01:00
TheTechnician27
bfd2775074 Qt: Add TextBrowserInteraction to labels where appropriate 2025-10-28 00:36:00 +01:00
TheTechnician27
94ccafd745 MainWindow: Make Settings > Video Capture checkbox toggle correctly 2025-10-27 19:25:25 -04:00
KamFretoZ
5c6049c4ae Qt: Add Create game shortcut functionality 2025-10-28 00:11:09 +01:00
PCSX2 Bot
090464c42d [ci skip] PAD: Update to latest controller database. 2025-10-27 18:43:11 +01:00
PCSX2 Bot
89a00db3d6 [ci skip] Qt: Update Base Translation. 2025-10-27 14:08:50 +01:00
dependabot[bot]
fc415dff93 GHActions: Update action scripts
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 08:36:36 -04:00
TheTechnician27
f648a9a438 QtHost: Fix locale crash on non-Windows machines 2025-10-25 17:58:00 -04:00
chaoticgd
cac6669423 Debugger: Respond to OS colour scheme changes 2025-10-25 09:48:18 -04:00
TheTechnician27
7db487a49b Game List: Improve dialog for Reset Play Time 2025-10-25 15:32:52 +02:00
TJnotJT
c96607fe37 GSRunner: Allow loading GS settings from INI. 2025-10-24 21:44:52 -04:00
SternXD
ba0dae5f57 ImGui: Rename BackgroundProgressDialog to ProgressDialog
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 21:41:42 -04:00
SternXD
5fe5148e86 BPM: Add Network and HDD settings page
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 21:41:42 -04:00
TJnotJT
5445cb516a GS: Handle zero clears separately in transfer dump. 2025-10-24 21:40:37 -04:00
PCSX2 Bot
84a29ffcca [ci skip] Qt: Update Base Translation. 2025-10-25 02:03:31 +02:00
PCSX2 Bot
e56075976f [ci skip] Qt: Update Base Translation. 2025-10-24 15:53:19 -04:00
SternXD
0f709735c0 FullscreenUI: Add footer for clear bindings
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 15:53:05 -04:00
SternXD
ad3f0fd6cd ImGuiFullscreen: Add append and queue footer hints
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 15:53:05 -04:00
TheTechnician27
fbfdacd589 Game Grid: Set Size Hint for List View 2025-10-24 15:50:44 -04:00
chaoticgd
aedc51e151 Qt: Set QStyleHints colorScheme property properly 2025-10-23 20:17:23 -04:00
TheTechnician27
fe95a697f4 GameListModel: Remove dead, nonsense switch case 2025-10-23 19:52:38 -04:00
TheTechnician27
f99cf28429 MainWindow: Add a separator between Achievements and Controllers 2025-10-23 10:38:43 -04:00
JordanTheToaster
bea1eb0cf9 MacOS: Fix for missing Metal Toolchain 2025-10-23 09:03:43 -04:00
Silent
3cf21e0ab6 usb_eyetoy: Clean up COM initialization, prevent a double-release
If the EyeToy camera was enabled in the settings, but not enabled
by the game, it would call CoUninitialize without having called
CoInitializeEx first.
2025-10-22 09:48:06 -04:00
Silent
521b32c253 USB: Initialize COM before using Cubeb on Windows 2025-10-22 09:48:06 -04:00
Silent
190b525ca6 CubebAudioStream: Call CoInitializeEx before creating cubeb 2025-10-22 09:48:06 -04:00
PCSX2 Bot
baa00e4d38 [ci skip] Qt: Update Base Translation. 2025-10-22 13:37:14 +02:00
TheTechnician27
51bc6c1465 pcsx2-qt: Add buddies and tabstops 2025-10-21 18:48:54 -04:00
JordanTheToaster
ea8492082a Windows: Optimize Qt build for size 2025-10-21 12:06:14 -04:00
dependabot[bot]
9d1fd23d78 Bump the ci-deps group with 2 updates
Bumps the ci-deps group with 2 updates: [flatpak/flatpak-github-actions](https://github.com/flatpak/flatpak-github-actions) and [actions/setup-node](https://github.com/actions/setup-node).


Updates `flatpak/flatpak-github-actions` from 6.5 to 6.6
- [Release notes](https://github.com/flatpak/flatpak-github-actions/releases)
- [Commits](https://github.com/flatpak/flatpak-github-actions/compare/v6.5...92ae9851ad316786193b1fd3f40c4b51eb5cb101)

Updates `actions/setup-node` from 5 to 6
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: flatpak/flatpak-github-actions
  dependency-version: '6.6'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci-deps
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: ci-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 11:44:24 -04:00
TellowKrinkle
9ff575377d GHActions: Reduce ccache max size
Reduces the amount of GH cache quota they take up.

[ci skip]
2025-10-21 11:43:37 -04:00
JordanTheToaster
47250293cd Mac OS: Use Xcode 26.0.1 2025-10-20 19:01:03 -04:00
JordanTheToaster
f9b346521d Mac OS: Bump runner to Mac OS 26 2025-10-20 19:01:03 -04:00
TheTechnician27
b5a4c71eff RTC: Add option to choose between system locale and yyyy-MM-dd HH:mm:ss 2025-10-20 18:20:41 +02:00
JordanTheToaster
2604256424 GameDB: Various fixes 2025-10-20 17:49:22 +02:00
chaoticgd
906ac6a8ea Patch: Fix some incorrect format strings 2025-10-20 15:38:33 +02:00
TheTechnician27
3db21e0579 FSUI: Remove defunct option and move OSD position options 2025-10-19 22:29:31 -05:00
PCSX2 Bot
abd03884de [ci skip] Qt: Update Base Translation. 2025-10-19 20:02:42 -05:00
TheLastRar
a159256b07 FSUI: Allow specifying default button in message dialogs 2025-10-18 02:26:09 +02:00
Silent
d9a2618b7a Achievements: Use the actual image names for cache
Fixes an issue where PCSX2 cache was unaware of the game or achievement
icon getting updated in the backend, and it continued to show
the old image
2025-10-18 02:24:04 +02:00
PCSX2 Bot
dca0291cfb [ci skip] Qt: Update Base Translation. 2025-10-18 02:23:19 +02:00
JordanTheToaster
b261873471 GameDB: Wild Arms 5 SPS fix 2025-10-18 02:23:03 +02:00
TellowKrinkle
bfd01c913a macOS: Update to Qt 6.10.0 2025-10-17 18:11:04 -04:00
TellowKrinkle
818b3fe779 macOS: Update to Qt 6.9.3 2025-10-17 18:11:04 -04:00
TellowKrinkle
66a28e4488 macOS: Optimize Qt build for size
Saves a MB or so, there's no reason we need speed optimizations in our GUI toolkit
2025-10-17 18:11:04 -04:00
TellowKrinkle
28e2ecf920 macOS: Fix parallel kddockwidgets build for universal dependencies 2025-10-17 18:11:04 -04:00
TellowKrinkle
d3dbf53fa7 MacOS: Make universal build script executable 2025-10-17 18:11:04 -04:00
SternXD
8fb2940f25 Qt/BPM: Improve memory card shutdown warning clarity and safety emphasis 2025-10-15 23:48:35 -05:00
chaoticgd
55498762f9 Qt: Fix typo in Windows dependencies build script 2025-10-15 18:07:05 -04:00
JordanTheToaster
50baaf39d6 Deps: Update Mac OS ffmpeg to 8.0 2025-10-15 08:32:19 -04:00
lightningterror
4743ccac8c GS/HW/Shaders: Fix afail shader typo.
No dual source blend means PS_NO_COLOR1 is true, no output on target 1.
2025-10-15 00:57:33 +02:00
lightningterror
0dc3fc6228 GS/HW: Unify blend levels when barriers are and are not supported.
When barriers/multidraw fb copy is supported no change.
When barriers/multidraw fb copy isn't supported keep the same levels but only enable them if there's no overlap.
2025-10-14 02:19:43 +02:00
lightningterror
a7a4583c84 GS/HW: Enable any fbmask emulation if Blending is above minimum.
Since dx11/12 now supports sw blending and copies/draw area/quads optimizations have been made perf impact will be lower so we can use the same unified path if texture barriers are disabled.
2025-10-14 02:19:43 +02:00
lightningterror
6b52937262 GS/TC: Make sure target is created before setting any flags.
Xenosaga 3 fails to create a target because the rect is empty so we should avoid setting any flags since it's null.
2025-10-14 02:19:08 +02:00
PCSX2 Bot
df9caf6fb8 [ci skip] Qt: Update Base Translation. 2025-10-14 02:17:24 +02:00
Jordan
be5f8d2e60 GameDB: Various fixes for issues in Xenosaga 3 HyperSonic Xtreme Jeopardy and Jak 3. 2025-10-13 21:56:30 +02:00
Sean
b0e01ca518 Debugger: Floating point display in memory view (#13362) 2025-10-13 12:51:37 -04:00
TheTechnician27
4e85272393 Settings: Remove obsolete transitionary function 2025-10-13 12:47:20 -04:00
dependabot[bot]
bffed9a839 [ci skip] CI: Bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#13383)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 08:49:29 -04:00
PCSX2 Bot
7ed0c0d031 [ci skip] Qt: Update Base Translation. 2025-10-13 10:38:02 +02:00
TheTechnician27
ae6e4c98f6 AudioSettingsWidget: Disambiguate overloaded volume name 2025-10-12 17:17:59 -05:00
TheTechnician27
38cfa9912d SPU2: Fix interface for output muting 2025-10-12 17:17:59 -05:00
JordanTheToaster
b02318e2b6 Misc: Qt 6.10.0 Build Fixes 2025-10-12 00:39:34 -05:00
JordanTheToaster
db45263221 Deps: Update Windows and Linux to Qt 6.10.0 2025-10-12 00:39:34 -05:00
JordanTheToaster
3fd9625f1c 3rdparty: Update rcheevos v12.1.0 2025-10-11 20:17:13 +02:00
JordanTheToaster
2978b6050d GameDB: Black adjust HPO 2025-10-11 20:05:44 +02:00
PCSX2 Bot
274acddcf1 [ci skip] Qt: Update Base Translation. 2025-10-11 20:04:52 +02:00
JordanTheToaster
8dffc85707 Misc: Qt 6.10.0 Build Fixes 2025-10-10 23:20:48 -05:00
lightningterror
f799631a70 Qt: Update dx12 info.
Allow texture barrier override, update blending tooltip.
2025-10-10 08:10:31 +02:00
lightningterror
d744f0dfeb GS/DX12: Backport multidraw fb copy from dx11 to dx12. 2025-10-10 08:10:31 +02:00
Ty
bdb8de6d3b translations: Syncing Crowdin translations (#13372) 2025-10-09 16:37:44 -04:00
TJnotJT
e751f367ca GS/SW: Rewrite vertex ST values if they might overflow the rasterizer fixed point format. 2025-10-09 19:42:27 +02:00
TJnotJT
13f2d87ba8 GS: Add ST NaN tracking to the vertex trace.
Add fields that will trace if any of the S/Q or T/Q values is NaN. Ignore any NaN ST values while calculating min/max.
2025-10-09 19:42:27 +02:00
TheLastRar
3cac2cf7c2 Qt: Add null check for gamelist icons 2025-10-09 16:57:55 +02:00
TheLastRar
a67409bfc6 Deps: Patch SDL3 to fix erroneous uninitialise 2025-10-09 12:53:40 +02:00
lightningterror
6a556c9968 GS/HW: Fix manual de swizzle -Wsign-compare warnings. 2025-10-09 03:28:00 +02:00
TJnotJT
4d93285ca2 GS: Use triangle quad detection in primitive overlap detection.
Coauthored-by: TellowKrinkle
2025-10-09 03:26:08 +02:00
Ty
98c35a308d ci: Set NPROCS in the linux gs runner dependency script 2025-10-08 16:24:15 -04:00
Ty Lamontagne
7da97e6d80 cmake/ci: Allow gs runner builds without building all of pcsx2-qt 2025-10-08 15:26:34 -04:00
Ty Lamontagne
edf686752a GSRunner: Support GNU/Linux 2025-10-08 15:26:34 -04:00
lightningterror
b7e17646a3 GS/DX12: Backport dx11 full rt copy optimizations.
We are copying the whole RT so just call CopyResource instead of CopyTextureRegion which will be faster.
2025-10-08 02:24:32 +02:00
JordanTheToaster
3f437e7496 3rdparty: Update fmt to 12.0.0 2025-10-07 15:12:49 +02:00
lightningterror
7ab6c62dee GS/HW: Update manual deswizzle detection.
Check if a draw is either a palette or 32bit depth format, also check for swizzle format is 16 or 32bit.

Expand/update quadrant detection, use the quadrant size of what it should be
from the page size and compare it to the actual quadrant size from the vertex positions.
2025-10-07 11:48:32 +02:00
lightningterror
2a7bf35f20 GS/TC: Point m_from_target to new target if it's also a source in CombineAlignedInsideTargets.
Issue is it was erasing the source which doesn't get passed back to the main draw function,
so we should be setting m_from_target to either new target or null because it is linked.
2025-10-07 11:48:32 +02:00
TJnotJT
08552a83cc GS/SW: Clamp colors, Z, fog to allowed ranges. 2025-10-07 07:34:26 +02:00
TJnotJT
ee417ee4c5 GS/SW: Silence code analyzer warnings. 2025-10-07 07:34:26 +02:00
TJnotJT
869837f5f0 GS/SW: Accurate rounding for points. 2025-10-07 07:34:26 +02:00
TJnotJT
7ab6bac39a GS/SW: Improve accuracy of line drawing and edge antialiasing. 2025-10-07 07:34:26 +02:00
dependabot[bot]
e388294004 [ci skip] GitHub Action updates
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 18:46:46 -04:00
JordanTheToaster
bba5ef67ff GameDB: Various fixes 2025-10-06 18:45:20 -04:00
JordanTheToaster
83dc2de6ca GameDB: Hot Wheels Beat That fixes 2025-10-05 14:55:22 -04:00
PCSX2 Bot
f47fbda0e6 [ci skip] Qt: Update Base Translation. 2025-10-04 02:02:43 +02:00
KamFretoZ
d34d2af849 OSD: Add ability to show the amount of activated patches 2025-10-04 01:59:41 +02:00
JordanTheToaster
db92af8ca4 Deps: Update SDL3 to v3.2.24 2025-10-04 01:56:36 +02:00
lightningterror
47897fc2be GS/TC: Add caching to block offset calculation for TranslateAlignedRectByPage and DirtyRectByPage. 2025-10-04 01:55:52 +02:00
lightningterror
b451602f69 GS/TC: Check for y offset when calculating block offset.
Make sure the rect starts at the beginning.
2025-10-04 01:55:52 +02:00
lightningterror
96ac35a4bd GS/TC: Optimize block offset calculations in ComputeSurfaceOffset.
Same deal as before, swap the fast linear loops to be second since they will be faster.
2025-10-04 01:55:52 +02:00
lightningterror
ddd33a0701 GS/HW: Make sure we use date barrier on sw fbmask instead of primid date.
Edge case when we have sw fbmask but no sw blending.
2025-10-04 01:55:07 +02:00
lightningterror
d5e4a1c896 GS/DX11: Add missing write_rg shader bit for date barrier on texture shuffle. 2025-10-03 07:39:52 +02:00
lightningterror
9674fd773c GS/DX12/VK: Fix CopyRect partial clear regression.
Partial clears texture type should be either render target or depth stencil.
2025-10-03 07:39:52 +02:00
lightningterror
01e916b132 GS/HW: Remove rebinding read only dsv after framebuffer optimizations on dx11.
The condition would never be triggered so useless.

Clean up SendHWDraw across renderers.

Recycle tex for rt copy if colclip tex fails.

Set flags for colclip CreateRenderTarget clear to false on dx11, consistent across renderers.
2025-10-03 07:39:52 +02:00
lightningterror
1867791653 GS/DX12: Backport depth test and sampling from dx11.
Unlike in dx11 where we create a separate dsv as read only we can transition the current dsv as read only which is cleaner.
2025-10-03 07:39:52 +02:00
JordanTheToaster
bc11ff0571 3rdparty: Update cubeb to e495bee 2025-10-01 19:11:12 -04:00
lightningterror
e550cf9b63 GS/TC: Optimize block offset calculation.
Swap offset x and y loops:
Having the y loop first allows for better optimizations/caching since it's bigger.
Also optimize start point loop conditions for target rect.
2025-09-30 22:01:46 +02:00
TJnotJT
bafd87694c GS/HW: Unify barrier handling in DX11/GL/VK/MTL.
Make sure the one_barrier/full_barrier flags are passed correctly depending on the pass (main or alpha second pass).

Check that the drawlist is non-empty when a full barrier is requested.
2025-09-30 05:53:59 +02:00
TJnotJT
caf6298ad1 GS/DX11: Unify batched/per-prim multidraw and do partial copies for barriers. 2025-09-30 05:53:59 +02:00
TJnotJT
7f0d287512 GS: Refactor drawlist saving for full barrier multidraw.
Get drawlist when needing full barriers and API doesn't support texture barrier.

Make drawlist computation lazy (only computed when will be used).

When API does not provide barriers get bbox of batched draws.
2025-09-30 05:53:59 +02:00
PCSX2 Bot
2177de2238 [ci skip] Qt: Update Base Translation. 2025-09-30 02:19:21 +02:00
Ty
32973c746e CI: Use pull_request_target for deps dispatch 2025-09-29 20:05:27 -04:00
xujibbs
3bf94330ef CDVD: Make IsoHasher string translatable 2025-09-29 11:50:12 -04:00
Ty
34ae3b1eb0 [ci skip] CI: Use GITHUB_TOKEN to dispatch actions onto windows deps 2025-09-29 11:47:49 -04:00
JordanTheToaster
b9288d4845 Deps: Update harfbuzz to 12.0.0 2025-09-29 11:40:11 -04:00
PCSX2 Bot
a13cb32eb1 [ci skip] Qt: Update Base Translation. 2025-09-29 04:42:45 +02:00
Ty
9d72ad785d CI: Automatically build windows deps when PRs change them 2025-09-28 18:58:56 -04:00
JordanTheToaster
0afde446c4 Deps: Update kddockwidgets to v2.3.0 2025-09-28 18:15:23 -04:00
SternXD
49111a6fbe Achievements: Harden RetroAchievements Hardcore Compliance
Fixes #13273
 - Totally block save state loading, frame advance, and disable cheats in hardcore
 - Disable load state UI buttons when hardcore mode active (leave message telling them if they want to load state if the user somehow gets past this)
 - Add consistent OSD messaging for all hardcore restrictions

Signed-off-by: SternXD <stern@sidestore.io>
2025-09-28 18:14:33 -04:00
sean
251140fd52 Debugger: Store validated copies of pasted instructions 2025-09-28 17:53:13 -04:00
sean
f57c7d216c Debugger: Validate new instructions before pasting 2025-09-28 17:53:13 -04:00
sean
36157402c8 Debugger: Move position of paste instruction text in context menu 2025-09-28 17:53:13 -04:00
recursean
51ee8f4015 Debugger: start context function for multi-instruction paste 2025-09-28 17:53:13 -04:00
JordanTheToaster
fb85bb9076 GameDB: Various fixes 2025-09-26 20:36:56 +02:00
PCSX2 Bot
0a391e2407 [ci skip] Qt: Update Base Translation. 2025-09-26 03:36:33 +02:00
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
430 changed files with 277213 additions and 208340 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,4 @@
# These are supported funding model platforms
github: [PCSX2]
liberapay: PCSX2

11
.github/labeler.yml vendored
View File

@@ -22,6 +22,10 @@
- '3rdparty/**/*'
- '**/3rdpartyDeps.props'
- '.gitmodules'
'requires-win-deps-build':
- changed-files:
- any-glob-to-any-file:
- '.github/workflows/scripts/windows/*'
'Documentation':
- changed-files:
- any-glob-to-any-file:
@@ -36,6 +40,13 @@
- 'pcsx2-qt/**/*'
- '3rdparty/Qt/*'
- '3rdparty/Qt/**/*'
'OSD / ImGui':
- changed-files:
- any-glob-to-any-file:
- 'pcsx2/ImGui/*'
- 'pcsx2/ImGui/**/*'
- '3rdparty/imgui/*'
- '3rdparty/imgui/**/*'
'GameDB':
- changed-files:
- any-glob-to-any-file:

View File

@@ -45,7 +45,7 @@ jobs:
name: ${{ inputs.jobName }}
runs-on: ${{ inputs.os }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.7
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.9
options: --privileged
timeout-minutes: 60
@@ -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
@@ -129,7 +129,7 @@ jobs:
- name: Push to Flathub (beta)
if: ${{ inputs.publish == true && (inputs.stableBuild == false || inputs.stableBuild == 'false') }}
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
flat-manager-url: https://hub.flathub.org/
repository: beta
@@ -138,7 +138,7 @@ jobs:
- name: Push to Flathub (stable)
if: ${{ inputs.publish == true && (inputs.stableBuild == true || inputs.stableBuild == 'true') }}
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
flat-manager-url: https://hub.flathub.org/
repository: stable
@@ -153,7 +153,7 @@ jobs:
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts

View File

@@ -55,7 +55,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 9
CCACHE_MAXSIZE: 500M
CCACHE_MAXSIZE: 100M
steps:
- name: Checkout Repository
@@ -174,7 +174,7 @@ jobs:
- name: Upload artifact
if: inputs.buildAppImage == true
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts

View File

@@ -12,7 +12,7 @@ on:
os:
required: false
type: string
default: macos-15
default: macos-26
patchesUrl:
required: false
type: string
@@ -42,7 +42,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 9
CCACHE_MAXSIZE: 500M
CCACHE_MAXSIZE: 100M
# Only way to use a secret in an if statement
SIGN_KEY: ${{ secrets.APPLE_SIGN_P12_B64 }}
@@ -62,8 +62,11 @@ jobs:
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Use Xcode 16.4
run: sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Use Xcode 26.0.1
run: sudo xcode-select -s /Applications/Xcode_26.0.1.app
- name: Install Metal Toolchain
run: xcodebuild -downloadComponent MetalToolchain
- name: Prepare Artifact Metadata
id: artifact-metadata
@@ -91,7 +94,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/deps
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh', '.github/workflows/scripts/common/*.patch') }}
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
@@ -194,7 +197,7 @@ jobs:
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: "*.tar.xz"

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@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe
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@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe
if: steps.tag_version.outputs.new_tag && github.event_name != 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -168,7 +168,7 @@ jobs:
- name: Prepare Artifact Folder
run: mkdir ./ci-artifacts/
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
name: Download all Artifacts
with:
path: ./ci-artifacts/
@@ -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@v6
with:
node-version: 22

View File

@@ -1,13 +0,0 @@
diff --git a/src/core/indicators/ClassicDropIndicatorOverlay.h b/src/core/indicators/ClassicDropIndicatorOverlay.h
index 2dfb9718a..9b01f002e 100644
--- a/src/core/indicators/ClassicDropIndicatorOverlay.h
+++ b/src/core/indicators/ClassicDropIndicatorOverlay.h
@@ -11,7 +11,7 @@
#pragma once
-#include "core/DropIndicatorOverlay.h"
+#include <kddockwidgets/core/DropIndicatorOverlay.h>
namespace KDDockWidgets {

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

@@ -50,12 +50,6 @@ declare -a MANUAL_LIBS=(
"libfreetype.so.6"
)
declare -a REMOVE_LIBS=(
'libwayland-client.so*'
'libwayland-cursor.so*'
'libwayland-egl.so*'
)
set -e
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage
@@ -130,7 +124,7 @@ echo "Running linuxdeploy to create AppDir..."
# Interestingly, specifying the module doesn't copy the module, only the required plugins for it
# https://github.com/linuxdeploy/linuxdeploy-plugin-qt/issues/160#issuecomment-2655543893
EXTRA_QT_MODULES="core;gui;svg;waylandclient;waylandcompositor;widgets;xcbqpa" \
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
EXTRA_PLATFORM_PLUGINS="libqwayland.so" \
DEPLOY_PLATFORM_THEMES="1" \
QMAKE="$DEPSDIR/bin/qmake" \
NO_STRIP="1" \
@@ -140,16 +134,6 @@ $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt
echo "Copying resources into AppDir..."
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
# Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin
# copies them, not the "main" linuxdeploy binary, and plugins don't inherit the
# include list...
for lib in "${REMOVE_LIBS[@]}"; do
for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do
echo " Removing problematic library ${libpath}."
rm -f "$libpath"
done
done
# Restore unstripped deps (for cache).
rm -fr "$DEPSDIR"
mv "$DEPSDIR.bak" "$DEPSDIR"

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=12.0.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.1
LIBJPEGTURBO=3.1.2
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.20
QT=6.9.2
SDL=SDL3-3.2.26
QT=6.10.0
QTAPNG=1.3.0
LZ4=1.10.0
ZSTD=1.5.7
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -37,27 +38,29 @@ 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
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 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
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $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
8a023f7e2f57dedc02e2ab10c975f7cb3cccac9b8f0823c12fd6824834549139 qtimageformats-everywhere-src-$QT.tar.xz
d984cab8f26334aa1c15e5b8f0cd9f1b7c0c1289fe0b68c1c84ab469b75605a5 qtsvg-everywhere-src-$QT.tar.xz
d8b7f7e8e970cc0b975205fd6d5832ea917ef3e751df69b97439c1cddd67a489 qttools-everywhere-src-$QT.tar.xz
c73bb6281ed365c0f954f4b1b6e1b13e1b3fefd94854f46fcd9a412f641f7ed6 qttranslations-everywhere-src-$QT.tar.xz
cad79806565568f12f9983fed69219416abcee9d5deef4abdfcf94aa2eef7781 qtwayland-everywhere-src-$QT.tar.xz
ead4623bcb54a32257c5b3e3a5aec6d16ec96f4cda58d2e003f5a0c16f72046d qtbase-everywhere-src-$QT.tar.xz
64450a52507c540de53616ed5e516df0e0905a99d3035ddfaa690f2b3f7c0cea qtimageformats-everywhere-src-$QT.tar.xz
5ed2c0e04d5e73ff75c2a2ed92db5dc1788ba70f704fc2b71bc21644beda2533 qtsvg-everywhere-src-$QT.tar.xz
d86d5098cf3e3e599f37e18df477e65908fc8f036e10ea731b3469ec4fdbd02a qttools-everywhere-src-$QT.tar.xz
326e8253cfd0cb5745238117f297da80e30ce8f4c1db81990497bd388b026cde qttranslations-everywhere-src-$QT.tar.xz
603f2b0a259b24bd0fb14f880d7761b1d248118a42a6870cdbe8fdda4173761f 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
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.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
@@ -109,7 +116,9 @@ echo "Building libjpegturbo..."
rm -fr "libjpeg-turbo-$LIBJPEGTURBO"
tar xf "libjpeg-turbo-$LIBJPEGTURBO.tar.gz"
cd "libjpeg-turbo-$LIBJPEGTURBO"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build -G Ninja
# On non debian or debian based Linux systems, libjpeg-turbo will set CMAKE_INSTALL_DEFAULT_LIBDIR "lib64" (or libx32)
# That will prevent CMake from finding the deps libjpeg later on. if we set CMAKE_INSTALL_DEFAULT_LIBDIR, libjpeg-turbo will leave it as is, so set it to "lib"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -DCMAKE_INSTALL_DEFAULT_LIBDIR="lib" -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
@@ -250,11 +259,20 @@ 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"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build -G Ninja
cmake --build build --parallel
ninja -C build install

View File

@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -e
if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
NPROCS="$(getconf _NPROCESSORS_ONLN)"
INSTALLDIR="$1"
if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.14.1
HARFBUZZ=12.0.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.22
LZ4=1.10.0
ZSTD=1.5.7
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
SHADERC_GLSLANG=efd24d75bcbc55620e759f6bf42c45a32abac5f8
SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
mkdir -p deps-build
cd deps-build
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.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
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
curl -L \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \
-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" \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.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" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
-o "plutovg-$PLUTOVG.tar.gz" "https://github.com/sammycage/plutovg/archive/v$PLUTOVG.tar.gz" \
-o "plutosvg-$PLUTOSVG.tar.gz" "https://github.com/sammycage/plutosvg/archive/v$PLUTOSVG.tar.gz"
shasum -a 256 --check SHASUMS
echo "Building libbacktrace..."
rm -fr "libbacktrace-$LIBBACKTRACE"
unzip "$LIBBACKTRACE.zip"
cd "libbacktrace-$LIBBACKTRACE"
./configure --prefix="$INSTALLDIR"
make
make install
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
cd ..
echo "Building LZ4..."
rm -fr "lz4-$LZ4"
tar xf "lz4-$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir -G Ninja build/cmake
cmake --build build-dir --parallel
ninja -C build-dir install
cd ..
echo "Building Zstandard..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType without HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building HarfBuzz..."
rm -fr "harfbuzz-$HARFBUZZ"
tar xf "harfbuzz-$HARFBUZZ.tar.gz"
cd "harfbuzz-$HARFBUZZ"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -DHB_HAVE_FREETYPE=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -B build -G Ninja \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType with HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz"
cd "$SDL"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoVG..."
rm -fr "plutovg-$PLUTOVG"
tar xf "plutovg-$PLUTOVG.tar.gz"
cd "plutovg-$PLUTOVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoSVG..."
rm -fr "plutosvg-$PLUTOSVG"
tar xf "plutosvg-$PLUTOSVG.tar.gz"
cd "plutosvg-$PLUTOSVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOSVG_ENABLE_FREETYPE=ON -DPLUTOSVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building shaderc..."
rm -fr "shaderc-$SHADERC"
tar xf "shaderc-$SHADERC.tar.gz"
cd "shaderc-$SHADERC"
cd third_party
tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
mv "glslang-$SHADERC_GLSLANG" "glslang"
tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Cleaning up..."
cd ..
rm -r deps-build

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.26.tar.gz",
"sha256": "dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2"
}
],
"cleanup": [

View File

@@ -14,13 +14,9 @@
{
"type": "git",
"url": "https://github.com/KDAB/KDDockWidgets.git",
"tag": "v2.2.3",
"commit": "28d16d0431d7cdc9f36cb619d22621146fdfab44",
"tag": "v2.3.0",
"commit": "c38711026e17e34916dd82c6fcbdcc0d2342f541",
"disable-submodules": true
},
{
"type": "patch",
"path": "../../../common/kddockwidgets-dodgy-include.patch"
}
],
"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

@@ -1,15 +1,15 @@
{
"app-id": "net.pcsx2.PCSX2",
"runtime": "org.kde.Platform",
"runtime-version": "6.9",
"runtime-version": "6.10",
"sdk": "org.kde.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.llvm18"
"org.freedesktop.Sdk.Extension.llvm20"
],
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"directory": "lib/ffmpeg",
"version": "24.08",
"version": "25.08",
"add-ld-path": ".",
"autodownload": true
}
@@ -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,10 +47,11 @@
"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",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm18/bin/clang++",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm20/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm20/bin/clang++",
"-DCMAKE_EXE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_MODULE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",

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=12.0.0
SDL=SDL3-3.2.26
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
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
QT=6.10.0
QTAPNG=1.3.0
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -77,27 +78,29 @@ 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
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $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
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
ead4623bcb54a32257c5b3e3a5aec6d16ec96f4cda58d2e003f5a0c16f72046d qtbase-everywhere-src-$QT.tar.xz
64450a52507c540de53616ed5e516df0e0905a99d3035ddfaa690f2b3f7c0cea qtimageformats-everywhere-src-$QT.tar.xz
5ed2c0e04d5e73ff75c2a2ed92db5dc1788ba70f704fc2b71bc21644beda2533 qtsvg-everywhere-src-$QT.tar.xz
d86d5098cf3e3e599f37e18df477e65908fc8f036e10ea731b3469ec4fdbd02a qttools-everywhere-src-$QT.tar.xz
326e8253cfd0cb5745238117f297da80e30ce8f4c1db81990497bd388b026cde 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
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.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
@@ -278,6 +285,10 @@ echo "Installing Qt Base..."
rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
# We could run macdeployqt twice, but that's even more janky than patching it.
@@ -313,7 +324,7 @@ tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -324,7 +335,7 @@ tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_system_webp=ON
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF -DFEATURE_system_webp=ON
make "-j$NPROCS"
make install
cd ../..
@@ -335,7 +346,7 @@ tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -364,19 +375,28 @@ tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF
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"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
cmake --build build --parallel
cmake --install build
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Building PlutoVG..."

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=12.0.0
SDL=SDL3-3.2.26
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
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
KDDOCKWIDGETS=2.2.3
PLUTOVG=1.3.0
QT=6.10.0
QTAPNG=1.3.0
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
@@ -53,31 +54,34 @@ CMAKE_COMMON=(
-DCMAKE_PREFIX_PATH="$INSTALLDIR"
-DCMAKE_INSTALL_PREFIX="$INSTALLDIR"
-DCMAKE_OSX_ARCHITECTURES="x86_64"
-DCMAKE_APPLE_SILICON_PROCESSOR="x86_64"
-DCMAKE_INSTALL_NAME_DIR='$<INSTALL_PREFIX>/lib'
)
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
467600ae090dd28616fa37369faf4e3143198ff1da37729b552137e47f751a67 $SDL.tar.gz
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $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
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
ead4623bcb54a32257c5b3e3a5aec6d16ec96f4cda58d2e003f5a0c16f72046d qtbase-everywhere-src-$QT.tar.xz
64450a52507c540de53616ed5e516df0e0905a99d3035ddfaa690f2b3f7c0cea qtimageformats-everywhere-src-$QT.tar.xz
5ed2c0e04d5e73ff75c2a2ed92db5dc1788ba70f704fc2b71bc21644beda2533 qtsvg-everywhere-src-$QT.tar.xz
d86d5098cf3e3e599f37e18df477e65908fc8f036e10ea731b3469ec4fdbd02a qttools-everywhere-src-$QT.tar.xz
326e8253cfd0cb5745238117f297da80e30ce8f4c1db81990497bd388b026cde 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
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -88,6 +92,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 +102,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 +162,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
@@ -226,26 +234,18 @@ rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# Patch Qt to fix a bug with message boxes on Tahoe
patch -p1 < "$SCRIPTDIR/clickbutton.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
# We could run macdeployqt twice, but that's even more janky than patching it.
# https://github.com/qt/qtbase/commit/7b018629c3c3ab23665bf1da00c43c1546042035
# The QProcess default wait time of 30s may be too short in e.g. CI environments where processes may be blocked
# for a longer time waiting for CPU or IO.
patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
--- shared.cpp
+++ shared.cpp
@@ -152,7 +152,7 @@
LogDebug() << " inspecting" << binaryPath;
QProcess otool;
otool.start("otool", QStringList() << "-L" << binaryPath);
- otool.waitForFinished();
+ otool.waitForFinished(-1);
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
LogError() << otool.readAllStandardError();
@@ -1122,14 +1122,8 @@
addPlugins(QStringLiteral("networkinformation"));
}
@@ -264,7 +264,7 @@ patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
// Platforminputcontext plugins if QtGui is in use
EOF
cmake -B build "${CMAKE_COMMON[@]}" -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
cmake -B build "${CMAKE_COMMON[@]}" -DCMAKE_BUILD_TYPE=MinSizeRel -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
make -C build "-j$NPROCS"
make -C build install
cd ..
@@ -275,7 +275,7 @@ tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -286,7 +286,7 @@ tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_system_webp=ON
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF -DFEATURE_system_webp=ON
make "-j$NPROCS"
make install
cd ../..
@@ -297,7 +297,7 @@ tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -326,16 +326,25 @@ tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF
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"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake "${CMAKE_COMMON[@]}" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
make -C build "-j$NPROCS"
make -C build install

View File

@@ -0,0 +1,16 @@
--- a/src/plugins/platforms/cocoa/qcocoamessagedialog.mm
+++ b/src/plugins/platforms/cocoa/qcocoamessagedialog.mm
@@ -88,6 +88,11 @@ bool QCocoaMessageDialog::show(Qt::WindowModality windowModality)
qCWarning(lcQpaDialogs, "Cannot run window modal dialog without parent window");
return false;
}
+
+ // Tahoe has issues with window-modal alert buttons not responding to mouse
+ if (windowModality == Qt::WindowModal
+ && QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSTahoe)
+ return false;
// And without options we don't know what to show
if (!options())
# Source https://codereview.qt-project.org/c/qt/qtbase/+/689796

View File

@@ -0,0 +1,78 @@
diff --git a/.cmake.conf b/.cmake.conf
index 9a21ff42a74..d6707ba7dff 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -51,7 +51,7 @@ set(QT_MAX_NEW_POLICY_CMAKE_VERSION_QT_APPLE "3.21")
set(QT_SUPPORTED_MIN_MACOS_SDK_VERSION "14")
set(QT_SUPPORTED_MAX_MACOS_SDK_VERSION "26")
set(QT_SUPPORTED_MIN_MACOS_XCODE_VERSION "15")
-set(QT_SUPPORTED_MIN_MACOS_VERSION "13")
+set(QT_SUPPORTED_MIN_MACOS_VERSION "11")
set(QT_SUPPORTED_MAX_MACOS_VERSION_TESTED "26")
set(QT_SUPPORTED_MIN_IOS_SDK_VERSION "17")
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d3a14fc67eb..1553b956fe3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,6 +12,10 @@ cmake_minimum_required(VERSION 3.16)
# Get the repo version and CMake policy details
include(.cmake.conf)
+if(APPLE)
+ add_compile_options(-Werror=unguarded-availability-new)
+endif()
+
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtBaseHelpers.cmake)
qt_internal_check_if_path_has_symlinks("${CMAKE_BINARY_DIR}")
diff --git a/src/corelib/global/qsysinfo.cpp b/src/corelib/global/qsysinfo.cpp
index ae762c0cc6d..9171a5736b4 100644
--- a/src/corelib/global/qsysinfo.cpp
+++ b/src/corelib/global/qsysinfo.cpp
@@ -1027,7 +1027,7 @@ QByteArray QSysInfo::machineUniqueId()
{
#if defined(Q_OS_DARWIN) && __has_include(<IOKit/IOKitLib.h>)
char uuid[UuidStringLen + 1];
- io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman);
return QByteArray(uuid);
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index 9f27dbe694e..c023a48cad3 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -372,7 +372,7 @@ bool qt_apple_runningWithLiquidGlass()
return config;
#endif
- QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
+ QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
if (!nvram) {
qWarning("Failed to locate NVRAM entry in IO registry");
return {};
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 6b33d94d58c..867389e4c93 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -323,6 +323,8 @@ a normal (not maximized or full screen) top-level window.
m_view.safeAreaInsets.bottom
};
+ if (@available(macOS 12, *)) {
+
// The screen's safe area insets represent the distances from the screen's
// edges at which content isn't obscured. The view's safe area margins do
// not include the screen's insets automatically, so we need to manually
@@ -355,6 +357,10 @@ a normal (not maximized or full screen) top-level window.
};
return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
+
+ } else {
+ return viewSafeAreaMargins.toMargins();
+ }
}
void QCocoaWindow::updateSafeAreaMarginsIfNeeded()

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=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.20
set QT=6.9.2
set QTMINOR=6.9
set LIBPNGLONG=1.6.50
set SDL=SDL3-3.2.26
set QT=6.10.0
set QTMINOR=6.10
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 KDDOCKWIDGETS=2.4.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 0f3e036294974736982d8ec00f0bed763cd9f75ab9eacf8effe413af5d78ef06 || 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 "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 "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" d3b5379edcace266273d789249b6d68ae9495ec1b0b562ba6d039034cd315d8e || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" ac2fe34a9f1c1451b6785474e9b1b64eb59edef6553be3d630240f16a730456d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c12f8bfb617e4a03da104be36f6966ba7f64bee331f0095da1a649a1149796d2 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c6d0f0a512304ad87b20f5ff604442dd8d55769d659576ecfe5462fcd7bb9b7d || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" e6cc1ebf62cf37d81f3b86990086108518037bb383e75da327f297cc4fc1ae36 || 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 "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || 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
@@ -92,7 +96,7 @@ if %DEBUG%==1 (
echo Building release libraries...
)
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG"
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL="/DEBUG"
set ARM64TOOLCHAIN=-DCMAKE_TOOLCHAIN_FILE="%SCRIPTDIR%\cmake-toolchain-windows-arm64.cmake"
echo Building Zlib...
@@ -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
@@ -190,7 +197,7 @@ cd .. || goto error
if %DEBUG%==1 (
set QTBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -G "Ninja Multi-Config"
) else (
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja
)
echo Building Qt base...
@@ -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 (
@@ -259,7 +282,6 @@ echo "Building KDDockWidgets..."
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\kddockwidgets-dodgy-include.patch" || goto error
cmake -B build %ARM64TOOLCHAIN% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets %KDDOCKWIDGETSBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

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=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.20
set QT=6.9.2
set QTMINOR=6.9
set SDL=SDL3-3.2.26
set LIBPNGLONG=1.6.50
set QT=6.10.0
set QTMINOR=6.10
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 KDDOCKWIDGETS=2.4.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 0f3e036294974736982d8ec00f0bed763cd9f75ab9eacf8effe413af5d78ef06 || 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 "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 "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" d3b5379edcace266273d789249b6d68ae9495ec1b0b562ba6d039034cd315d8e || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" ac2fe34a9f1c1451b6785474e9b1b64eb59edef6553be3d630240f16a730456d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c12f8bfb617e4a03da104be36f6966ba7f64bee331f0095da1a649a1149796d2 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c6d0f0a512304ad87b20f5ff604442dd8d55769d659576ecfe5462fcd7bb9b7d || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" e6cc1ebf62cf37d81f3b86990086108518037bb383e75da327f297cc4fc1ae36 || 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 "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || 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
@@ -90,7 +94,7 @@ if %DEBUG%==1 (
echo Building release libraries...
)
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_RELEASE="/DEBUG"
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_MINSIZEREL="/DEBUG"
echo Building Zlib...
rmdir /S /Q "zlib-%ZLIB%"
@@ -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
@@ -187,7 +194,7 @@ cd .. || goto error
if %DEBUG%==1 (
set QTBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -G "Ninja Multi-Config"
) else (
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja
)
echo Building Qt base...
@@ -226,7 +233,7 @@ ninja install || goto error
cd ..\.. || goto error
echo Building Qt Tools...
rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
rmdir /S /Q "qttools-everywhere-src-%QT%"
%SEVENZIP% x "qttools-everywhere-src-%QT%.zip" || goto error
cd "qttools-everywhere-src-%QT%" || goto error
mkdir build || 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 (
@@ -260,7 +283,6 @@ echo "Building KDDockWidgets..."
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\kddockwidgets-dodgy-include.patch" || goto error
cmake -B build -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets %KDDOCKWIDGETSBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

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

@@ -154,7 +154,7 @@ jobs:
cmake --build build --config Release --target unittests
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: |
@@ -186,7 +186,7 @@ jobs:
}
- name: Upload artifact - with symbols
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
path: |

View File

@@ -0,0 +1,17 @@
name: 🖥️ 📦 Dispatch Windows Deps Build
on:
pull_request_target:
types: [closed]
jobs:
trigger:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'requires-win-deps-build')
runs-on: ubuntu-latest
steps:
- name: Dispatch to windows-dependencies repo
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.DEPS_REPO_DISPATCH_ACCESS_TOKEN }}
repository: pcsx2/pcsx2-windows-dependencies
event-type: deps-update

View File

@@ -1,7 +1,117 @@
# libcubeb - Cross-platform Audio I/O Library
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
See INSTALL.md for build instructions.
`libcubeb` is a cross-platform C library for high and low-latency audio input/output. It provides a simple, consistent API for audio playback and recording across multiple platforms and audio backends. It is written in C, C++ and Rust, with a C ABI and [Rust](https://github.com/mozilla/cubeb-rs) bindings. While originally written for use in the Firefox Web browser, a number of other software projects have adopted it.
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
## Features
Licensed under an ISC-style license. See LICENSE for details.
- **Cross-platform support**: Windows, macOS, Linux, Android, and other platforms
- **Versatile**: Optimized for low-latency real-time audio applications, or power efficient higher latency playback
- **A/V sync**: Latency compensated audio clock reporting for easy audio/video synchronization
- **Full-duplex support**: Simultaneous audio input and output, reclocked
- **Device enumeration**: Query available audio devices
- **Audio processing for speech**: Can use VoiceProcessing IO on recent macOS
## Supported Backends & status
| *Backend* | *Support Level* | *Platform version* | *Notes* |
|-------------------|-----------------|--------------------|--------------------------------------------------|
| PulseAudio (Rust) | Tier-1 | | Main Linux desktop backend |
| AudioUnit (Rust) | Tier-1 | | Main macOS backend |
| WASAPI | Tier-1 | Windows >= 7 | Main Windows backend |
| AAudio | Tier-1 | Android >= 8 | Main Android backend for most devices |
| OpenSL | Tier-1 | Android >= 2.3 | Android backend for older devices |
| OSS | Tier-2 | | |
| sndio | Tier-2 | | |
| Sun | Tier-2 | | |
| WinMM | Tier-3 | Windows XP | Was Tier-1, Firefox minimum Windows version 7. |
| AudioTrack | Tier-3 | Android < 2.3 | Was Tier-1, Firefox minimum Android version 4.1. |
| ALSA | Tier-3 | | |
| JACK | Tier-3 | | |
| KAI | Tier-3 | | |
| PulseAudio (C) | Tier-4 | | Was Tier-1, superseded by Rust |
| AudioUnit (C++) | Tier-4 | | Was Tier-1, superseded by Rust |
Tier-1: Actively maintained. Should have CI coverage. Critical for Firefox.
Tier-2: Actively maintained by contributors. CI coverage appreciated.
Tier-3: Maintainers/patches accepted. Status unclear.
Tier-4: Deprecated, obsolete. Scheduled to be removed.
Note that the support level is not a judgement of the relative merits
of a backend, only the current state of support, which is informed
by Firefox's needs, the responsiveness of a backend's
maintainer, and the level of contributions to that backend.
## Building
### Prerequisites
- CMake 3.15 or later
- Non-ancient MSVC, clang or gcc, for compiling both C and C++
- Platform-specific audio libraries (automatically detected)
- Optional but recommended: Rust compiler to compile and link more recent backends for macOS and PulseAudio
### Quick build
```bash
git clone https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build
cmake --build build
```
### Better build with Rust backends
```bash
git clone --recursive https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build -DBUILD_RUST_LIBS=ON
cmake --build build
```
### Platform-Specific Notes
**Windows**: Supports Visual Studio 2015+ and MinGW-w64. Use `-G "Visual Studio 16 2019"` or `-G "MinGW Makefiles"`.
**macOS**: Requires Xcode command line tools. Audio frameworks are automatically linked.
**Linux**: Development packages for desired backends:
```bash
# Ubuntu/Debian
sudo apt-get install libpulse-dev libasound2-dev libjack-dev
# Fedora/RHEL
sudo dnf install pulseaudio-libs-devel alsa-lib-devel jack-audio-connection-kit-devel
```
**Android**: Use with Android NDK. AAudio requires API level 26+.
## Testing
Run the test suite:
```bash
cd build
ctest
```
Use the interactive test tool:
```bash
./cubeb-test
```
## License
Licensed under an ISC-style license. See [LICENSE](LICENSE) for details.
## Contributing
Contributions are welcome! Please see the [contribution guidelines](CONTRIBUTING.md) and check the [issue tracker](https://github.com/mozilla/cubeb/issues).
## Links
- [GitHub Repository](https://github.com/mozilla/cubeb)
- [API Documentation](https://mozilla.github.io/cubeb/)

View File

@@ -49,6 +49,7 @@ extern "C" {
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
output_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
if (rv != CUBEB_OK) {
@@ -62,6 +63,7 @@ extern "C" {
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
input_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
@@ -193,39 +195,39 @@ typedef uint32_t cubeb_channel_layout;
// Some common layout definitions.
enum {
CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
CUBEB_LAYOUT_MONO = (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = (uint32_t)CUBEB_LAYOUT_MONO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = (uint32_t)CUBEB_LAYOUT_STEREO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = (uint32_t)CUBEB_LAYOUT_3F | (uint32_t)CHANNEL_LOW_FREQUENCY,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F1 =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = (uint32_t)CUBEB_LAYOUT_2F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = (uint32_t)CUBEB_LAYOUT_3F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = (uint32_t)CUBEB_LAYOUT_2F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = (uint32_t)CUBEB_LAYOUT_3F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = (uint32_t)CUBEB_LAYOUT_3F2_BACK | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
};
/** Miscellaneous stream preferences. */
@@ -279,7 +281,10 @@ typedef struct {
cubeb_channel_layout
layout; /**< Requested channel layout. This must be consistent with the
provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_input_processing_params input_params; /**< Requested input processing
params. Ignored for output streams. At present, only supported on the
WASAPI backend; others should use cubeb_set_input_processing_params. */
} cubeb_stream_params;
/** Audio device description */
@@ -414,6 +419,13 @@ typedef struct {
size_t count; /**< Device count in collection. */
} cubeb_device_collection;
/** Array of compiled backends returned by `cubeb_get_backend_names`. */
typedef struct {
const char * const *
names; /**< Array of strings representing backend names. */
size_t count; /**< Length of the array. */
} cubeb_backend_names;
/** User supplied data callback.
- Calling other cubeb functions from this callback is unsafe.
- The code in the callback should be non-blocking.
@@ -454,6 +466,8 @@ typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* This callback will be called when devices are added or removed from the
* system, or when the default device changes for the specified device type.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
@@ -485,17 +499,18 @@ CUBEB_EXPORT int
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Returns a list of backend names which can be supplid to cubeb_init().
Array is null-terminated. */
CUBEB_EXPORT const char**
cubeb_get_backend_names();
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const *
cubeb_get_backend_id(cubeb * context);
/** Get a read-only array of strings identifying available backends.
These can be passed as `backend_name` parameter to `cubeb_init`.
@retval Struct containing the array with backend names. */
CUBEB_EXPORT cubeb_backend_names
cubeb_get_backend_names();
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@param max_channels The maximum number of channels.
@@ -674,7 +689,7 @@ cubeb_stream_get_current_device(cubeb_stream * stm,
application is accessing audio input. When all inputs are muted they can
prove to the user that the application is not actively capturing any input.
@param stream the stream for which to set input mute state
@param muted whether the input should mute or not
@param mute whether the input should mute or not
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if this stream does not have an input
device
@@ -745,14 +760,16 @@ cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
a new device or a device is removed, or when the default device
changes for the specified device type.
@param context
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both
sides.
@param callback a function called whenever the system device list changes.
@param callback a function called whenever the system device list changes,
including when default devices change.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in

View File

@@ -31,6 +31,10 @@ struct cubeb_stream {
int
pulse_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_PULSE_RUST)
int
pulse_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_JACK)
int
jack_init(cubeb ** context, char const * context_name);
@@ -43,6 +47,10 @@ alsa_init(cubeb ** context, char const * context_name);
int
audiounit_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT_RUST)
int
audiounit_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_WINMM)
int
winmm_init(cubeb ** context, char const * context_name);
@@ -55,10 +63,30 @@ wasapi_init(cubeb ** context, char const * context_name);
int
sndio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SUN)
int
sun_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OPENSL)
int
opensl_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OSS)
int
oss_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AAUDIO)
int
aaudio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOTRACK)
int
audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int
kai_init(cubeb ** context, char const * context_name);
#endif
static int
validate_stream_params(cubeb_stream_params * input_stream_params,
@@ -123,6 +151,10 @@ cubeb_init(cubeb ** context, char const * context_name,
if (!strcmp(backend_name, "pulse")) {
#if defined(USE_PULSE)
init_oneshot = pulse_init;
#endif
} else if (!strcmp(backend_name, "pulse-rust")) {
#if defined(USE_PULSE_RUST)
init_oneshot = pulse_rust_init;
#endif
} else if (!strcmp(backend_name, "jack")) {
#if defined(USE_JACK)
@@ -135,6 +167,10 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "audiounit")) {
#if defined(USE_AUDIOUNIT)
init_oneshot = audiounit_init;
#endif
} else if (!strcmp(backend_name, "audiounit-rust")) {
#if defined(USE_AUDIOUNIT_RUST)
init_oneshot = audiounit_rust_init;
#endif
} else if (!strcmp(backend_name, "wasapi")) {
#if defined(USE_WASAPI)
@@ -147,10 +183,30 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "sndio")) {
#if defined(USE_SNDIO)
init_oneshot = sndio_init;
#endif
} else if (!strcmp(backend_name, "sun")) {
#if defined(USE_SUN)
init_oneshot = sun_init;
#endif
} else if (!strcmp(backend_name, "opensl")) {
#if defined(USE_OPENSL)
init_oneshot = opensl_init;
#endif
} else if (!strcmp(backend_name, "oss")) {
#if defined(USE_OSS)
init_oneshot = oss_init;
#endif
} else if (!strcmp(backend_name, "aaudio")) {
#if defined(USE_AAUDIO)
init_oneshot = aaudio_init;
#endif
} else if (!strcmp(backend_name, "audiotrack")) {
#if defined(USE_AUDIOTRACK)
init_oneshot = audiotrack_init;
#endif
} else if (!strcmp(backend_name, "kai")) {
#if defined(USE_KAI)
init_oneshot = kai_init;
#endif
} else {
/* Already set */
@@ -163,6 +219,9 @@ cubeb_init(cubeb ** context, char const * context_name,
* to override all other choices
*/
init_oneshot,
#if defined(USE_PULSE_RUST)
pulse_rust_init,
#endif
#if defined(USE_PULSE)
pulse_init,
#endif
@@ -178,6 +237,9 @@ cubeb_init(cubeb ** context, char const * context_name,
#if defined(USE_OSS)
oss_init,
#endif
#if defined(USE_AUDIOUNIT_RUST)
audiounit_rust_init,
#endif
#if defined(USE_AUDIOUNIT)
audiounit_init,
#endif
@@ -189,6 +251,18 @@ cubeb_init(cubeb ** context, char const * context_name,
#endif
#if defined(USE_SUN)
sun_init,
#endif
#if defined(USE_AAUDIO)
aaudio_init,
#endif
#if defined(USE_OPENSL)
opensl_init,
#endif
#if defined(USE_AUDIOTRACK)
audiotrack_init,
#endif
#if defined(USE_KAI)
kai_init,
#endif
};
int i;
@@ -214,13 +288,26 @@ cubeb_init(cubeb ** context, char const * context_name,
return CUBEB_ERROR;
}
const char**
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
}
cubeb_backend_names
cubeb_get_backend_names()
{
static const char* backend_names[] = {
static const char * const backend_names[] = {
#if defined(USE_PULSE)
"pulse",
#endif
#if defined(USE_PULSE_RUST)
"pulse-rust",
#endif
#if defined(USE_JACK)
"jack",
#endif
@@ -230,6 +317,9 @@ cubeb_get_backend_names()
#if defined(USE_AUDIOUNIT)
"audiounit",
#endif
#if defined(USE_AUDIOUNIT_RUST)
"audiounit-rust",
#endif
#if defined(USE_WASAPI)
"wasapi",
#endif
@@ -239,23 +329,30 @@ cubeb_get_backend_names()
#if defined(USE_SNDIO)
"sndio",
#endif
#if defined(USE_SUN)
"sun",
#endif
#if defined(USE_OPENSL)
"opensl",
#endif
#if defined(USE_OSS)
"oss",
#endif
NULL,
#if defined(USE_AAUDIO)
"aaudio",
#endif
#if defined(USE_AUDIOTRACK)
"audiotrack",
#endif
#if defined(USE_KAI)
"kai",
#endif
};
return backend_names;
}
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
return (cubeb_backend_names){
.names = backend_names,
.count = NELEMS(backend_names),
};
}
int

View File

@@ -213,12 +213,19 @@ struct cubeb_stream {
cubeb_device_changed_callback device_changed_callback = nullptr;
owned_critical_section device_changed_callback_lock;
/* Stream creation parameters */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
device_info input_device;
device_info output_device;
/* Format descriptions */

View File

@@ -16,8 +16,8 @@
#include <time.h>
#endif
static std::atomic<cubeb_log_level> g_cubeb_log_level;
static std::atomic<cubeb_log_callback> g_cubeb_log_callback;
std::atomic<cubeb_log_level> g_cubeb_log_level;
std::atomic<cubeb_log_callback> g_cubeb_log_callback;
/** The maximum size of a log message, after having been formatted. */
const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
@@ -32,6 +32,133 @@ cubeb_noop_log_callback(char const * /* fmt */, ...)
{
}
/**
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message {
public:
cubeb_log_message() { *storage = '\0'; }
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
size_t length = strlen(str);
/* paranoia against malformed message */
assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
return;
}
PodCopy(storage, str, length);
storage[length] = '\0';
}
char const * get() { return storage; }
private:
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE]{};
};
/** Lock-free asynchronous logger, made so that logging from a
* real-time audio callback does not block the audio thread. */
class cubeb_async_logger {
public:
/* This is thread-safe since C++11 */
static cubeb_async_logger & get()
{
static cubeb_async_logger instance;
return instance;
}
void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
cubeb_log_message msg(str);
auto * owned_queue = msg_queue.load();
// Check if the queue is being deallocated. If not, grab ownership. If yes,
// return, the message won't be logged.
if (!owned_queue ||
!msg_queue.compare_exchange_strong(owned_queue, nullptr)) {
return;
}
owned_queue->enqueue(msg);
// Return ownership.
msg_queue.store(owned_queue);
}
void run()
{
assert(logging_thread.get_id() == std::thread::id());
logging_thread = std::thread([this]() {
CUBEB_REGISTER_THREAD("cubeb_log");
while (!shutdown_thread) {
cubeb_log_message msg;
while (msg_queue_consumer.load()->dequeue(&msg, 1)) {
cubeb_log_internal_no_format(msg.get());
}
std::this_thread::sleep_for(
std::chrono::milliseconds(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS));
}
CUBEB_UNREGISTER_THREAD();
});
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
if (msg_queue) {
msg_queue.load()->reset_thread_ids();
}
}
void start()
{
auto * queue =
new lock_free_queue<cubeb_log_message>(CUBEB_LOG_MESSAGE_QUEUE_DEPTH);
msg_queue.store(queue);
msg_queue_consumer.store(queue);
shutdown_thread = false;
run();
}
void stop()
{
assert(((g_cubeb_log_callback == cubeb_noop_log_callback) ||
!g_cubeb_log_callback) &&
"Only call stop after logging has been disabled.");
shutdown_thread = true;
if (logging_thread.get_id() != std::thread::id()) {
logging_thread.join();
logging_thread = std::thread();
auto * owned_queue = msg_queue.load();
// Check if the queue is being used. If not, grab ownership. If yes,
// try again shortly. At this point, the logging thread has been joined,
// so nothing is going to dequeue.
// If there is a valid pointer here, then the real-time audio thread that
// logs won't attempt to write into the queue, and instead drop the
// message.
while (!msg_queue.compare_exchange_weak(owned_queue, nullptr)) {
}
delete owned_queue;
msg_queue_consumer.store(nullptr);
}
}
private:
cubeb_async_logger() {}
~cubeb_async_logger()
{
assert(logging_thread.get_id() == std::thread::id() &&
(g_cubeb_log_callback == cubeb_noop_log_callback ||
!g_cubeb_log_callback));
if (msg_queue.load()) {
delete msg_queue.load();
}
}
/** This is quite a big data structure, but is only instantiated if the
* asynchronous logger is used. The two pointers point to the same object, but
* the first one can be temporarily null when a message is being enqueued. */
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue = {nullptr};
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue_consumer = {
nullptr};
std::atomic<bool> shutdown_thread = {false};
std::thread logging_thread;
};
void
cubeb_log_internal(char const * file, uint32_t line, char const * fmt, ...)
{
@@ -49,6 +176,29 @@ cubeb_log_internal_no_format(const char * msg)
g_cubeb_log_callback.load()(msg);
}
void
cubeb_async_log(char const * fmt, ...)
{
// This is going to copy a 256 bytes array around, which is fine.
// We don't want to allocate memory here, because this is made to
// be called from a real-time callback.
va_list args;
va_start(args, fmt);
char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
cubeb_async_logger::get().push(msg);
va_end(args);
}
void
cubeb_async_log_reset_threads(void)
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}
void
cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
{
@@ -57,8 +207,15 @@ cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
// nullptr, to prevent a TOCTOU race between checking the pointer
if (log_callback && log_level != CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = log_callback;
if (log_level == CUBEB_LOG_VERBOSE) {
cubeb_async_logger::get().start();
}
} else if (!log_callback || CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = cubeb_noop_log_callback;
// This returns once the thread has joined.
// This is safe even if CUBEB_LOG_VERBOSE was not set; the thread will
// simply not be joinable.
cubeb_async_logger::get().stop();
} else {
assert(false && "Incorrect parameters passed to cubeb_log_set");
}

View File

@@ -39,7 +39,12 @@ cubeb_log_get_callback(void);
void
cubeb_log_internal_no_format(const char * msg);
void
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...)
PRINTF_FORMAT(3, 4);
void
cubeb_async_log(const char * fmt, ...) PRINTF_FORMAT(1, 2);
void
cubeb_async_log_reset_threads(void);
#ifdef __cplusplus
}
@@ -55,9 +60,16 @@ cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
} \
} while (0)
#define ALOG_INTERNAL(level, fmt, ...) \
do { \
if (cubeb_log_get_level() >= level && cubeb_log_get_callback()) { \
cubeb_async_log(fmt, ##__VA_ARGS__); \
} \
} while (0)
/* Asynchronous logging macros to log in real-time callbacks. */
/* Should not be used on android due to the use of global/static variables. */
#define ALOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define ALOGV(msg, ...) ALOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) ALOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#endif // CUBEB_LOG

View File

@@ -371,3 +371,9 @@ cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler)
{
return resampler->stats();
}

View File

@@ -84,6 +84,20 @@ cubeb_resampler_destroy(cubeb_resampler * resampler);
long
cubeb_resampler_latency(cubeb_resampler * resampler);
/**
* Test-only introspection API to ensure that there is no buffering
* buildup when resampling.
*/
typedef struct {
size_t input_input_buffer_size;
size_t input_output_buffer_size;
size_t output_input_buffer_size;
size_t output_output_buffer_size;
} cubeb_resampler_stats;
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler);
#if defined(__cplusplus)
}
#endif

View File

@@ -56,6 +56,7 @@ struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual cubeb_resampler_stats stats() = 0;
virtual ~cubeb_resampler() {}
};
@@ -86,6 +87,16 @@ public:
virtual long latency() { return 0; }
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats;
stats.input_input_buffer_size = internal_input_buffer.length();
stats.input_output_buffer_size = 0;
stats.output_input_buffer_size = 0;
stats.output_output_buffer_size = 0;
return stats;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
@@ -122,6 +133,20 @@ public:
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats = {};
if (input_processor) {
stats.input_input_buffer_size = input_processor->input_buffer_size();
stats.input_output_buffer_size = input_processor->output_buffer_size();
}
if (output_processor) {
stats.output_input_buffer_size = output_processor->input_buffer_size();
stats.output_output_buffer_size = output_processor->output_buffer_size();
}
return stats;
}
virtual long latency()
{
if (input_processor && output_processor) {
@@ -280,29 +305,28 @@ public:
}
/** Returns the number of frames to pass in the input of the resampler to have
* exactly `output_frame_count` resampled frames. This can return a number
* slightly bigger than what is strictly necessary, but it guaranteed that the
* number of output frames will be exactly equal. */
* at least `output_frame_count` resampled frames. */
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); // Check overflow
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left =
samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio -
resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
return (uint32_t)ceilf(input_frames_needed);
float input_frames_needed_frac =
static_cast<float>(output_frame_count) * resampling_ratio;
// speex_resample()` can be irregular in its consumption of input samples.
// Provide one more frame than the number that would be required with
// regular consumption, to make the speex resampler behave more regularly,
// and so predictably.
auto input_frame_needed =
1 + static_cast<int32_t>(ceilf(input_frames_needed_frac));
input_frame_needed -= std::min(unresampled_frames_left, input_frame_needed);
return input_frame_needed;
}
/** Returns a pointer to the input buffer, that contains empty space for at
* least `frame_count` elements. This is useful so that consumer can directly
* write into the input buffer of the resampler. The pointer returned is
* adjusted so that leftover data are not overwritten.
* least `frame_count` elements. This is useful so that consumer can
* directly write into the input buffer of the resampler. The pointer
* returned is adjusted so that leftover data are not overwritten.
*/
T * input_buffer(size_t frame_count)
{
@@ -312,8 +336,8 @@ public:
return resampling_in_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
@@ -331,6 +355,9 @@ public:
}
}
size_t input_buffer_size() const { return resampling_in_buffer.length(); }
size_t output_buffer_size() const { return resampling_out_buffer.length(); }
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
@@ -359,6 +386,7 @@ private:
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
@@ -371,8 +399,8 @@ private:
auto_array<T> resampling_out_buffer;
/** Additional latency inserted into the pipeline for synchronisation. */
uint32_t additional_latency;
/** When `input_buffer` is called, this allows tracking the number of samples
that were in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that were in the buffer. */
uint32_t leftover_samples;
};
@@ -417,8 +445,8 @@ public:
return delay_output_buffer.data();
}
/** Get a pointer to the first writable location in the input buffer>
* @parameter frames_needed the number of frames the user needs to write into
* the buffer.
* @parameter frames_needed the number of frames the user needs to write
* into the buffer.
* @returns a pointer to a location in the input buffer where #frames_needed
* can be writen. */
T * input_buffer(uint32_t frames_needed)
@@ -428,8 +456,8 @@ public:
frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t frames_written)
{
delay_input_buffer.set_length(leftover_samples +
@@ -450,8 +478,8 @@ public:
return to_pop;
}
/** Returns the number of frames one needs to input into the delay line to get
* #frames_needed frames back.
/** Returns the number of frames one needs to input into the delay line to
* get #frames_needed frames back.
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
@@ -469,19 +497,23 @@ public:
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
size_t input_buffer_size() const { return delay_input_buffer.length(); }
size_t output_buffer_size() const { return delay_output_buffer.length(); }
private:
/** The length, in frames, of this delay line */
uint32_t length;
/** When `input_buffer` is called, this allows tracking the number of samples
that where in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that where in the buffer. */
uint32_t leftover_samples;
/** The input buffer, where the delay is applied. */
auto_array<T> delay_input_buffer;
@@ -511,8 +543,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
"need at least one valid parameter pointer.");
/* All the streams we have have a sample rate that matches the target
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
sample rate, use a no-op resampler, that simply forwards the buffers to
the callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||

View File

@@ -4,8 +4,12 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0603
#endif // !_WIN32_WINNT
#ifndef NOMINMAX
#define NOMINMAX
#endif // !NOMINMAX
#include <algorithm>
#include <atomic>
@@ -37,31 +41,6 @@
#include "cubeb_tracing.h"
#include "cubeb_utils.h"
// Some people have reported glitches with IAudioClient3 capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
// https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
#define ALLOW_AUDIO_CLIENT_3_FOR_INPUT 0
// IAudioClient3::GetSharedModeEnginePeriod() seem to return min latencies
// bigger than IAudioClient::GetDevicePeriod(), which is confusing (10ms vs
// 3ms), though the default latency is usually the same and we should use the
// IAudioClient3 function anyway, as it's more correct
#define USE_AUDIO_CLIENT_3_MIN_PERIOD 1
// If this is true, we allow IAudioClient3 the creation of sessions with a
// latency above the default one (usually 10ms).
// Whether we should default this to true or false depend on many things:
// -Does creating a shared IAudioClient3 session (not locked to a format)
// actually forces all the IAudioClient(1) sessions to have the same latency?
// I could find no proof of that.
// -Does creating a shared IAudioClient3 session with a latency >= the default
// one actually improve the latency (as in how late the audio is) at all?
// -Maybe we could expose this as cubeb stream pref
// (e.g. take priority over other apps)?
#define ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT 1
// If this is true and the user specified a target latency >= the IAudioClient3
// max one, then we reject it and fall back to IAudioClient(1). There wouldn't
// be much point in having a low latency if that's not what the user wants.
#define REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX 0
// Windows 10 exposes the IAudioClient3 interface to create low-latency streams.
// Copy the interface definition from audioclient.h here to make the code
// simpler and so that we can still access IAudioClient3 via COM if cubeb was
@@ -229,11 +208,6 @@ struct auto_stream_ref {
cubeb_stream * stm;
};
using set_mm_thread_characteristics_function =
decltype(&AvSetMmThreadCharacteristicsW);
using revert_mm_thread_characteristics_function =
decltype(&AvRevertMmThreadCharacteristics);
extern cubeb_ops const wasapi_ops;
static com_heap_ptr<wchar_t>
@@ -304,8 +278,8 @@ wasapi_enumerate_devices_internal(cubeb * context, cubeb_device_type type,
static int
wasapi_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection);
static char const *
wstr_to_utf8(wchar_t const * str);
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str);
static std::unique_ptr<wchar_t const[]>
utf8_to_wstr(char const * str);
@@ -314,6 +288,15 @@ utf8_to_wstr(char const * str);
class wasapi_collection_notification_client;
class monitor_device_notifications;
typedef enum {
/* Clear options */
CUBEB_AUDIO_CLIENT2_NONE,
/* Use AUDCLNT_STREAMOPTIONS_RAW */
CUBEB_AUDIO_CLIENT2_RAW,
/* Use CUBEB_STREAM_PREF_COMMUNICATIONS */
CUBEB_AUDIO_CLIENT2_VOICE
} AudioClient2Option;
struct cubeb {
cubeb_ops const * ops = &wasapi_ops;
owned_critical_section lock;
@@ -331,13 +314,6 @@ struct cubeb {
nullptr;
void * output_collection_changed_user_ptr = nullptr;
UINT64 performance_counter_frequency;
/* Library dynamically opened to increase the render thread priority, and
the two function pointers we need. */
HMODULE mmcss_module = nullptr;
set_mm_thread_characteristics_function set_mm_thread_characteristics =
nullptr;
revert_mm_thread_characteristics_function revert_mm_thread_characteristics =
nullptr;
};
class wasapi_endpoint_notification_client;
@@ -360,20 +336,33 @@ struct cubeb_stream {
/* Mixer pameters. We need to convert the input stream to this
samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* Stream parameters. This is what the client requested,
* and what will be presented in the callback. */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* A MMDevice role for this stream: either communication or console here. */
ERole role;
/* True if this stream will transport voice-data. */
@@ -662,6 +651,10 @@ public:
LPCWSTR device_id)
{
LOG("collection: Audio device default changed, id = %S.", device_id);
/* Default device changes count as device collection changes */
monitor_notifications.notify(flow);
return S_OK;
}
@@ -772,7 +765,7 @@ public:
LPCWSTR device_id)
{
LOG("endpoint: Audio device default changed flow=%d role=%d "
"new_device_id=%ws.",
"new_device_id=%S.",
flow, role, device_id);
/* we only support a single stream type for now. */
@@ -783,11 +776,13 @@ public:
DWORD last_change_ms = timeGetTime() - last_device_change;
bool same_device = default_device_id && device_id &&
wcscmp(default_device_id.get(), device_id) == 0;
LOG("endpoint: Audio device default changed last_change=%u same_device=%d",
LOG("endpoint: Audio device default changed last_change=%lu same_device=%d",
last_change_ms, same_device);
if (last_change_ms > DEVICE_CHANGE_DEBOUNCE_MS || !same_device) {
if (device_id) {
default_device_id.reset(_wcsdup(device_id));
wchar_t * new_device_id = new wchar_t[wcslen(device_id) + 1];
wcscpy(new_device_id, device_id);
default_device_id.reset(new_device_id);
} else {
default_device_id.reset();
}
@@ -863,16 +858,12 @@ intern_device_id(cubeb * ctx, wchar_t const * id)
auto_lock lock(ctx->lock);
char const * tmp = wstr_to_utf8(id);
std::unique_ptr<char const[]> tmp = wstr_to_utf8(id);
if (!tmp) {
return nullptr;
}
char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
free((void *)tmp);
return interned;
return cubeb_strings_intern(ctx->device_ids, tmp.get());
}
bool
@@ -977,7 +968,7 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
cubeb_resampler_fill(stm->resampler.get(), input_buffer,
&input_frames_count, dest, output_frames_needed);
if (out_frames < 0) {
ALOGV("Callback refill error: %d", out_frames);
ALOGV("Callback refill error: %ld", out_frames);
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return out_frames;
}
@@ -1263,8 +1254,8 @@ refill_callback_duplex(cubeb_stream * stm)
XASSERT(has_input(stm) && has_output(stm));
if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
rv = get_input_buffer(stm);
if (!rv) {
return rv;
}
}
@@ -1274,7 +1265,6 @@ refill_callback_duplex(cubeb_stream * stm)
rv = get_output_buffer(stm, output_buffer, output_frames);
if (!rv) {
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
return rv;
}
@@ -1291,9 +1281,11 @@ refill_callback_duplex(cubeb_stream * stm)
stm->total_output_frames += output_frames;
ALOGV("in: %zu, out: %zu, missing: %ld, ratio: %f", stm->total_input_frames,
stm->total_output_frames,
static_cast<long>(stm->total_output_frames) - stm->total_input_frames,
ALOGV("in: %llu, out: %llu, missing: %ld, ratio: %f",
(unsigned long long)stm->total_input_frames,
(unsigned long long)stm->total_output_frames,
static_cast<long long>(stm->total_output_frames) -
static_cast<long long>(stm->total_input_frames),
static_cast<float>(stm->total_output_frames) / stm->total_input_frames);
long got;
@@ -1438,8 +1430,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
/* We could consider using "Pro Audio" here for WebAudio and
maybe WebRTC. */
mmcss_handle =
stm->context->set_mm_thread_characteristics(L"Audio", &mmcss_task_index);
mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %lx",
@@ -1519,8 +1510,8 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
is_playing = stm->refill_callback(stm);
break;
case WAIT_OBJECT_0 + 3: { /* input available */
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
bool rv = get_input_buffer(stm);
if (!rv) {
is_playing = false;
continue;
}
@@ -1532,8 +1523,11 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
break;
}
default:
LOG("case %lu not handled in render loop.", waitResult);
XASSERT(false);
LOG("render_loop: waitResult=%lu (lastError=%lu) unhandled, exiting",
waitResult, GetLastError());
is_playing = false;
hr = E_FAIL;
continue;
}
}
@@ -1547,7 +1541,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
if (mmcss_handle) {
stm->context->revert_mm_thread_characteristics(mmcss_handle);
AvRevertMmThreadCharacteristics(mmcss_handle);
}
if (FAILED(hr)) {
@@ -1560,18 +1554,6 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
void
wasapi_destroy(cubeb * context);
HANDLE WINAPI
set_mm_thread_characteristics_noop(LPCWSTR, LPDWORD mmcss_task_index)
{
return (HANDLE)1;
}
BOOL WINAPI
revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
{
return true;
}
HRESULT
register_notification_client(cubeb_stream * stm)
{
@@ -1807,31 +1789,6 @@ wasapi_init(cubeb ** context, char const * context_name)
ctx->performance_counter_frequency = 0;
}
ctx->mmcss_module = LoadLibraryW(L"Avrt.dll");
bool success = false;
if (ctx->mmcss_module) {
ctx->set_mm_thread_characteristics =
reinterpret_cast<set_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module, "AvSetMmThreadCharacteristicsW"));
ctx->revert_mm_thread_characteristics =
reinterpret_cast<revert_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module,
"AvRevertMmThreadCharacteristics"));
success = ctx->set_mm_thread_characteristics &&
ctx->revert_mm_thread_characteristics;
}
if (!success) {
// This is not a fatal error, but we might end up glitching when
// the system is under high load.
LOG("Could not load avrt.dll or fetch AvSetMmThreadCharacteristicsW "
"AvRevertMmThreadCharacteristics: %lx",
GetLastError());
ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
ctx->revert_mm_thread_characteristics =
&revert_mm_thread_characteristics_noop;
}
*context = ctx;
return CUBEB_OK;
@@ -1839,7 +1796,6 @@ wasapi_init(cubeb ** context, char const * context_name)
}
namespace {
enum ShutdownPhase { OnStop, OnDestroy };
bool
stop_and_join_render_thread(cubeb_stream * stm)
@@ -1855,16 +1811,7 @@ stop_and_join_render_thread(cubeb_stream * stm)
return false;
}
/* Wait five seconds for the rendering thread to return. It's supposed to
* check its event loop very often, five seconds is rather conservative.
* Note: 5*1s loop to work around timer sleep issues on pre-Windows 8. */
DWORD r;
for (int i = 0; i < 5; ++i) {
r = WaitForSingleObject(stm->thread, 1000);
if (r == WAIT_OBJECT_0) {
break;
}
}
DWORD r = WaitForSingleObject(stm->thread, INFINITE);
if (r != WAIT_OBJECT_0) {
LOG("stop_and_join_render_thread: WaitForSingleObject on thread failed: "
"%lx, %lx",
@@ -1888,10 +1835,6 @@ wasapi_destroy(cubeb * context)
}
}
if (context->mmcss_module) {
FreeLibrary(context->mmcss_module);
}
delete context;
}
@@ -1949,44 +1892,6 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
return CUBEB_ERROR;
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// This is unreliable as we can't know the actual mixer format cubeb will
// ask for later on (nor we can branch on ALLOW_AUDIO_CLIENT_3_FOR_INPUT),
// and the min latency can change based on that.
com_ptr<IAudioClient3> client3;
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
client3.receive_vpp());
if (SUCCEEDED(hr)) {
WAVEFORMATEX * mix_format = nullptr;
hr = client3->GetMixFormat(&mix_format);
if (SUCCEEDED(hr)) {
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
max_period = 0;
hr = client3->GetSharedModeEnginePeriod(mix_format, &default_period,
&fundamental_period, &min_period,
&max_period);
auto sample_rate = mix_format->nSamplesPerSec;
CoTaskMemFree(mix_format);
if (SUCCEEDED(hr)) {
// Print values in the same format as IAudioDevice::GetDevicePeriod()
REFERENCE_TIME min_period_rt(frames_to_hns(sample_rate, min_period));
REFERENCE_TIME default_period_rt(
frames_to_hns(sample_rate, default_period));
LOG("default device period: %I64d, minimum device period: %I64d",
default_period_rt, min_period_rt);
*latency_frames = hns_to_frames(params.rate, min_period_rt);
LOG("Minimum latency in frames: %u", *latency_frames);
return CUBEB_OK;
}
}
}
#endif
com_ptr<IAudioClient> client;
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
client.receive_vpp());
@@ -2006,8 +1911,18 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
LOG("default device period: %I64d, minimum device period: %I64d",
default_period, minimum_period);
// The minimum_period is only relevant in exclusive streams.
/* If we're on Windows 10, we can use IAudioClient3 to get minimal latency.
Otherwise, according to the docs, the best latency we can achieve is by
synchronizing the stream and the engine.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx
*/
// #ifdef _WIN32_WINNT_WIN10
#if 0
*latency_frames = hns_to_frames(params.rate, minimum_period);
#else
*latency_frames = hns_to_frames(params.rate, default_period);
#endif
LOG("Minimum latency in frames: %u", *latency_frames);
@@ -2044,6 +1959,21 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
return CUBEB_OK;
}
int
wasapi_get_supported_input_processing_params(
cubeb * ctx, cubeb_input_processing_params * params)
{
// This is not entirely accurate -- windows doesn't document precisely what
// AudioCategory_Communications does -- but assume that we can set all or none
// of them.
*params = static_cast<cubeb_input_processing_params>(
CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION);
return CUBEB_OK;
}
static void
waveformatex_update_derived_properties(WAVEFORMATEX * format)
{
@@ -2097,10 +2027,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
if (hr == S_FALSE) {
/* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
and handle the eventual upmix/downmix ourselves. Ignore the subformat of
the suggestion, since it seems to always be IEEE_FLOAT.
This fallback doesn't update the bit depth, so if a device
only supported bit depths cubeb doesn't support, so IAudioClient3
streams might fail */
the suggestion, since it seems to always be IEEE_FLOAT. */
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
WAVEFORMATEXTENSIBLE * closest_pcm =
@@ -2122,7 +2049,8 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
}
static int
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client,
AudioClient2Option option)
{
com_ptr<IAudioClient2> audio_client2;
audio_client->QueryInterface<IAudioClient2>(audio_client2.receive());
@@ -2131,10 +2059,14 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
"AUDCLNT_STREAMOPTIONS_RAW.");
return CUBEB_OK;
}
AudioClientProperties properties = {0};
AudioClientProperties properties = {};
properties.cbSize = sizeof(AudioClientProperties);
#ifndef __MINGW32__
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
if (option == CUBEB_AUDIO_CLIENT2_RAW) {
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
} else if (option == CUBEB_AUDIO_CLIENT2_VOICE) {
properties.eCategory = AudioCategory_Communications;
}
#endif
HRESULT hr = audio_client2->SetClientProperties(&properties);
if (FAILED(hr)) {
@@ -2144,12 +2076,12 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
return CUBEB_OK;
}
#if 0
bool
initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
cubeb_stream * stm,
const com_heap_ptr<WAVEFORMATEX> & mix_format,
DWORD flags, EDataFlow direction,
REFERENCE_TIME latency_hns)
DWORD flags, EDataFlow direction)
{
com_ptr<IAudioClient3> audio_client3;
audio_client->QueryInterface<IAudioClient3>(audio_client3.receive());
@@ -2165,22 +2097,24 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
return false;
}
// Some people have reported glitches with capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
if (direction == eCapture) {
LOG("Audio stream is capture, not using IAudioClient3");
return false;
}
// Possibly initialize a shared-mode stream using IAudioClient3. Initializing
// a stream this way lets you request lower latencies, but also locks the
// global WASAPI engine at that latency.
// - If we request a shared-mode stream, streams created with IAudioClient
// might have their latency adjusted to match. When the shared-mode stream
// is closed, they'll go back to normal.
// - If there's already a shared-mode stream running, if it created with the
// AUDCLNT_STREAMOPTIONS_MATCH_FORMAT option, the audio engine would be
// locked to that format, so we have to match it (a custom one would fail).
// - We don't lock the WASAPI engine to a format, as it's antisocial towards
// other apps, especially if we locked to a latency >= than its default.
// - If the user requested latency is >= the default one, we might still
// accept it (without locking the format) depending on
// ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT, as we might want to prioritize
// to lower our latency over other apps
// (there might still be latency advantages compared to IAudioDevice(1)).
// will
// have their latency adjusted to match. When the shared-mode stream is
// closed, they'll go back to normal.
// - If there's already a shared-mode stream running, then we cannot request
// the engine change to a different latency - we have to match it.
// - It's antisocial to lock the WASAPI engine at its default latency. If we
// would do this, then stop and use IAudioClient instead.
HRESULT hr;
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
@@ -2192,59 +2126,28 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not get shared mode engine period: error: %lx", hr);
return false;
}
uint32_t requested_latency =
hns_to_frames(mix_format->nSamplesPerSec, latency_hns);
#if !ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT
uint32_t requested_latency = stm->latency;
if (requested_latency >= default_period) {
LOG("Requested latency %i equal or greater than default latency %i,"
" not using IAudioClient3",
LOG("Requested latency %i greater than default latency %i, not using "
"IAudioClient3",
requested_latency, default_period);
return false;
}
#elif REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
if (requested_latency > max_period) {
// Fallback to IAudioClient(1) as it's more accepting of large latencies
LOG("Requested latency %i greater than max latency %i,"
" not using IAudioClient3",
requested_latency, max_period);
return false;
}
#endif
LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i",
default_period, fundamental_period, min_period, max_period);
// Snap requested latency to a valid value
uint32_t old_requested_latency = requested_latency;
// The period is required to be a multiple of the fundamental period
// (and >= min and <= max, which should still be true)
requested_latency -= requested_latency % fundamental_period;
if (requested_latency < min_period) {
requested_latency = min_period;
}
// Likely unnecessary, but won't hurt
if (requested_latency > max_period) {
requested_latency = max_period;
}
requested_latency -= (requested_latency - min_period) % fundamental_period;
if (requested_latency != old_requested_latency) {
LOG("Requested latency %i was adjusted to %i", old_requested_latency,
requested_latency);
}
DWORD new_flags = flags;
// Always add these flags to IAudioClient3, they might help
// if the stream doesn't have the same format as the audio engine.
new_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
new_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
hr = audio_client3->InitializeSharedAudioStream(new_flags, requested_latency,
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
// This error should be returned first even if
// the period was locked (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED)
if (hr == AUDCLNT_E_INVALID_STREAM_FLAG) {
LOG("Got AUDCLNT_E_INVALID_STREAM_FLAG, removing some flags");
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
}
if (SUCCEEDED(hr)) {
return true;
} else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) {
@@ -2256,37 +2159,22 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
}
uint32_t current_period = 0;
WAVEFORMATEX * current_format_ptr = nullptr;
WAVEFORMATEX * current_format = nullptr;
// We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise
// GetCurrentSharedModeEnginePeriod will return E_POINTER
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format_ptr,
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format,
&current_period);
CoTaskMemFree(current_format);
if (FAILED(hr)) {
LOG("Could not get current shared mode engine period: error: %lx", hr);
return false;
}
com_heap_ptr<WAVEFORMATEX> current_format(current_format_ptr);
if (current_format->nSamplesPerSec != mix_format->nSamplesPerSec) {
// Unless some other external app locked the shared mode engine period
// within our audio initialization, this is unlikely to happen, though we
// can't respect the user selected latency, so we fallback on IAudioClient
LOG("IAudioClient3::GetCurrentSharedModeEnginePeriod() returned a "
"different mixer format (nSamplesPerSec) from "
"IAudioClient::GetMixFormat(); not using IAudioClient3");
return false;
}
#if REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
// Reject IAudioClient3 if we can't respect the user target latency.
// We don't need to check against default_latency anymore,
// as the current_period is already the best one we could get.
if (old_requested_latency > current_period) {
LOG("Requested latency %i greater than currently locked shared mode "
"latency %i, not using IAudioClient3",
old_requested_latency, current_period);
if (current_period >= default_period) {
LOG("Current shared mode engine period %i too high, not using IAudioClient",
current_period);
return false;
}
#endif
hr = audio_client3->InitializeSharedAudioStream(flags, current_period,
mix_format.get(), NULL);
@@ -2299,6 +2187,7 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
return false;
}
#endif
#define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
@@ -2322,12 +2211,6 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = direction == eRender;
#endif
stm->stream_reset_lock.assert_current_thread_owns();
// If user doesn't specify a particular device, we can choose another one when
// the given devid is unavailable.
@@ -2364,14 +2247,17 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
/* Get a client. We will get all other interfaces we need from
* this pointer. */
if (allow_audio_client_3) {
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
}
if (!allow_audio_client_3 || hr == E_NOINTERFACE) {
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
hr = device->Activate(__uuidof(IAudioClient3),
CLSCTX_INPROC_SERVER,
NULL, audio_client.receive_vpp());
if (hr == E_NOINTERFACE) {
#endif
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Could not activate the device to get an audio"
@@ -2494,21 +2380,41 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
}
if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) {
if (initialize_iaudioclient2(audio_client) != CUBEB_OK) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_RAW) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else if (direction == eCapture &&
(stream_params->prefs & CUBEB_STREAM_PREF_VOICE) &&
stream_params->input_params != CUBEB_INPUT_PROCESSING_PARAM_NONE) {
if (stream_params->input_params ==
(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION)) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_VOICE) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else {
LOG("Invalid combination of input processing params %#x",
stream_params->input_params);
return CUBEB_ERROR;
}
}
if (allow_audio_client_3 &&
initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction,
latency_hns)) {
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
LOG("Initialized with IAudioClient3");
} else {
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns,
0, mix_format.get(), NULL);
#endif
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, 0,
mix_format.get(), NULL);
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
@@ -2970,6 +2876,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
}
}
cubeb_async_log_reset_threads();
stm->thread =
(HANDLE)_beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm,
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
@@ -3031,7 +2938,7 @@ wasapi_stream_add_ref(cubeb_stream * stm)
{
XASSERT(stm);
LONG result = InterlockedIncrement(&stm->ref_count);
LOGV("Stream ref count incremented = %i (%p)", result, stm);
LOGV("Stream ref count incremented = %ld (%p)", result, stm);
return result;
}
@@ -3041,7 +2948,7 @@ wasapi_stream_release(cubeb_stream * stm)
XASSERT(stm);
LONG result = InterlockedDecrement(&stm->ref_count);
LOGV("Stream ref count decremented = %i (%p)", result, stm);
LOGV("Stream ref count decremented = %ld (%p)", result, stm);
if (result == 0) {
LOG("Stream ref count hit zero, destroying (%p)", stm);
@@ -3303,7 +3210,7 @@ wasapi_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
static char const *
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str)
{
int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
@@ -3311,8 +3218,8 @@ wstr_to_utf8(LPCWSTR str)
return nullptr;
}
char * ret = static_cast<char *>(malloc(size));
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
std::unique_ptr<char[]> ret(new char[size]);
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret.get(), size, NULL, NULL);
return ret;
}
@@ -3440,7 +3347,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant namevar;
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
if (SUCCEEDED(hr) && namevar.vt == VT_LPWSTR) {
ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
ret.friendly_name = wstr_to_utf8(namevar.pwszVal).release();
}
if (!ret.friendly_name) {
// This is not fatal, but a valid string is expected in all cases.
@@ -3461,7 +3368,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant instancevar;
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
if (SUCCEEDED(hr) && instancevar.vt == VT_LPWSTR) {
ret.group_id = wstr_to_utf8(instancevar.pwszVal);
ret.group_id = wstr_to_utf8(instancevar.pwszVal).release();
}
}
@@ -3477,7 +3384,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA |
CUBEB_DEVICE_PREF_NOTIFICATION);
} else if (defaults->is_default(flow, eCommunications, device_id.get())) {
}
if (defaults->is_default(flow, eCommunications, device_id.get())) {
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
}
@@ -3504,7 +3412,6 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
CUBEB_DEVICE_FMT_S16NE);
ret.default_format = CUBEB_DEVICE_FMT_F32NE;
prop_variant fmtvar;
WAVEFORMATEX * wfx = NULL;
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
@@ -3514,7 +3421,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
ret.max_channels = pcm->wf.nChannels;
} else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
wfx = reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
WAVEFORMATEX * wfx =
reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
wfx->wFormatTag == WAVE_FORMAT_PCM) {
@@ -3524,30 +3432,9 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
}
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// Here we assume an IAudioClient3 stream will successfully
// be initialized later (it might fail)
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = flow == eRender;
#endif
com_ptr<IAudioClient3> client3;
uint32_t def, fun, min, max;
if (allow_audio_client_3 && wfx &&
SUCCEEDED(dev->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER,
NULL, client3.receive_vpp())) &&
SUCCEEDED(
client3->GetSharedModeEnginePeriod(wfx, &def, &fun, &min, &max))) {
ret.latency_lo = min;
// This latency might actually be used as "default" and not "max" later on,
// so we return the default (we never really want to use the max anyway)
ret.latency_hi = def;
} else
#endif
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
} else {
@@ -3638,7 +3525,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
{
return wasapi_enumerate_devices_internal(
context, type, out,
DEVICE_STATE_ACTIVE /*| DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED*/);
DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED);
}
static int
@@ -3656,6 +3543,14 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/,
return CUBEB_OK;
}
int
wasapi_set_input_processing_params(cubeb_stream * stream,
cubeb_input_processing_params params)
{
LOG("Cannot set voice processing params after init. Use cubeb_stream_init.");
return CUBEB_ERROR_NOT_SUPPORTED;
}
static int
wasapi_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
@@ -3736,7 +3631,8 @@ cubeb_ops const wasapi_ops = {
/*.get_max_channel_count =*/wasapi_get_max_channel_count,
/*.get_min_latency =*/wasapi_get_min_latency,
/*.get_preferred_sample_rate =*/wasapi_get_preferred_sample_rate,
/*.get_supported_input_processing_params =*/NULL,
/*.get_supported_input_processing_params =*/
wasapi_get_supported_input_processing_params,
/*.enumerate_devices =*/wasapi_enumerate_devices,
/*.device_collection_destroy =*/wasapi_device_collection_destroy,
/*.destroy =*/wasapi_destroy,
@@ -3751,7 +3647,7 @@ cubeb_ops const wasapi_ops = {
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_set_input_mute =*/NULL,
/*.stream_set_input_processing_params =*/NULL,
/*.stream_set_input_processing_params =*/wasapi_set_input_processing_params,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback =*/NULL,
/*.register_device_collection_changed =*/

View File

@@ -41,10 +41,10 @@
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#if defined(ARM4_ASM) + defined(ARM5E_ASM) + defined(BFIN_ASM) > 1
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
@@ -56,10 +56,10 @@
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#if defined(ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#ifdef FIXED_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
@@ -117,9 +117,9 @@ typedef spx_word32_t spx_sig_t;
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#elif defined(ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#elif defined(BFIN_ASM)
#include "fixed_bfin.h"
#endif
@@ -177,16 +177,13 @@ typedef float spx_word32_t;
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_32_32(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
@@ -210,7 +207,7 @@ typedef float spx_word32_t;
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
#if defined(CONFIG_TI_C54X) || defined(CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2

View File

@@ -69,22 +69,18 @@
/* result fits in 16 bits */
#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
#define MULT16_16_16(a,b) (((spx_word16_t)(a))*((spx_word16_t)(b)))
/* result fits in 32 bits */
#define MULT16_32_32(a,b) (((spx_word16_t)(a))*((spx_word32_t)(b)))
/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
#define MULT16_32_P15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,MULT16_32_Q15(a,b))
#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))

View File

@@ -46,7 +46,7 @@
Smith, Julius O. Digital Audio Resampling Home Page
Center for Computer Research in Music and Acoustics (CCRMA),
Stanford University, 2007.
Web published at http://ccrma.stanford.edu/~jos/resample/.
Web published at https://ccrma.stanford.edu/~jos/resample/.
There is one main difference, though. This resampler uses cubic
interpolation instead of linear interpolation in the above paper. This
@@ -63,9 +63,12 @@
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
static void *speex_alloc (int size) {return calloc(size,1);}
static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
static void speex_free (void *ptr) {free(ptr);}
static void *speex_alloc(int size) {return calloc(size,1);}
static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
static void speex_free(void *ptr) {free(ptr);}
#ifndef EXPORT
#define EXPORT
#endif
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
@@ -75,7 +78,6 @@ static void speex_free (void *ptr) {free(ptr);}
#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include "stack_alloc.h"
#include <math.h>
#include <limits.h>
@@ -91,18 +93,18 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967296U
#define UINT32_MAX 4294967295U
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#include "resample_sse.h"
#endif
#ifdef _USE_NEON
#ifdef USE_NEON
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
/* Number of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else
@@ -194,16 +196,14 @@ struct FuncDef {
int oversample;
};
static const struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
#define KAISER12 (&_KAISER12)*/
static const struct FuncDef _KAISER10 = {kaiser10_table, 32};
#define KAISER10 (&_KAISER10)
static const struct FuncDef _KAISER8 = {kaiser8_table, 32};
#define KAISER8 (&_KAISER8)
static const struct FuncDef _KAISER6 = {kaiser6_table, 32};
#define KAISER6 (&_KAISER6)
static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
#define KAISER12 (&kaiser12_funcdef)
static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
#define KAISER10 (&kaiser10_funcdef)
static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
#define KAISER8 (&kaiser8_funcdef)
static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
#define KAISER6 (&kaiser6_funcdef)
struct QualityMapping {
int base_length;
@@ -473,7 +473,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
sum = SATURATE32PSHR(sum, 15, 32767);
#else
cubic_coef(frac, interp);
@@ -572,6 +572,7 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
(void)in;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
out[out_stride * out_sample++] = 0;
@@ -589,16 +590,15 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
return out_sample;
}
static int _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div)
static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
{
speex_assert(result);
spx_uint32_t major = value / div;
spx_uint32_t remainder = value % div;
spx_uint32_t major = value / den;
spx_uint32_t remain = value % den;
/* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul
|| major * mul > UINT32_MAX - remainder * mul / div)
if (remain > UINT32_MAX / num || major > UINT32_MAX / num
|| major * num > UINT32_MAX - remain * num / den)
return RESAMPLER_ERR_OVERFLOW;
*result = remainder * mul / div + major * mul;
*result = remain * num / den + major * num;
return RESAMPLER_ERR_SUCCESS;
}
@@ -619,7 +619,7 @@ static int update_filter(SpeexResamplerState *st)
{
/* down-sampling */
st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
if (_muldiv(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
goto fail;
/* Round up to make sure we have a multiple of 8 for SSE */
st->filt_len = ((st->filt_len-1)&(~0x7))+8;
@@ -638,12 +638,12 @@ static int update_filter(SpeexResamplerState *st)
st->cutoff = quality_map[st->quality].upsample_bandwidth;
}
/* Choose the resampling type that requires the least amount of memory */
#ifdef RESAMPLE_FULL_SINC_TABLE
use_direct = 1;
if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
goto fail;
#else
/* Choose the resampling type that requires the least amount of memory */
use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
&& INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
#endif
@@ -733,16 +733,18 @@ static int update_filter(SpeexResamplerState *st)
{
spx_uint32_t j;
spx_uint32_t olen = old_length;
spx_uint32_t start = i*st->mem_alloc_size;
spx_uint32_t magic_samples = st->magic_samples[i];
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-1+st->magic_samples[i];j--;)
st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
for (j=0;j<st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = 0;
olen = old_length + 2*magic_samples;
for (j=old_length-1+magic_samples;j--;)
st->mem[start+j+magic_samples] = st->mem[i*old_alloc_size+j];
for (j=0;j<magic_samples;j++)
st->mem[start+j] = 0;
st->magic_samples[i] = 0;
}
if (st->filt_len > olen)
@@ -750,17 +752,18 @@ static int update_filter(SpeexResamplerState *st)
/* If the new filter length is still bigger than the "augmented" length */
/* Copy data going backward */
for (j=0;j<olen-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
st->mem[start+(st->filt_len-2-j)] = st->mem[start+(olen-2-j)];
/* Then put zeros for lack of anything better */
for (;j<st->filt_len-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
st->mem[start+(st->filt_len-2-j)] = 0;
/* Adjust last_sample */
st->last_sample[i] += (st->filt_len - olen)/2;
} else {
/* Put back some of the magic! */
st->magic_samples[i] = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
magic_samples = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+magic_samples;j++)
st->mem[start+j] = st->mem[start+j+magic_samples];
st->magic_samples[i] = magic_samples;
}
}
} else if (st->filt_len < old_length)
@@ -977,8 +980,7 @@ EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t cha
const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
#ifdef VAR_ARRAYS
const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
VARDECL(spx_word16_t *ystack);
ALLOC(ystack, ylen, spx_word16_t);
spx_word16_t ystack[ylen];
#else
const unsigned int ylen = FIXED_STACK_ALLOC;
spx_word16_t ystack[FIXED_STACK_ALLOC];
@@ -1093,7 +1095,7 @@ EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_r
*out_rate = st->out_rate;
}
static inline spx_uint32_t _gcd(spx_uint32_t a, spx_uint32_t b)
static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
{
while (b != 0)
{
@@ -1123,7 +1125,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
st->num_rate = ratio_num;
st->den_rate = ratio_den;
fact = _gcd (st->num_rate, st->den_rate);
fact = compute_gcd(st->num_rate, st->den_rate);
st->num_rate /= fact;
st->den_rate /= fact;
@@ -1132,7 +1134,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
{
for (i=0;i<st->nb_channels;i++)
{
if (_muldiv(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
return RESAMPLER_ERR_OVERFLOW;
/* Safety net */
if (st->samp_frac_num[i] >= st->den_rate)

View File

@@ -36,14 +36,26 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <arm_neon.h>
#include <stdint.h>
#ifdef FIXED_POINT
#ifdef __thumb2__
#if defined(__aarch64__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("fmov s0, %w[a]\n"
"sqxtn h0, s0\n"
"sxtl v0.4s, v0.4h\n"
"fmov %w[ret], s0\n"
: [ret] "=r" (ret)
: [a] "r" (a)
: "v0" );
return ret;
}
#elif defined(__thumb2__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("ssat %[ret], #16, %[a]"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: );
return ret;
@@ -54,7 +66,7 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
asm ("vmov.s32 d0[0], %[a]\n"
"vqmovn.s32 d0, q0\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
@@ -64,7 +76,63 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
#define WORD2INT(x) (saturate_32bit_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4h}, [%[b]], #8\n"
" ld1 {v20.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smull v0.4s, v16.4h, v20.4h\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smull v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smlal v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.ne 2b\n"
"3:"
" cmp %w[remainder], #0\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4h}, [%[b]], #8\n"
" ld1 {v22.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" b.ne 4b\n"
"5:"
" saddlv d0, v0.4s\n"
" sqxtn s0, d0\n"
" sqrshrn h0, s0, #15\n"
" sxtl v0.4s, v0.4h\n"
" fmov %w[ret], s0\n"
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v0",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
@@ -112,33 +180,104 @@ static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, u
" vqmovn.s64 d0, q0\n"
" vqrshrn.s32 d0, q0, #15\n"
" vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "q0",
"d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23");
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23");
return ret;
}
#elif defined(FLOATING_POINT)
#endif // !defined(__aarch64__)
#elif defined(FLOATING_POINT)
#if defined(__aarch64__)
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("fcvtas s1, %s[a]\n"
"sqxtn h1, s1\n"
"sxtl v1.4s, v1.4h\n"
"fmov %w[ret], s1\n"
: [ret] "=r" (ret)
: [a] "w" (a)
: "v1");
return ret;
}
#else
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("vmov.f32 d0[0], %[a]\n"
"vcvt.s32.f32 d0, d0, #15\n"
"vqrshrn.s32 d0, q0, #15\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#endif
#undef WORD2INT
#define WORD2INT(x) (saturate_float_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4s}, [%[b]], #16\n"
" ld1 {v20.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" fmul v2.4s, v17.4s, v21.4s\n"
" fmul v3.4s, v18.4s, v22.4s\n"
" fmul v4.4s, v19.4s, v23.4s\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmla v1.4s, v16.4s, v20.4s\n"
" fmla v2.4s, v17.4s, v21.4s\n"
" fmla v3.4s, v18.4s, v22.4s\n"
" fmla v4.4s, v19.4s, v23.4s\n"
" b.ne 2b\n"
"3:"
" fadd v16.4s, v1.4s, v2.4s\n"
" fadd v17.4s, v3.4s, v4.4s\n"
" cmp %w[remainder], #0\n"
" fadd v1.4s, v16.4s, v17.4s\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4s}, [%[b]], #16\n"
" ld1 {v22.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmla v1.4s, v18.4s, v22.4s\n"
" b.ne 4b\n"
"5:"
" faddp v1.4s, v1.4s, v1.4s\n"
" faddp %[ret].4s, v1.4s, v1.4s\n"
: [ret] "=w" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v1", "v2", "v3", "v4",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
@@ -191,11 +330,12 @@ static inline float inner_product_single(const float *a, const float *b, unsigne
" vadd.f32 d0, d0, d1\n"
" vpadd.f32 d0, d0, d0\n"
" vmov.f32 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+l" (len), [remainder] "+l" (remainder)
:
: "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11");
: "cc", "q0", "q1", "q2", "q3",
"q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11");
return ret;
}
#endif // defined(__aarch64__)
#endif

View File

@@ -71,7 +71,7 @@ static inline float interpolate_product_single(const float *a, const float *b, u
return ret;
}
#ifdef _USE_SSE2
#ifdef USE_SSE2
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE

View File

@@ -1,3 +1,176 @@
# 12.0.0 - 2025-09-17
- Optimized the default floating point formatting
(https://github.com/fmtlib/fmt/issues/3675,
https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a
`double` with format string compilation into a stack allocated buffer is
more than 60% faster in version 12.0 compared to 11.2 according to
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
```
Function Time (ns) Speedup
fmt11 34.471 1.00x
fmt12 21.000 1.64x
```
<img width="766" height="609" src="https://github.com/user-attachments/assets/d7d768ad-7543-468c-b0bb-449abf73b31b" />
- Added `constexpr` support to `fmt::format`. For example:
```c++
#include <fmt/compile.h>
using namespace fmt::literals;
std::string s = fmt::format(""_cf, 42);
```
now works at compile time provided that `std::string` supports `constexpr`
(https://github.com/fmtlib/fmt/issues/3403,
https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin.
- Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact
required size at compile time.
For example:
```c++
#include <fmt/compile.h>
constexpr auto s = FMT_STATIC_FORMAT("{}", 42);
```
compiles to just
```s
__ZL1s:
.asciiz "42"
```
It can be accessed as a C string with `s.c_str()` or as a string view with
`s.str()`.
- Improved C++20 module support
(https://github.com/fmtlib/fmt/pull/4451,
https://github.com/fmtlib/fmt/pull/4459,
https://github.com/fmtlib/fmt/pull/4476,
https://github.com/fmtlib/fmt/pull/4488,
https://github.com/fmtlib/fmt/issues/4491,
https://github.com/fmtlib/fmt/pull/4495).
Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort.
- Switched to using estimated display width in precision. For example:
```c++
fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱");
```
prints
![](https://github.com/user-attachments/assets/6c4446b3-13eb-43b9-b74a-b4543540ad6a)
because `🐱` has an estimated width of 2
(https://github.com/fmtlib/fmt/issues/4272,
https://github.com/fmtlib/fmt/pull/4443,
https://github.com/fmtlib/fmt/pull/4475).
Thanks @nikhilreddydev and @localspook.
- Fix interaction between debug presentation, precision, and width for strings
(https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook.
- Implemented allocator propagation on `basic_memory_buffer` move
(https://github.com/fmtlib/fmt/issues/4487,
https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat.
- Fixed an ambiguity between `std::reference_wrapper<T>` and `format_as`
formatters (https://github.com/fmtlib/fmt/issues/4424,
https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin.
- Removed the following deprecated APIs:
- `has_formatter`: use `is_formattable` instead,
- `basic_format_args::parse_context_type`,
`basic_format_args::formatter_type` and similar aliases in context types,
- wide stream overload of `fmt::printf`,
- wide stream overloads of `fmt::print` that take text styles,
- `is_*char` traits,
- `fmt::localtime`.
- Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`.
- Improved diagnostics for the incorrect usage of `fmt::ptr`
(https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter.
- Made handling of ANSI escape sequences more efficient
(https://github.com/fmtlib/fmt/pull/4511,
https://github.com/fmtlib/fmt/pull/4528).
Thanks @localspook and @Anas-Hamdane.
- Fixed a buffer overflow on all emphasis flags set
(https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko.
- Fixed an integer overflow for precision close to the max `int` value.
- Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496,
https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark.
- Fixed `back_insert_iterator` detection, preventing a fallback on slower path
that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454).
- Fixed handling of invalid glibc `FILE` buffers
(https://github.com/fmtlib/fmt/issues/4469).
- Added `wchar_t` support to the `std::byte` formatter
(https://github.com/fmtlib/fmt/issues/4479,
https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus.
- Changed component prefix from `fmt-` to `fmt_` for compatibility with
NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc`
(https://github.com/fmtlib/fmt/issues/4441,
https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein.
- Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom
`fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505).
Thanks @HazardyKnusperkeks.
- Switched to `FMT_THROW` on reporting format errors so that it can be
overriden by users when exceptions are disabled
(https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks.
- Improved master project detection and disabled install targets when using
{fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536).
Thanks @crueter.
- Made various code improvements
(https://github.com/fmtlib/fmt/pull/4445,
https://github.com/fmtlib/fmt/pull/4448,
https://github.com/fmtlib/fmt/pull/4473,
https://github.com/fmtlib/fmt/pull/4522).
Thanks @localspook, @tchaikov and @way4sahil.
- Added Conan instructions to the docs
(https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries.
- Removed Bazel files to avoid issues with downstream packaging
(https://github.com/fmtlib/fmt/pull/4530). Thanks @mering.
- Added more entries for generated files to `.gitignore`
(https://github.com/fmtlib/fmt/pull/4355,
https://github.com/fmtlib/fmt/pull/4512).
Thanks @dinomight and @localspook.
- Fixed various warnings and compilation issues
(https://github.com/fmtlib/fmt/pull/4447,
https://github.com/fmtlib/fmt/issues/4470,
https://github.com/fmtlib/fmt/pull/4474,
https://github.com/fmtlib/fmt/pull/4477,
https://github.com/fmtlib/fmt/pull/4471,
https://github.com/fmtlib/fmt/pull/4483,
https://github.com/fmtlib/fmt/pull/4515,
https://github.com/fmtlib/fmt/issues/4533,
https://github.com/fmtlib/fmt/pull/4534).
Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4,
@mattiasljungstrom and @FatihBAKIR.
# 11.2.0 - 2025-05-03
- Added the `s` specifier for `std::error_code`. It allows formatting an error
@@ -56,17 +229,18 @@
https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
- Added error reporting for duplicate named arguments
(https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
(https://github.com/fmtlib/fmt/issues/4282,
https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
(https://github.com/fmtlib/fmt/issues/4375,
https://github.com/fmtlib/fmt/issues/4394).
- Optimized `text_style` using bit packing
(https://github.com/fmtlib/fmt/pull/4363). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook.
- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
https://github.com/fmtlib/fmt/pull/4383). Thanks @LocalSpook.
https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook.
- Fixed a flush issue in `fmt::print` when using libstdc++
(https://github.com/fmtlib/fmt/issues/4398).
@@ -107,13 +281,14 @@
`float` (https://github.com/fmtlib/fmt/issues/3649).
- Moved `is_compiled_string` to the public API
(https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
(https://github.com/fmtlib/fmt/issues/4335,
https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
- Simplified implementation of `operator""_cf`
(https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook.
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
Thanks @LocalSpook.
Thanks @localspook.
- Fixed handling of BMI paths with the Ninja generator
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.

View File

@@ -4,8 +4,9 @@
[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.

View File

@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
@@ -212,7 +212,7 @@ template <typename Context> class dynamic_format_arg_store {
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110200
#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -201,14 +201,6 @@
# define FMT_NODISCARD
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
@@ -260,7 +252,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v11 {
inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
@@ -356,6 +348,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -396,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif
#ifdef FMT_USE_INT128
@@ -463,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
@@ -540,7 +536,7 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr.
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -616,19 +612,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
// DEPRECATED! Will be merged with is_char and moved to detail.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// Specifies if `T` is a character (code unit) type.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender;
using appender = basic_appender<char>;
@@ -781,7 +764,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
@@ -921,14 +904,47 @@ template <typename Char = char> class parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to std::locale to avoid the heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT
namespace detail {
// Specifies if `T` is a code unit type.
template <typename T> struct is_code_unit : std::false_type {};
template <> struct is_code_unit<char> : std::true_type {};
template <> struct is_code_unit<wchar_t> : std::true_type {};
template <> struct is_code_unit<char16_t> : std::true_type {};
template <> struct is_code_unit<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
#endif
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
@@ -1057,11 +1073,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
@@ -1069,7 +1085,7 @@ template <typename Char> struct named_arg_info {
int id;
};
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
@@ -1173,7 +1189,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>;
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1679,12 +1695,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public:
template <typename... T>
@@ -2033,6 +2049,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2047,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
@@ -2056,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
@@ -2176,7 +2209,7 @@ template <typename Context> class value {
static_assert(N <= 64, "unsupported _BitInt");
}
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2252,7 +2285,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x);
#endif
}
custom.format = format_custom<value_type, formatter<value_type, char_type>>;
custom.format = format_custom<value_type>;
}
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2263,10 +2296,10 @@ template <typename Context> class value {
}
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
template <typename T>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) {
auto f = Formatter();
auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2293,35 +2326,14 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2338,8 +2350,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type>
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2358,8 +2371,8 @@ struct named_arg_store {
}
named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
@@ -2372,7 +2385,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
@@ -2656,22 +2669,17 @@ class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
/// The character type for the output.
using char_type = char;
using char_type = char; ///< The character type for the output.
using iterator = appender;
using format_arg = basic_format_arg<context>;
using parse_context_type FMT_DEPRECATED = parse_context<>;
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
@@ -2692,7 +2700,7 @@ class context {
// Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
@@ -2779,9 +2787,6 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
@@ -2978,9 +2983,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY

View File

@@ -38,6 +38,7 @@ FMT_BEGIN_NAMESPACE
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
// DEPRECATED!
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
@@ -161,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
@@ -282,8 +272,6 @@ namespace detail {
#define FMT_NOMACRO
template <typename T = void> struct null {};
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); }
@@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
}
template <typename CodeUnit> struct codecvt_result {
static constexpr const size_t max_size = 32;
static constexpr size_t max_size = 32;
CodeUnit buf[max_size];
CodeUnit* end;
};
@@ -443,11 +431,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
using common_rep = typename std::common_type<FromRep, typename To::rep,
decltype(factor::num)>::type;
int ec = 0;
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
from.count(), ec);
if (ec) throw_duration_error();
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
@@ -458,6 +442,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
count, ec));
@@ -471,6 +456,8 @@ template <typename To, typename FromRep, typename FromPeriod,
std::is_floating_point<typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST
// Preserve infinity and NaN.
if (!isfinite(from.count())) return static_cast<To>(from.count());
// Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts.
int ec;
@@ -487,7 +474,7 @@ template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
// Mixed integer <-> float cast is not supported by safe duration_cast.
return std::chrono::duration_cast<To>(from);
}
@@ -501,86 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count();
}
namespace tz {
// DEPRECATED!
struct time_zone {
template <typename Duration, typename LocalTime>
auto to_sys(LocalTime) -> sys_time<Duration> {
return {};
}
};
template <typename... T> auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> void _tzset(T...) {}
} // namespace tz
// DEPRECATED!
inline void tzset_once() {
static bool init = []() {
using namespace tz;
_tzset();
return false;
}();
ignore_unused(init);
}
} // namespace detail
FMT_BEGIN_EXPORT
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
inline dispatcher(std::time_t t) : time_(t) {}
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
#if FMT_USE_LOCAL_TIME
template <typename Duration>
FMT_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
-> std::tm {
using namespace std::chrono;
using namespace detail::tz;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
}
#endif
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
@@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr const size_t len = 8;
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
char tmp[len];
std::memcpy(tmp, &digits, len);
@@ -1000,16 +911,16 @@ template <typename T>
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
bool set_tm_zone(T& time, char* tz) {
auto set_tm_zone(T& time, char* tz) -> bool {
time.tm_zone = tz;
return true;
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
bool set_tm_zone(T&, char*) {
auto set_tm_zone(T&, char*) -> bool {
return false;
}
inline char* utc() {
inline auto utc() -> char* {
static char tz[] = "UTC";
return tz;
}
@@ -2230,7 +2141,7 @@ template <typename Char> struct formatter<std::tm, Char> {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref();
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);

View File

@@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape {
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
buffer[size++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[size++] = static_cast<Char>('0' + value / 10u);
buffer[size++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
buffer[size++] = static_cast<Char>('m');
return;
}
@@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
size = 19;
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
@@ -411,26 +409,28 @@ template <typename Char> struct ansi_color_escape {
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
buffer[size++] = static_cast<Char>(';');
}
buffer[index++] = static_cast<Char>(0);
buffer[size - 1] = static_cast<Char>('m');
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
return buffer + size;
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
Char buffer[7u + 4u * num_emphases];
size_t size = 0;
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {

View File

@@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
@@ -41,18 +39,40 @@ namespace detail {
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
/**
* Converts a string literal into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires support
* for class types in constant template parameters (a C++20 feature).
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* using namespace fmt::literals;
* std::string s = fmt::format("{}"_cf, 42);
*/
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
namespace detail {
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
constexpr auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
template <typename... T> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name);
}
@@ -105,8 +125,8 @@ template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data);
}
};
@@ -115,8 +135,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
-> text<Char> {
return {{&s[pos], size}};
}
@@ -124,8 +144,8 @@ template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value;
return out;
}
@@ -133,7 +153,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
constexpr auto get_arg_checked(const Args&... args) -> const T& {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
@@ -146,13 +166,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
template <typename Char, typename V, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
@@ -170,10 +190,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
constexpr static auto try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
@@ -183,8 +203,8 @@ template <typename Char> struct runtime_named_field {
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
@@ -197,17 +217,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
template <typename Char, typename V, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
formatter<V, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
-> OutputIt {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
return fmt.format(get_arg_checked<V, N>(args...), ctx);
}
};
@@ -219,8 +239,8 @@ template <typename L, typename R> struct concat {
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
@@ -230,14 +250,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
@@ -270,8 +290,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
int next_arg_id) -> parse_specs_result<T, Char> {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -285,16 +305,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
constexpr auto on_auto() -> int {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
constexpr auto on_index(int id) -> int {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
constexpr auto on_name(basic_string_view<Char> id) -> int {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
@@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
template <typename CompiledFormat, typename... T,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const T&... args)
-> std::basic_string<Char> {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
template <typename OutputIt, typename CompiledFormat, typename... T,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt {
return cf.format(out, args...);
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
-> std::basic_string<typename S::char_type> {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -466,72 +487,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
return fmt::format(compiled, std::forward<T>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
return fmt::format_to(out, compiled, std::forward<T>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return buf.count();
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
void print(std::FILE* f, const S& fmt, T&&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
void print(const S& fmt, T&&... args) {
print(stdout, fmt, std::forward<T>(args)...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
template <size_t N> class static_format_result {
private:
char data[N];
public:
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
};
/**
* Formats arguments according to the format string `fmt_str` and produces
* a string of the exact required size at compile time. Both the format string
* and the arguments must be compile-time expressions.
*
* The resulting string can be accessed as a C string via `c_str()` or as
* a `fmt::string_view` via `str()`.
*
* **Example**:
*
* // Produces the static string "42" at compile time.
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
* const char* s = result.c_str();
*/
#define FMT_STATIC_FORMAT(fmt_str, ...) \
fmt::static_format_result< \
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
FMT_COMPILE(fmt_str), __VA_ARGS__)
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -22,7 +22,7 @@
#include "format.h"
#if FMT_USE_LOCALE
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale>
#endif
@@ -31,14 +31,49 @@
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
#else
namespace detail {
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
} // namespace detail
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
using namespace detail;
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
namespace detail {
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
@@ -79,33 +114,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#if FMT_MSC_VERSION || defined(__NVCC__)
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +181,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
}
// Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@@ -275,7 +282,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +377,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr const uint128_fallback pow10_significands[] = {
static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1044,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k];
#else
static constexpr const uint64_t powers_of_5_64[] = {
static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float).
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
return s;
}
// Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Removes trailing zeros and returns the number of zeros removed (double).
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
FMT_ASSERT(n != 0, "");
// This magic number is ceil(2^90 / 10^8).
constexpr uint64_t magic_number = 12379400392853802749ull;
auto nm = umul128(n, magic_number);
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
constexpr uint32_t ten_pow_8 = 100000000u;
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
auto n32 = static_cast<uint32_t>(n / ten_pow_8);
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
int num_zeros = remove_trailing_zeros(n32, 8);
n = n32;
return s;
return num_zeros;
}
// If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case
template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
decimal_fp<T> ret_value;
// Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
parse_format_string(fmt,
format_handler<>{parse_context<>(fmt), {out, args, loc}});
}
template <typename T> struct span {
@@ -1546,10 +1550,11 @@ template <typename F> class glibc_file : public file_base<F> {
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
return memchr(end, '\n', static_cast<size_t>(size));
}
void flush() { fflush_unlocked(this->file_); }
@@ -1573,7 +1578,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
@@ -1594,7 +1599,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size;
}
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else

View File

@@ -33,8 +33,8 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
// Generate a unique explicit instantiation in every translation unit using a
// tag type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
@@ -18,10 +18,6 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
@@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
@@ -46,7 +40,7 @@ template <typename Char> class basic_printf_context {
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto locale() -> locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
@@ -74,10 +68,9 @@ inline auto find<false, char>(const char* first, const char* last, char value,
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <bool IS_SIGNED> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
return value <= to_unsigned(max_value<int>());
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
@@ -95,7 +88,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
return max_of(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
};
const Char* start = parse_ctx.begin();
@@ -571,15 +566,19 @@ inline auto vsprintf(basic_string_view<Char> fmt,
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
-> std::wstring {
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
@@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
template <typename... T>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
}
/**
@@ -621,11 +617,6 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -11,7 +11,6 @@
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
@@ -31,7 +30,7 @@ template <typename T> class is_map {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -40,17 +39,16 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
template <typename T, size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
@@ -120,7 +118,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
static constexpr bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
@@ -170,7 +168,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{}));
public:
static constexpr const bool value =
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
@@ -208,7 +206,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
template <typename Tuple, typename Char, size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
@@ -219,7 +217,7 @@ template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
@@ -281,14 +279,15 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail
FMT_EXPORT
template <typename T> struct is_tuple_like {
static constexpr const bool value =
static constexpr bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
@@ -343,8 +342,9 @@ struct formatter<Tuple, Char,
}
};
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
@@ -368,6 +368,7 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -670,7 +671,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
FMT_EXPORT
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
@@ -685,15 +687,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -725,14 +727,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
@@ -754,7 +756,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep)
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}

View File

@@ -15,15 +15,13 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
# include <typeinfo> // std::type_info
# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
@@ -62,27 +60,26 @@
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#ifdef FMT_CPP_LIB_FILESYSTEM
// Use the provided definition.
#elif defined(__cpp_lib_filesystem)
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else
# define FMT_CPP_LIB_FILESYSTEM 0
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#ifdef FMT_CPP_LIB_VARIANT
// Use the provided definition.
#elif defined(__cpp_lib_variant)
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
#else
# define FMT_CPP_LIB_VARIANT 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
@@ -111,8 +108,168 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
}
}
#endif // FMT_CPP_LIB_FILESYSTEM
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
#endif
#if FMT_CPP_LIB_VARIANT
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename Variant, typename Char> class is_variant_formattable {
template <size_t... Is>
static auto check(std::index_sequence<Is...>) -> std::conjunction<
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
public:
static constexpr bool value = decltype(check(
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
};
#endif // FMT_CPP_LIB_VARIANT
#if FMT_USE_RTTI
template <typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<char>(out, string_view(ti.name()));
# endif
}
#endif // FMT_USE_RTTI
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr bool value = std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value &&
has_flip<T>::value;
};
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr bool value = true;
};
#endif
template <typename T, typename Enable = void>
struct has_format_as : std::false_type {};
template <typename T>
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct has_format_as_member : std::false_type {};
template <typename T>
struct has_format_as_member<
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
: std::true_type {};
} // namespace detail
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
#if FMT_CPP_LIB_FILESYSTEM
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
@@ -162,39 +319,20 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
template <std::size_t N, typename Char>
template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
// This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
for (auto pos = N; pos > 0; --pos)
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
@@ -209,10 +347,8 @@ struct formatter<std::bitset<N>, Char>
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@@ -251,30 +387,9 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
@@ -301,11 +416,9 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -323,42 +436,12 @@ template <> struct formatter<std::source_location> {
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
static constexpr bool value = detail::is_variant_like_<T>::value;
};
template <typename Char> struct formatter<std::monostate, Char> {
@@ -374,10 +457,10 @@ template <typename Char> struct formatter<std::monostate, Char> {
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
struct formatter<Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>,
detail::is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@@ -402,10 +485,9 @@ struct formatter<
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
@@ -459,101 +541,29 @@ template <> struct formatter<std::error_code> {
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
template <> struct formatter<std::type_info> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
return detail::write_demangled_name(ctx.out(), ti);
}
};
#endif
#endif // FMT_USE_RTTI
template <typename T, typename Char>
template <typename T>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
@@ -570,43 +580,15 @@ struct formatter<
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
return detail::write_bytes<char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
@@ -621,14 +603,6 @@ struct formatter<BitRef, Char,
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -715,7 +689,11 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
// Guard against format_as because reference_wrapper is
// implicitly convertible to T&.
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
!detail::has_format_as<T>::value &&
!detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -725,4 +703,5 @@ struct formatter<std::reference_wrapper<T>, Char,
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@@ -55,6 +55,16 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
#endif
return false;
}
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args,
locale_ref loc = {}) {
static_assert(!std::is_same<Char, char>::value, "");
auto out = basic_appender<Char>(buf);
parse_format_string(
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
}
} // namespace detail
FMT_BEGIN_EXPORT
@@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
@@ -151,13 +157,13 @@ auto join(std::initializer_list<T> list, wstring_view sep)
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
@@ -187,24 +193,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
@@ -215,7 +217,7 @@ template <typename OutputIt, typename S,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
basic_format_args<buffered_context<Char>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
@@ -231,27 +233,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
-> typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
@@ -260,7 +259,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);

View File

@@ -50,6 +50,8 @@ module;
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
#endif
#include <cerrno>

View File

@@ -8,6 +8,12 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
#if FMT_USE_LOCALE
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept
@@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref)
@@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);
// Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
// DEPRECATED!
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail

View File

@@ -66,14 +66,14 @@ using rwresult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
inline unsigned convert_rwcount(size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
@@ -185,7 +185,7 @@ void buffered_file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
int buffered_file::descriptor() const {
auto buffered_file::descriptor() const -> int {
#ifdef FMT_HAS_SYSTEM
// fileno is a macro on OpenBSD.
# ifdef fileno
@@ -240,7 +240,7 @@ void file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
long long file::size() const {
auto file::size() const -> long long {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
@@ -251,7 +251,7 @@ long long file::size() const {
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
FMT_THROW(windows_error(error, "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
@@ -266,7 +266,7 @@ long long file::size() const {
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
auto file::read(void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -274,7 +274,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
auto file::write(const void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -282,7 +282,7 @@ std::size_t file::write(const void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
file file::dup(int fd) {
auto file::dup(int fd) -> file {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
@@ -308,7 +308,7 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
if (result == -1) ec = std::error_code(errno, std::generic_category());
}
buffered_file file::fdopen(const char* mode) {
auto file::fdopen(const char* mode) -> buffered_file {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
@@ -355,7 +355,7 @@ pipe::pipe() {
}
# if !defined(__MSDOS__)
long getpagesize() {
auto getpagesize() -> long {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);

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

@@ -1,3 +1,19 @@
# v12.1.0
* add rc_client_get_user_subset_summary
* add validation warning for using MeasuredIf without Measured
* add validation warning for using ResetIf without hit targets
* add rapi function for update_rich_presence
* add gap to RC_CONSOLE_WII memory map to make it easier to convert pointers
* improve range validation logic
* fix error Remembering float value
* fix MeasuredIf evaluation in rich presence
* fix parsing of code notes with addresses above 0x7FFFFFFF
* fix double evaluation of rich presence parameters
* fix validation of SubSource chain
* fix error if rc_client_allow_background_memory_reads called before calling rc_client_begin_load_raintegration
* fix memory corruption when mixing legacy and new-format macros in rich presence
* fix invalid pointer reference when iterator gets cloned
# v12.0.0
* rc_client changes
* add RC_CLIENT_EVENT_SUBSET_COMPLETED event

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));
@@ -5810,7 +5880,7 @@ void rc_client_do_frame(rc_client_t* client)
richpresence = client->game->runtime.richpresence;
if (richpresence && richpresence->richpresence)
rc_update_richpresence(richpresence->richpresence, client->state.legacy_peek, client, NULL);
rc_update_richpresence_internal(richpresence->richpresence, client->state.legacy_peek, client);
rc_mutex_unlock(&client->state.mutex);

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

@@ -223,6 +223,8 @@ static void rc_client_init_raintegration(rc_client_t* client,
external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client));
if (external_client->set_spectator_mode_enabled)
external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client));
if (external_client->set_allow_background_memory_reads)
external_client->set_allow_background_memory_reads(client->state.allow_background_memory_reads);
/* attach the external client and call the callback */
client->state.external_client = external_client;

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

@@ -8,7 +8,7 @@
RC_BEGIN_C_DECLS
#define RCHEEVOS_VERSION_MAJOR 12
#define RCHEEVOS_VERSION_MINOR 0
#define RCHEEVOS_VERSION_MINOR 1
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)

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;
}
@@ -603,8 +607,8 @@ static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state
eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
}
static void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
const rc_condition_t* condition_end = condition + num_conditions;
for (; condition < condition_end; ++condition) {
switch (condition->type) {

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

@@ -311,6 +311,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
void rc_reset_condset(rc_condset_t* self);
rc_condition_t* rc_condset_get_conditions(rc_condset_t* self);
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, rc_eval_state_t* eval_state, int can_short_circuit);
enum {
RC_PROCESSING_COMPARE_DEFAULT = 0,
@@ -379,6 +380,7 @@ int rc_lboard_state_active(int state);
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);

View File

@@ -133,79 +133,93 @@ static uint32_t rc_max_value(const rc_operand_t* operand)
}
}
static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* operand)
static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper, uint32_t oper_min_val, uint32_t oper_max_val)
{
switch (oper) {
case RC_OPERATOR_MULT:
{
unsigned long long scaled = ((unsigned long long)value) * rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
unsigned long long scaled = ((unsigned long long)*min_val) * oper_min_val;
*min_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
return (uint32_t)scaled;
scaled = ((unsigned long long)*max_val) * oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
break;
}
case RC_OPERATOR_DIV:
{
const uint32_t min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return value / min_val;
}
*min_val = (oper_max_val == 0) ? *min_val : (*min_val / oper_max_val);
*max_val = (oper_min_val == 0) ? *max_val : (*max_val / oper_min_val);
break;
case RC_OPERATOR_AND:
return rc_max_value(operand);
*min_val = 0;
*max_val &= oper_max_val;
break;
case RC_OPERATOR_XOR:
return value | rc_max_value(operand);
*min_val = 0;
*max_val |= oper_max_val;
break;
case RC_OPERATOR_MOD:
{
const uint32_t divisor = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return (divisor >= value) ? (divisor - 1) : value;
}
*min_val = 0;
*max_val = (*max_val >= oper_max_val) ? oper_max_val - 1 : *max_val;
break;
case RC_OPERATOR_ADD:
{
unsigned long scaled = ((unsigned long)value) + rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
if (*min_val > *max_val) { /* underflow occurred */
*max_val += oper_max_val;
}
else {
unsigned long scaled = ((unsigned long)*max_val) + oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
}
return (uint32_t)scaled;
}
*min_val += oper_min_val;
break;
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)
return value - op_max;
return 0xFFFFFFFF;
}
*min_val -= oper_max_val;
*max_val -= oper_min_val;
break;
case RC_OPERATOR_SUB_PARENT:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand);
if (op_max > value)
return op_max - value;
return 0xFFFFFFFF;
uint32_t temp = oper_min_val - *max_val;
*max_val = oper_max_val - *min_val;
*min_val = temp;
break;
}
default:
return value;
break;
}
}
static uint32_t rc_max_chain_value(const rc_operand_t* operand)
static void rc_chain_get_value_range(const rc_operand_t* operand, uint32_t* min_val, uint32_t* max_val)
{
if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref;
if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) {
const uint32_t op_max = rc_max_chain_value(&modified_memref->parent);
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier);
if (modified_memref->modifier_type == RC_OPERATOR_DIV &&
rc_operand_is_memref(&modified_memref->modifier) &&
rc_operands_are_equal(&modified_memref->modifier, &modified_memref->parent)) {
/* division by self can only return 0 or 1. */
*min_val = 0;
*max_val = 1;
}
else {
uint32_t modifier_min_val, modifier_max_val;
rc_chain_get_value_range(&modified_memref->parent, min_val, max_val);
rc_chain_get_value_range(&modified_memref->modifier, &modifier_min_val, &modifier_max_val);
rc_combine_ranges(min_val, max_val, modified_memref->modifier_type, modifier_min_val, modifier_max_val);
}
return;
}
}
return rc_max_value(operand);
*min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
*max_val = rc_max_value(operand);
}
static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition)
@@ -291,7 +305,7 @@ static int rc_validate_range(uint32_t min_val, uint32_t max_val, char oper, uint
return 1;
}
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address)
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address, int has_hits)
{
const rc_condition_t* cond;
char buffer[128];
@@ -299,6 +313,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
int in_add_hits = 0;
int in_add_address = 0;
int is_combining = 0;
int has_measured = 0;
int measuredif_index = -1;
if (!condset) {
*result = '\0';
@@ -351,11 +367,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;
}
@@ -383,6 +399,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
is_combining = 0;
break;
}
if (!has_hits) {
snprintf(result, result_size, "Condition %d: No captured hits to reset", index);
return 0;
}
if (cond->required_hits == 1) {
snprintf(result, result_size, "Condition %d: Hit target of 1 is redundant on ResetIf", index);
return 0;
@@ -398,6 +418,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
in_add_hits = 0;
}
has_measured |= (cond->type == RC_CONDITION_MEASURED);
if (cond->type == RC_CONDITION_MEASURED_IF && measuredif_index == -1)
measuredif_index = index;
is_combining = 0;
break;
}
@@ -424,10 +448,16 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index);
const rc_operand_t* operand2 = &cond->operand2;
uint8_t oper = cond->oper;
uint32_t max = rc_max_chain_value(operand1);
uint32_t min, max;
uint32_t max_val = rc_max_value(operand2);
uint32_t min_val;
rc_chain_get_value_range(operand1, &min, &max);
if (min > max) { /* underflow */
min = 0;
max = 0xFFFFFFFF;
}
if (!is_memref1) {
/* pretend constant was on right side */
operand2 = operand1;
@@ -477,6 +507,7 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
break;
}
/* min_val and max_val are the range allowed by operand2. max is the upper value from operand1. */
if (!rc_validate_range(min_val, max_val, oper, max, result + prefix_length, result_size - prefix_length))
return 0;
}
@@ -487,19 +518,48 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
return 0;
}
if (measuredif_index != -1 && !has_measured) {
snprintf(result, result_size, "Condition %d: MeasuredIf without Measured", measuredif_index);
return 0;
}
*result = '\0';
return 1;
}
static int rc_condset_has_hittargets(const rc_condset_t* condset)
{
if (condset->num_hittarget_conditions > 0)
return 1;
/* pause and reset conditions may have hittargets and won't be classified as hittarget conditions.
* measured conditions may also have hittargets.
*/
if (condset->num_pause_conditions || condset->num_reset_conditions || condset->num_measured_conditions) {
const rc_condition_t* condition = rc_condset_get_conditions((rc_condset_t*)condset);
/* ASSERT: don't need to add num_hittarget_conditions because it must be 0 per earlier check */
const rc_condition_t* stop = condition + condset->num_pause_conditions
+ condset->num_reset_conditions + condset->num_measured_conditions;
for (; condition < stop; ++condition) {
if (condition->required_hits)
return 1;
}
}
return 0;
}
int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t max_address)
{
return rc_validate_condset_internal(condset, result, result_size, 0, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, 0, max_address, has_hits);
}
int rc_validate_condset_for_console(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id)
{
const uint32_t max_address = rc_console_max_address(console_id);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address, has_hits);
}
static int rc_validate_is_combining_condition(const rc_condition_t* condition)
@@ -721,7 +781,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;
@@ -845,7 +905,7 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co
switch (compare_condition->type)
{
case RC_CONDITION_PAUSE_IF:
if (conditions != compare_conditions)
if (conditions != compare_conditions) /* PauseIf only affects conditions in same group */
break;
/* fallthrough */
case RC_CONDITION_RESET_IF:
@@ -895,7 +955,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,40 +1015,49 @@ 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 && rc_condset_has_hittargets(trigger->requirement);
if (!has_hits) {
for (alt = trigger->alternative; alt; alt = alt->next) {
if (rc_condset_has_hittargets(alt)) {
has_hits = 1;
break;
}
}
}
if (!trigger->alternative) {
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address, has_hits))
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 ");
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address, has_hits))
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;
for (alt = trigger->alternative; alt; alt = alt->next, ++index) {
char altname[16];
const size_t prefix_length = snprintf(result, result_size, "Alt%d ", index);
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address))
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address, has_hits))
return 0;
/* 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;
@@ -722,14 +723,19 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self) {
}
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, void* unused_L) {
rc_richpresence_display_t* display;
(void)unused_L;
rc_update_richpresence_memrefs(richpresence, peek, peek_ud);
rc_update_values(richpresence->values, peek, peek_ud);
rc_update_richpresence_internal(richpresence, peek, peek_ud);
}
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud) {
rc_richpresence_display_t* display;
for (display = richpresence->first_display; display; display = display->next) {
if (display->has_required_hits)
rc_test_trigger(&display->trigger, peek, peek_ud, unused_L);
rc_test_trigger(&display->trigger, peek, peek_ud, NULL);
}
}

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

@@ -47,7 +47,10 @@ add_subdirectory(common)
# make pcsx2
add_subdirectory(pcsx2)
add_subdirectory(pcsx2-qt)
if(ENABLE_QT_UI)
add_subdirectory(pcsx2-qt)
endif()
# Updater is Windows only for now.
if (WIN32)
@@ -62,9 +65,6 @@ endif()
# gsrunner
if(ENABLE_GSRUNNER)
if (NOT WIN32 AND NOT APPLE)
message(WARNING "GSRunner is only supported on Windows and macOS and may not build on your system")
endif()
add_subdirectory(pcsx2-gsrunner)
else()
add_subdirectory(pcsx2-gsrunner EXCLUDE_FROM_ALL)

Binary file not shown.

View File

@@ -1,58 +0,0 @@
†---------------------†
Disassembly view:
†---------------------†
Ctrl + G Goto
Ctrl + E Edit Breakpoint
Ctrl + D Enable/disable breakpoint
Ctrl + B Add breakpoint
Left Go back one branch level/goto pc
Right Follow branch/position memory view to accessed address
Up Move cursor up one line
Down Move cursor down one line
Page Up Move visible area up one page
Page Down Move visible area down one page
F10 Step over
F11 Step into
Tab Toggle display symbols
Left Click Select line/toggle breakpoint if line is already highlighted
Right Click Open context menu
†---------------------†
Memory View:
†---------------------†
Ctrl + G Goto
Ctrl + B Add breakpoint
Left Move cursor back one byte/nibble
Right Move cursor ahead one byte/nibble
Up Move cursor up one line
Down Move cursor down one line
Page Up Move cursor up one page
Page Down Move cursor down one page
0-9,A-F Overwrite hex nibble
Any Overwrite ansi byte
Left Click Select byte/nibble
Right Click Open context menu
Ctrl+Wheel Zoom memory view
Esc Return to previous goto address
Ctrl+V Paste a hex string into memory
†---------------------†
Breakpoint List:
†---------------------†
Up Select previous item
Down Select next item
Delete Remove selected breakpoint
Return Edit selected breakpoint
Space Toggle enable state of selected breakpoint
†---------------------†

File diff suppressed because it is too large Load Diff

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