diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 1c70b3f03d..d6b95bc7b0 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -26,6 +26,9 @@ namespace Avalonia.Win32.Interop public delegate void TimeCallback(uint uTimerID, uint uMsg, UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2); + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void WaitOrTimerCallback(IntPtr lpParameter, bool timerOrWaitFired); + public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); public enum Cursor @@ -848,11 +851,25 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow); - [DllImport("Winmm.dll")] - public static extern uint timeKillEvent(uint uTimerID); + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateTimerQueue(); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool DeleteTimerQueueEx(IntPtr TimerQueue, IntPtr CompletionEvent); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateTimerQueueTimer( + out IntPtr phNewTimer, + IntPtr TimerQueue, + WaitOrTimerCallback Callback, + IntPtr Parameter, + uint DueTime, + uint Period, + uint Flags); - [DllImport("Winmm.dll")] - public static extern uint timeSetEvent(uint uDelay, uint uResolution, TimeCallback lpTimeProc, UIntPtr dwUser, uint fuEvent); + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool DeleteTimerQueueTimer(IntPtr TimerQueue, IntPtr Timer, IntPtr CompletionEvent); [DllImport("user32.dll")] public static extern int ToUnicode( @@ -972,9 +989,6 @@ namespace Avalonia.Win32.Interop [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetMonitorInfo([In] IntPtr hMonitor, [Out] MONITORINFO lpmi); - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); - [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); diff --git a/src/Windows/Avalonia.Win32/RenderTimer.cs b/src/Windows/Avalonia.Win32/RenderTimer.cs index cea1bf94a5..0cb107d3a9 100644 --- a/src/Windows/Avalonia.Win32/RenderTimer.cs +++ b/src/Windows/Avalonia.Win32/RenderTimer.cs @@ -1,5 +1,6 @@ using System; using System.Reactive.Disposables; +using System.Threading; using Avalonia.Rendering; using Avalonia.Win32.Interop; @@ -7,7 +8,21 @@ namespace Avalonia.Win32 { internal class RenderTimer : DefaultRenderTimer { - private UnmanagedMethods.TimeCallback timerDelegate; + private UnmanagedMethods.WaitOrTimerCallback timerDelegate; + + private static IntPtr _timerQueue; + + private static void EnsureTimerQueueCreated() + { + if (Volatile.Read(ref _timerQueue) == null) + { + var queue = UnmanagedMethods.CreateTimerQueue(); + if (Interlocked.CompareExchange(ref _timerQueue, queue, IntPtr.Zero) != IntPtr.Zero) + { + UnmanagedMethods.DeleteTimerQueueEx(queue, IntPtr.Zero); + } + } + } public RenderTimer(int framesPerSecond) : base(framesPerSecond) @@ -16,23 +31,25 @@ namespace Avalonia.Win32 protected override IDisposable StartCore(Action tick) { - timerDelegate = (id, uMsg, user, dw1, dw2) => - { - UnmanagedMethods.QueryPerformanceCounter(out long tickCount); - tick(tickCount); - }; + EnsureTimerQueueCreated(); + var msPerFrame = 1000 / FramesPerSecond; + + timerDelegate = (_, __) => tick(TimeStampToFrames()); - var handle = UnmanagedMethods.timeSetEvent( - (uint)(1000 / FramesPerSecond), - 0, + UnmanagedMethods.CreateTimerQueueTimer( + out var timer, + _timerQueue, timerDelegate, - UIntPtr.Zero, - 1); + IntPtr.Zero, + (uint)msPerFrame, + (uint)msPerFrame, + 0 + ); return Disposable.Create(() => { timerDelegate = null; - UnmanagedMethods.timeKillEvent(handle); + UnmanagedMethods.DeleteTimerQueueTimer(_timerQueue, timer, IntPtr.Zero); }); } }