From fe4945d395f4a119a340decf8fc311f98c7bcfd2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 18 Mar 2023 18:55:00 +0300 Subject: [PATCH] Win32 dispatcher fixes --- .../Threading/Dispatcher.Timers.cs | 35 ++++++++++--------- .../Threading/DispatcherTimer.cs | 2 +- .../Interop/UnmanagedMethods.cs | 2 +- .../Avalonia.Win32/Win32DispatcherImpl.cs | 30 ++++++++-------- src/Windows/Avalonia.Win32/Win32Platform.cs | 29 +++++---------- 5 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs index 0bf087918e..269d10707e 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs @@ -17,23 +17,21 @@ public partial class Dispatcher private void UpdateOSTimer() { - lock (InstanceLock) - { - var nextDueTime = - (_dueTimeForTimers.HasValue && _dueTimeForBackgroundProcessing.HasValue) - ? Math.Min(_dueTimeForTimers.Value, _dueTimeForBackgroundProcessing.Value) - : _dueTimeForTimers ?? _dueTimeForBackgroundProcessing; - if(_osTimerSetTo == nextDueTime) - return; - _impl.UpdateTimer(_osTimerSetTo = nextDueTime); - } + VerifyAccess(); + var nextDueTime = + (_dueTimeForTimers.HasValue && _dueTimeForBackgroundProcessing.HasValue) ? + Math.Min(_dueTimeForTimers.Value, _dueTimeForBackgroundProcessing.Value) : + _dueTimeForTimers ?? _dueTimeForBackgroundProcessing; + if (_osTimerSetTo == nextDueTime) + return; + _impl.UpdateTimer(_osTimerSetTo = nextDueTime); } - internal void UpdateOSTimerForTimers() + internal void RescheduleTimers() { if (!CheckAccess()) { - Post(UpdateOSTimerForTimers, DispatcherPriority.Send); + Post(RescheduleTimers, DispatcherPriority.Send); return; } @@ -89,7 +87,7 @@ public partial class Dispatcher } } - UpdateOSTimerForTimers(); + RescheduleTimers(); } internal void RemoveTimer(DispatcherTimer timer) @@ -103,15 +101,18 @@ public partial class Dispatcher } } - UpdateOSTimerForTimers(); + RescheduleTimers(); } private void OnOSTimer() { + _impl.UpdateTimer(null); + _osTimerSetTo = null; bool needToPromoteTimers = false; bool needToProcessQueue = false; lock (InstanceLock) { + _impl.UpdateTimer(_osTimerSetTo = null); needToPromoteTimers = _dueTimeForTimers.HasValue && _dueTimeForTimers.Value <= Clock.TickCount; if (needToPromoteTimers) _dueTimeForTimers = null; @@ -119,13 +120,13 @@ public partial class Dispatcher _dueTimeForBackgroundProcessing.Value <= Clock.TickCount; if (needToProcessQueue) _dueTimeForBackgroundProcessing = null; - UpdateOSTimer(); } if (needToPromoteTimers) PromoteTimers(); if (needToProcessQueue) ExecuteJobsCore(); + UpdateOSTimer(); } internal void PromoteTimers() @@ -195,10 +196,10 @@ public partial class Dispatcher } finally { - UpdateOSTimerForTimers(); + RescheduleTimers(); } } internal static List SnapshotTimersForUnitTests() => s_uiThread!._timers.ToList(); -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index f7ed03379e..0c235ee161 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -132,7 +132,7 @@ public partial class DispatcherTimer if (updateOSTimer) { - _dispatcher.UpdateOSTimerForTimers(); + _dispatcher.RescheduleTimers(); } } } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 9618a5b4cd..7510b48270 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1796,7 +1796,7 @@ namespace Avalonia.Win32.Interop QS_SENDMESSAGE = 0x0040, QS_HOTKEY = 0x0080, QS_ALLPOSTMESSAGE = 0x0100, - QS_EVENT = 0x0200, + QS_EVENT = 0x02000, QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON, QS_INPUT = QS_MOUSE | QS_KEY, QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY, diff --git a/src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs b/src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs index 72a60902ba..3c2f7842ba 100644 --- a/src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs +++ b/src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs @@ -10,13 +10,10 @@ internal class Win32DispatcherImpl : IControlledDispatcherImpl, IDispatcherClock { private readonly IntPtr _messageWindow; private static Thread? s_uiThread; - private IntPtr? _timerHandle; - private readonly TimerProc _timerDelegate; public Win32DispatcherImpl(IntPtr messageWindow) { _messageWindow = messageWindow; s_uiThread = Thread.CurrentThread; - _timerDelegate = TimerProc; } public bool CurrentThreadIsLoopThread => s_uiThread == Thread.CurrentThread; @@ -37,26 +34,27 @@ internal class Win32DispatcherImpl : IControlledDispatcherImpl, IDispatcherClock public event Action? Signaled; public event Action? Timer; - void TimerProc(IntPtr hWnd, uint uMsg, IntPtr nIdEvent, uint dwTime) => Timer?.Invoke(); + public void FireTimer() => Timer?.Invoke(); public void UpdateTimer(int? dueTimeInMs) { - if (_timerHandle.HasValue) - KillTimer(IntPtr.Zero, _timerHandle.Value); if (dueTimeInMs == null) - return; - - var interval = (uint)Math.Max(1, TickCount - dueTimeInMs.Value); - - _timerHandle = SetTimer( - IntPtr.Zero, - IntPtr.Zero, - interval, - _timerDelegate); + { + KillTimer(_messageWindow, (IntPtr)Win32Platform.TIMERID_DISPATCHER); + } + else + { + var interval = (uint)Math.Max(1, TickCount - dueTimeInMs.Value); + SetTimer( + _messageWindow, + (IntPtr)Win32Platform.TIMERID_DISPATCHER, + interval, + null!); + } } public bool CanQueryPendingInput => true; - + public bool HasPendingInput { get diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 6a5841f36f..857af3a783 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -112,6 +112,7 @@ namespace Avalonia.Win32 private static readonly Win32Platform s_instance = new(); private static Win32PlatformOptions? s_options; private static Compositor? s_compositor; + internal const int TIMERID_DISPATCHER = 1; private WndProc? _wndProcDelegate; private IntPtr _hwnd; @@ -182,27 +183,7 @@ namespace Avalonia.Win32 s_compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), platformGraphics); } - - public bool HasMessages() - { - return PeekMessage(out _, IntPtr.Zero, 0, 0, 0); - } - - public void ProcessMessage() - { - if (GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1) - { - TranslateMessage(ref msg); - DispatchMessage(ref msg); - } - else - { - Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform) - ?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(ProcessMessage), Marshal.GetLastWin32Error()); - - } - } - + public event EventHandler? ShutdownRequested; [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] @@ -238,6 +219,12 @@ namespace Avalonia.Win32 win32PlatformSettings.OnColorValuesChanged(); } } + + if (msg == (uint)WindowsMessage.WM_TIMER) + { + if (wParam == (IntPtr)TIMERID_DISPATCHER) + _dispatcher?.FireTimer(); + } TrayIconImpl.ProcWnd(hWnd, msg, wParam, lParam);