Sfoglia il codice sorgente

Win32 dispatcher fixes

Nikita Tsukanov 2 anni fa
parent
commit
fe4945d395

+ 18 - 17
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<DispatcherTimer> SnapshotTimersForUnitTests() =>
         s_uiThread!._timers.ToList();
-}
+}

+ 1 - 1
src/Avalonia.Base/Threading/DispatcherTimer.cs

@@ -132,7 +132,7 @@ public partial class DispatcherTimer
 
             if (updateOSTimer)
             {
-                _dispatcher.UpdateOSTimerForTimers();
+                _dispatcher.RescheduleTimers();
             }
         }
     }

+ 1 - 1
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,

+ 14 - 16
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

+ 8 - 21
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<IRenderLoop>(), 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<ShutdownRequestedEventArgs>? 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);