lightweight variant (#1)
Reviewed-on: #1 Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CursorLang.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// A timer that ticks on the message loop — the agent's stand-in for
|
||||
/// <c>DispatcherTimer</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <c>SetTimer</c> with a null window binds the timer to the thread rather than to a
|
||||
/// window, and <c>DispatchMessage</c> calls the callback straight from the loop. The
|
||||
/// upshot is the same as with a dispatcher timer: the tick arrives on the thread that
|
||||
/// owns the hook and the popup, so nothing needs marshalling and nothing races.
|
||||
///
|
||||
/// The callback lives in a field for the reason a hook procedure does: the only
|
||||
/// reference to it is held by Win32, and a collected delegate takes the process down
|
||||
/// with it at the first tick.
|
||||
/// </remarks>
|
||||
internal sealed class MessageTimer : IDisposable
|
||||
{
|
||||
/// <summary>Windows will not go below this, and pretending otherwise misleads.</summary>
|
||||
private const uint MinimumIntervalMilliseconds = 10;
|
||||
|
||||
private readonly TimerProc _callback;
|
||||
|
||||
private nuint _id;
|
||||
|
||||
internal MessageTimer() => _callback = OnTimer;
|
||||
|
||||
internal event EventHandler? Tick;
|
||||
|
||||
internal TimeSpan Interval { get; set; } = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
internal bool IsRunning => _id != 0;
|
||||
|
||||
/// <summary>Starts the timer, or restarts it from zero when it is already running.</summary>
|
||||
internal void Start()
|
||||
{
|
||||
Stop();
|
||||
|
||||
var milliseconds = (uint)Math.Clamp(
|
||||
Math.Round(Interval.TotalMilliseconds), MinimumIntervalMilliseconds, int.MaxValue);
|
||||
|
||||
_id = SetTimer(IntPtr.Zero, 0, milliseconds, _callback);
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
if (_id == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KillTimer(IntPtr.Zero, _id);
|
||||
_id = 0;
|
||||
}
|
||||
|
||||
public void Dispose() => Stop();
|
||||
|
||||
private void OnTimer(IntPtr window, uint message, nuint id, uint time) =>
|
||||
Tick?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
private delegate void TimerProc(IntPtr hWnd, uint message, nuint idEvent, uint time);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern nuint SetTimer(IntPtr hWnd, nuint nIDEvent, uint uElapse, TimerProc lpTimerFunc);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool KillTimer(IntPtr hWnd, nuint uIDEvent);
|
||||
}
|
||||
Reference in New Issue
Block a user