fix(core): drive auto-lock idle from real input, not widget focus
The auto-lock idle clock was reset whenever any ImGui item was active, so a text field left focused (a half-filled send form, say) counted as continuous activity and kept the wallet unlocked indefinitely, well past the configured idle timeout. - The idle clock is now fed by real input events (key, text, mouse, touch, pen, gamepad button) as they are ingested on the main thread, plus a still-held mouse button so a drag or scrollbar grab that emits no new event still counts. A focused but untouched widget no longer does. - A new header-only IdleTracker owns the clock and never moves backwards, so an event stamped before the previous one cannot shorten the idle time. Behaviour change: a form left focused and idle past the timeout now auto-locks. The form's buffers are app state and survive the lock and unlock, so nothing typed is lost. Tests: a new IdleTracker case covers idle time growing with no events (enough to trip a 900s lock), activity resetting it, the clock never moving backwards, and a query before the last activity reading zero rather than a negative value. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,7 @@
|
||||
#include "util/seed_phrase.h"
|
||||
#include "util/local_time.h"
|
||||
#include "util/text_fit.h"
|
||||
#include "util/idle_tracker.h"
|
||||
#include "util/image_decode_core.h"
|
||||
#include "daemon/daemon_startup_diagnosis.h"
|
||||
#include "util/amount_format.h"
|
||||
@@ -1132,6 +1133,26 @@ void testSpendableFiltering()
|
||||
}
|
||||
|
||||
// S8: deferFor() is an absolute hold, independent of the page interval (the retry-storm fix).
|
||||
void testIdleTracker()
|
||||
{
|
||||
using dragonx::util::IdleTracker;
|
||||
using Clock = IdleTracker::Clock;
|
||||
const auto t0 = Clock::now();
|
||||
IdleTracker idle(t0);
|
||||
EXPECT_NEAR(idle.idleSeconds(t0), 0.0, 1e-9);
|
||||
// No events: idle time keeps growing, however long a (conceptual) field stays focused.
|
||||
EXPECT_NEAR(idle.idleSeconds(t0 + std::chrono::seconds(900)), 900.0, 1e-6);
|
||||
EXPECT_TRUE(idle.idleSeconds(t0 + std::chrono::seconds(901)) >= 900.0); // would trip a 900 s auto-lock
|
||||
// Activity resets it.
|
||||
idle.noteActivity(t0 + std::chrono::seconds(600));
|
||||
EXPECT_NEAR(idle.idleSeconds(t0 + std::chrono::seconds(900)), 300.0, 1e-6);
|
||||
// Never moves backwards.
|
||||
idle.noteActivity(t0 + std::chrono::seconds(100));
|
||||
EXPECT_NEAR(idle.idleSeconds(t0 + std::chrono::seconds(900)), 300.0, 1e-6);
|
||||
// A query before the last activity is 0, not negative.
|
||||
EXPECT_NEAR(idle.idleSeconds(t0 + std::chrono::seconds(10)), 0.0, 1e-9);
|
||||
}
|
||||
|
||||
void testRefreshSchedulerDeferFor()
|
||||
{
|
||||
using dragonx::services::RefreshScheduler;
|
||||
@@ -17475,6 +17496,7 @@ int main()
|
||||
testAmountFormatting();
|
||||
testSpendableFiltering();
|
||||
testRefreshScheduler();
|
||||
testIdleTracker();
|
||||
testRefreshSchedulerDeferFor();
|
||||
testRpcBusyGate();
|
||||
testIsLongWalletMethod();
|
||||
|
||||
Reference in New Issue
Block a user