Add Keep Nvidia Instant Replay on mod - #5030
Conversation
Watches ShadowPlay's Instant Replay state and switches it back on whenever something turns it off, so Instant Replay isn't quietly disabled when you needed the clip you didn't get. The state is read from HKCU and watched with RegNotifyChangeKeyValue, so the mod reacts the moment the state changes rather than only on a poll. Changing it is done by replaying the user's configured Toggle Instant Replay shortcut. Optional process lists can pause the mod, or restrict Instant Replay to only be on while chosen programs run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for the pull request! This repository uses a two-stage review: an AI review that you run yourself, followed by a human review. To get started, comment See the pull request review process for the full details. |
|
/ai-review |
|
/ai-review |
|
@dixxi1208 |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. The mod is well written and the README is unusually clear about what it does and why. The main thing to reconsider is where it runs. 1. This should be a tool mod running in The mod installs no hooks at all - it only uses
There's also a smaller angle: The conversion is mechanical: The one thing worth verifying before you convert: 2. If the mod does stay in Windhawk injects into session 0 services, and NVIDIA runs an BOOL Wh_ModInit() {
DWORD sessionId;
if (ProcessIdToSessionId(GetCurrentProcessId(), &sessionId) && sessionId == 0) {
return FALSE;
}
...
}3.
bool shouldBeOn = settings.onlyWhileRunning.empty() ||
IsAnyProcessRunning(settings.onlyWhileRunning);so a failed Return a tri-state (or an static bool IsAnyProcessRunning(const std::vector<std::wstring>& patterns, bool* enumerated) {
*enumerated = true;
...
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) {
*enumerated = false;
return false;
}
...
}and in the loop, 4. Cross-reference the existing ShadowPlay mod. mods/shadowplay-do-not-disable.wh.cpp already targets Optional improvements
Minor polish - none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
Review feedback from the windhawk-mods submission: - Run as a tool mod in a dedicated windhawk.exe process instead of injecting into nvcontainer.exe. The mod installs no hooks and reads no NvContainer state, so there was no reason to be in-process. This removes the hand-rolled single-instance mutex, the per-instance watchdog thread and registry notification in every nvcontainer.exe, and the ambiguity about which user's hive HKCU refers to. - IsAnyProcessRunning now returns std::optional<bool> so a failed process enumeration is distinguishable from nothing-matched. Previously a transient CreateToolhelp32Snapshot failure with a non-empty onlyWhileRunning list would make the mod switch Instant Replay off while a game was running. - Backing off no longer blinds the mod: it keeps reading the state and clears the backoff as soon as Instant Replay is where it should be, so a resolved conflict recovers immediately instead of after the full wait. - Backoff is now exponential, 30s doubling to the configured maximum, so a one-off failure costs seconds rather than the full period. - Program lists match the exact file name unless the entry contains a backslash, in which case it is a path substring. Avoids notepad matching notepad++.exe. - Case-insensitive matching uses CompareStringOrdinal instead of towlower, which only folds ASCII under the default locale. - Use WindhawkUtils::StringSetting and std::clamp; close the registry key before the notification event it refers to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice submission overall — the tool-mod conversion is the right call, the README already explains how this differs from 1. The mod can press the shortcut a second time before the first press has taken effect, undoing its own toggle. After The result is a double press that cancels itself, then a mismatch on the next cycle, then Confirm the transition before counting the attempt as failed: // Returns true if the state reached `expected` before the timeout. `*stop` is
// set when the mod is shutting down, in which case the caller must break out.
static bool WaitForState(InstantReplayState expected, DWORD timeoutMs, bool* stop) {
ULONGLONG deadline = GetTickCount64() + timeoutMs;
for (;;) {
if (GetInstantReplayState() == expected) {
return true;
}
ULONGLONG now = GetTickCount64();
if (now >= deadline) {
return false;
}
if (StopRequested((DWORD)std::min<ULONGLONG>(500, deadline - now))) {
*stop = true;
return false;
}
}
}and after the press, only 2. With The setting description advertises At minimum, don't offer "never back off": 3. The default-shortcut fallback blindly presses the combination the README tells users to avoid. When Since a missing 4. For each entry in the snapshot the code calls Decide once per call whether any pattern needs the path: bool needsPath = std::any_of(patterns.begin(), patterns.end(), [](const std::wstring& p) {
return p.find(L'\\') != std::wstring::npos;
});and skip the 5. The tool-mod boilerplate isn't a verbatim copy of the wiki snippet. The launcher block differs from https://github.com/ramensoftware/windhawk/wiki/Mods-as-tools:-Running-mods-in-a-dedicated-process in a handful of places — Optional improvements
Minor polish — none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
Second round of review feedback from the windhawk-mods submission: - Confirm the state actually changed after pressing the shortcut, with an 8 second WaitForState, instead of relying on a 3 second minimum interval. Starting the Instant Replay ring buffer isn't instant, and a notification for one of the other values in the key could arrive while the state value was still stale, so the mod could press again and undo its own toggle. The toggle streak now counts real failures only. - Remove the never-back-off option. The shortcut can fail in ways the mod cannot detect (overlay off, UIPI silently dropping the input), so an unbounded retry meant injecting keystrokes into the user's session forever. The backoff minimum is now 30s, and after a few rounds at the configured maximum the mod gives up until the state or the settings change. - Don't guess at Nvidia's Alt+Shift+F10 default when no shortcut is configured. That was the one case with no evidence a shortcut exists, and it's the combination the readme tells users to avoid because Alt+Shift cycles the keyboard layout. The mod now presses nothing and logs why. - Only resolve full process paths when a pattern actually needs one. The OpenProcess sweep over every process ran on every cycle even though the exact-file-name form only needs entry.szExeFile. - Validate the virtual-key codes read from the registry before feeding them to SendInput. - Paste the tool mod snippet verbatim from the wiki. The previous copy came from net-toggle.wh.cpp, which carries a locally modified version using the W-suffixed API names. - Retry opening the registry key inside the loop, so configuring ShadowPlay after the mod is running still gets immediate reactions instead of silently degrading to polling. Log when the key or event isn't available. - Simplify GetInstantReplayState to RRF_RT_DWORD, which already accepts the REG_BINARY form and enforces the size. - Minimum check interval raised to 5s. - Document the key-ups releasing physically held keys, that Instant Replay's state persists after the mod is disabled, and that backslash patterns can't match processes the mod can't open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. The mod is in good shape: the tool-mod boilerplate is a verbatim copy of the wiki snippet, every declared setting is read with the matching API and nothing is dead, the watchdog thread is signalled and joined in 1. The "gave up" state can't be cleared by the fix the mod itself asks the user to make. Once The same latch also makes the mod permanently useless after a purely transient inability to inject - a workstation locked for an hour or so with Instant Replay off burns the whole ladder (~75 min to Re-arm on the thing the log names. Split the shortcut read out of // Reads IRToggleHKeyCount + IRToggleHKey<n>; empty when none is configured.
static std::vector<WORD> ReadToggleHotkey();
std::vector<WORD> hotkeyAtGiveUp; // captured where gaveUp is set
...
if (gaveUp) {
if (ReadToggleHotkey() == hotkeyAtGiveUp) {
continue;
}
// The shortcut changed - the user acted on the log message, try again.
gaveUp = false;
toggleStreak = 0;
currentBackoffSec = 0;
roundsAtMaxBackoff = 0;
}Optionally also re-arm on a long timer (say once every few hours). That's still bounded - it's not the unbounded keystroke injection that the earlier round asked you to remove - and it covers the causes the mod can't observe, like the overlay being switched back on. 2. License: MIT vs AlwaysShadow's GPL-3.0.
Optional improvements
Minor polish - none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
Third round of review feedback from the windhawk-mods submission: - The give-up latch could not be cleared by the fix its own log message asks for. Configuring a Toggle Instant Replay shortcut, or switching the In-Game Overlay back on, left the mod inert until it was disabled and re-enabled. The shortcut is now read separately, captured when the mod gives up, and compared each cycle, so changing it re-arms immediately. A periodic retry every few hours covers the causes the mod cannot observe. - Don't count failures that were never the mod's fault. A locked session or a UAC secure desktop means injected input can't reach anything, so the cycle is skipped via OpenInputDesktop instead of timing out and driving the backoff ladder toward giving up. - Don't press while the user is holding any of the shortcut's keys or any modifier. The combination Nvidia would see isn't the configured one, so the toggle fails and counts as a conflict, and the key-ups steal keys the user is actually using. - A missing shortcut no longer counts as a failed attempt, since there was no attempt, and its log message is latched like the other recurring ones. - Set wScan and KEYEVENTF_EXTENDEDKEY so shortcuts containing extended keys (arrows, Insert/Delete/Home/End/PgUp/PgDn, right Alt/Ctrl, numpad) describe the right physical key. - ContainsNoCase uses a single FindNLSStringEx call instead of one CompareStringOrdinal per character offset. - QueryFullProcessImageNameW gets a 1024 WCHAR buffer, so paths longer than MAX_PATH no longer fall back silently to the bare file name. - Name the accepted ranges for pollIntervalSec and conflictBackoffSec in their descriptions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/ai-review |
|
This pull request has already had 3 AI reviews in the last 24 hours, which is the limit, so no review was posted this time. Comment |
Adds
nvidia-keep-instant-replay-on, a mod that turns Nvidia ShadowPlay's Instant Replay back on whenever something switches it off.Instant Replay tends to quietly switch itself off after a driver update, a crash, a stray hotkey or a game that toggles it, and you only find out when you needed the clip you didn't get.
How it works. The state is read from
HKCU\SOFTWARE\NVIDIA Corporation\Global\ShadowPlay\NVSPCAPSand the key is watched withRegNotifyChangeKeyValue, so the mod reacts the moment the state changes rather than only on a poll. Changing it is done by replaying the user's configured Toggle Instant Replay shortcut, read fresh from the same key each time. Optional process lists can pause the mod, or restrict Instant Replay to only be on while chosen programs run.Windhawk injects into every
nvcontainer.exe; a session-local named mutex elects a single instance to do the work, and instances running under other accounts can't read the state and stay out of the way on their own.Same goal as AlwaysShadow, but as a mod so there's no extra program to install or keep running. AlwaysShadow prefers a local HTTP server hosted by NvContainer and only falls back to the shortcut; that server came from GeForce Experience's NvNode backend and isn't present on the Nvidia App, so this mod uses the shortcut only, which works on both.
Source repository: https://github.com/dixxi1208/NvidiaInstantReplayFix
Changelog
If this pull request updates an existing mod, describe the changes below:
Not applicable — this is a new mod, not an update to an existing one.
Mod authorship
If this pull request introduces a new mod, please complete the section below.
This mod was created by:
Please select the options that best apply. Your selection does not affect the acceptance criteria, but it helps reviewers understand the context of the code and provide relevant feedback.