From edcced230ccef6ebdecc44050d5fe27289e80259 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 7 Jul 2020 21:29:53 +0300 Subject: [PATCH 1/7] Use custom WaitForMultipleObjectsEx to prevent STA/COM fuckery --- .../AvaloniaSynchronizationContext.cs | 23 ++++++++++++++++++- src/Avalonia.FreeDesktop/DBusHelper.cs | 2 +- .../Interop/UnmanagedMethods.cs | 17 ++++++++++++++ .../Avalonia.Win32/NonPumpingWaitProvider.cs | 15 ++++++++++++ src/Windows/Avalonia.Win32/Win32Platform.cs | 1 + 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 38a23f918f..166832398f 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; namespace Avalonia.Threading @@ -7,6 +8,18 @@ namespace Avalonia.Threading /// public class AvaloniaSynchronizationContext : SynchronizationContext { + public interface INonPumpingPlatformWaitProvider + { + int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); + } + + private readonly INonPumpingPlatformWaitProvider _waitProvider; + + public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider) + { + _waitProvider = waitProvider; + } + /// /// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer. /// @@ -22,7 +35,8 @@ namespace Avalonia.Threading return; } - SetSynchronizationContext(new AvaloniaSynchronizationContext()); + SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current + .GetService())); } /// @@ -39,5 +53,12 @@ namespace Avalonia.Threading else Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait(); } + + public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + { + if (_waitProvider != null) + return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout); + return base.Wait(waitHandles, waitAll, millisecondsTimeout); + } } } diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs index b445f86613..91c4c28995 100644 --- a/src/Avalonia.FreeDesktop/DBusHelper.cs +++ b/src/Avalonia.FreeDesktop/DBusHelper.cs @@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop public void Initialized() { lock (_lock) - _ctx = new AvaloniaSynchronizationContext(); + _ctx = new AvaloniaSynchronizationContext(null); } } public static Connection Connection { get; private set; } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 1aec4f0016..392ca31282 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -1382,6 +1383,22 @@ namespace Avalonia.Win32.Interop throw new Exception("RtlGetVersion failed!"); } } + + [DllImport("kernel32", EntryPoint="WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int IntWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable); + + public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); + + internal static int WaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable) + { + int result = IntWaitForMultipleObjectsEx(nCount, pHandles, bWaitAll, dwMilliseconds, bAlertable); + if(result == WAIT_FAILED) + { + throw new Win32Exception(); + } + + return result; + } [DllImport("user32.dll")] internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); diff --git a/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs b/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs new file mode 100644 index 0000000000..a0160fcfbd --- /dev/null +++ b/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia.Threading; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + internal class NonPumpingWaitProvider : AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider + { + public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + { + return UnmanagedMethods.WaitForMultipleObjectsEx(waitHandles.Length, waitHandles, waitAll, + millisecondsTimeout, false); + } + } +} diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index b7bb0e19ba..af6058d197 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -93,6 +93,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToSingleton() .Bind().ToConstant(s_instance) + .Bind().ToConstant(new NonPumpingWaitProvider()) .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()); if (options.AllowEglInitialization) From 9309d779c74b2ea8747c4b719be395e91e0f201f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 10 Jul 2020 16:00:17 +0300 Subject: [PATCH 2/7] Call SetWaitNotificationRequired --- src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 166832398f..f920fec82e 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -18,6 +18,8 @@ namespace Avalonia.Threading public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider) { _waitProvider = waitProvider; + if (_waitProvider != null) + SetWaitNotificationRequired(); } /// From efd5bbeaa9a7104c15cab284288b4730c3b3ea56 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 10 Jul 2020 16:21:53 +0300 Subject: [PATCH 3/7] Added an incantation nobody probably even remembers about anymore --- src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index f920fec82e..40cf81358f 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.ConstrainedExecution; using System.Threading; namespace Avalonia.Threading @@ -56,6 +57,7 @@ namespace Avalonia.Threading Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait(); } + [PrePrepareMethod] public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { if (_waitProvider != null) From c96f328d0633cdb27d126ac965fa746d1b3c999e Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 14 Jul 2020 07:49:39 +0300 Subject: [PATCH 4/7] Fix typo in PointerReleasedEventMessage --- src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index f5c673d5f9..931c27c575 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -192,7 +192,7 @@ namespace Avalonia.Controls.Remote.Server GetAvaloniaInputModifiers(pressed.Modifiers))); }, DispatcherPriority.Input); } - if (obj is PointerPressedEventMessage released) + if (obj is PointerReleasedEventMessage released) { Dispatcher.UIThread.Post(() => { From 162583b2decfd264c6e4be9661d31209045eb36e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 14 Jul 2020 14:30:31 -0300 Subject: [PATCH 5/7] add a failing unit test. --- .../ApplicationTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index e533001242..d485d424fb 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Reactive.Subjects; +using Avalonia.Data; using Avalonia.Threading; using Avalonia.UnitTests; using Xunit; @@ -32,5 +34,20 @@ namespace Avalonia.Controls.UnitTests Assert.True(raised); } } + + [Fact] + public void Can_Bind_To_DataContext() + { + using (UnitTestApplication.Start()) + { + var application = Application.Current; + + application.DataContext = "Test"; + + application.Bind(Application.NameProperty, new Binding(".")); + + Assert.Equal("Test", Application.Current.Name); + } + } } } From f1eae6ce1289140063f36f8c86800f3d2cf2a314 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 14 Jul 2020 14:31:00 -0300 Subject: [PATCH 6/7] correctly cast to IDataContextProvider. --- src/Markup/Avalonia.Markup/Data/BindingBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs index 7c4e7b5efe..3dbc83a7df 100644 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ b/src/Markup/Avalonia.Markup/Data/BindingBase.cs @@ -137,9 +137,9 @@ namespace Avalonia.Data { Contract.Requires(target != null); - if (!(target is IStyledElement)) + if (!(target is IDataContextProvider)) { - target = anchor as IStyledElement; + target = anchor as IDataContextProvider; if (target == null) { From a5499a908d8a7d740fc2873ce16298c08c34deae Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 14 Jul 2020 14:33:45 -0300 Subject: [PATCH 7/7] remove usings. --- tests/Avalonia.Controls.UnitTests/ApplicationTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index d485d424fb..58ddc8ca60 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Reactive.Subjects; using Avalonia.Data; -using Avalonia.Threading; using Avalonia.UnitTests; using Xunit;