Skip to content

Instantly share code, notes, and snippets.

@jnm2
Created October 1, 2021 14:26
Show Gist options
  • Save jnm2/245e67a574cc8af2ddaeaf97e06a1bb9 to your computer and use it in GitHub Desktop.
Save jnm2/245e67a574cc8af2ddaeaf97e06a1bb9 to your computer and use it in GitHub Desktop.
// Csproj: <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.1.588-beta" PrivateAssets="all" />
// NativeMethods.txt: GetLastInputInfo
using System;
using System.Threading;
using Windows.Win32;
using Windows.Win32.UI.KeyboardAndMouseInput;
public sealed class InactivityWatcher : IDisposable
{
private readonly TimeSpan inactivityDuration;
private readonly Timer timer;
private bool stopOrStartWasCalled;
public event EventHandler Inactive;
public InactivityWatcher(TimeSpan inactivityDuration)
{
this.inactivityDuration = inactivityDuration;
timer = new(OnTimerCallback);
}
public void Dispose() => timer.Dispose();
public void Restart()
{
stopOrStartWasCalled = true;
timer.Change(dueTime: inactivityDuration, period: Timeout.InfiniteTimeSpan);
}
public void Stop()
{
stopOrStartWasCalled = true;
timer.Change(dueTime: Timeout.InfiniteTimeSpan, period: Timeout.InfiniteTimeSpan);
}
private unsafe void OnTimerCallback(object state)
{
// Workaround for https://github.com/microsoft/win32metadata/issues/683#issuecomment-931662592
var lastInputInfo = new LASTINPUTINFO { cbSize = (uint)sizeof(LASTINPUTINFO) };
if (!PInvoke.GetLastInputInfo(&lastInputInfo))
{
Restart();
return;
}
var timeSinceLastInputEvent = TimeSpan.FromMilliseconds(Environment.TickCount - lastInputInfo.dwTime);
if (timeSinceLastInputEvent < inactivityDuration)
{
timer.Change(dueTime: inactivityDuration - timeSinceLastInputEvent, period: Timeout.InfiniteTimeSpan);
return;
}
stopOrStartWasCalled = false;
try
{
Inactive.Invoke(this, EventArgs.Empty);
}
finally
{
if (!stopOrStartWasCalled) Restart();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment