Files
ObsidianDragon/src/util/idle_tracker.h
T
DanSandClaude Opus 4.8 0aaf33ecf0 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>
2026-10-12 21:42:23 -05:00

23 lines
737 B
C++

// Idle tracking for the auto-lock: pure, header-only, main-thread only.
#pragma once
#include <chrono>
namespace dragonx {
namespace util {
class IdleTracker {
public:
using Clock = std::chrono::steady_clock;
explicit IdleTracker(Clock::time_point start = Clock::now()) : last_(start) {}
// Never moves backwards (an event stamped before the last one must not shorten the idle time).
void noteActivity(Clock::time_point t = Clock::now()) { if (t > last_) last_ = t; }
double idleSeconds(Clock::time_point now = Clock::now()) const {
return now <= last_ ? 0.0 : std::chrono::duration<double>(now - last_).count();
}
private:
Clock::time_point last_;
};
} // namespace util
} // namespace dragonx