fix(windows): recover tray and hooks after system sleep/hibernate#94
fix(windows): recover tray and hooks after system sleep/hibernate#94debugtheworldbot merged 7 commits intomainfrom
Conversation
- Rename _lastMouseHookTick → _lastHookCallbackTick, update in keyboard hook callback too (prevents false watchdog reinstalls during keyboard-only use) - Move StatsManager.HandleSystemResume to Task.Run (avoids blocking UI thread with sync file I/O) - Remove redundant SaveStats/SaveHistorySnapshot calls in HandleSystemResume and SynchronizeCurrentDay - Fix constructor double-save: only call UpdateNotificationBaselines+SaveStats when date hasn't changed - Add _midnightTimerLock to prevent race between resume recovery and midnight timer callback - Clean up hook thread on StartHookThread failure, use 'using' for ManualResetEventSlim
There was a problem hiding this comment.
Pull request overview
This PR improves Windows reliability after sleep/hibernate/lock/unlock by proactively recovering input hooks, resynchronizing daily stats, and rebuilding tray integration when Explorer restarts.
Changes:
- Add Windows system event handlers (power/session) and a TaskbarCreated watcher to recreate tray integration after resume or Explorer restarts.
- Add hook-thread restart/retry logic and broaden watchdog detection to include keyboard activity, plus additional transient-state resets for input monitoring.
- Add stats “current day” synchronization on midnight and system resume, with a lock around midnight timer rescheduling.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| KeyStats.Windows/KeyStats/App.xaml.cs | Register power/session handlers and TaskbarCreated watcher; rebuild tray integration on resume/Explorer restart. |
| KeyStats.Windows/KeyStats/Services/InputMonitorService.cs | Add resume handling + hook recovery retries/timeouts; refactor hook thread lifecycle; expand watchdog signal sources. |
| KeyStats.Windows/KeyStats/Services/StatsManager.cs | Centralize “current day” sync; reschedule midnight timer safely; add resume entry point. |
| KeyStats.Windows/KeyStats/Helpers/NativeInterop.cs | Add RegisterWindowMessage P/Invoke for TaskbarCreated detection. |
| .agents/skills/posthog-cli-queries/SKILL.md | Add agent “skill” documentation for querying PostHog via CLI/scripts. |
| .agents/skills/posthog-cli-queries/scripts/dashboard_list.sh | Script to list PostHog dashboards using local credentials. |
| .agents/skills/posthog-cli-queries/scripts/dashboard_fetch.sh | Script to fetch a PostHog dashboard JSON or a summary view. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Code Review
This pull request introduces a new PostHog CLI skill for querying project data and enhances the Windows application's resilience to system events. Key improvements include handling power state changes (suspend/resume), session locks, and taskbar restarts to ensure the tray icon and input hooks remain functional. The InputMonitorService and StatsManager were refactored to support these recovery flows. Review feedback highlights opportunities to improve the robustness of JSON parsing in the dashboard script and to optimize performance by removing an unused, expensive history cloning operation.
- Remove dead historySnapshot variable from SynchronizeCurrentDay - Use TryStopHookThread in ReinstallHooks for consistent thread cleanup - Narrow _pressedKeys lock scope: only guard HashSet.Add/Remove, keep GetKeyName and P/Invoke outside - Replace DateTime _lastResumeRecoveryUtc with long ticks + Interlocked for thread-safe debounce - Add null safety to jq result length in dashboard_fetch.sh
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| curl -fsS \ | ||
| -H "Authorization: Bearer ${token}" \ |
There was a problem hiding this comment.
This script passes the PostHog API token directly on the curl command line via -H "Authorization: Bearer ...", which can be exposed to other local users via process listings. Consider supplying headers via a temporary config/header file (e.g., curl --config / -H @file) or another mechanism that doesn’t put the token in argv.
| curl -fsS \ | |
| -H "Authorization: Bearer ${token}" \ | |
| header_file="$(mktemp)" | |
| trap 'rm -f "${header_file}"' EXIT | |
| chmod 600 "${header_file}" | |
| printf 'Authorization: Bearer %s\n' "${token}" > "${header_file}" | |
| curl -fsS \ | |
| -H @"${header_file}" \ |
| response="$( | ||
| curl -fsS \ | ||
| -H "Authorization: Bearer ${token}" \ |
There was a problem hiding this comment.
This script passes the PostHog API token directly on the curl command line via -H "Authorization: Bearer ...", which can be exposed to other local users via process listings. Consider supplying headers via a temporary config/header file (e.g., curl --config / -H @file) or another mechanism that doesn’t put the token in argv.
| response="$( | |
| curl -fsS \ | |
| -H "Authorization: Bearer ${token}" \ | |
| header_file="$(mktemp)" | |
| chmod 600 "${header_file}" | |
| trap 'rm -f "${header_file}"' EXIT | |
| printf 'Authorization: Bearer %s\n' "${token}" > "${header_file}" | |
| response="$( | |
| curl -fsS \ | |
| -H @"${header_file}" \ |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
SystemEvents.PowerModeChanged) and session switch listener to proactively recover hooks and trayTaskbarCreatedmessage) to prevent orphaned tray iconsChanges
System event handling (
App.xaml.cs)PowerModeChanged/SessionSwitchevent handlersHandleSystemResume()runs on background thread to avoid blocking UITaskbarCreatedWatcher(NativeWindow) to listen for Explorer taskbar recreationRecreateTrayIntegration(), use named methods for event handlersInput monitor recovery (
InputMonitorService.cs)HandleSystemResume()with retry logic (3 attempts × 750ms interval)StartHookThread()/TryStopHookThread()/ResetTransientState()readyEvent.Wait, clean up thread state on failureHasRecentKeyboardActivity), not just mouse movement_lastMouseHookTick→_lastHookCallbackTick, update in both keyboard and mouse callbackslockprotection for_pressedKeysto fix race condition withResetTransientStateDate synchronization (
StatsManager.cs)HandleSystemResume()callingSynchronizeCurrentDay()+ reschedule midnight timer_midnightTimerLockto prevent race between resume recovery and midnight timer callbackSaveStats/SaveHistorySnapshotcallsNative API (
NativeInterop.cs)RegisterWindowMessage/GetAsyncKeyStateP/Invoke declarationsTest plan
🤖 Generated with Claude Code