Browse Source

Nullability annotations for Avalonia.Win32

pull/10307/head
Julien Lebosquain 4 years ago
parent
commit
0d32343410
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 2
      src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
  2. 4
      src/Avalonia.Base/Platform/Storage/FilePickerFileType.cs
  3. 1
      src/Avalonia.Base/Rendering/IRenderTimer.cs
  4. 2
      src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs
  5. 8
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  6. 2
      src/Avalonia.Controls/WindowBase.cs
  7. 2
      src/Windows/Avalonia.Win32/AngleOptions.cs
  8. 2
      src/Windows/Avalonia.Win32/Automation/AutomationNode.ExpandCollapse.cs
  9. 2
      src/Windows/Avalonia.Win32/Automation/AutomationNode.RangeValue.cs
  10. 2
      src/Windows/Avalonia.Win32/Automation/AutomationNode.Scroll.cs
  11. 8
      src/Windows/Avalonia.Win32/Automation/AutomationNode.Selection.cs
  12. 2
      src/Windows/Avalonia.Win32/Automation/AutomationNode.Toggle.cs
  13. 2
      src/Windows/Avalonia.Win32/Automation/AutomationNode.Value.cs
  14. 10
      src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
  15. 2
      src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
  16. 4
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  17. 30
      src/Windows/Avalonia.Win32/ClipboardFormats.cs
  18. 18
      src/Windows/Avalonia.Win32/ClipboardImpl.cs
  19. 37
      src/Windows/Avalonia.Win32/DataObject.cs
  20. 8
      src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs
  21. 28
      src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
  22. 41
      src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs
  23. 2
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  24. 78
      src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
  25. 9
      src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs
  26. 3
      src/Windows/Avalonia.Win32/Interop/Automation/IGridProvider.cs
  27. 2
      src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderFragment.cs
  28. 5
      src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderFragmentRoot.cs
  29. 2
      src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderSimple.cs
  30. 5
      src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderSimple2.cs
  31. 2
      src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs
  32. 3
      src/Windows/Avalonia.Win32/Interop/Automation/IValueProvider.cs
  33. 10
      src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreProviderApi.cs
  34. 4
      src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs
  35. 15
      src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
  36. 54
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  37. 19
      src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs
  38. 9
      src/Windows/Avalonia.Win32/OffscreenParentWindow.cs
  39. 12
      src/Windows/Avalonia.Win32/OleContext.cs
  40. 17
      src/Windows/Avalonia.Win32/OleDataObject.cs
  41. 43
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  42. 6
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs
  43. 2
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleEglInterface.cs
  44. 30
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleExternalD3D11Texture2D.cs
  45. 6
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleExternalObjectsFeature.cs
  46. 3
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32EglDisplay.cs
  47. 16
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
  48. 16
      src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
  49. 45
      src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
  50. 122
      src/Windows/Avalonia.Win32/OpenGl/WglGdiResourceManager.cs
  51. 18
      src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs
  52. 4
      src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs
  53. 15
      src/Windows/Avalonia.Win32/PopupImpl.cs
  54. 19
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  55. 32
      src/Windows/Avalonia.Win32/TrayIconImpl.cs
  56. 17
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  57. 42
      src/Windows/Avalonia.Win32/Win32NativeControlHost.cs
  58. 9
      src/Windows/Avalonia.Win32/Win32NativeToManagedMenuExporter.cs
  59. 50
      src/Windows/Avalonia.Win32/Win32Platform.cs
  60. 8
      src/Windows/Avalonia.Win32/Win32PlatformSettings.cs
  61. 12
      src/Windows/Avalonia.Win32/Win32StorageProvider.cs
  62. 27
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
  63. 7
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs
  64. 49
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
  65. 10
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
  66. 11
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs
  67. 16
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
  68. 20
      src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs
  69. 7
      src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs
  70. 8
      src/Windows/Avalonia.Win32/WinScreen.cs
  71. 82
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  72. 8
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  73. 7
      src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs
  74. 206
      src/Windows/Avalonia.Win32/WindowImpl.cs
  75. 13
      src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs
  76. 11
      src/tools/DevGenerators/Helpers.cs

2
src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs

@ -26,6 +26,6 @@ namespace Avalonia.Platform
bool CurrentThreadIsLoopThread { get; } bool CurrentThreadIsLoopThread { get; }
event Action<DispatcherPriority?> Signaled; event Action<DispatcherPriority?>? Signaled;
} }
} }

4
src/Avalonia.Base/Platform/Storage/FilePickerFileType.cs

@ -7,9 +7,9 @@ namespace Avalonia.Platform.Storage;
/// </summary> /// </summary>
public sealed class FilePickerFileType public sealed class FilePickerFileType
{ {
public FilePickerFileType(string name) public FilePickerFileType(string? name)
{ {
Name = name; Name = name ?? string.Empty;
} }
/// <summary> /// <summary>

1
src/Avalonia.Base/Rendering/IRenderTimer.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Threading.Tasks;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Rendering namespace Avalonia.Rendering

2
src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs

@ -13,6 +13,6 @@ namespace Avalonia.Platform
/// <remarks> /// <remarks>
/// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit. /// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit.
/// </remarks> /// </remarks>
event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested; event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
} }
} }

8
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -19,7 +19,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Gets or sets a method called when the minimized/maximized state of the window changes. /// Gets or sets a method called when the minimized/maximized state of the window changes.
/// </summary> /// </summary>
Action<WindowState> WindowStateChanged { get; set; } Action<WindowState>? WindowStateChanged { get; set; }
/// <summary> /// <summary>
/// Sets the title of the window. /// Sets the title of the window.
@ -42,7 +42,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Called when a disabled window received input. Can be used to activate child windows. /// Called when a disabled window received input. Can be used to activate child windows.
/// </summary> /// </summary>
Action GotInputWhenDisabled { get; set; } Action? GotInputWhenDisabled { get; set; }
/// <summary> /// <summary>
/// Enables or disables system window decorations (title bar, buttons, etc) /// Enables or disables system window decorations (title bar, buttons, etc)
@ -68,7 +68,7 @@ namespace Avalonia.Platform
/// Gets or sets a method called before the underlying implementation is destroyed. /// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing. /// Return true to prevent the underlying implementation from closing.
/// </summary> /// </summary>
Func<WindowCloseReason, bool> Closing { get; set; } Func<WindowCloseReason, bool>? Closing { get; set; }
/// <summary> /// <summary>
/// Gets a value to indicate if the platform was able to extend client area to non-client area. /// Gets a value to indicate if the platform was able to extend client area to non-client area.
@ -78,7 +78,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Gets or Sets an action that is called whenever one of the extend client area properties changed. /// Gets or Sets an action that is called whenever one of the extend client area properties changed.
/// </summary> /// </summary>
Action<bool> ExtendClientAreaToDecorationsChanged { get; set; } Action<bool>? ExtendClientAreaToDecorationsChanged { get; set; }
/// <summary> /// <summary>
/// Gets a flag that indicates if Managed decorations i.e. caption buttons are required. /// Gets a flag that indicates if Managed decorations i.e. caption buttons are required.

2
src/Avalonia.Controls/WindowBase.cs

@ -94,7 +94,7 @@ namespace Avalonia.Controls
private set { SetAndRaise(IsActiveProperty, ref _isActive, value); } private set { SetAndRaise(IsActiveProperty, ref _isActive, value); }
} }
public Screens Screens { get; private set; } public Screens Screens { get; }
/// <summary> /// <summary>
/// Gets or sets the owner of the window. /// Gets or sets the owner of the window.

2
src/Windows/Avalonia.Win32/AngleOptions.cs

@ -17,6 +17,6 @@ namespace Avalonia.Win32
new GlVersion(GlProfileType.OpenGLES, 2, 0) new GlVersion(GlProfileType.OpenGLES, 2, 0)
}; };
public IList<PlatformApi> AllowedPlatformApis { get; set; } = null; public IList<PlatformApi>? AllowedPlatformApis { get; set; } = null;
} }
} }

2
src/Windows/Avalonia.Win32/Automation/AutomationNode.ExpandCollapse.cs

@ -2,8 +2,6 @@
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.IExpandCollapseProvider internal partial class AutomationNode : UIA.IExpandCollapseProvider

2
src/Windows/Avalonia.Win32/Automation/AutomationNode.RangeValue.cs

@ -1,8 +1,6 @@
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.IRangeValueProvider internal partial class AutomationNode : UIA.IRangeValueProvider

2
src/Windows/Avalonia.Win32/Automation/AutomationNode.Scroll.cs

@ -1,8 +1,6 @@
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.IScrollProvider, UIA.IScrollItemProvider internal partial class AutomationNode : UIA.IScrollProvider, UIA.IScrollItemProvider

8
src/Windows/Avalonia.Win32/Automation/AutomationNode.Selection.cs

@ -1,12 +1,9 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.ISelectionProvider, UIA.ISelectionItemProvider internal partial class AutomationNode : UIA.ISelectionProvider, UIA.ISelectionItemProvider
@ -27,8 +24,7 @@ namespace Avalonia.Win32.Automation
UIA.IRawElementProviderSimple[] UIA.ISelectionProvider.GetSelection() UIA.IRawElementProviderSimple[] UIA.ISelectionProvider.GetSelection()
{ {
var peers = InvokeSync<ISelectionProvider, IReadOnlyList<AutomationPeer>>(x => x.GetSelection()); var peers = InvokeSync<ISelectionProvider, IReadOnlyList<AutomationPeer>>(x => x.GetSelection());
return peers?.Select(x => (UIA.IRawElementProviderSimple)GetOrCreate(x)!).ToArray() ?? return peers.Select(x => (UIA.IRawElementProviderSimple)GetOrCreate(x)).ToArray();
Array.Empty<UIA.IRawElementProviderSimple>();
} }
void UIA.ISelectionItemProvider.AddToSelection() => InvokeSync<ISelectionItemProvider>(x => x.AddToSelection()); void UIA.ISelectionItemProvider.AddToSelection() => InvokeSync<ISelectionItemProvider>(x => x.AddToSelection());

2
src/Windows/Avalonia.Win32/Automation/AutomationNode.Toggle.cs

@ -1,8 +1,6 @@
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.IToggleProvider internal partial class AutomationNode : UIA.IToggleProvider

2
src/Windows/Avalonia.Win32/Automation/AutomationNode.Value.cs

@ -2,8 +2,6 @@
using Avalonia.Automation.Provider; using Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Interop.Automation; using UIA = Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
internal partial class AutomationNode : UIA.IValueProvider internal partial class AutomationNode : UIA.IValueProvider

10
src/Windows/Avalonia.Win32/Automation/AutomationNode.cs

@ -12,8 +12,6 @@ using Avalonia.Threading;
using Avalonia.Win32.Interop.Automation; using Avalonia.Win32.Interop.Automation;
using AAP = Avalonia.Automation.Provider; using AAP = Avalonia.Automation.Provider;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
[ComVisible(true)] [ComVisible(true)]
@ -25,7 +23,7 @@ namespace Avalonia.Win32.Automation
IRawElementProviderAdviseEvents, IRawElementProviderAdviseEvents,
IInvokeProvider IInvokeProvider
{ {
private static Dictionary<AutomationProperty, UiaPropertyId> s_propertyMap = new Dictionary<AutomationProperty, UiaPropertyId>() private static Dictionary<AutomationProperty, UiaPropertyId> s_propertyMap = new()
{ {
{ AutomationElementIdentifiers.BoundingRectangleProperty, UiaPropertyId.BoundingRectangle }, { AutomationElementIdentifiers.BoundingRectangleProperty, UiaPropertyId.BoundingRectangle },
{ AutomationElementIdentifiers.ClassNameProperty, UiaPropertyId.ClassName }, { AutomationElementIdentifiers.ClassNameProperty, UiaPropertyId.ClassName },
@ -46,8 +44,7 @@ namespace Avalonia.Win32.Automation
{ SelectionPatternIdentifiers.SelectionProperty, UiaPropertyId.SelectionSelection }, { SelectionPatternIdentifiers.SelectionProperty, UiaPropertyId.SelectionSelection },
}; };
private static ConditionalWeakTable<AutomationPeer, AutomationNode> s_nodes = private static ConditionalWeakTable<AutomationPeer, AutomationNode> s_nodes = new();
new ConditionalWeakTable<AutomationPeer, AutomationNode>();
private readonly int[] _runtimeId; private readonly int[] _runtimeId;
private int _raiseFocusChanged; private int _raiseFocusChanged;
@ -174,11 +171,12 @@ namespace Avalonia.Win32.Automation
NavigateDirection.LastChild => GetOrCreate(Peer.GetChildren().LastOrDefault()), NavigateDirection.LastChild => GetOrCreate(Peer.GetChildren().LastOrDefault()),
_ => null, _ => null,
}; };
}) as IRawElementProviderFragment; });
} }
public void SetFocus() => InvokeSync(() => Peer.SetFocus()); public void SetFocus() => InvokeSync(() => Peer.SetFocus());
[return: NotNullIfNotNull(nameof(peer))]
public static AutomationNode? GetOrCreate(AutomationPeer? peer) public static AutomationNode? GetOrCreate(AutomationPeer? peer)
{ {
return peer is null ? null : s_nodes.GetValue(peer, Create); return peer is null ? null : s_nodes.GetValue(peer, Create);

2
src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs

@ -6,8 +6,6 @@ using Avalonia.Automation.Provider;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop.Automation; using Avalonia.Win32.Interop.Automation;
#nullable enable
namespace Avalonia.Win32.Automation namespace Avalonia.Win32.Automation
{ {
[RequiresUnreferencedCode("Requires .NET COM interop")] [RequiresUnreferencedCode("Requires .NET COM interop")]

4
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -6,7 +6,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" /> <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<Compile Include="..\..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
@ -16,7 +15,8 @@
<MicroComIdl Include="Win32Com\win32.idl" CSharpInteropPath="Win32Com\Win32.Generated.cs" /> <MicroComIdl Include="Win32Com\win32.idl" CSharpInteropPath="Win32Com\Win32.Generated.cs" />
<MicroComIdl Include="DirectX\directx.idl" CSharpInteropPath="DirectX\directx.Generated.cs" /> <MicroComIdl Include="DirectX\directx.idl" CSharpInteropPath="DirectX\directx.Generated.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildThisFileDirectory)\..\..\..\build\System.Drawing.Common.props" /> <Import Project="..\..\..\build\System.Drawing.Common.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" /> <Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\SourceGenerators.props" /> <Import Project="..\..\..\build\SourceGenerators.props" />
<ItemGroup> <ItemGroup>

30
src/Windows/Avalonia.Win32/ClipboardFormats.cs

@ -8,15 +8,15 @@ using Avalonia.Utilities;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
static class ClipboardFormats internal static class ClipboardFormats
{ {
private const int MAX_FORMAT_NAME_LENGTH = 260; private const int MAX_FORMAT_NAME_LENGTH = 260;
class ClipboardFormat private class ClipboardFormat
{ {
public ushort Format { get; private set; } public ushort Format { get; }
public string Name { get; private set; } public string Name { get; }
public ushort[] Synthesized { get; private set; } public ushort[] Synthesized { get; }
public ClipboardFormat(string name, ushort format, params ushort[] synthesized) public ClipboardFormat(string name, ushort format, params ushort[] synthesized)
{ {
@ -26,14 +26,14 @@ namespace Avalonia.Win32
} }
} }
private static readonly List<ClipboardFormat> FormatList = new List<ClipboardFormat>() private static readonly List<ClipboardFormat> s_formatList = new()
{ {
new ClipboardFormat(DataFormats.Text, (ushort)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (ushort)UnmanagedMethods.ClipboardFormat.CF_TEXT), new ClipboardFormat(DataFormats.Text, (ushort)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (ushort)UnmanagedMethods.ClipboardFormat.CF_TEXT),
new ClipboardFormat(DataFormats.FileNames, (ushort)UnmanagedMethods.ClipboardFormat.CF_HDROP), new ClipboardFormat(DataFormats.FileNames, (ushort)UnmanagedMethods.ClipboardFormat.CF_HDROP),
}; };
private static string QueryFormatName(ushort format) private static string? QueryFormatName(ushort format)
{ {
var sb = StringBuilderCache.Acquire(MAX_FORMAT_NAME_LENGTH); var sb = StringBuilderCache.Acquire(MAX_FORMAT_NAME_LENGTH);
if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0) if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0)
@ -43,16 +43,16 @@ namespace Avalonia.Win32
public static string GetFormat(ushort format) public static string GetFormat(ushort format)
{ {
lock (FormatList) lock (s_formatList)
{ {
var pd = FormatList.FirstOrDefault(f => f.Format == format || Array.IndexOf(f.Synthesized, format) >= 0); var pd = s_formatList.FirstOrDefault(f => f.Format == format || Array.IndexOf(f.Synthesized, format) >= 0);
if (pd == null) if (pd == null)
{ {
string name = QueryFormatName(format); string? name = QueryFormatName(format);
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
name = string.Format("Unknown_Format_{0}", format); name = $"Unknown_Format_{format}";
pd = new ClipboardFormat(name, format); pd = new ClipboardFormat(name, format);
FormatList.Add(pd); s_formatList.Add(pd);
} }
return pd.Name; return pd.Name;
} }
@ -60,16 +60,16 @@ namespace Avalonia.Win32
public static ushort GetFormat(string format) public static ushort GetFormat(string format)
{ {
lock (FormatList) lock (s_formatList)
{ {
var pd = FormatList.FirstOrDefault(f => StringComparer.OrdinalIgnoreCase.Equals(f.Name, format)); var pd = s_formatList.FirstOrDefault(f => StringComparer.OrdinalIgnoreCase.Equals(f.Name, format));
if (pd == null) if (pd == null)
{ {
int id = UnmanagedMethods.RegisterClipboardFormat(format); int id = UnmanagedMethods.RegisterClipboardFormat(format);
if (id == 0) if (id == 0)
throw new Win32Exception(); throw new Win32Exception();
pd = new ClipboardFormat(format, (ushort)id); pd = new ClipboardFormat(format, (ushort)id);
FormatList.Add(pd); s_formatList.Add(pd);
} }
return pd.Format; return pd.Format;
} }

18
src/Windows/Avalonia.Win32/ClipboardImpl.cs

@ -30,7 +30,7 @@ namespace Avalonia.Win32
return Disposable.Create(() => UnmanagedMethods.CloseClipboard()); return Disposable.Create(() => UnmanagedMethods.CloseClipboard());
} }
public async Task<string> GetTextAsync() public async Task<string?> GetTextAsync()
{ {
using(await OpenClipboard()) using(await OpenClipboard())
{ {
@ -52,19 +52,17 @@ namespace Avalonia.Win32
} }
} }
public async Task SetTextAsync(string text) public async Task SetTextAsync(string? text)
{ {
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
using(await OpenClipboard()) using(await OpenClipboard())
{ {
UnmanagedMethods.EmptyClipboard(); UnmanagedMethods.EmptyClipboard();
var hGlobal = Marshal.StringToHGlobalUni(text); if (text is not null)
UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, hGlobal); {
var hGlobal = Marshal.StringToHGlobalUni(text);
UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, hGlobal);
}
} }
} }
@ -121,7 +119,7 @@ namespace Avalonia.Win32
} }
} }
public async Task<object> GetDataAsync(string format) public async Task<object?> GetDataAsync(string format)
{ {
Dispatcher.UIThread.VerifyAccess(); Dispatcher.UIThread.VerifyAccess();
var i = OleRetryCount; var i = OleRetryCount;

37
src/Windows/Avalonia.Win32/DataObject.cs

@ -22,9 +22,9 @@ namespace Avalonia.Win32
// Compatibility with WinForms + WPF... // Compatibility with WinForms + WPF...
internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray(); internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
class FormatEnumerator : CallbackBase, Win32Com.IEnumFORMATETC private class FormatEnumerator : CallbackBase, Win32Com.IEnumFORMATETC
{ {
private FORMATETC[] _formats; private readonly FORMATETC[] _formats;
private uint _current; private uint _current;
private FormatEnumerator(FORMATETC[] formats, uint current) private FormatEnumerator(FORMATETC[] formats, uint current)
@ -105,16 +105,12 @@ namespace Avalonia.Win32
public DataObject(IDataObject wrapped) public DataObject(IDataObject wrapped)
{ {
if (wrapped == null) _wrapped = wrapped switch
{ {
throw new ArgumentNullException(nameof(wrapped)); null => throw new ArgumentNullException(nameof(wrapped)),
} DataObject or OleDataObject => throw new ArgumentException($"Cannot wrap a {wrapped.GetType()}"),
if (_wrapped is DataObject || _wrapped is OleDataObject) _ => wrapped
{ };
throw new InvalidOperationException();
}
_wrapped = wrapped;
} }
#region IDataObject #region IDataObject
@ -128,17 +124,17 @@ namespace Avalonia.Win32
return _wrapped.GetDataFormats(); return _wrapped.GetDataFormats();
} }
IEnumerable<string> IDataObject.GetFileNames() IEnumerable<string>? IDataObject.GetFileNames()
{ {
return _wrapped.GetFileNames(); return _wrapped.GetFileNames();
} }
string IDataObject.GetText() string? IDataObject.GetText()
{ {
return _wrapped.GetText(); return _wrapped.GetText();
} }
object IDataObject.Get(string dataFormat) object? IDataObject.Get(string dataFormat)
{ {
return _wrapped.Get(dataFormat); return _wrapped.Get(dataFormat);
} }
@ -182,9 +178,6 @@ namespace Avalonia.Win32
if (_wrapped is Win32Com.IDataObject ole) if (_wrapped is Win32Com.IDataObject ole)
return ole.GetCanonicalFormatEtc(formatIn); return ole.GetCanonicalFormatEtc(formatIn);
var formatOut = new FORMATETC();
formatOut.ptd = IntPtr.Zero;
throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
} }
@ -264,9 +257,9 @@ namespace Avalonia.Win32
private uint WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) private uint WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
{ {
object data = _wrapped.Get(dataFormat); object data = _wrapped.Get(dataFormat)!;
if (dataFormat == DataFormats.Text || data is string) if (dataFormat == DataFormats.Text || data is string)
return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data)); return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data) ?? string.Empty);
if (dataFormat == DataFormats.FileNames && data is IEnumerable<string> files) if (dataFormat == DataFormats.FileNames && data is IEnumerable<string> files)
return WriteFileListToHGlobal(ref hGlobal, files); return WriteFileListToHGlobal(ref hGlobal, files);
if (data is Stream stream) if (data is Stream stream)
@ -286,7 +279,7 @@ namespace Avalonia.Win32
} }
if (data is IEnumerable<byte> bytes) if (data is IEnumerable<byte> bytes)
{ {
var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray(); var byteArr = bytes as byte[] ?? bytes.ToArray();
return WriteBytesToHGlobal(ref hGlobal, byteArr); return WriteBytesToHGlobal(ref hGlobal, byteArr);
} }
return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data)); return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data));
@ -332,7 +325,7 @@ namespace Avalonia.Win32
private static uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files) private static uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
{ {
if (!files?.Any() ?? false) if (!files.Any())
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray(); char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray();
@ -392,7 +385,7 @@ namespace Avalonia.Win32
public void ReleaseWrapped() public void ReleaseWrapped()
{ {
_wrapped = null; _wrapped = null!;
} }
#endregion #endregion
} }

8
src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs

@ -1,17 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
#pragma warning disable CS0649 #pragma warning disable CS0649
namespace Avalonia.Win32.DirectX namespace Avalonia.Win32.DirectX
{ {
#nullable enable
public unsafe struct HANDLE public unsafe struct HANDLE
{ {
public readonly void* Value; public readonly void* Value;
@ -288,5 +281,4 @@ namespace Avalonia.Win32.DirectX
public D3D11_RESOURCE_MISC_FLAG MiscFlags; public D3D11_RESOURCE_MISC_FLAG MiscFlags;
} }
#nullable restore
} }

28
src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs

@ -1,24 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Win32.OpenGl.Angle;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods; using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.DirectX namespace Avalonia.Win32.DirectX
{ {
#pragma warning disable CA1416 // This should only be reachable on Windows
#nullable enable
public unsafe class DxgiConnection : IRenderTimer public unsafe class DxgiConnection : IRenderTimer
{ {
public const uint ENUM_CURRENT_SETTINGS = unchecked((uint)(-1)); public const uint ENUM_CURRENT_SETTINGS = unchecked((uint)(-1));
@ -26,11 +16,11 @@ namespace Avalonia.Win32.DirectX
public bool RunsInBackground => true; public bool RunsInBackground => true;
public event Action<TimeSpan>? Tick; public event Action<TimeSpan>? Tick;
private object _syncLock; private readonly object _syncLock;
private IDXGIOutput? _output = null; private IDXGIOutput? _output;
private Stopwatch? _stopwatch = null; private Stopwatch? _stopwatch;
private const string LogArea = "DXGI"; private const string LogArea = "DXGI";
public DxgiConnection(object syncLock) public DxgiConnection(object syncLock)
@ -51,9 +41,9 @@ namespace Avalonia.Win32.DirectX
} }
} }
private unsafe void RunLoop() private void RunLoop()
{ {
_stopwatch = System.Diagnostics.Stopwatch.StartNew(); _stopwatch = Stopwatch.StartNew();
try try
{ {
GetBestOutputToVWaitOn(); GetBestOutputToVWaitOn();
@ -162,7 +152,7 @@ namespace Avalonia.Win32.DirectX
} }
// Used the windows composition as a blueprint for this startup/creation // Used the windows composition as a blueprint for this startup/creation
static private bool TryCreateAndRegisterCore() private static bool TryCreateAndRegisterCore()
{ {
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
var pumpLock = new object(); var pumpLock = new object();
@ -170,9 +160,7 @@ namespace Avalonia.Win32.DirectX
{ {
try try
{ {
DxgiConnection connection; var connection = new DxgiConnection(pumpLock);
connection = new DxgiConnection(pumpLock);
AvaloniaLocator.CurrentMutable.BindToSelf(connection); AvaloniaLocator.CurrentMutable.BindToSelf(connection);
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connection); AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connection);
@ -191,6 +179,4 @@ namespace Avalonia.Win32.DirectX
return tcs.Task.Result; return tcs.Task.Result;
} }
} }
#nullable restore
#pragma warning restore CA1416 // Validate platform compatibility
} }

41
src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs

@ -1,40 +1,28 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces; using Avalonia.OpenGL.Surfaces;
using Avalonia.Win32.OpenGl.Angle; using Avalonia.Win32.OpenGl.Angle;
using MicroCom.Runtime; using MicroCom.Runtime;
using static Avalonia.OpenGL.Egl.EglGlPlatformSurfaceBase;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.DirectX namespace Avalonia.Win32.DirectX
{ {
#pragma warning disable CA1416 // Validate platform compatibility, if you enter this not on windows you have messed up badly
#nullable enable
public unsafe class DxgiRenderTarget : EglPlatformSurfaceRenderTargetBase public unsafe class DxgiRenderTarget : EglPlatformSurfaceRenderTargetBase
{ {
// DXGI_FORMAT_B8G8R8A8_UNORM is target texture format as per ANGLE documentation // DXGI_FORMAT_B8G8R8A8_UNORM is target texture format as per ANGLE documentation
public const uint DXGI_USAGE_RENDER_TARGET_OUTPUT = 0x00000020U; public const uint DXGI_USAGE_RENDER_TARGET_OUTPUT = 0x00000020U;
private readonly Guid ID3D11Texture2DGuid = Guid.Parse("6F15AAF2-D208-4E89-9AB4-489535D34F9C");
private EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window;
private DxgiConnection _connection; private readonly DxgiConnection _connection;
private IDXGIDevice? _dxgiDevice = null; private readonly IDXGIDevice? _dxgiDevice;
private IDXGIFactory2? _dxgiFactory = null; private readonly IDXGIFactory2? _dxgiFactory;
private IDXGISwapChain1? _swapChain = null; private readonly IDXGISwapChain1? _swapChain;
private IUnknown? _renderTexture = null; private readonly uint _flagsUsed;
private Interop.UnmanagedMethods.RECT _clientRect = default; private IUnknown? _renderTexture;
private RECT _clientRect;
private uint _flagsUsed;
private Guid ID3D11Texture2DGuid = Guid.Parse("6F15AAF2-D208-4E89-9AB4-489535D34F9C");
public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window, EglContext context, DxgiConnection connection) : base(context) public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window, EglContext context, DxgiConnection connection) : base(context)
{ {
@ -80,11 +68,11 @@ namespace Avalonia.Win32.DirectX
null null
); );
Interop.UnmanagedMethods.RECT pClientRect; GetClientRect(_window.Handle, out var pClientRect);
GetClientRect(_window.Handle, out pClientRect);
_clientRect = pClientRect; _clientRect = pClientRect;
} }
/// <inheritdoc />
public override IGlPlatformSurfaceRenderingSession BeginDrawCore() public override IGlPlatformSurfaceRenderingSession BeginDrawCore()
{ {
if (_swapChain is null) if (_swapChain is null)
@ -98,8 +86,7 @@ namespace Avalonia.Win32.DirectX
var success = false; var success = false;
try try
{ {
Interop.UnmanagedMethods.RECT pClientRect; GetClientRect(_window.Handle, out var pClientRect);
GetClientRect(_window.Handle, out pClientRect);
if (!RectsEqual(pClientRect, _clientRect)) if (!RectsEqual(pClientRect, _clientRect))
{ {
// we gotta resize // we gotta resize
@ -137,7 +124,7 @@ namespace Avalonia.Win32.DirectX
var res = base.BeginDraw(surface, _window.Size, _window.Scaling, () => var res = base.BeginDraw(surface, _window.Size, _window.Scaling, () =>
{ {
_swapChain.Present((ushort)0U, (ushort)0U); _swapChain.Present((ushort)0U, (ushort)0U);
surface?.Dispose(); surface.Dispose();
transaction?.Dispose(); transaction?.Dispose();
contextLock?.Dispose(); contextLock?.Dispose();
}, true); }, true);
@ -178,6 +165,4 @@ namespace Avalonia.Win32.DirectX
} }
} }
#pragma warning restore CA1416 // Validate platform compatibility
#nullable restore
} }

2
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -4,8 +4,6 @@ using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
#nullable enable
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
internal class FramebufferManager : IFramebufferPlatformSurface, IDisposable internal class FramebufferManager : IFramebufferPlatformSurface, IDisposable

78
src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.Threading; using Avalonia.Threading;
@ -10,21 +11,21 @@ namespace Avalonia.Win32.Input
/// <summary> /// <summary>
/// A Windows input method editor based on Windows Input Method Manager (IMM32). /// A Windows input method editor based on Windows Input Method Manager (IMM32).
/// </summary> /// </summary>
class Imm32InputMethod : ITextInputMethodImpl internal class Imm32InputMethod : ITextInputMethodImpl
{ {
public IntPtr HWND { get; private set; } public IntPtr Hwnd { get; private set; }
private IntPtr _currentHimc; private IntPtr _currentHimc;
private WindowImpl _parent; private WindowImpl? _parent;
private ITextInputMethodClient _client;
private Imm32CaretManager _caretManager = new(); private Imm32CaretManager _caretManager;
private ushort _langId; private ushort _langId;
private const int _caretMargin = 1; private const int CaretMargin = 1;
public ITextInputMethodClient Client => _client; public ITextInputMethodClient? Client { get; private set; }
public bool IsActive => _client != null; [MemberNotNullWhen(true, nameof(Client))]
public bool IsActive => Client != null;
public bool IsComposing { get; set; } public bool IsComposing { get; set; }
@ -32,12 +33,12 @@ namespace Avalonia.Win32.Input
public void CreateCaret() public void CreateCaret()
{ {
_caretManager.TryCreate(HWND); _caretManager.TryCreate(Hwnd);
} }
public void EnableImm() public void EnableImm()
{ {
var himc = ImmGetContext(HWND); var himc = ImmGetContext(Hwnd);
if(himc == IntPtr.Zero) if(himc == IntPtr.Zero)
{ {
@ -51,13 +52,13 @@ namespace Avalonia.Win32.Input
DisableImm(); DisableImm();
} }
ImmAssociateContext(HWND, himc); ImmAssociateContext(Hwnd, himc);
ImmReleaseContext(HWND, himc); ImmReleaseContext(Hwnd, himc);
_currentHimc = himc; _currentHimc = himc;
_caretManager.TryCreate(HWND); _caretManager.TryCreate(Hwnd);
} }
} }
@ -67,7 +68,7 @@ namespace Avalonia.Win32.Input
Reset(); Reset();
ImmAssociateContext(HWND, IntPtr.Zero); ImmAssociateContext(Hwnd, IntPtr.Zero);
_caretManager.TryDestroy(); _caretManager.TryDestroy();
@ -76,7 +77,7 @@ namespace Avalonia.Win32.Input
public void SetLanguageAndWindow(WindowImpl parent, IntPtr hwnd, IntPtr HKL) public void SetLanguageAndWindow(WindowImpl parent, IntPtr hwnd, IntPtr HKL)
{ {
HWND = hwnd; Hwnd = hwnd;
_parent = parent; _parent = parent;
_langId = PRIMARYLANGID(LGID(HKL)); _langId = PRIMARYLANGID(LGID(HKL));
@ -98,9 +99,9 @@ namespace Avalonia.Win32.Input
{ {
DisableImm(); DisableImm();
HWND = IntPtr.Zero; Hwnd = IntPtr.Zero;
_parent = null; _parent = null;
_client = null; Client = null;
_langId = 0; _langId = 0;
IsComposing = false; IsComposing = false;
@ -108,13 +109,13 @@ namespace Avalonia.Win32.Input
//Dependant on CurrentThread. When Avalonia will support Multiple Dispatchers - //Dependant on CurrentThread. When Avalonia will support Multiple Dispatchers -
//every Dispatcher should have their own InputMethod. //every Dispatcher should have their own InputMethod.
public static Imm32InputMethod Current { get; } = new Imm32InputMethod(); public static Imm32InputMethod Current { get; } = new();
public void Reset() public void Reset()
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
var himc = ImmGetContext(HWND); var himc = ImmGetContext(Hwnd);
if (IsComposing) if (IsComposing)
{ {
@ -123,13 +124,13 @@ namespace Avalonia.Win32.Input
IsComposing = false; IsComposing = false;
} }
ImmReleaseContext(HWND, himc); ImmReleaseContext(Hwnd, himc);
}); });
} }
public void SetClient(ITextInputMethodClient client) public void SetClient(ITextInputMethodClient? client)
{ {
_client = client; Client = client;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
@ -152,7 +153,7 @@ namespace Avalonia.Win32.Input
public void SetCursorRect(Rect rect) public void SetCursorRect(Rect rect)
{ {
var focused = GetActiveWindow() == HWND; var focused = GetActiveWindow() == Hwnd;
if (!focused) if (!focused)
{ {
@ -161,7 +162,7 @@ namespace Avalonia.Win32.Input
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
var himc = ImmGetContext(HWND); var himc = ImmGetContext(Hwnd);
if (himc == IntPtr.Zero) if (himc == IntPtr.Zero)
{ {
@ -170,7 +171,7 @@ namespace Avalonia.Win32.Input
MoveImeWindow(rect, himc); MoveImeWindow(rect, himc);
ImmReleaseContext(HWND, himc); ImmReleaseContext(Hwnd, himc);
}); });
} }
@ -219,7 +220,7 @@ namespace Avalonia.Win32.Input
// the caret to move the position of their candidate windows. // the caret to move the position of their candidate windows.
// On the other hand, Korean IMEs require the lower-left corner of the // On the other hand, Korean IMEs require the lower-left corner of the
// caret to move their candidate windows. // caret to move their candidate windows.
y2 += _caretMargin; y2 += CaretMargin;
} }
// Need to return here since some Chinese IMEs would stuck if set // Need to return here since some Chinese IMEs would stuck if set
@ -238,7 +239,7 @@ namespace Avalonia.Win32.Input
dwIndex = 0, dwIndex = 0,
dwStyle = CFS_EXCLUDE, dwStyle = CFS_EXCLUDE,
ptCurrentPos = new POINT {X = x1, Y = y1}, ptCurrentPos = new POINT {X = x1, Y = y1},
rcArea = new RECT {left = x1, top = y1, right = x2, bottom = y2 + _caretMargin} rcArea = new RECT {left = x1, top = y1, right = x2, bottom = y2 + CaretMargin}
}; };
ImmSetCandidateWindow(himc, ref excludeRectangle); ImmSetCandidateWindow(himc, ref excludeRectangle);
@ -275,34 +276,21 @@ namespace Avalonia.Win32.Input
return; return;
} }
if(!IsActive || !_client.SupportsPreedit) if(!IsActive || !Client.SupportsPreedit)
{ {
return; return;
} }
var composition = GetCompositionString(); var composition = GetCompositionString();
_client.SetPreeditText(composition); Client.SetPreeditText(composition);
} }
private string GetCompositionString() private string? GetCompositionString()
{ {
var himc = ImmGetContext(HWND); var himc = ImmGetContext(Hwnd);
var length = ImmGetCompositionString(himc, GCS.GCS_COMPSTR, IntPtr.Zero, 0); return ImmGetCompositionString(himc, GCS.GCS_COMPSTR);
var buffer = new byte[length];
unsafe
{
fixed (byte* bufferPtr = buffer)
{
var error = ImmGetCompositionString(himc, GCS.GCS_COMPSTR, (IntPtr)bufferPtr, (uint)length);
return Encoding.Unicode.GetString(buffer, 0, buffer.Length);
}
}
} }
~Imm32InputMethod() ~Imm32InputMethod()

9
src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs

@ -5,9 +5,10 @@ using Avalonia.Win32.Interop;
namespace Avalonia.Win32.Input namespace Avalonia.Win32.Input
{ {
class WindowsMouseDevice : MouseDevice internal class WindowsMouseDevice : MouseDevice
{ {
private readonly IPointer _pointer; private readonly IPointer _pointer;
public WindowsMouseDevice() : base(WindowsMousePointer.CreatePointer(out var pointer)) public WindowsMouseDevice() : base(WindowsMousePointer.CreatePointer(out var pointer))
{ {
_pointer = pointer; _pointer = pointer;
@ -15,14 +16,14 @@ namespace Avalonia.Win32.Input
// Normally user should use IPointer.Capture instead of MouseDevice.Capture, // Normally user should use IPointer.Capture instead of MouseDevice.Capture,
// But on Windows we need to handle WM_MOUSE capture manually without having access to the Pointer. // But on Windows we need to handle WM_MOUSE capture manually without having access to the Pointer.
internal void Capture(IInputElement control) internal void Capture(IInputElement? control)
{ {
_pointer.Capture(control); _pointer.Capture(control);
} }
internal class WindowsMousePointer : Pointer internal class WindowsMousePointer : Pointer
{ {
private WindowsMousePointer() : base(Pointer.GetNextFreeId(),PointerType.Mouse, true) private WindowsMousePointer() : base(GetNextFreeId(),PointerType.Mouse, true)
{ {
} }
@ -31,7 +32,7 @@ namespace Avalonia.Win32.Input
return pointer = new WindowsMousePointer(); return pointer = new WindowsMousePointer();
} }
protected override void PlatformCapture(IInputElement element) protected override void PlatformCapture(IInputElement? element)
{ {
var hwnd = (TopLevel.GetTopLevel(element as Visual)?.PlatformImpl as WindowImpl) var hwnd = (TopLevel.GetTopLevel(element as Visual)?.PlatformImpl as WindowImpl)
?.Handle.Handle; ?.Handle.Handle;

3
src/Windows/Avalonia.Win32/Interop/Automation/IGridProvider.cs

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
@ -8,7 +7,7 @@ namespace Avalonia.Win32.Interop.Automation
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IGridProvider public interface IGridProvider
{ {
IRawElementProviderSimple GetItem(int row, int column); IRawElementProviderSimple? GetItem(int row, int column);
int RowCount { get; } int RowCount { get; }
int ColumnCount { get; } int ColumnCount { get; }
} }

2
src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderFragment.cs

@ -1,8 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#nullable enable
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
{ {
[ComVisible(true)] [ComVisible(true)]

5
src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderFragmentRoot.cs

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
@ -8,7 +7,7 @@ namespace Avalonia.Win32.Interop.Automation
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IRawElementProviderFragmentRoot : IRawElementProviderFragment public interface IRawElementProviderFragmentRoot : IRawElementProviderFragment
{ {
IRawElementProviderFragment ElementProviderFromPoint(double x, double y); IRawElementProviderFragment? ElementProviderFromPoint(double x, double y);
IRawElementProviderFragment GetFocus(); IRawElementProviderFragment? GetFocus();
} }
} }

2
src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderSimple.cs

@ -1,8 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#nullable enable
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
{ {
[Flags] [Flags]

5
src/Windows/Avalonia.Win32/Interop/Automation/IRawElementProviderSimple2.cs

@ -1,7 +1,4 @@
using System; using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
#nullable enable
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
{ {

2
src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation

3
src/Windows/Avalonia.Win32/Interop/Automation/IValueProvider.cs

@ -1,8 +1,5 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#nullable enable
namespace Avalonia.Win32.Interop.Automation namespace Avalonia.Win32.Interop.Automation
{ {
[ComVisible(true)] [ComVisible(true)]

10
src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreProviderApi.cs

@ -71,21 +71,21 @@ namespace Avalonia.Win32.Interop.Automation
public static extern bool UiaClientsAreListening(); public static extern bool UiaClientsAreListening();
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr UiaReturnRawElementProvider(IntPtr hwnd, IntPtr wParam, IntPtr lParam, IRawElementProviderSimple el); public static extern IntPtr UiaReturnRawElementProvider(IntPtr hwnd, IntPtr wParam, IntPtr lParam, IRawElementProviderSimple? el);
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern int UiaHostProviderFromHwnd(IntPtr hwnd, [MarshalAs(UnmanagedType.Interface)] out IRawElementProviderSimple provider); public static extern int UiaHostProviderFromHwnd(IntPtr hwnd, [MarshalAs(UnmanagedType.Interface)] out IRawElementProviderSimple provider);
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern int UiaRaiseAutomationEvent(IRawElementProviderSimple provider, int id); public static extern int UiaRaiseAutomationEvent(IRawElementProviderSimple? provider, int id);
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern int UiaRaiseAutomationPropertyChangedEvent(IRawElementProviderSimple provider, int id, object oldValue, object newValue); public static extern int UiaRaiseAutomationPropertyChangedEvent(IRawElementProviderSimple? provider, int id, object? oldValue, object? newValue);
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern int UiaRaiseStructureChangedEvent(IRawElementProviderSimple provider, StructureChangeType structureChangeType, int[] runtimeId, int runtimeIdLen); public static extern int UiaRaiseStructureChangedEvent(IRawElementProviderSimple? provider, StructureChangeType structureChangeType, int[]? runtimeId, int runtimeIdLen);
[DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", CharSet = CharSet.Unicode)]
public static extern int UiaDisconnectProvider(IRawElementProviderSimple provider); public static extern int UiaDisconnectProvider(IRawElementProviderSimple? provider);
} }
} }

4
src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs

@ -63,8 +63,8 @@ namespace Avalonia.Win32.Interop.Automation
} }
#endif #endif
var comConfig = AppContext.GetData("System.Runtime.InteropServices.BuiltInComInterop.IsSupported"); var comConfig = AppContext.GetData("System.Runtime.InteropServices.BuiltInComInterop.IsSupported") as string;
return comConfig == null || bool.Parse(comConfig.ToString()); return comConfig == null || bool.Parse(comConfig);
} }
[DllImport("UIAutomationCore.dll", EntryPoint = "UiaLookupId", CharSet = CharSet.Unicode)] [DllImport("UIAutomationCore.dll", EntryPoint = "UiaLookupId", CharSet = CharSet.Unicode)]

15
src/Windows/Avalonia.Win32/Interop/TaskBarList.cs

@ -7,8 +7,8 @@ namespace Avalonia.Win32.Interop
internal class TaskBarList internal class TaskBarList
{ {
private static IntPtr s_taskBarList; private static IntPtr s_taskBarList;
private static HrInit s_hrInitDelegate; private static HrInit? s_hrInitDelegate;
private static MarkFullscreenWindow s_markFullscreenWindowDelegate; private static MarkFullscreenWindow? s_markFullscreenWindowDelegate;
/// <summary> /// <summary>
/// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
@ -28,10 +28,7 @@ namespace Avalonia.Win32.Interop
{ {
var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer(); var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer();
if (s_hrInitDelegate is null) s_hrInitDelegate ??= Marshal.GetDelegateForFunctionPointer<HrInit>((*ptr)->HrInit);
{
s_hrInitDelegate = Marshal.GetDelegateForFunctionPointer<HrInit>((*ptr)->HrInit);
}
if (s_hrInitDelegate(s_taskBarList) != HRESULT.S_OK) if (s_hrInitDelegate(s_taskBarList) != HRESULT.S_OK)
{ {
@ -44,10 +41,8 @@ namespace Avalonia.Win32.Interop
{ {
var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer(); var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer();
if (s_markFullscreenWindowDelegate is null) s_markFullscreenWindowDelegate ??=
{ Marshal.GetDelegateForFunctionPointer<MarkFullscreenWindow>((*ptr)->MarkFullscreenWindow);
s_markFullscreenWindowDelegate = Marshal.GetDelegateForFunctionPointer<MarkFullscreenWindow>((*ptr)->MarkFullscreenWindow);
}
s_markFullscreenWindowDelegate(s_taskBarList, hwnd, fullscreen); s_markFullscreenWindowDelegate(s_taskBarList, hwnd, fullscreen);
} }

54
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1,14 +1,12 @@
#nullable enable
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices.ComTypes;
using System.Text; using System.Text;
using Avalonia.MicroCom;
using MicroCom.Runtime; using MicroCom.Runtime;
using Avalonia.Win32.Win32Com;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
#pragma warning disable 169, 649 #pragma warning disable 169, 649
@ -1092,7 +1090,7 @@ namespace Avalonia.Win32.Interop
public const int SizeOf_BITMAPINFOHEADER = 40; public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)] [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
unsafe internal static extern int GetMouseMovePointsEx( internal static extern int GetMouseMovePointsEx(
uint cbSize, MOUSEMOVEPOINT* pointsIn, uint cbSize, MOUSEMOVEPOINT* pointsIn,
MOUSEMOVEPOINT* pointsBufferOut, int nBufPoints, uint resolution); MOUSEMOVEPOINT* pointsBufferOut, int nBufPoints, uint resolution);
@ -1167,7 +1165,7 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr CreateWindowEx( public static extern IntPtr CreateWindowEx(
int dwExStyle, int dwExStyle,
uint lpClassName, uint lpClassName,
string lpWindowName, string? lpWindowName,
uint dwStyle, uint dwStyle,
int x, int x,
int y, int y,
@ -1218,7 +1216,7 @@ namespace Avalonia.Win32.Interop
public static extern int GetMessageTime(); public static extern int GetMessageTime();
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string lpModuleName); public static extern IntPtr GetModuleHandle(string? lpModuleName);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern int GetSystemMetrics(SystemMetric smIndex); public static extern int GetSystemMetrics(SystemMetric smIndex);
@ -1255,7 +1253,7 @@ namespace Avalonia.Win32.Interop
} }
else else
{ {
return (uint)SetWindowLong64b(hWnd, nIndex, new IntPtr((uint)value)).ToInt32(); return (uint)SetWindowLong64b(hWnd, nIndex, new IntPtr(value)).ToInt32();
} }
} }
@ -1421,7 +1419,7 @@ namespace Avalonia.Win32.Interop
public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance); public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")]
public static extern bool SetWindowText(IntPtr hwnd, string lpString); public static extern bool SetWindowText(IntPtr hwnd, string? lpString);
public enum ClassLongIndex : int public enum ClassLongIndex : int
{ {
@ -1482,7 +1480,7 @@ namespace Avalonia.Win32.Interop
internal static extern int CoCreateInstance(ref Guid clsid, internal static extern int CoCreateInstance(ref Guid clsid,
IntPtr ignore1, int ignore2, ref Guid iid, [Out] out IntPtr pUnkOuter); IntPtr ignore1, int ignore2, ref Guid iid, [Out] out IntPtr pUnkOuter);
internal unsafe static T CreateInstance<T>(ref Guid clsid, ref Guid iid) where T : IUnknown internal static T CreateInstance<T>(ref Guid clsid, ref Guid iid) where T : IUnknown
{ {
var hresult = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out IntPtr pUnk); var hresult = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out IntPtr pUnk);
if (hresult != 0) if (hresult != 0)
@ -1490,7 +1488,7 @@ namespace Avalonia.Win32.Interop
throw new COMException("CreateInstance", hresult); throw new COMException("CreateInstance", hresult);
} }
using var unk = MicroComRuntime.CreateProxyFor<IUnknown>(pUnk, true); using var unk = MicroComRuntime.CreateProxyFor<IUnknown>(pUnk, true);
return MicroComRuntime.QueryInterface<T>(unk); return unk.QueryInterface<T>();
} }
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
@ -1581,7 +1579,7 @@ namespace Avalonia.Win32.Interop
public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi); public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi);
[DllImport("user32")] [DllImport("user32")]
public static extern unsafe bool GetTouchInputInfo( public static extern bool GetTouchInputInfo(
IntPtr hTouchInput, IntPtr hTouchInput,
uint cInputs, uint cInputs,
TOUCHINPUT* pInputs, TOUCHINPUT* pInputs,
@ -1680,7 +1678,7 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr GlobalSize(IntPtr hGlobal); public static extern IntPtr GlobalSize(IntPtr hGlobal);
[DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)] [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)]
public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder? lpszFile, int cch);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)] [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
internal static extern void DoDragDrop(IntPtr dataObject, IntPtr dropSource, int allowedEffects, [Out] out int finalEffect); internal static extern void DoDragDrop(IntPtr dataObject, IntPtr dropSource, int allowedEffects, [Out] out int finalEffect);
@ -1734,7 +1732,7 @@ namespace Avalonia.Win32.Interop
public DWM_BLURBEHIND(bool enabled) public DWM_BLURBEHIND(bool enabled)
{ {
fEnable = enabled ? true : false; fEnable = enabled;
hRgnBlur = IntPtr.Zero; hRgnBlur = IntPtr.Zero;
fTransitionOnMaximized = false; fTransitionOnMaximized = false;
dwFlags = DWM_BB.Enable; dwFlags = DWM_BB.Enable;
@ -1855,20 +1853,22 @@ namespace Avalonia.Win32.Interop
[DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode)] [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
public static extern int ImmGetCompositionString(IntPtr hIMC, GCS dwIndex, [Out, Optional] IntPtr lpBuf, uint dwBufLen); public static extern int ImmGetCompositionString(IntPtr hIMC, GCS dwIndex, [Out, Optional] IntPtr lpBuf, uint dwBufLen);
public static string ImmGetCompositionString(IntPtr hIMC, GCS dwIndex) public static string? ImmGetCompositionString(IntPtr hIMC, GCS dwIndex)
{ {
int bufferLength = ImmGetCompositionString(hIMC, dwIndex, IntPtr.Zero, 0); int bufferLength = ImmGetCompositionString(hIMC, dwIndex, IntPtr.Zero, 0);
if (bufferLength > 0) if (bufferLength > 0)
{ {
var buffer = new byte[bufferLength]; var buffer = bufferLength <= 64 ? stackalloc byte[bufferLength] : new byte[bufferLength];
fixed(byte* bufferPtr = buffer) fixed (byte* bufferPtr = buffer)
{ {
var error = ImmGetCompositionString(hIMC, dwIndex, (IntPtr)bufferPtr, (uint)bufferLength); var result = ImmGetCompositionString(hIMC, dwIndex, (IntPtr)bufferPtr, (uint)bufferLength);
if (result >= 0)
return Marshal.PtrToStringUni((IntPtr)bufferPtr); {
} return Marshal.PtrToStringUni((IntPtr)bufferPtr);
}
}
} }
return null; return null;
@ -2380,7 +2380,7 @@ namespace Avalonia.Win32.Interop
} }
} }
[StructLayoutAttribute(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct _DROPFILES internal struct _DROPFILES
{ {
public Int32 pFiles; public Int32 pFiles;
@ -2390,7 +2390,7 @@ namespace Avalonia.Win32.Interop
public bool fWide; public bool fWide;
} }
[StructLayoutAttribute(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct STGMEDIUM internal struct STGMEDIUM
{ {
public TYMED tymed; public TYMED tymed;
@ -2398,7 +2398,7 @@ namespace Avalonia.Win32.Interop
public IntPtr pUnkForRelease; public IntPtr pUnkForRelease;
} }
[StructLayoutAttribute(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct FORMATETC internal struct FORMATETC
{ {
public ushort cfFormat; public ushort cfFormat;
@ -2507,14 +2507,14 @@ namespace Avalonia.Win32.Interop
public int uCallbackMessage; public int uCallbackMessage;
public IntPtr hIcon; public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szTip; public string? szTip;
public int dwState = 0; public int dwState = 0;
public int dwStateMask = 0; public int dwStateMask = 0;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szInfo; public string? szInfo;
public int uTimeoutOrVersion; public int uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szInfoTitle; public string? szInfoTitle;
public NIIF dwInfoFlags; public NIIF dwInfoFlags;
} }
} }

19
src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Threading; using System.Threading;
using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
@ -18,8 +17,8 @@ namespace Avalonia.Win32
SetSynchronizationContext(this); SetSynchronizationContext(this);
} }
public override void Post(SendOrPostCallback d, object state) => _inner.Post(d, state); public override void Post(SendOrPostCallback d, object? state) => _inner.Post(d, state);
public override void Send(SendOrPostCallback d, object state) => _inner.Send(d, state); public override void Send(SendOrPostCallback d, object? state) => _inner.Send(d, state);
#if !NET6_0_OR_GREATER #if !NET6_0_OR_GREATER
[PrePrepareMethod] [PrePrepareMethod]
@ -32,23 +31,15 @@ namespace Avalonia.Win32
public void Dispose() => SetSynchronizationContext(_inner); public void Dispose() => SetSynchronizationContext(_inner);
public static IDisposable Use() public static IDisposable? Use()
{ {
var current = Current; var current = Current;
if (current == null) return current is null or NonPumpingSyncContext ? null : new NonPumpingSyncContext(current);
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
return null;
}
if (current is NonPumpingSyncContext)
return null;
return new NonPumpingSyncContext(current);
} }
internal class HelperImpl : NonPumpingLockHelper.IHelperImpl internal class HelperImpl : NonPumpingLockHelper.IHelperImpl
{ {
IDisposable NonPumpingLockHelper.IHelperImpl.Use() => NonPumpingSyncContext.Use(); IDisposable? NonPumpingLockHelper.IHelperImpl.Use() => NonPumpingSyncContext.Use();
} }
} }
} }

9
src/Windows/Avalonia.Win32/OffscreenParentWindow.cs

@ -1,17 +1,18 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Platform;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
class OffscreenParentWindow internal class OffscreenParentWindow
{ {
public static IntPtr Handle { get; } = CreateParentWindow(); public static IntPtr Handle { get; } = CreateParentWindow();
private static UnmanagedMethods.WndProc s_wndProcDelegate;
private static UnmanagedMethods.WndProc? s_wndProcDelegate;
private static IntPtr CreateParentWindow() private static IntPtr CreateParentWindow()
{ {
s_wndProcDelegate = new UnmanagedMethods.WndProc(ParentWndProc); s_wndProcDelegate = ParentWndProc;
var wndClassEx = new UnmanagedMethods.WNDCLASSEX var wndClassEx = new UnmanagedMethods.WNDCLASSEX
{ {

12
src/Windows/Avalonia.Win32/OleContext.cs

@ -11,18 +11,16 @@ namespace Avalonia.Win32
{ {
internal class OleContext internal class OleContext
{ {
private static OleContext s_current; private static OleContext? s_current;
internal static OleContext Current internal static OleContext? Current
{ {
get get
{ {
if (!IsValidOleThread()) if (!IsValidOleThread())
return null; return null;
if (s_current == null) return s_current ??= new OleContext();
s_current = new OleContext();
return s_current;
} }
} }
@ -41,7 +39,7 @@ namespace Avalonia.Win32
Thread.CurrentThread.GetApartmentState() == ApartmentState.STA; Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;
} }
internal bool RegisterDragDrop(IPlatformHandle hwnd, IDropTarget target) internal bool RegisterDragDrop(IPlatformHandle? hwnd, IDropTarget? target)
{ {
if (hwnd?.HandleDescriptor != "HWND" || target == null) if (hwnd?.HandleDescriptor != "HWND" || target == null)
{ {
@ -52,7 +50,7 @@ namespace Avalonia.Win32
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK; return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK;
} }
internal bool UnregisterDragDrop(IPlatformHandle hwnd) internal bool UnregisterDragDrop(IPlatformHandle? hwnd)
{ {
if (hwnd?.HandleDescriptor != "HWND") if (hwnd?.HandleDescriptor != "HWND")
{ {

17
src/Windows/Avalonia.Win32/OleDataObject.cs

@ -8,7 +8,6 @@ using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Binary;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.MicroCom;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using MicroCom.Runtime; using MicroCom.Runtime;
@ -35,23 +34,23 @@ namespace Avalonia.Win32
return GetDataFormatsCore().Distinct(); return GetDataFormatsCore().Distinct();
} }
public string GetText() public string? GetText()
{ {
return (string)GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT); return (string?)GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT);
} }
public IEnumerable<string> GetFileNames() public IEnumerable<string>? GetFileNames()
{ {
return (IEnumerable<string>)GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT); return (IEnumerable<string>?)GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT);
} }
public object Get(string dataFormat) public object? Get(string dataFormat)
{ {
return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT); return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT);
} }
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "We still use BinaryFormatter for WinForms dragndrop compatability")] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "We still use BinaryFormatter for WinForms dragndrop compatability")]
private unsafe object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) private unsafe object? GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
{ {
var formatEtc = new Interop.FORMATETC(); var formatEtc = new Interop.FORMATETC();
formatEtc.cfFormat = ClipboardFormats.GetFormat(format); formatEtc.cfFormat = ClipboardFormats.GetFormat(format);
@ -100,7 +99,7 @@ namespace Avalonia.Win32
private static IEnumerable<string> ReadFileNamesFromHGlobal(IntPtr hGlobal) private static IEnumerable<string> ReadFileNamesFromHGlobal(IntPtr hGlobal)
{ {
List<string> files = new List<string>(); var files = new List<string>();
int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0); int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0);
if (fileCount > 0) if (fileCount > 0)
{ {
@ -118,7 +117,7 @@ namespace Avalonia.Win32
return files; return files;
} }
private static string ReadStringFromHGlobal(IntPtr hGlobal) private static string? ReadStringFromHGlobal(IntPtr hGlobal)
{ {
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try try

43
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -1,5 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.MicroCom; using Avalonia.MicroCom;
@ -13,16 +13,16 @@ namespace Avalonia.Win32
internal class OleDropTarget : CallbackBase, Win32Com.IDropTarget internal class OleDropTarget : CallbackBase, Win32Com.IDropTarget
{ {
private readonly IInputRoot _target; private readonly IInputRoot _target;
private readonly ITopLevelImpl _tl; private readonly ITopLevelImpl _topLevel;
private readonly IDragDropDevice _dragDevice; private readonly IDragDropDevice _dragDevice;
private IDataObject _currentDrag = null; private IDataObject? _currentDrag;
public OleDropTarget(ITopLevelImpl tl, IInputRoot target) public OleDropTarget(ITopLevelImpl topLevel, IInputRoot target, IDragDropDevice dragDevice)
{ {
_dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>(); _topLevel = topLevel;
_tl = tl;
_target = target; _target = target;
_dragDevice = dragDevice;
} }
public static DropEffect ConvertDropEffect(DragDropEffects operation) public static DropEffect ConvertDropEffect(DragDropEffects operation)
@ -71,10 +71,11 @@ namespace Avalonia.Win32
unsafe void Win32Com.IDropTarget.DragEnter(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect) unsafe void Win32Com.IDropTarget.DragEnter(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{ {
var dispatch = _tl?.Input; var dispatch = _topLevel.Input;
if (dispatch == null) if (dispatch == null)
{ {
*pdwEffect= (int)DropEffect.None; *pdwEffect= (int)DropEffect.None;
return;
} }
SetDataObject(pDataObj); SetDataObject(pDataObj);
@ -94,10 +95,11 @@ namespace Avalonia.Win32
unsafe void Win32Com.IDropTarget.DragOver(int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect) unsafe void Win32Com.IDropTarget.DragOver(int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{ {
var dispatch = _tl?.Input; var dispatch = _topLevel.Input;
if (dispatch == null) if (dispatch == null || _currentDrag is null)
{ {
*pdwEffect = (int)DropEffect.None; *pdwEffect = (int)DropEffect.None;
return;
} }
var args = new RawDragEvent( var args = new RawDragEvent(
@ -115,14 +117,20 @@ namespace Avalonia.Win32
void Win32Com.IDropTarget.DragLeave() void Win32Com.IDropTarget.DragLeave()
{ {
var dispatch = _topLevel.Input;
if (dispatch == null || _currentDrag is null)
{
return;
}
try try
{ {
_tl?.Input(new RawDragEvent( dispatch(new RawDragEvent(
_dragDevice, _dragDevice,
RawDragEventType.DragLeave, RawDragEventType.DragLeave,
_target, _target,
default, default,
null, _currentDrag,
DragDropEffects.None, DragDropEffects.None,
RawInputModifiers.None RawInputModifiers.None
)); ));
@ -137,10 +145,11 @@ namespace Avalonia.Win32
{ {
try try
{ {
var dispatch = _tl?.Input; var dispatch = _topLevel.Input;
if (dispatch == null) if (dispatch == null)
{ {
*pdwEffect = (int)DropEffect.None; *pdwEffect = (int)DropEffect.None;
return;
} }
SetDataObject(pDataObj); SetDataObject(pDataObj);
@ -163,6 +172,7 @@ namespace Avalonia.Win32
} }
} }
[MemberNotNull(nameof(_currentDrag))]
private void SetDataObject(Win32Com.IDataObject pDataObj) private void SetDataObject(Win32Com.IDataObject pDataObj)
{ {
var newDrag = GetAvaloniaObjectFromCOM(pDataObj); var newDrag = GetAvaloniaObjectFromCOM(pDataObj);
@ -178,9 +188,9 @@ namespace Avalonia.Win32
// OleDataObject keeps COM reference, so it should be disposed. // OleDataObject keeps COM reference, so it should be disposed.
if (_currentDrag is OleDataObject oleDragSource) if (_currentDrag is OleDataObject oleDragSource)
{ {
oleDragSource?.Dispose(); oleDragSource.Dispose();
_currentDrag = null;
} }
_currentDrag = null;
} }
private Point GetDragLocation(UnmanagedMethods.POINT dragPoint) private Point GetDragLocation(UnmanagedMethods.POINT dragPoint)
@ -194,7 +204,7 @@ namespace Avalonia.Win32
ReleaseDataObject(); ReleaseDataObject();
} }
public static unsafe IDataObject GetAvaloniaObjectFromCOM(Win32Com.IDataObject pDataObj) public static IDataObject GetAvaloniaObjectFromCOM(Win32Com.IDataObject pDataObj)
{ {
if (pDataObj is null) if (pDataObj is null)
{ {
@ -205,8 +215,7 @@ namespace Avalonia.Win32
return disposableDataObject; return disposableDataObject;
} }
var dataObject = MicroComRuntime.TryUnwrapManagedObject(pDataObj) as DataObject; if (MicroComRuntime.TryUnwrapManagedObject(pDataObj) is DataObject dataObject)
if (dataObject is not null)
{ {
return dataObject; return dataObject;
} }

6
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs

@ -14,7 +14,7 @@ internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory
Display: AngleWin32EglDisplay { PlatformApi: AngleOptions.PlatformApi.DirectX11 } Display: AngleWin32EglDisplay { PlatformApi: AngleOptions.PlatformApi.DirectX11 }
} && surface is IDirect3D11TexturePlatformSurface; } && surface is IDirect3D11TexturePlatformSurface;
class RenderTargetWrapper : EglPlatformSurfaceRenderTargetBase private class RenderTargetWrapper : EglPlatformSurfaceRenderTargetBase
{ {
private readonly AngleWin32EglDisplay _angle; private readonly AngleWin32EglDisplay _angle;
private readonly IDirect3D11TextureRenderTarget _target; private readonly IDirect3D11TextureRenderTarget _target;
@ -31,8 +31,8 @@ internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory
{ {
var success = false; var success = false;
var contextLock = Context.EnsureCurrent(); var contextLock = Context.EnsureCurrent();
IDirect3D11TextureRenderTargetRenderSession session = null; IDirect3D11TextureRenderTargetRenderSession? session = null;
EglSurface surface = null; EglSurface? surface = null;
try try
{ {
try try

2
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleEglInterface.cs

@ -22,7 +22,7 @@ namespace Avalonia.OpenGL.Angle
} }
[GetProcAddress("eglCreateDeviceANGLE", true)] [GetProcAddress("eglCreateDeviceANGLE", true)]
public partial IntPtr CreateDeviceANGLE(int deviceType, IntPtr nativeDevice, int[] attribs); public partial IntPtr CreateDeviceANGLE(int deviceType, IntPtr nativeDevice, int[]? attribs);
[GetProcAddress("eglReleaseDeviceANGLE", true)] [GetProcAddress("eglReleaseDeviceANGLE", true)]
public partial void ReleaseDeviceANGLE(IntPtr device); public partial void ReleaseDeviceANGLE(IntPtr device);

30
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleExternalD3D11Texture2D.cs

@ -1,8 +1,5 @@
#nullable enable
using System; using System;
using System.Threading;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.DirectX; using Avalonia.Win32.DirectX;
@ -15,9 +12,12 @@ namespace Avalonia.Win32.OpenGl.Angle;
internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
{ {
private readonly EglContext _context; private readonly EglContext _context;
private ID3D11Texture2D _texture2D; private ID3D11Texture2D? _texture2D;
private EglSurface _eglSurface; private EglSurface? _eglSurface;
private IDXGIKeyedMutex _mutex; private IDXGIKeyedMutex? _mutex;
private IDXGIKeyedMutex Mutex
=> _mutex ?? throw new ObjectDisposedException(nameof(AngleExternalMemoryD3D11Texture2D));
public unsafe AngleExternalMemoryD3D11Texture2D(EglContext context, ID3D11Texture2D texture2D, PlatformGraphicsExternalImageProperties props) public unsafe AngleExternalMemoryD3D11Texture2D(EglContext context, ID3D11Texture2D texture2D, PlatformGraphicsExternalImageProperties props)
{ {
@ -40,14 +40,14 @@ internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
int temp = 0; int temp = 0;
gl.GenTextures(1, &temp); gl.GenTextures(1, &temp);
TextureId = temp; TextureId = temp;
gl.BindTexture(GlConsts.GL_TEXTURE_2D, TextureId); gl.BindTexture(GL_TEXTURE_2D, TextureId);
if (_context.Display.EglInterface.BindTexImage(_context.Display.Handle, _eglSurface.DangerousGetHandle(), if (_context.Display.EglInterface.BindTexImage(_context.Display.Handle, _eglSurface.DangerousGetHandle(),
EGL_BACK_BUFFER) == 0) EGL_BACK_BUFFER) == 0)
throw OpenGlException.GetFormattedException("eglBindTexImage", _context.Display.EglInterface); throw OpenGlException.GetFormattedException("eglBindTexImage", _context.Display.EglInterface);
} }
public void Dispose() public void Dispose()
{ {
@ -56,17 +56,15 @@ internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
_context.GlInterface.DeleteTexture(TextureId); _context.GlInterface.DeleteTexture(TextureId);
TextureId = 0; TextureId = 0;
_eglSurface?.Dispose(); _eglSurface?.Dispose();
_eglSurface = null!; _eglSurface = null;
_texture2D?.Dispose(); _texture2D?.Dispose();
_texture2D = null!; _texture2D = null;
_mutex?.Dispose(); _mutex?.Dispose();
_mutex = null!; _mutex = null;
} }
public void AcquireKeyedMutex(uint key) => Mutex.AcquireSync(key, int.MaxValue);
public void AcquireKeyedMutex(uint key) => _mutex.AcquireSync(key, int.MaxValue); public void ReleaseKeyedMutex(uint key) => Mutex.ReleaseSync(key);
public void ReleaseKeyedMutex(uint key) => _mutex.ReleaseSync(key);
public int TextureId { get; private set; } public int TextureId { get; private set; }
public int InternalFormat { get; } public int InternalFormat { get; }
@ -75,7 +73,7 @@ internal class AngleExternalMemoryD3D11Texture2D : IGlExternalImageTexture
internal class AngleExternalMemoryD3D11ExportedTexture2D : AngleExternalMemoryD3D11Texture2D, IGlExportableExternalImageTexture internal class AngleExternalMemoryD3D11ExportedTexture2D : AngleExternalMemoryD3D11Texture2D, IGlExportableExternalImageTexture
{ {
static IPlatformHandle GetHandle(ID3D11Texture2D texture2D) private static IPlatformHandle GetHandle(ID3D11Texture2D texture2D)
{ {
using var resource = texture2D.QueryInterface<IDXGIResource>(); using var resource = texture2D.QueryInterface<IDXGIResource>();
return new PlatformHandle(resource.SharedHandle, return new PlatformHandle(resource.SharedHandle,

6
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleExternalObjectsFeature.cs

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Avalonia.Controls.Documents;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
@ -93,8 +91,8 @@ internal class AngleExternalObjectsFeature : IGlContextExternalObjectsFeature, I
return default; return default;
} }
public byte[] DeviceLuid { get; } public byte[]? DeviceLuid { get; }
public byte[] DeviceUuid { get; } public byte[]? DeviceUuid => null;
public void Dispose() public void Dispose()
{ {

3
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32EglDisplay.cs

@ -1,4 +1,3 @@
#nullable enable annotations
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -97,7 +96,7 @@ namespace Avalonia.Win32.OpenGl.Angle
DirectXUnmanagedMethods.D3D11CreateDevice(chosenAdapter?.GetNativeIntPtr() ?? IntPtr.Zero, DirectXUnmanagedMethods.D3D11CreateDevice(chosenAdapter?.GetNativeIntPtr() ?? IntPtr.Zero,
D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_UNKNOWN, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_UNKNOWN,
IntPtr.Zero, 0, featureLevels, (uint)featureLevels.Length, IntPtr.Zero, 0, featureLevels, (uint)featureLevels.Length,
7, out pD3dDevice, out var featureLevel, null); 7, out pD3dDevice, out _, null);
if (pD3dDevice == IntPtr.Zero) if (pD3dDevice == IntPtr.Zero)

16
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.OpenGL; using Avalonia.OpenGL;
@ -12,11 +13,14 @@ namespace Avalonia.Win32.OpenGl.Angle;
internal class AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphicsOpenGlContextFactory internal class AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphicsOpenGlContextFactory
{ {
private readonly Win32AngleEglInterface _egl; private readonly Win32AngleEglInterface _egl;
private AngleWin32EglDisplay _sharedDisplay; private readonly AngleWin32EglDisplay? _sharedDisplay;
private EglContext _sharedContext; private EglContext? _sharedContext;
public bool UsesSharedContext => PlatformApi == AngleOptions.PlatformApi.DirectX9;
[MemberNotNullWhen(true, nameof(_sharedDisplay))]
public bool UsesSharedContext => _sharedDisplay is not null;
public AngleOptions.PlatformApi PlatformApi { get; }
public AngleOptions.PlatformApi PlatformApi { get; } = AngleOptions.PlatformApi.DirectX11;
public IPlatformGraphicsContext CreateContext() public IPlatformGraphicsContext CreateContext()
{ {
if (UsesSharedContext) if (UsesSharedContext)
@ -72,7 +76,7 @@ internal class AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphics
} }
public static AngleWin32PlatformGraphics TryCreate(AngleOptions options) public static AngleWin32PlatformGraphics? TryCreate(AngleOptions? options)
{ {
Win32AngleEglInterface egl; Win32AngleEglInterface egl;
try try
@ -109,7 +113,7 @@ internal class AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphics
} }
else else
{ {
AngleWin32EglDisplay sharedDisplay = null; AngleWin32EglDisplay? sharedDisplay = null;
try try
{ {
sharedDisplay = AngleWin32EglDisplay.CreateD3D9Display(egl); sharedDisplay = AngleWin32EglDisplay.CreateD3D9Display(egl);

16
src/Windows/Avalonia.Win32/OpenGl/WglContext.cs

@ -7,22 +7,22 @@ using Avalonia.Platform;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts;
namespace Avalonia.Win32.OpenGl namespace Avalonia.Win32.OpenGl
{ {
class WglContext : IGlContext internal class WglContext : IGlContext
{ {
private object _lock = new object(); private readonly object _lock = new();
private readonly WglContext _sharedWith; private readonly WglContext? _sharedWith;
private readonly IntPtr _context; private readonly IntPtr _context;
private readonly IntPtr _hWnd; private readonly IntPtr _hWnd;
private readonly IntPtr _dc; private readonly IntPtr _dc;
private readonly int _pixelFormat; private readonly int _pixelFormat;
private readonly PixelFormatDescriptor _formatDescriptor; private readonly PixelFormatDescriptor _formatDescriptor;
public IntPtr Handle => _context; public IntPtr Handle => _context;
public WglContext(WglContext sharedWith, GlVersion version, IntPtr context, IntPtr hWnd, IntPtr dc, int pixelFormat, public WglContext(WglContext? sharedWith, GlVersion version, IntPtr context, IntPtr hWnd, IntPtr dc, int pixelFormat,
PixelFormatDescriptor formatDescriptor) PixelFormatDescriptor formatDescriptor)
{ {
Version = version; Version = version;
@ -54,7 +54,7 @@ namespace Avalonia.Win32.OpenGl
public GlVersion Version { get; } public GlVersion Version { get; }
public GlInterface GlInterface { get; } public GlInterface GlInterface { get; }
public int SampleCount { get; } public int SampleCount => 0;
public int StencilSize { get; } public int StencilSize { get; }
private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc; private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc;
@ -97,12 +97,12 @@ namespace Avalonia.Win32.OpenGl
} }
public bool CanCreateSharedContext => true; public bool CanCreateSharedContext => true;
public IGlContext CreateSharedContext(IEnumerable<GlVersion> preferredVersions = null) public IGlContext? CreateSharedContext(IEnumerable<GlVersion>? preferredVersions = null)
{ {
var versions = preferredVersions?.Append(Version).ToArray() ?? new[] { Version }; var versions = preferredVersions?.Append(Version).ToArray() ?? new[] { Version };
return WglDisplay.CreateContext(versions, _sharedWith ?? this); return WglDisplay.CreateContext(versions, _sharedWith ?? this);
} }
public object TryGetFeature(Type featureType) => null; public object? TryGetFeature(Type featureType) => null;
} }
} }

45
src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.Threading; using Avalonia.Threading;
@ -19,29 +20,31 @@ namespace Avalonia.Win32.OpenGl
private static int _defaultPixelFormat; private static int _defaultPixelFormat;
public static IntPtr OpenGl32Handle = LoadLibrary("opengl32"); public static IntPtr OpenGl32Handle = LoadLibrary("opengl32");
private delegate bool WglChoosePixelFormatARBDelegate(IntPtr hdc, int[] piAttribIList, float[] pfAttribFList, private delegate bool WglChoosePixelFormatARBDelegate(IntPtr hdc, int[]? piAttribIList, float[]? pfAttribFList,
int nMaxFormats, int[] piFormats, out int nNumFormats); int nMaxFormats, int[] piFormats, out int nNumFormats);
private static WglChoosePixelFormatARBDelegate WglChoosePixelFormatArb; private static WglChoosePixelFormatARBDelegate? s_wglChoosePixelFormatArb;
private delegate IntPtr WglCreateContextAttribsARBDelegate(IntPtr hDC, IntPtr hShareContext, int[] attribList); private delegate IntPtr WglCreateContextAttribsARBDelegate(IntPtr hDC, IntPtr hShareContext, int[]? attribList);
private static WglCreateContextAttribsARBDelegate WglCreateContextAttribsArb; private static WglCreateContextAttribsARBDelegate? s_wglCreateContextAttribsArb;
private delegate void GlDebugMessageCallbackDelegate(IntPtr callback, IntPtr userParam); private delegate void GlDebugMessageCallbackDelegate(IntPtr callback, IntPtr userParam);
private static GlDebugMessageCallbackDelegate GlDebugMessageCallback; private static GlDebugMessageCallbackDelegate? s_glDebugMessageCallback;
private delegate void DebugCallbackDelegate(int source, int type, int id, int severity, int len, IntPtr message, private delegate void DebugCallbackDelegate(int source, int type, int id, int severity, int len, IntPtr message,
IntPtr userParam); IntPtr userParam);
static bool Initialize() [MemberNotNullWhen(true, nameof(s_wglChoosePixelFormatArb))]
{ [MemberNotNullWhen(true, nameof(s_wglCreateContextAttribsArb))]
if (!_initialized.HasValue) [MemberNotNullWhen(true, nameof(s_glDebugMessageCallback))]
_initialized = InitializeCore(); private static bool Initialize() => _initialized ??= InitializeCore();
return _initialized.Value;
} [MemberNotNullWhen(true, nameof(s_wglChoosePixelFormatArb))]
static bool InitializeCore() [MemberNotNullWhen(true, nameof(s_wglCreateContextAttribsArb))]
[MemberNotNullWhen(true, nameof(s_glDebugMessageCallback))]
private static bool InitializeCore()
{ {
Dispatcher.UIThread.VerifyAccess(); Dispatcher.UIThread.VerifyAccess();
_bootstrapWindow = WglGdiResourceManager.CreateOffscreenWindow(); _bootstrapWindow = WglGdiResourceManager.CreateOffscreenWindow();
@ -64,20 +67,20 @@ namespace Avalonia.Win32.OpenGl
return false; return false;
wglMakeCurrent(_bootstrapDc, _bootstrapContext); wglMakeCurrent(_bootstrapDc, _bootstrapContext);
WglCreateContextAttribsArb = Marshal.GetDelegateForFunctionPointer<WglCreateContextAttribsARBDelegate>( s_wglCreateContextAttribsArb = Marshal.GetDelegateForFunctionPointer<WglCreateContextAttribsARBDelegate>(
wglGetProcAddress("wglCreateContextAttribsARB")); wglGetProcAddress("wglCreateContextAttribsARB"));
WglChoosePixelFormatArb = s_wglChoosePixelFormatArb =
Marshal.GetDelegateForFunctionPointer<WglChoosePixelFormatARBDelegate>( Marshal.GetDelegateForFunctionPointer<WglChoosePixelFormatARBDelegate>(
wglGetProcAddress("wglChoosePixelFormatARB")); wglGetProcAddress("wglChoosePixelFormatARB"));
GlDebugMessageCallback = s_glDebugMessageCallback =
Marshal.GetDelegateForFunctionPointer<GlDebugMessageCallbackDelegate>( Marshal.GetDelegateForFunctionPointer<GlDebugMessageCallbackDelegate>(
wglGetProcAddress("glDebugMessageCallback")); wglGetProcAddress("glDebugMessageCallback"));
var formats = new int[1]; var formats = new int[1];
WglChoosePixelFormatArb(_bootstrapDc, new int[] s_wglChoosePixelFormatArb(_bootstrapDc, new int[]
{ {
WGL_DRAW_TO_WINDOW_ARB, 1, WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
@ -106,12 +109,12 @@ namespace Avalonia.Win32.OpenGl
Console.Error.WriteLine(err); Console.Error.WriteLine(err);
} }
public static WglContext CreateContext(GlVersion[] versions, IGlContext share) public static WglContext? CreateContext(GlVersion[] versions, IGlContext? share)
{ {
if (!Initialize()) if (!Initialize())
return null; return null;
var shareContext = (WglContext)share; var shareContext = share as WglContext;
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null)) using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null))
{ {
@ -125,7 +128,7 @@ namespace Avalonia.Win32.OpenGl
IntPtr context; IntPtr context;
using (shareContext?.Lock()) using (shareContext?.Lock())
{ {
context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero, context = s_wglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero,
new[] new[]
{ {
// major // major
@ -142,7 +145,7 @@ namespace Avalonia.Win32.OpenGl
} }
using(new WglRestoreContext(dc, context, null)) using(new WglRestoreContext(dc, context, null))
GlDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero); s_glDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero);
if (context != IntPtr.Zero) if (context != IntPtr.Zero)
return new WglContext(shareContext, version, context, window, dc, return new WglContext(shareContext, version, context, window, dc,
_defaultPixelFormat, _defaultPfd); _defaultPixelFormat, _defaultPfd);

122
src/Windows/Avalonia.Win32/OpenGl/WglGdiResourceManager.cs

@ -17,45 +17,69 @@ namespace Avalonia.Win32.OpenGl;
internal class WglGdiResourceManager internal class WglGdiResourceManager
{ {
class GetDCOp private class GetDCOp
{ {
public IntPtr Window; public readonly IntPtr Window;
public TaskCompletionSource<IntPtr> Result; public readonly TaskCompletionSource<IntPtr> Result;
public GetDCOp(IntPtr window, TaskCompletionSource<IntPtr> result)
{
Window = window;
Result = result;
}
} }
class ReleaseDCOp private class ReleaseDCOp
{ {
public IntPtr Window; public readonly IntPtr Window;
public IntPtr DC; public readonly IntPtr DC;
public TaskCompletionSource<object> Result; public readonly TaskCompletionSource<object?> Result;
public ReleaseDCOp(IntPtr window, IntPtr dc, TaskCompletionSource<object?> result)
{
Window = window;
DC = dc;
Result = result;
}
} }
class CreateWindowOp private class CreateWindowOp
{ {
public TaskCompletionSource<IntPtr> Result; public readonly TaskCompletionSource<IntPtr> Result;
public CreateWindowOp(TaskCompletionSource<IntPtr> result)
{
Result = result;
}
} }
class DestroyWindowOp private class DestroyWindowOp
{ {
public IntPtr Window; public readonly IntPtr Window;
public TaskCompletionSource<object> Result; public readonly TaskCompletionSource<object?> Result;
public DestroyWindowOp(IntPtr window, TaskCompletionSource<object?> result)
{
Window = window;
Result = result;
}
} }
private static readonly Queue<object> s_Queue = new(); private static readonly Queue<object> s_queue = new();
private static readonly AutoResetEvent s_Event = new(false); private static readonly AutoResetEvent s_event = new(false);
private static readonly ushort s_WindowClass; private static readonly ushort s_windowClass;
private static readonly UnmanagedMethods.WndProc s_wndProcDelegate = WndProc; private static readonly UnmanagedMethods.WndProc s_wndProcDelegate = WndProc;
static void Worker() private static void Worker()
{ {
while (true) while (true)
{ {
s_Event.WaitOne(); s_event.WaitOne();
lock (s_Queue) lock (s_queue)
{ {
if(s_Queue.Count == 0) if(s_queue.Count == 0)
continue; continue;
var job = s_Queue.Dequeue(); var job = s_queue.Dequeue();
if (job is GetDCOp getDc) if (job is GetDCOp getDc)
getDc.Result.TrySetResult(UnmanagedMethods.GetDC(getDc.Window)); getDc.Result.TrySetResult(UnmanagedMethods.GetDC(getDc.Window));
else if (job is ReleaseDCOp releaseDc) else if (job is ReleaseDCOp releaseDc)
@ -66,7 +90,7 @@ internal class WglGdiResourceManager
else if (job is CreateWindowOp createWindow) else if (job is CreateWindowOp createWindow)
createWindow.Result.TrySetResult(UnmanagedMethods.CreateWindowEx( createWindow.Result.TrySetResult(UnmanagedMethods.CreateWindowEx(
0, 0,
s_WindowClass, s_windowClass,
null, null,
(int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW, (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW,
0, 0,
@ -97,16 +121,15 @@ internal class WglGdiResourceManager
style = (int)UnmanagedMethods.ClassStyles.CS_OWNDC style = (int)UnmanagedMethods.ClassStyles.CS_OWNDC
}; };
s_WindowClass = UnmanagedMethods.RegisterClassEx(ref wndClassEx); s_windowClass = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
var th = new Thread(Worker) { IsBackground = true, Name = "Win32 OpenGL HDC manager" }; var th = new Thread(Worker) { IsBackground = true, Name = "Win32 OpenGL HDC manager" };
// This makes CLR to automatically pump the event queue from WaitOne // This makes CLR to automatically pump the event queue from WaitOne
th.SetApartmentState(ApartmentState.STA); th.SetApartmentState(ApartmentState.STA);
th.Start(); th.Start();
} }
private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{ {
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
} }
@ -114,53 +137,36 @@ internal class WglGdiResourceManager
public static IntPtr CreateOffscreenWindow() public static IntPtr CreateOffscreenWindow()
{ {
var tcs = new TaskCompletionSource<IntPtr>(); var tcs = new TaskCompletionSource<IntPtr>();
lock(s_Queue) lock (s_queue)
s_Queue.Enqueue(new CreateWindowOp() s_queue.Enqueue(new CreateWindowOp(tcs));
{ s_event.Set();
Result = tcs
});
s_Event.Set();
return tcs.Task.Result; return tcs.Task.Result;
} }
public static IntPtr GetDC(IntPtr hWnd) public static IntPtr GetDC(IntPtr hWnd)
{ {
var tcs = new TaskCompletionSource<IntPtr>(); var tcs = new TaskCompletionSource<IntPtr>();
lock(s_Queue) lock (s_queue)
s_Queue.Enqueue(new GetDCOp s_queue.Enqueue(new GetDCOp(hWnd, tcs));
{ s_event.Set();
Window = hWnd,
Result = tcs
});
s_Event.Set();
return tcs.Task.Result; return tcs.Task.Result;
} }
public static void ReleaseDC(IntPtr hWnd, IntPtr hDC) public static void ReleaseDC(IntPtr hWnd, IntPtr hDC)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object?>();
lock(s_Queue) lock (s_queue)
s_Queue.Enqueue(new ReleaseDCOp() s_queue.Enqueue(new ReleaseDCOp(hWnd, hDC, tcs));
{ s_event.Set();
Window = hWnd,
DC = hDC,
Result = tcs
});
s_Event.Set();
tcs.Task.Wait(); tcs.Task.Wait();
} }
public static void DestroyWindow(IntPtr hWnd) public static void DestroyWindow(IntPtr hWnd)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object?>();
lock(s_Queue) lock (s_queue)
s_Queue.Enqueue(new DestroyWindowOp() s_queue.Enqueue(new DestroyWindowOp(hWnd, tcs));
{ s_event.Set();
Window = hWnd,
Result = tcs
});
s_Event.Set();
tcs.Task.Wait(); tcs.Task.Wait();
} }
} }

18
src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs

@ -6,27 +6,31 @@ using Avalonia.Platform;
namespace Avalonia.Win32.OpenGl namespace Avalonia.Win32.OpenGl
{ {
class WglPlatformOpenGlInterface : IPlatformGraphics internal class WglPlatformOpenGlInterface : IPlatformGraphics
{ {
public WglContext PrimaryContext { get; } public WglContext PrimaryContext { get; }
public bool UsesSharedContext => false; public bool UsesSharedContext => false;
IPlatformGraphicsContext IPlatformGraphics.CreateContext() => CreateContext(); IPlatformGraphicsContext IPlatformGraphics.CreateContext() => CreateContext();
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
public IGlContext CreateContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null);
private WglPlatformOpenGlInterface(WglContext primary) public IGlContext CreateContext()
=> WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null)
?? throw new OpenGlException("Unable to create additional WGL context");
private WglPlatformOpenGlInterface(WglContext primary)
{ {
PrimaryContext = primary; PrimaryContext = primary;
} }
public static WglPlatformOpenGlInterface TryCreate() public static WglPlatformOpenGlInterface? TryCreate()
{ {
try try
{ {
var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions(); var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions();
var primary = WglDisplay.CreateContext(opts.WglProfiles.ToArray(), null); if (WglDisplay.CreateContext(opts.WglProfiles.ToArray(), null) is { } primary)
return new WglPlatformOpenGlInterface(primary); {
return new WglPlatformOpenGlInterface(primary);
}
} }
catch (Exception e) catch (Exception e)
{ {

4
src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs

@ -8,11 +8,11 @@ namespace Avalonia.Win32.OpenGl
{ {
internal class WglRestoreContext : IDisposable internal class WglRestoreContext : IDisposable
{ {
private readonly object _monitor; private readonly object? _monitor;
private readonly IntPtr _oldDc; private readonly IntPtr _oldDc;
private readonly IntPtr _oldContext; private readonly IntPtr _oldContext;
public WglRestoreContext(IntPtr gc, IntPtr context, object monitor, bool takeMonitor = true) public WglRestoreContext(IntPtr gc, IntPtr context, object? monitor, bool takeMonitor = true)
{ {
_monitor = monitor; _monitor = monitor;
_oldDc = wglGetCurrentDC(); _oldDc = wglGetCurrentDC();

15
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -5,9 +5,9 @@ using Avalonia.Win32.Interop;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
class PopupImpl : WindowImpl, IPopupImpl internal class PopupImpl : WindowImpl, IPopupImpl
{ {
private readonly IWindowBaseImpl _parent; private readonly IWindowBaseImpl? _parent;
private bool _dropShadowHint = true; private bool _dropShadowHint = true;
private Size? _maxAutoSize; private Size? _maxAutoSize;
@ -92,7 +92,7 @@ namespace Avalonia.Win32
IntPtr.Zero); IntPtr.Zero);
s_parentHandle = IntPtr.Zero; s_parentHandle = IntPtr.Zero;
PopupImpl.EnableBoxShadow(result, _dropShadowHint); EnableBoxShadow(result, _dropShadowHint);
return result; return result;
} }
@ -113,7 +113,7 @@ namespace Avalonia.Win32
// This is needed because we are calling virtual methods from constructors // This is needed because we are calling virtual methods from constructors
// One fabulous design decision leads to another, I guess // One fabulous design decision leads to another, I guess
static IWindowBaseImpl SaveParentHandle(IWindowBaseImpl parent) private static IWindowBaseImpl SaveParentHandle(IWindowBaseImpl parent)
{ {
s_parentHandle = parent.Handle.Handle; s_parentHandle = parent.Handle.Handle;
return parent; return parent;
@ -126,7 +126,7 @@ namespace Avalonia.Win32
} }
private PopupImpl(IWindowBaseImpl parent, bool dummy) : base() private PopupImpl(IWindowBaseImpl parent, bool dummy)
{ {
_parent = parent; _parent = parent;
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
@ -159,10 +159,7 @@ namespace Avalonia.Win32
{ {
_dropShadowHint = enabled; _dropShadowHint = enabled;
if (Handle != null) EnableBoxShadow(Handle.Handle, enabled);
{
PopupImpl.EnableBoxShadow(Handle.Handle, enabled);
}
} }
public IPopupPositioner PopupPositioner { get; } public IPopupPositioner PopupPositioner { get; }

19
src/Windows/Avalonia.Win32/ScreenImpl.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32 namespace Avalonia.Win32
@ -11,12 +10,15 @@ namespace Avalonia.Win32
[Unstable] [Unstable]
public class ScreenImpl : IScreenImpl public class ScreenImpl : IScreenImpl
{ {
private Screen[]? _allScreens;
/// <inheritdoc />
public int ScreenCount public int ScreenCount
{ {
get => GetSystemMetrics(SystemMetric.SM_CMONITORS); get => GetSystemMetrics(SystemMetric.SM_CMONITORS);
} }
private Screen[] _allScreens; /// <inheritdoc />
public IReadOnlyList<Screen> AllScreens public IReadOnlyList<Screen> AllScreens
{ {
get get
@ -38,7 +40,7 @@ namespace Avalonia.Win32
if (method != IntPtr.Zero) if (method != IntPtr.Zero)
{ {
GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _);
dpi = (double)x; dpi = x;
} }
else else
{ {
@ -74,7 +76,8 @@ namespace Avalonia.Win32
_allScreens = null; _allScreens = null;
} }
public Screen ScreenFromWindow(IWindowBaseImpl window) /// <inheritdoc />
public Screen? ScreenFromWindow(IWindowBaseImpl window)
{ {
var handle = window.Handle.Handle; var handle = window.Handle.Handle;
@ -83,7 +86,8 @@ namespace Avalonia.Win32
return FindScreenByHandle(monitor); return FindScreenByHandle(monitor);
} }
public Screen ScreenFromPoint(PixelPoint point) /// <inheritdoc />
public Screen? ScreenFromPoint(PixelPoint point)
{ {
var monitor = MonitorFromPoint(new POINT var monitor = MonitorFromPoint(new POINT
{ {
@ -94,7 +98,8 @@ namespace Avalonia.Win32
return FindScreenByHandle(monitor); return FindScreenByHandle(monitor);
} }
public Screen ScreenFromRect(PixelRect rect) /// <inheritdoc />
public Screen? ScreenFromRect(PixelRect rect)
{ {
var monitor = MonitorFromRect(new RECT var monitor = MonitorFromRect(new RECT
{ {
@ -107,7 +112,7 @@ namespace Avalonia.Win32
return FindScreenByHandle(monitor); return FindScreenByHandle(monitor);
} }
private Screen FindScreenByHandle(IntPtr handle) private Screen? FindScreenByHandle(IntPtr handle)
{ {
return AllScreens.Cast<WinScreen>().FirstOrDefault(m => m.Handle == handle); return AllScreens.Cast<WinScreen>().FirstOrDefault(m => m.Handle == handle);
} }

32
src/Windows/Avalonia.Win32/TrayIconImpl.cs

@ -11,8 +11,6 @@ using Avalonia.Styling;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
#nullable enable
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
[Unstable] [Unstable]
@ -25,9 +23,9 @@ namespace Avalonia.Win32
private IconImpl? _icon; private IconImpl? _icon;
private string? _tooltipText; private string? _tooltipText;
private readonly Win32NativeToManagedMenuExporter _exporter; private readonly Win32NativeToManagedMenuExporter _exporter;
private static readonly Dictionary<int, TrayIconImpl> s_trayIcons = new Dictionary<int, TrayIconImpl>(); private static readonly Dictionary<int, TrayIconImpl> s_trayIcons = new();
private bool _disposedValue; private bool _disposedValue;
private static readonly uint WM_TASKBARCREATED = UnmanagedMethods.RegisterWindowMessage("TaskbarCreated"); private static readonly uint WM_TASKBARCREATED = RegisterWindowMessage("TaskbarCreated");
public TrayIconImpl() public TrayIconImpl()
{ {
@ -62,17 +60,20 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public void SetIcon(IWindowIconImpl? icon) public void SetIcon(IWindowIconImpl? icon)
{ {
_icon = icon as IconImpl; _icon = icon as IconImpl;
UpdateIcon(); UpdateIcon();
} }
/// <inheritdoc />
public void SetIsVisible(bool visible) public void SetIsVisible(bool visible)
{ {
UpdateIcon(!visible); UpdateIcon(!visible);
} }
/// <inheritdoc />
public void SetToolTipText(string? text) public void SetToolTipText(string? text)
{ {
_tooltipText = text; _tooltipText = text;
@ -209,8 +210,11 @@ namespace Avalonia.Win32
private void MoveResize(PixelPoint position, Size size, double scaling) private void MoveResize(PixelPoint position, Size size, double scaling)
{ {
PlatformImpl!.Move(position); if (PlatformImpl is { } platformImpl)
PlatformImpl!.Resize(size, PlatformResizeReason.Layout); {
platformImpl.Move(position);
platformImpl.Resize(size, PlatformResizeReason.Layout);
}
} }
protected override void ArrangeCore(Rect finalRect) protected override void ArrangeCore(Rect finalRect)
@ -221,7 +225,7 @@ namespace Avalonia.Win32
{ {
Anchor = PopupAnchor.TopLeft, Anchor = PopupAnchor.TopLeft,
Gravity = PopupGravity.BottomRight, Gravity = PopupGravity.BottomRight,
AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.Scaling, new Size(1, 1)), AnchorRectangle = new Rect(Position.ToPoint(Screens.Primary?.Scaling ?? 1.0), new Size(1, 1)),
Size = finalRect.Size, Size = finalRect.Size,
ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY, ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY,
}); });
@ -247,18 +251,22 @@ namespace Avalonia.Win32
{ {
get get
{ {
var point = _hiddenWindow.Screens.Primary.Bounds.TopLeft; if (_hiddenWindow.Screens.Primary is { } screen)
var size = _hiddenWindow.Screens.Primary.Bounds.Size; {
return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.Scaling, size.Height * _hiddenWindow.Screens.Primary.Scaling); var point = screen.Bounds.TopLeft;
var size = screen.Bounds.Size;
return new Rect(point.X, point.Y, size.Width * screen.Scaling, size.Height * screen.Scaling);
}
return default;
} }
} }
public void MoveAndResize(Point devicePoint, Size virtualSize) public void MoveAndResize(Point devicePoint, Size virtualSize)
{ {
_moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.Scaling); _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, Scaling);
} }
public double Scaling => _hiddenWindow.Screens.Primary.Scaling; public double Scaling => _hiddenWindow.Screens.Primary?.Scaling ?? 1.0;
} }
} }

17
src/Windows/Avalonia.Win32/Win32GlManager.cs

@ -1,6 +1,4 @@
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.DirectX; using Avalonia.Win32.DirectX;
using Avalonia.Win32.OpenGl; using Avalonia.Win32.OpenGl;
@ -11,16 +9,19 @@ namespace Avalonia.Win32
{ {
static class Win32GlManager static class Win32GlManager
{ {
public static IPlatformGraphics? Initialize()
public static IPlatformGraphics Initialize()
{ {
var gl = InitializeCore(); var gl = InitializeCore();
AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(gl);
AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(gl); if (gl is not null)
{
AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(gl);
}
return gl; return gl;
} }
static IPlatformGraphics InitializeCore() private static IPlatformGraphics? InitializeCore()
{ {
var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions(); var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions();
@ -55,7 +56,5 @@ namespace Avalonia.Win32
return null; return null;
} }
} }
} }

42
src/Windows/Avalonia.Win32/Win32NativeControlHost.cs

@ -1,13 +1,13 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
class Win32NativeControlHost : INativeControlHostImpl internal class Win32NativeControlHost : INativeControlHostImpl
{ {
private readonly bool _useLayeredWindow; private readonly bool _useLayeredWindow;
public WindowImpl Window { get; } public WindowImpl Window { get; }
@ -18,7 +18,7 @@ namespace Avalonia.Win32
Window = window; Window = window;
} }
void AssertCompatible(IPlatformHandle handle) private void AssertCompatible(IPlatformHandle handle)
{ {
if (!IsCompatibleWith(handle)) if (!IsCompatibleWith(handle))
throw new ArgumentException($"Don't know what to do with {handle.HandleDescriptor}"); throw new ArgumentException($"Don't know what to do with {handle.HandleDescriptor}");
@ -33,7 +33,7 @@ namespace Avalonia.Win32
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create)
{ {
var holder = new DumbWindow(_useLayeredWindow, Window.Handle.Handle); var holder = new DumbWindow(_useLayeredWindow, Window.Handle.Handle);
Win32NativeControlAttachment attachment = null; Win32NativeControlAttachment? attachment = null;
try try
{ {
var child = create(holder); var child = create(holder);
@ -46,7 +46,7 @@ namespace Avalonia.Win32
catch catch
{ {
attachment?.Dispose(); attachment?.Dispose();
holder?.Destroy(); holder.Destroy();
throw; throw;
} }
} }
@ -60,13 +60,13 @@ namespace Avalonia.Win32
public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == "HWND"; public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == "HWND";
class DumbWindow : IDisposable, INativeControlHostDestroyableControlHandle private class DumbWindow : IDisposable, INativeControlHostDestroyableControlHandle
{ {
public IntPtr Handle { get;} public IntPtr Handle { get;}
public string HandleDescriptor => "HWND"; public string HandleDescriptor => "HWND";
public void Destroy() => Dispose(); public void Destroy() => Dispose();
UnmanagedMethods.WndProc _wndProcDelegate; private readonly UnmanagedMethods.WndProc _wndProcDelegate;
private readonly string _className; private readonly string _className;
public DumbWindow(bool layered = false, IntPtr? parent = null) public DumbWindow(bool layered = false, IntPtr? parent = null)
@ -103,9 +103,7 @@ namespace Avalonia.Win32
UnmanagedMethods.LayeredWindowFlags.LWA_ALPHA); UnmanagedMethods.LayeredWindowFlags.LWA_ALPHA);
} }
private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
protected virtual unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{ {
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
} }
@ -128,11 +126,11 @@ namespace Avalonia.Win32
} }
} }
class Win32NativeControlAttachment : INativeControlHostControlTopLevelAttachment private class Win32NativeControlAttachment : INativeControlHostControlTopLevelAttachment
{ {
private DumbWindow _holder; private DumbWindow? _holder;
private IPlatformHandle _child; private IPlatformHandle? _child;
private Win32NativeControlHost _attachedTo; private Win32NativeControlHost? _attachedTo;
public Win32NativeControlAttachment(DumbWindow holder, IPlatformHandle child) public Win32NativeControlAttachment(DumbWindow holder, IPlatformHandle child)
{ {
@ -142,7 +140,8 @@ namespace Avalonia.Win32
UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.Show); UnmanagedMethods.ShowWindow(child.Handle, UnmanagedMethods.ShowWindowCommand.Show);
} }
void CheckDisposed() [MemberNotNull(nameof(_holder))]
private void CheckDisposed()
{ {
if (_holder == null) if (_holder == null)
throw new ObjectDisposedException(nameof(Win32NativeControlAttachment)); throw new ObjectDisposedException(nameof(Win32NativeControlAttachment));
@ -158,13 +157,13 @@ namespace Avalonia.Win32
_attachedTo = null; _attachedTo = null;
} }
public INativeControlHostImpl AttachedTo public INativeControlHostImpl? AttachedTo
{ {
get => _attachedTo; get => _attachedTo;
set set
{ {
CheckDisposed(); CheckDisposed();
_attachedTo = (Win32NativeControlHost) value; _attachedTo = value as Win32NativeControlHost;
if (_attachedTo == null) if (_attachedTo == null)
{ {
UnmanagedMethods.ShowWindow(_holder.Handle, UnmanagedMethods.ShowWindowCommand.Hide); UnmanagedMethods.ShowWindow(_holder.Handle, UnmanagedMethods.ShowWindowCommand.Hide);
@ -179,6 +178,7 @@ namespace Avalonia.Win32
public void HideWithSize(Size size) public void HideWithSize(Size size)
{ {
CheckDisposed();
UnmanagedMethods.SetWindowPos(_holder.Handle, IntPtr.Zero, UnmanagedMethods.SetWindowPos(_holder.Handle, IntPtr.Zero,
-100, -100, 1, 1, -100, -100, 1, 1,
UnmanagedMethods.SetWindowPosFlags.SWP_HIDEWINDOW | UnmanagedMethods.SetWindowPosFlags.SWP_HIDEWINDOW |
@ -198,8 +198,12 @@ namespace Avalonia.Win32
bounds *= _attachedTo.Window.RenderScaling; bounds *= _attachedTo.Window.RenderScaling;
var pixelRect = new PixelRect((int)bounds.X, (int)bounds.Y, Math.Max(1, (int)bounds.Width), var pixelRect = new PixelRect((int)bounds.X, (int)bounds.Y, Math.Max(1, (int)bounds.Width),
Math.Max(1, (int)bounds.Height)); Math.Max(1, (int)bounds.Height));
UnmanagedMethods.MoveWindow(_child.Handle, 0, 0, pixelRect.Width, pixelRect.Height, true); if (_child is not null)
{
UnmanagedMethods.MoveWindow(_child.Handle, 0, 0, pixelRect.Width, pixelRect.Height, true);
}
UnmanagedMethods.SetWindowPos(_holder.Handle, IntPtr.Zero, pixelRect.X, pixelRect.Y, pixelRect.Width, UnmanagedMethods.SetWindowPos(_holder.Handle, IntPtr.Zero, pixelRect.X, pixelRect.Y, pixelRect.Width,
pixelRect.Height, pixelRect.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_SHOWWINDOW UnmanagedMethods.SetWindowPosFlags.SWP_SHOWWINDOW

9
src/Windows/Avalonia.Win32/Win32NativeToManagedMenuExporter.cs

@ -1,12 +1,7 @@
using System.Collections.Generic; using Avalonia.Reactive;
using Avalonia.Reactive;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Media.Imaging;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
@ -19,7 +14,7 @@ namespace Avalonia.Win32
_nativeMenu = nativeMenu; _nativeMenu = nativeMenu;
} }
private AvaloniaList<MenuItem> Populate(NativeMenu nativeMenu) private static AvaloniaList<MenuItem> Populate(NativeMenu nativeMenu)
{ {
var result = new AvaloniaList<MenuItem>(); var result = new AvaloniaList<MenuItem>();

50
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -17,12 +17,10 @@ using Avalonia.Rendering.Composition;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Win32.Input; using Avalonia.Win32.Input;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia namespace Avalonia
{ {
#nullable enable
public static class Win32ApplicationExtensions public static class Win32ApplicationExtensions
{ {
public static AppBuilder UseWin32(this AppBuilder builder) public static AppBuilder UseWin32(this AppBuilder builder)
@ -106,17 +104,19 @@ namespace Avalonia
public IPlatformGraphics? CustomPlatformGraphics { get; set; } public IPlatformGraphics? CustomPlatformGraphics { get; set; }
} }
} }
#nullable restore
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
public class Win32Platform : IPlatformThreadingInterface, IWindowingPlatform, IPlatformIconLoader, IPlatformLifetimeEventsImpl public class Win32Platform : IPlatformThreadingInterface, IWindowingPlatform, IPlatformIconLoader, IPlatformLifetimeEventsImpl
{ {
private static readonly Win32Platform s_instance = new Win32Platform(); private static readonly Win32Platform s_instance = new();
private static Thread _uiThread; private static Thread? s_uiThread;
private UnmanagedMethods.WndProc _wndProcDelegate; private static Win32PlatformOptions? s_options;
private static Compositor? s_compositor;
private WndProc? _wndProcDelegate;
private IntPtr _hwnd; private IntPtr _hwnd;
private readonly List<Delegate> _delegates = new List<Delegate>(); private readonly List<Delegate> _delegates = new();
public Win32Platform() public Win32Platform()
{ {
@ -135,9 +135,12 @@ namespace Avalonia.Win32
public static Version WindowsVersion { get; } = RtlGetVersion(); public static Version WindowsVersion { get; } = RtlGetVersion();
internal static bool UseOverlayPopups => Options.OverlayPopups; internal static bool UseOverlayPopups => Options.OverlayPopups;
public static Win32PlatformOptions Options { get; private set; }
internal static Compositor Compositor { get; private set; } public static Win32PlatformOptions Options
=> s_options ?? throw new InvalidOperationException($"{nameof(Win32Platform)} hasn't been initialized");
internal static Compositor Compositor
=> s_compositor ?? throw new InvalidOperationException($"{nameof(Win32Platform)} hasn't been initialized");
public static void Initialize() public static void Initialize()
{ {
@ -146,7 +149,7 @@ namespace Avalonia.Win32
public static void Initialize(Win32PlatformOptions options) public static void Initialize(Win32PlatformOptions options)
{ {
Options = options; s_options = options;
var renderTimer = options.ShouldRenderOnUIThread ? new UiThreadRenderTimer(60) : new DefaultRenderTimer(60); var renderTimer = options.ShouldRenderOnUIThread ? new UiThreadRenderTimer(60) : new DefaultRenderTimer(60);
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
@ -171,26 +174,24 @@ namespace Avalonia.Win32
.Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider()) .Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider())
.Bind<IPlatformLifetimeEventsImpl>().ToConstant(s_instance); .Bind<IPlatformLifetimeEventsImpl>().ToConstant(s_instance);
_uiThread = Thread.CurrentThread; s_uiThread = Thread.CurrentThread;
var platformGraphics = options?.CustomPlatformGraphics var platformGraphics = options.CustomPlatformGraphics
?? Win32GlManager.Initialize(); ?? Win32GlManager.Initialize();
if (OleContext.Current != null) if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>(); AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), platformGraphics); s_compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), platformGraphics);
} }
public bool HasMessages() public bool HasMessages()
{ {
UnmanagedMethods.MSG msg; return PeekMessage(out _, IntPtr.Zero, 0, 0, 0);
return PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
} }
public void ProcessMessage() public void ProcessMessage()
{ {
if (GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1) if (GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1)
{ {
TranslateMessage(ref msg); TranslateMessage(ref msg);
@ -222,8 +223,7 @@ namespace Avalonia.Win32
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action callback) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action callback)
{ {
UnmanagedMethods.TimerProc timerDelegate = TimerProc timerDelegate = (_, _, _, _) => callback();
(hWnd, uMsg, nIDEvent, dwTime) => callback();
IntPtr handle = SetTimer( IntPtr handle = SetTimer(
IntPtr.Zero, IntPtr.Zero,
@ -253,11 +253,11 @@ namespace Avalonia.Win32
new IntPtr(SignalL)); new IntPtr(SignalL));
} }
public bool CurrentThreadIsLoopThread => _uiThread == Thread.CurrentThread; public bool CurrentThreadIsLoopThread => s_uiThread == Thread.CurrentThread;
public event Action<DispatcherPriority?> Signaled; public event Action<DispatcherPriority?>? Signaled;
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested; public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
@ -301,11 +301,11 @@ namespace Avalonia.Win32
private void CreateMessageWindow() private void CreateMessageWindow()
{ {
// Ensure that the delegate doesn't get garbage collected by storing it as a field. // Ensure that the delegate doesn't get garbage collected by storing it as a field.
_wndProcDelegate = new UnmanagedMethods.WndProc(WndProc); _wndProcDelegate = WndProc;
UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX WNDCLASSEX wndClassEx = new WNDCLASSEX
{ {
cbSize = Marshal.SizeOf<UnmanagedMethods.WNDCLASSEX>(), cbSize = Marshal.SizeOf<WNDCLASSEX>(),
lpfnWndProc = _wndProcDelegate, lpfnWndProc = _wndProcDelegate,
hInstance = GetModuleHandle(null), hInstance = GetModuleHandle(null),
lpszClassName = "AvaloniaMessageWindow " + Guid.NewGuid(), lpszClassName = "AvaloniaMessageWindow " + Guid.NewGuid(),
@ -326,7 +326,7 @@ namespace Avalonia.Win32
} }
} }
public ITrayIconImpl CreateTrayIcon () public ITrayIconImpl CreateTrayIcon()
{ {
return new TrayIconImpl(); return new TrayIconImpl();
} }

8
src/Windows/Avalonia.Win32/Win32PlatformSettings.cs

@ -1,8 +1,6 @@
using System; using System;
using System.Runtime.InteropServices;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop;
using Avalonia.Win32.WinRT; using Avalonia.Win32.WinRT;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
@ -10,14 +8,14 @@ namespace Avalonia.Win32;
internal class Win32PlatformSettings : DefaultPlatformSettings internal class Win32PlatformSettings : DefaultPlatformSettings
{ {
private PlatformColorValues _lastColorValues; private PlatformColorValues? _lastColorValues;
public override Size GetTapSize(PointerType type) public override Size GetTapSize(PointerType type)
{ {
return type switch return type switch
{ {
PointerType.Touch => new(10, 10), PointerType.Touch => new(10, 10),
_ => new(GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXDRAG), GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYDRAG)), _ => new(GetSystemMetrics(SystemMetric.SM_CXDRAG), GetSystemMetrics(SystemMetric.SM_CYDRAG)),
}; };
} }
@ -26,7 +24,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
return type switch return type switch
{ {
PointerType.Touch => new(16, 16), PointerType.Touch => new(16, 16),
_ => new(GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXDOUBLECLK), GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYDOUBLECLK)), _ => new(GetSystemMetrics(SystemMetric.SM_CXDOUBLECLK), GetSystemMetrics(SystemMetric.SM_CYDOUBLECLK)),
}; };
} }

12
src/Windows/Avalonia.Win32/Win32StorageProvider.cs

@ -1,13 +1,10 @@
#nullable enable using System;
using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.MicroCom;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO; using Avalonia.Platform.Storage.FileIO;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
@ -146,7 +143,7 @@ namespace Avalonia.Win32
} }
} }
var showResult = frm.Show(_windowImpl.Handle!.Handle); var showResult = frm.Show(_windowImpl.Handle.Handle);
if ((uint)showResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED) if ((uint)showResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED)
{ {
@ -188,7 +185,7 @@ namespace Avalonia.Win32
var message = new Win32Exception(ex.HResult).Message; var message = new Win32Exception(ex.HResult).Message;
throw new COMException(message, ex); throw new COMException(message, ex);
} }
})!; });
} }
@ -220,7 +217,6 @@ namespace Avalonia.Win32
} }
var size = Marshal.SizeOf<UnmanagedMethods.COMDLG_FILTERSPEC>(); var size = Marshal.SizeOf<UnmanagedMethods.COMDLG_FILTERSPEC>();
var arr = new byte[size];
var resultArr = new byte[size * filters.Count]; var resultArr = new byte[size * filters.Count];
for (int i = 0; i < filters.Count; i++) for (int i = 0; i < filters.Count; i++)
@ -236,7 +232,7 @@ namespace Avalonia.Win32
{ {
var filterStr = new UnmanagedMethods.COMDLG_FILTERSPEC var filterStr = new UnmanagedMethods.COMDLG_FILTERSPEC
{ {
pszName = filter.Name ?? string.Empty, pszName = filter.Name,
pszSpec = string.Join(";", filter.Patterns) pszSpec = string.Join(";", filter.Patterns)
}; };

27
src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs

@ -1,14 +1,13 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.MicroCom;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition namespace Avalonia.Win32.WinRT.Composition
{ {
abstract class WinUIEffectBase : WinRTInspectable, IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop internal abstract class WinUIEffectBase : WinRTInspectable, IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{ {
private IGraphicsEffectSource[] _sources; private IGraphicsEffectSource[]? _sources;
public WinUIEffectBase(params IGraphicsEffectSource[] _sources) public WinUIEffectBase(params IGraphicsEffectSource[] _sources)
{ {
@ -32,7 +31,7 @@ namespace Avalonia.Win32.WinRT.Composition
throw new COMException("Not supported", unchecked((int)0x80004001)); throw new COMException("Not supported", unchecked((int)0x80004001));
public abstract uint PropertyCount { get; } public abstract uint PropertyCount { get; }
public abstract IPropertyValue GetProperty(uint index); public abstract IPropertyValue? GetProperty(uint index);
public IGraphicsEffectSource GetSource(uint index) public IGraphicsEffectSource GetSource(uint index)
{ {
@ -53,14 +52,14 @@ namespace Avalonia.Win32.WinRT.Composition
_sources = null; _sources = null;
} }
} }
class WinUIGaussianBlurEffect : WinUIEffectBase internal class WinUIGaussianBlurEffect : WinUIEffectBase
{ {
public WinUIGaussianBlurEffect(IGraphicsEffectSource source) : base(source) public WinUIGaussianBlurEffect(IGraphicsEffectSource source) : base(source)
{ {
} }
enum D2D1_GAUSSIANBLUR_OPTIMIZATION private enum D2D1_GAUSSIANBLUR_OPTIMIZATION
{ {
D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED, D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED,
D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED, D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED,
@ -68,14 +67,14 @@ namespace Avalonia.Win32.WinRT.Composition
D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD
}; };
enum D2D1_BORDER_MODE private enum D2D1_BORDER_MODE
{ {
D2D1_BORDER_MODE_SOFT, D2D1_BORDER_MODE_SOFT,
D2D1_BORDER_MODE_HARD, D2D1_BORDER_MODE_HARD,
D2D1_BORDER_MODE_FORCE_DWORD D2D1_BORDER_MODE_FORCE_DWORD
}; };
enum D2D1GaussianBlurProp private enum D2D1GaussianBlurProp
{ {
D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION, D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION,
@ -87,7 +86,7 @@ namespace Avalonia.Win32.WinRT.Composition
public override uint PropertyCount => 3; public override uint PropertyCount => 3;
public override IPropertyValue GetProperty(uint index) public override IPropertyValue? GetProperty(uint index)
{ {
switch ((D2D1GaussianBlurProp)index) switch ((D2D1GaussianBlurProp)index)
{ {
@ -105,14 +104,14 @@ namespace Avalonia.Win32.WinRT.Composition
return null; return null;
} }
} }
class SaturationEffect : WinUIEffectBase internal class SaturationEffect : WinUIEffectBase
{ {
public SaturationEffect(IGraphicsEffectSource source) : base(source) public SaturationEffect(IGraphicsEffectSource source) : base(source)
{ {
} }
enum D2D1_SATURATION_PROP private enum D2D1_SATURATION_PROP
{ {
D2D1_SATURATION_PROP_SATURATION, D2D1_SATURATION_PROP_SATURATION,
D2D1_SATURATION_PROP_FORCE_DWORD D2D1_SATURATION_PROP_FORCE_DWORD
@ -122,7 +121,7 @@ namespace Avalonia.Win32.WinRT.Composition
public override uint PropertyCount => 1; public override uint PropertyCount => 1;
public override IPropertyValue GetProperty(uint index) public override IPropertyValue? GetProperty(uint index)
{ {
switch ((D2D1_SATURATION_PROP)index) switch ((D2D1_SATURATION_PROP)index)
{ {

7
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs

@ -3,7 +3,6 @@ using System.Numerics;
using System.Threading; using System.Threading;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Win32.Interop;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition; namespace Avalonia.Win32.WinRT.Composition;
@ -12,8 +11,8 @@ internal class WinUiCompositedWindow : IDisposable
{ {
public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; } public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; }
private readonly WinUiCompositionShared _shared; private readonly WinUiCompositionShared _shared;
private readonly ICompositionRoundedRectangleGeometry _compositionRoundedRectangleGeometry; private readonly ICompositionRoundedRectangleGeometry? _compositionRoundedRectangleGeometry;
private readonly IVisual _mica; private readonly IVisual? _mica;
private readonly IVisual _blur; private readonly IVisual _blur;
private readonly IVisual _visual; private readonly IVisual _visual;
private PixelSize _size; private PixelSize _size;
@ -25,7 +24,7 @@ internal class WinUiCompositedWindow : IDisposable
lock (_shared.SyncRoot) lock (_shared.SyncRoot)
{ {
_compositionRoundedRectangleGeometry?.Dispose(); _compositionRoundedRectangleGeometry?.Dispose();
_blur?.Dispose(); _blur.Dispose();
_mica?.Dispose(); _mica?.Dispose();
_visual.Dispose(); _visual.Dispose();
_surfaceBrush.Dispose(); _surfaceBrush.Dispose();

49
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs

@ -1,15 +1,8 @@
using System; using System;
using System.Runtime.InteropServices;
using Avalonia.MicroCom;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.Win32.DirectX; using Avalonia.Win32.DirectX;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using Avalonia.Win32.OpenGl.Angle;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition namespace Avalonia.Win32.WinRT.Composition
@ -18,7 +11,7 @@ namespace Avalonia.Win32.WinRT.Composition
{ {
private readonly WinUiCompositionShared _shared; private readonly WinUiCompositionShared _shared;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
private WinUiCompositedWindow _window; private WinUiCompositedWindow? _window;
private BlurEffect _blurEffect; private BlurEffect _blurEffect;
public WinUiCompositedWindowSurface(WinUiCompositionShared shared, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) public WinUiCompositedWindowSurface(WinUiCompositionShared shared, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
@ -52,6 +45,8 @@ namespace Avalonia.Win32.WinRT.Composition
internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget
{ {
private static readonly Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
private readonly IPlatformGraphicsContext _context; private readonly IPlatformGraphicsContext _context;
private readonly WinUiCompositedWindow _window; private readonly WinUiCompositedWindow _window;
private readonly IUnknown _d3dDevice; private readonly IUnknown _d3dDevice;
@ -63,6 +58,7 @@ namespace Avalonia.Win32.WinRT.Composition
private PixelSize _size; private PixelSize _size;
private bool _lost; private bool _lost;
private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
private readonly ICompositionDrawingSurface _drawingSurface;
public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context, public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context,
WinUiCompositedWindow window, IntPtr device, WinUiCompositedWindow window, IntPtr device,
@ -83,28 +79,33 @@ namespace Avalonia.Win32.WinRT.Composition
_surface = _drawingSurface.QueryInterface<ICompositionSurface>(); _surface = _drawingSurface.QueryInterface<ICompositionSurface>();
_surfaceInterop = _drawingSurface.QueryInterface<ICompositionDrawingSurfaceInterop>(); _surfaceInterop = _drawingSurface.QueryInterface<ICompositionDrawingSurfaceInterop>();
} }
finally catch
{ {
if (_surfaceInterop == null) _surface?.Dispose();
Dispose(); _surfaceInterop?.Dispose();
_drawingSurface?.Dispose();
_compositionDevice2?.Dispose();
_compositionDevice?.Dispose();
_interop?.Dispose();
_compositor?.Dispose();
_d3dDevice?.Dispose();
throw;
} }
} }
public void Dispose() public void Dispose()
{ {
_surface?.Dispose(); _surface.Dispose();
_surfaceInterop?.Dispose(); _surfaceInterop.Dispose();
_drawingSurface?.Dispose(); _drawingSurface.Dispose();
_compositionDevice2?.Dispose(); _compositionDevice2.Dispose();
_compositionDevice?.Dispose(); _compositionDevice.Dispose();
_interop?.Dispose(); _interop.Dispose();
_compositor?.Dispose(); _compositor.Dispose();
_d3dDevice?.Dispose(); _d3dDevice.Dispose();
} }
public bool IsCorrupted => _context.IsLost || _lost; public bool IsCorrupted => _context.IsLost || _lost;
private static Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
private readonly ICompositionDrawingSurface _drawingSurface;
public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw()
{ {
@ -155,12 +156,12 @@ namespace Avalonia.Win32.WinRT.Composition
{ {
if (needsEndDraw) if (needsEndDraw)
_surfaceInterop.EndDraw(); _surfaceInterop.EndDraw();
transaction?.Dispose(); transaction.Dispose();
} }
} }
} }
class Session : IDirect3D11TextureRenderTargetRenderSession private class Session : IDirect3D11TextureRenderTargetRenderSession
{ {
private readonly IDisposable _transaction; private readonly IDisposable _transaction;
private readonly PixelSize _size; private readonly PixelSize _size;
@ -200,4 +201,4 @@ namespace Avalonia.Win32.WinRT.Composition
public double Scaling => _scaling; public double Scaling => _scaling;
} }
} }
} }

10
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs

@ -8,11 +8,11 @@ internal class WinUiCompositionShared : IDisposable
public ICompositor Compositor { get; } public ICompositor Compositor { get; }
public ICompositor5 Compositor5 { get; } public ICompositor5 Compositor5 { get; }
public ICompositorDesktopInterop DesktopInterop { get; } public ICompositorDesktopInterop DesktopInterop { get; }
public ICompositionBrush BlurBrush; public ICompositionBrush BlurBrush { get; }
public ICompositionBrush MicaBrush; public ICompositionBrush? MicaBrush { get; }
public object SyncRoot { get; } = new(); public object SyncRoot { get; } = new();
public static readonly Version MinHostBackdropVersion = new Version(10, 0, 22000); public static readonly Version MinHostBackdropVersion = new(10, 0, 22000);
public WinUiCompositionShared(ICompositor compositor) public WinUiCompositionShared(ICompositor compositor)
{ {
@ -26,9 +26,9 @@ internal class WinUiCompositionShared : IDisposable
public void Dispose() public void Dispose()
{ {
BlurBrush.Dispose(); BlurBrush.Dispose();
MicaBrush.Dispose(); MicaBrush?.Dispose();
DesktopInterop.Dispose(); DesktopInterop.Dispose();
Compositor.Dispose(); Compositor.Dispose();
Compositor5.Dispose(); Compositor5.Dispose();
} }
} }

11
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs

@ -1,4 +1,3 @@
using System;
using System.Numerics; using System.Numerics;
using MicroCom.Runtime; using MicroCom.Runtime;
@ -6,7 +5,7 @@ namespace Avalonia.Win32.WinRT.Composition;
internal static class WinUiCompositionUtils internal static class WinUiCompositionUtils
{ {
public static ICompositionBrush CreateMicaBackdropBrush(ICompositor compositor) public static ICompositionBrush? CreateMicaBackdropBrush(ICompositor compositor)
{ {
if (Win32Platform.WindowsVersion.Build < 22000) if (Win32Platform.WindowsVersion.Build < 22000)
return null; return null;
@ -18,7 +17,7 @@ internal static class WinUiCompositionUtils
return blurredWallpaperBackdropBrush?.QueryInterface<ICompositionBrush>(); return blurredWallpaperBackdropBrush?.QueryInterface<ICompositionBrush>();
} }
public static unsafe ICompositionBrush CreateAcrylicBlurBackdropBrush(ICompositor compositor) public static ICompositionBrush CreateAcrylicBlurBackdropBrush(ICompositor compositor)
{ {
using var backDropParameterFactory = using var backDropParameterFactory =
NativeWinRTMethods.CreateActivationFactory<ICompositionEffectSourceParameterFactory>( NativeWinRTMethods.CreateActivationFactory<ICompositionEffectSourceParameterFactory>(
@ -39,7 +38,7 @@ internal static class WinUiCompositionUtils
return compositionEffectBrush.QueryInterface<ICompositionBrush>(); return compositionEffectBrush.QueryInterface<ICompositionBrush>();
} }
public static ICompositionRoundedRectangleGeometry ClipVisual(ICompositor compositor, float? _backdropCornerRadius, params IVisual[] containerVisuals) public static ICompositionRoundedRectangleGeometry? ClipVisual(ICompositor compositor, float? _backdropCornerRadius, params IVisual?[] containerVisuals)
{ {
if (!_backdropCornerRadius.HasValue) if (!_backdropCornerRadius.HasValue)
return null; return null;
@ -77,7 +76,7 @@ internal static class WinUiCompositionUtils
public static ICompositionBrush CreateBackdropBrush(ICompositor compositor) public static ICompositionBrush CreateBackdropBrush(ICompositor compositor)
{ {
ICompositionBackdropBrush brush = null; ICompositionBackdropBrush? brush = null;
try try
{ {
if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion) if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion)
@ -98,4 +97,4 @@ internal static class WinUiCompositionUtils
brush?.Dispose(); brush?.Dispose();
} }
} }
} }

16
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs

@ -7,20 +7,17 @@ using Avalonia.Logging;
using Avalonia.MicroCom; using Avalonia.MicroCom;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using Avalonia.Win32.OpenGl.Angle;
using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition; namespace Avalonia.Win32.WinRT.Composition;
internal class WinUiCompositorConnection : IRenderTimer internal class WinUiCompositorConnection : IRenderTimer
{ {
private readonly WinUiCompositionShared _shared; private readonly WinUiCompositionShared _shared;
public event Action<TimeSpan> Tick; public event Action<TimeSpan>? Tick;
public bool RunsInBackground => true; public bool RunsInBackground => true;
public unsafe WinUiCompositorConnection() public WinUiCompositorConnection()
{ {
using var compositor = NativeWinRTMethods.CreateInstance<ICompositor>("Windows.UI.Composition.Compositor"); using var compositor = NativeWinRTMethods.CreateInstance<ICompositor>("Windows.UI.Composition.Compositor");
/* /*
@ -42,10 +39,9 @@ internal class WinUiCompositorConnection : IRenderTimer
_shared = new WinUiCompositionShared(compositor); _shared = new WinUiCompositionShared(compositor);
} }
static bool TryCreateAndRegisterCore() private static bool TryCreateAndRegisterCore()
{ {
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
var pumpLock = new object();
var th = new Thread(() => var th = new Thread(() =>
{ {
WinUiCompositorConnection connect; WinUiCompositorConnection connect;
@ -80,10 +76,10 @@ internal class WinUiCompositorConnection : IRenderTimer
return tcs.Task.Result; return tcs.Task.Result;
} }
class RunLoopHandler : CallbackBase, IAsyncActionCompletedHandler private class RunLoopHandler : CallbackBase, IAsyncActionCompletedHandler
{ {
private readonly WinUiCompositorConnection _parent; private readonly WinUiCompositorConnection _parent;
private Stopwatch _st = Stopwatch.StartNew(); private readonly Stopwatch _st = Stopwatch.StartNew();
public RunLoopHandler(WinUiCompositorConnection parent) public RunLoopHandler(WinUiCompositorConnection parent)
{ {
@ -101,7 +97,7 @@ internal class WinUiCompositorConnection : IRenderTimer
private void RunLoop() private void RunLoop()
{ {
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
AppDomain.CurrentDomain.ProcessExit += (sender, args) => AppDomain.CurrentDomain.ProcessExit += (_, _) =>
cts.Cancel(); cts.Cancel();
lock (_shared.SyncRoot) lock (_shared.SyncRoot)

20
src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs

@ -1,12 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT namespace Avalonia.Win32.WinRT
@ -108,24 +102,24 @@ namespace Avalonia.Win32.WinRT
[DllImport("combase.dll", PreserveSig = false)] [DllImport("combase.dll", PreserveSig = false)]
private static extern IntPtr RoGetActivationFactory(IntPtr activatableClassId, ref Guid iid); private static extern IntPtr RoGetActivationFactory(IntPtr activatableClassId, ref Guid iid);
private static bool _initialized; private static bool s_initialized;
private static void EnsureRoInitialized() private static void EnsureRoInitialized()
{ {
if (_initialized) if (s_initialized)
return; return;
RoInitialize(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA ? RoInitialize(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA ?
RO_INIT_TYPE.RO_INIT_SINGLETHREADED : RO_INIT_TYPE.RO_INIT_SINGLETHREADED :
RO_INIT_TYPE.RO_INIT_MULTITHREADED); RO_INIT_TYPE.RO_INIT_MULTITHREADED);
_initialized = true; s_initialized = true;
} }
} }
class HStringInterop : IDisposable internal class HStringInterop : IDisposable
{ {
private IntPtr _s; private IntPtr _s;
private bool _owns; private readonly bool _owns;
public HStringInterop(string s) public HStringInterop(string? s)
{ {
_s = s == null ? IntPtr.Zero : NativeWinRTMethods.WindowsCreateString(s); _s = s == null ? IntPtr.Zero : NativeWinRTMethods.WindowsCreateString(s);
_owns = true; _owns = true;
@ -139,7 +133,7 @@ namespace Avalonia.Win32.WinRT
public IntPtr Handle => _s; public IntPtr Handle => _s;
public unsafe string Value public unsafe string? Value
{ {
get get
{ {

7
src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs

@ -2,12 +2,11 @@
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.MicroCom;
using MicroCom.Runtime; using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT namespace Avalonia.Win32.WinRT
{ {
class WinRTInspectable : IInspectable, IMicroComShadowContainer internal class WinRTInspectable : IInspectable, IMicroComShadowContainer
{ {
public virtual void Dispose() public virtual void Dispose()
{ {
@ -25,9 +24,9 @@ namespace Avalonia.Win32.WinRT
*iidCount = (ulong) interfaces.Length; *iidCount = (ulong) interfaces.Length;
} }
public IntPtr RuntimeClassName => NativeWinRTMethods.WindowsCreateString(GetType().FullName); public IntPtr RuntimeClassName => NativeWinRTMethods.WindowsCreateString(GetType().FullName!);
public TrustLevel TrustLevel => TrustLevel.BaseTrust; public TrustLevel TrustLevel => TrustLevel.BaseTrust;
public MicroComShadow Shadow { get; set; } public MicroComShadow? Shadow { get; set; }
public virtual void OnReferencedFromNative() public virtual void OnReferencedFromNative()
{ {
} }

8
src/Windows/Avalonia.Win32/WinScreen.cs

@ -15,14 +15,16 @@ namespace Avalonia.Win32
public IntPtr Handle => _hMonitor; public IntPtr Handle => _hMonitor;
/// <inheritdoc />
public override int GetHashCode() public override int GetHashCode()
{ {
return (int)_hMonitor; return _hMonitor.GetHashCode();
} }
public override bool Equals(object obj) /// <inheritdoc />
public override bool Equals(object? obj)
{ {
return (obj is WinScreen screen) ? _hMonitor == screen._hMonitor : base.Equals(obj); return obj is WinScreen screen && _hMonitor == screen._hMonitor;
} }
} }
} }

82
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -1,12 +1,9 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Remote;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
@ -27,9 +24,9 @@ namespace Avalonia.Win32
protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{ {
const double wheelDelta = 120.0; const double wheelDelta = 120.0;
const long UiaRootObjectId = -25; const long uiaRootObjectId = -25;
uint timestamp = unchecked((uint)GetMessageTime()); uint timestamp = unchecked((uint)GetMessageTime());
RawInputEventArgs e = null; RawInputEventArgs? e = null;
var shouldTakeFocus = false; var shouldTakeFocus = false;
var message = (WindowsMessage)msg; var message = (WindowsMessage)msg;
switch (message) switch (message)
@ -92,7 +89,7 @@ namespace Avalonia.Win32
} }
// We need to release IMM context and state to avoid leaks. // We need to release IMM context and state to avoid leaks.
if (Imm32InputMethod.Current.HWND == _hwnd) if (Imm32InputMethod.Current.Hwnd == _hwnd)
{ {
Imm32InputMethod.Current.ClearLanguageAndWindow(); Imm32InputMethod.Current.ClearLanguageAndWindow();
} }
@ -104,7 +101,7 @@ namespace Avalonia.Win32
Closed?.Invoke(); Closed?.Invoke();
_mouseDevice.Dispose(); _mouseDevice.Dispose();
_touchDevice?.Dispose(); _touchDevice.Dispose();
//Free other resources //Free other resources
Dispose(); Dispose();
@ -140,7 +137,7 @@ namespace Avalonia.Win32
{ {
var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)); var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam));
if (key != Key.None) if (key != Key.None && _owner is not null)
{ {
e = new RawKeyEventArgs( e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance, WindowsKeyboardDevice.Instance,
@ -170,7 +167,7 @@ namespace Avalonia.Win32
{ {
var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)); var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam));
if (key != Key.None) if (key != Key.None && _owner is not null)
{ {
e = new RawKeyEventArgs( e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance, WindowsKeyboardDevice.Instance,
@ -185,7 +182,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_CHAR: case WindowsMessage.WM_CHAR:
{ {
// Ignore control chars and chars that were handled in WM_KEYDOWN. // Ignore control chars and chars that were handled in WM_KEYDOWN.
if (ToInt32(wParam) >= 32 && !_ignoreWmChar) if (ToInt32(wParam) >= 32 && !_ignoreWmChar && _owner is not null)
{ {
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner, e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner,
new string((char)ToInt32(wParam), 1)); new string((char)ToInt32(wParam), 1));
@ -204,7 +201,7 @@ namespace Avalonia.Win32
break; break;
} }
shouldTakeFocus = ShouldTakeFocusOnClick; shouldTakeFocus = ShouldTakeFocusOnClick;
if (ShouldIgnoreTouchEmulatedMessage()) if (ShouldIgnoreTouchEmulatedMessage() || _owner is null)
{ {
break; break;
} }
@ -213,7 +210,9 @@ namespace Avalonia.Win32
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
#pragma warning disable CS8509
message switch message switch
#pragma warning restore CS8509
{ {
WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown, WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown, WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown,
@ -221,7 +220,7 @@ namespace Avalonia.Win32
WindowsMessage.WM_XBUTTONDOWN => WindowsMessage.WM_XBUTTONDOWN =>
HighWord(ToInt32(wParam)) == 1 ? HighWord(ToInt32(wParam)) == 1 ?
RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Down :
RawPointerEventType.XButton2Down RawPointerEventType.XButton2Down,
}, },
DipFromLParam(lParam), GetMouseModifiers(wParam)); DipFromLParam(lParam), GetMouseModifiers(wParam));
break; break;
@ -236,7 +235,7 @@ namespace Avalonia.Win32
{ {
break; break;
} }
if (ShouldIgnoreTouchEmulatedMessage()) if (ShouldIgnoreTouchEmulatedMessage() || _owner is null)
{ {
break; break;
} }
@ -245,7 +244,9 @@ namespace Avalonia.Win32
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
#pragma warning disable CS8509
message switch message switch
#pragma warning restore CS8509
{ {
WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp, WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp,
WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp, WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp,
@ -309,6 +310,11 @@ namespace Avalonia.Win32
var prevPoint = _lastWmMousePoint; var prevPoint = _lastWmMousePoint;
_lastWmMousePoint = currPoint; _lastWmMousePoint = currPoint;
if (_owner is null)
{
break;
}
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
@ -317,7 +323,7 @@ namespace Avalonia.Win32
point, point,
GetMouseModifiers(wParam)) GetMouseModifiers(wParam))
{ {
IntermediatePoints = new Lazy<IReadOnlyList<RawPointerPoint>>(() => CreateLazyIntermediatePoints(currPoint, prevPoint)) IntermediatePoints = new Lazy<IReadOnlyList<RawPointerPoint>?>(() => CreateIntermediatePoints(currPoint, prevPoint))
}; };
break; break;
@ -325,7 +331,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_MOUSEWHEEL: case WindowsMessage.WM_MOUSEWHEEL:
{ {
if (IsMouseInPointerEnabled) if (IsMouseInPointerEnabled || _owner is null)
{ {
break; break;
} }
@ -341,7 +347,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_MOUSEHWHEEL: case WindowsMessage.WM_MOUSEHWHEEL:
{ {
if (IsMouseInPointerEnabled) if (IsMouseInPointerEnabled || _owner is null)
{ {
break; break;
} }
@ -362,6 +368,10 @@ namespace Avalonia.Win32
break; break;
} }
_trackingMouse = false; _trackingMouse = false;
if (_owner is null)
{
break;
}
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
@ -377,7 +387,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_NCMBUTTONDOWN: case WindowsMessage.WM_NCMBUTTONDOWN:
case WindowsMessage.WM_NCXBUTTONDOWN: case WindowsMessage.WM_NCXBUTTONDOWN:
{ {
if (IsMouseInPointerEnabled) if (IsMouseInPointerEnabled || _owner is null)
{ {
break; break;
} }
@ -385,7 +395,9 @@ namespace Avalonia.Win32
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
#pragma warning disable CS8509
message switch message switch
#pragma warning restore CS8509
{ {
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType
.NonClientLeftButtonDown, .NonClientLeftButtonDown,
@ -396,12 +408,12 @@ namespace Avalonia.Win32
RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Down :
RawPointerEventType.XButton2Down, RawPointerEventType.XButton2Down,
}, },
PointToClient(WindowImpl.PointFromLParam(lParam)), GetMouseModifiers(wParam)); PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam));
break; break;
} }
case WindowsMessage.WM_TOUCH: case WindowsMessage.WM_TOUCH:
{ {
if (_wmPointerEnabled) if (_wmPointerEnabled || _owner is null || Input is not { } input)
{ {
break; break;
} }
@ -414,7 +426,7 @@ namespace Avalonia.Win32
{ {
foreach (var touchInput in touchInputs) foreach (var touchInput in touchInputs)
{ {
Input?.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time, input.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time,
_owner, _owner,
touchInput.Flags.HasAllFlags(TouchInputFlags.TOUCHEVENTF_UP) ? touchInput.Flags.HasAllFlags(TouchInputFlags.TOUCHEVENTF_UP) ?
RawPointerEventType.TouchEnd : RawPointerEventType.TouchEnd :
@ -438,14 +450,14 @@ namespace Avalonia.Win32
case WindowsMessage.WM_POINTERUP: case WindowsMessage.WM_POINTERUP:
case WindowsMessage.WM_POINTERUPDATE: case WindowsMessage.WM_POINTERUPDATE:
{ {
if (!_wmPointerEnabled) if (!_wmPointerEnabled || _owner is null)
{ {
break; break;
} }
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp); GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
var eventType = GetEventType(message, info); var eventType = GetEventType(message, info);
var args = CreatePointerArgs(device, timestamp, eventType, point, modifiers, info.pointerId); var args = CreatePointerArgs(device, timestamp, _owner, eventType, point, modifiers, info.pointerId);
args.IntermediatePoints = CreateLazyIntermediatePoints(info); args.IntermediatePoints = CreateLazyIntermediatePoints(info);
e = args; e = args;
break; break;
@ -454,19 +466,19 @@ namespace Avalonia.Win32
case WindowsMessage.WM_POINTERLEAVE: case WindowsMessage.WM_POINTERLEAVE:
case WindowsMessage.WM_POINTERCAPTURECHANGED: case WindowsMessage.WM_POINTERCAPTURECHANGED:
{ {
if (!_wmPointerEnabled) if (!_wmPointerEnabled || _owner is null)
{ {
break; break;
} }
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp); GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
var eventType = device is TouchDevice ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow; var eventType = device is TouchDevice ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow;
e = CreatePointerArgs(device, timestamp, eventType, point, modifiers, info.pointerId); e = CreatePointerArgs(device, timestamp, _owner, eventType, point, modifiers, info.pointerId);
break; break;
} }
case WindowsMessage.WM_POINTERWHEEL: case WindowsMessage.WM_POINTERWHEEL:
case WindowsMessage.WM_POINTERHWHEEL: case WindowsMessage.WM_POINTERHWHEEL:
{ {
if (!_wmPointerEnabled) if (!_wmPointerEnabled || _owner is null)
{ {
break; break;
} }
@ -488,7 +500,7 @@ namespace Avalonia.Win32
} }
// Do not generate events, but release mouse capture on any other device input. // Do not generate events, but release mouse capture on any other device input.
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp); GetDevicePointerInfo(wParam, out var device, out _, out _, out _, ref timestamp);
if (device != _mouseDevice) if (device != _mouseDevice)
{ {
_mouseDevice.Capture(null); _mouseDevice.Capture(null);
@ -726,9 +738,9 @@ namespace Avalonia.Win32
break; break;
case WindowsMessage.WM_GETOBJECT: case WindowsMessage.WM_GETOBJECT:
if ((long)lParam == UiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable) if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner is Control control)
{ {
var peer = ControlAutomationPeer.CreatePeerForElement((Control)_owner); var peer = ControlAutomationPeer.CreatePeerForElement(control);
var node = AutomationNode.GetOrCreate(peer); var node = AutomationNode.GetOrCreate(peer);
return UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, wParam, lParam, node); return UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, wParam, lParam, node);
} }
@ -775,12 +787,12 @@ namespace Avalonia.Win32
} }
} }
private unsafe Lazy<IReadOnlyList<RawPointerPoint>> CreateLazyIntermediatePoints(POINTER_INFO info) private Lazy<IReadOnlyList<RawPointerPoint>?>? CreateLazyIntermediatePoints(POINTER_INFO info)
{ {
var historyCount = Math.Min((int)info.historyCount, MaxPointerHistorySize); var historyCount = Math.Min((int)info.historyCount, MaxPointerHistorySize);
if (historyCount > 1) if (historyCount > 1)
{ {
return new Lazy<IReadOnlyList<RawPointerPoint>>(() => return new Lazy<IReadOnlyList<RawPointerPoint>?>(() =>
{ {
s_intermediatePointsPooledList.Clear(); s_intermediatePointsPooledList.Clear();
s_intermediatePointsPooledList.Capacity = historyCount; s_intermediatePointsPooledList.Capacity = historyCount;
@ -829,7 +841,7 @@ namespace Avalonia.Win32
return null; return null;
} }
private unsafe IReadOnlyList<RawPointerPoint> CreateLazyIntermediatePoints(MOUSEMOVEPOINT movePoint, MOUSEMOVEPOINT prevMovePoint) private unsafe IReadOnlyList<RawPointerPoint> CreateIntermediatePoints(MOUSEMOVEPOINT movePoint, MOUSEMOVEPOINT prevMovePoint)
{ {
// To understand some of this code, please check MS docs: // To understand some of this code, please check MS docs:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmousemovepointsex#remarks // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmousemovepointsex#remarks
@ -888,11 +900,11 @@ namespace Avalonia.Win32
} }
} }
private RawPointerEventArgs CreatePointerArgs(IInputDevice device, ulong timestamp, RawPointerEventType eventType, RawPointerPoint point, RawInputModifiers modifiers, uint rawPointerId) private RawPointerEventArgs CreatePointerArgs(IInputDevice device, ulong timestamp, IInputRoot owner, RawPointerEventType eventType, RawPointerPoint point, RawInputModifiers modifiers, uint rawPointerId)
{ {
return device is TouchDevice return device is TouchDevice
? new RawTouchEventArgs(device, timestamp, _owner, eventType, point, modifiers, rawPointerId) ? new RawTouchEventArgs(device, timestamp, owner, eventType, point, modifiers, rawPointerId)
: new RawPointerEventArgs(device, timestamp, _owner, eventType, point, modifiers) : new RawPointerEventArgs(device, timestamp, owner, eventType, point, modifiers)
{ {
RawPointerId = rawPointerId RawPointerId = rawPointerId
}; };
@ -1029,7 +1041,7 @@ namespace Avalonia.Win32
// note: for non-ime language, also create it so that emoji panel tracks cursor // note: for non-ime language, also create it so that emoji panel tracks cursor
var langid = LGID(hkl); var langid = LGID(hkl);
if (langid == _langid && Imm32InputMethod.Current.HWND == Hwnd) if (langid == _langid && Imm32InputMethod.Current.Hwnd == Hwnd)
{ {
return; return;
} }

8
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -3,8 +3,6 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
#nullable enable
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
public partial class WindowImpl public partial class WindowImpl
@ -13,7 +11,7 @@ namespace Avalonia.Win32
private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
{ {
// Get the point coordinates for the hit test (screen space). // Get the point coordinates for the hit test (screen space).
var ptMouse = WindowImpl.PointFromLParam(lParam); var ptMouse = PointFromLParam(lParam);
// Get the window rectangle. // Get the window rectangle.
GetWindowRect(hWnd, out var rcWindow); GetWindowRect(hWnd, out var rcWindow);
@ -101,11 +99,9 @@ namespace Avalonia.Win32
lRet = (IntPtr)hittestResult; lRet = (IntPtr)hittestResult;
uint timestamp = unchecked((uint)GetMessageTime());
if (hittestResult == HitTestValues.HTCAPTION) if (hittestResult == HitTestValues.HTCAPTION)
{ {
var position = PointToClient(WindowImpl.PointFromLParam(lParam)); var position = PointToClient(PointFromLParam(lParam));
if (_owner is Window window) if (_owner is Window window)
{ {

7
src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs

@ -2,14 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Win32.Input;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {

206
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -1,33 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Controls; using Avalonia.Collections.Pooled;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Controls;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.OpenGL; using Avalonia.Input;
using Avalonia.OpenGL.Angle; using Avalonia.Metadata;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces; using Avalonia.Platform.Storage;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Win32.Automation; using Avalonia.Rendering;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.Input; using Avalonia.Win32.Input;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using Avalonia.Win32.OpenGl.Angle;
using Avalonia.Win32.OpenGl; using Avalonia.Win32.OpenGl;
using Avalonia.Win32.WinRT;
using Avalonia.Win32.WinRT.Composition; using Avalonia.Win32.WinRT.Composition;
using Avalonia.Win32.WinRT;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
using Avalonia.Collections.Pooled;
using Avalonia.Metadata;
using Avalonia.Platform.Storage;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.OpenGl.Angle;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
@ -37,13 +33,13 @@ namespace Avalonia.Win32
[Unstable] [Unstable]
public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
{ {
private static readonly List<WindowImpl> s_instances = new List<WindowImpl>(); private static readonly List<WindowImpl> s_instances = new();
private static readonly IntPtr DefaultCursor = LoadCursor( private static readonly IntPtr s_defaultCursor = LoadCursor(
IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW)); IntPtr.Zero, new IntPtr((int)UnmanagedMethods.Cursor.IDC_ARROW));
private static readonly Dictionary<WindowEdge, HitTestValues> s_edgeLookup = private static readonly Dictionary<WindowEdge, HitTestValues> s_edgeLookup =
new Dictionary<WindowEdge, HitTestValues> new()
{ {
{ WindowEdge.East, HitTestValues.HTRIGHT }, { WindowEdge.East, HitTestValues.HTRIGHT },
{ WindowEdge.North, HitTestValues.HTTOP }, { WindowEdge.North, HitTestValues.HTTOP },
@ -61,8 +57,8 @@ namespace Avalonia.Win32
private Thickness _extendedMargins; private Thickness _extendedMargins;
private Thickness _offScreenMargin; private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1; private double _extendTitleBarHint = -1;
private bool _isUsingComposition; private readonly bool _isUsingComposition;
private IBlurHost _blurHost; private readonly IBlurHost? _blurHost;
private PlatformResizeReason _resizeReason; private PlatformResizeReason _resizeReason;
private MOUSEMOVEPOINT _lastWmMousePoint; private MOUSEMOVEPOINT _lastWmMousePoint;
@ -76,26 +72,26 @@ namespace Avalonia.Win32
private readonly PenDevice _penDevice; private readonly PenDevice _penDevice;
private readonly ManagedDeferredRendererLock _rendererLock; private readonly ManagedDeferredRendererLock _rendererLock;
private readonly FramebufferManager _framebuffer; private readonly FramebufferManager _framebuffer;
private readonly object _gl; private readonly object? _gl;
private readonly bool _wmPointerEnabled; private readonly bool _wmPointerEnabled;
private Win32NativeControlHost _nativeControlHost; private readonly Win32NativeControlHost _nativeControlHost;
private IStorageProvider _storageProvider; private readonly IStorageProvider _storageProvider;
private WndProc _wndProcDelegate; private WndProc _wndProcDelegate;
private string _className; private string? _className;
private IntPtr _hwnd; private IntPtr _hwnd;
private IInputRoot _owner; private IInputRoot? _owner;
private WindowProperties _windowProperties; private WindowProperties _windowProperties;
private bool _trackingMouse;//ToDo - there is something missed. Needs investigation @Steven Kirk private bool _trackingMouse;//ToDo - there is something missed. Needs investigation @Steven Kirk
private bool _topmost; private bool _topmost;
private double _scaling = 1; private double _scaling = 1;
private WindowState _showWindowState; private WindowState _showWindowState;
private WindowState _lastWindowState; private WindowState _lastWindowState;
private OleDropTarget _dropTarget; private OleDropTarget? _dropTarget;
private Size _minSize; private Size _minSize;
private Size _maxSize; private Size _maxSize;
private POINT _maxTrackSize; private POINT _maxTrackSize;
private WindowImpl _parent; private WindowImpl? _parent;
private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default;
private bool _isCloseRequested; private bool _isCloseRequested;
private bool _shown; private bool _shown;
@ -142,7 +138,7 @@ namespace Avalonia.Win32
angle.PlatformApi == AngleOptions.PlatformApi.DirectX11; angle.PlatformApi == AngleOptions.PlatformApi.DirectX11;
_isUsingComposition = compositionConnector is { } && isUsingAngleDX11; _isUsingComposition = compositionConnector is { } && isUsingAngleDX11;
DxgiConnection dxgiConnection = null; DxgiConnection? dxgiConnection = null;
var isUsingDxgiSwapchain = false; var isUsingDxgiSwapchain = false;
if (!_isUsingComposition) if (!_isUsingComposition)
{ {
@ -159,23 +155,20 @@ namespace Avalonia.Win32
{ {
if (_isUsingComposition) if (_isUsingComposition)
{ {
var cgl = compositionConnector.CreateSurface(this); var cgl = compositionConnector!.CreateSurface(this);
_blurHost = cgl; _blurHost = cgl;
_gl = cgl; _gl = cgl;
_isUsingComposition = true;
} }
else if (isUsingDxgiSwapchain) else if (isUsingDxgiSwapchain)
{ {
var dxgigl = new DxgiSwapchainWindow(dxgiConnection, this); var dxgigl = new DxgiSwapchainWindow(dxgiConnection!, this);
_gl = dxgigl; _gl = dxgigl;
} }
else else
{ {
if (glPlatform is AngleWin32PlatformGraphics egl2) if (glPlatform is AngleWin32PlatformGraphics)
_gl = new EglGlPlatformSurface(this); _gl = new EglGlPlatformSurface(this);
else if (glPlatform is WglPlatformOpenGlInterface wgl) else if (glPlatform is WglPlatformOpenGlInterface)
_gl = new WglGlPlatformSurface(this); _gl = new WglGlPlatformSurface(this);
} }
} }
@ -187,29 +180,41 @@ namespace Avalonia.Win32
s_instances.Add(this); s_instances.Add(this);
} }
public Action Activated { get; set; } /// <inheritdoc />
public Action? Activated { get; set; }
public Func<WindowCloseReason, bool> Closing { get; set; } /// <inheritdoc />
public Func<WindowCloseReason, bool>? Closing { get; set; }
public Action Closed { get; set; } /// <inheritdoc />
public Action? Closed { get; set; }
public Action Deactivated { get; set; } /// <inheritdoc />
public Action? Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; } /// <inheritdoc />
public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect> Paint { get; set; } /// <inheritdoc />
public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } /// <inheritdoc />
public Action<Size, PlatformResizeReason>? Resized { get; set; }
public Action<double> ScalingChanged { get; set; } /// <inheritdoc />
public Action<double>? ScalingChanged { get; set; }
public Action<PixelPoint> PositionChanged { get; set; } /// <inheritdoc />
public Action<PixelPoint>? PositionChanged { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } /// <inheritdoc />
public Action<WindowState>? WindowStateChanged { get; set; }
public Action LostFocus { get; set; } /// <inheritdoc />
public Action? LostFocus { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } /// <inheritdoc />
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public Thickness BorderThickness public Thickness BorderThickness
{ {
@ -240,10 +245,13 @@ namespace Avalonia.Win32
private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.IsPrimary)?.Scaling ?? 1; private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.IsPrimary)?.Scaling ?? 1;
/// <inheritdoc />
public double RenderScaling => _scaling; public double RenderScaling => _scaling;
/// <inheritdoc />
public double DesktopScaling => RenderScaling; public double DesktopScaling => RenderScaling;
/// <inheritdoc />
public Size ClientSize public Size ClientSize
{ {
get get
@ -254,6 +262,7 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public Size? FrameSize public Size? FrameSize
{ {
get get
@ -269,14 +278,18 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public IScreenImpl Screen { get; } public IScreenImpl Screen { get; }
/// <inheritdoc />
public IPlatformHandle Handle { get; private set; } public IPlatformHandle Handle { get; private set; }
/// <inheritdoc />
public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling); public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling);
public IMouseDevice MouseDevice => _mouseDevice; public IMouseDevice MouseDevice => _mouseDevice;
/// <inheritdoc />
public WindowState WindowState public WindowState WindowState
{ {
get get
@ -314,13 +327,15 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public WindowTransparencyLevel TransparencyLevel { get; private set; } public WindowTransparencyLevel TransparencyLevel { get; private set; }
protected IntPtr Hwnd => _hwnd; protected IntPtr Hwnd => _hwnd;
private bool IsMouseInPointerEnabled => _wmPointerEnabled && IsMouseInPointerEnabled(); private bool IsMouseInPointerEnabled => _wmPointerEnabled && IsMouseInPointerEnabled();
public object TryGetFeature(Type featureType) /// <inheritdoc />
public object? TryGetFeature(Type featureType)
{ {
if (featureType == typeof(ITextInputMethodImpl)) if (featureType == typeof(ITextInputMethodImpl))
{ {
@ -339,7 +354,8 @@ namespace Avalonia.Win32
return null; return null;
} }
/// <inheritdoc />
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{ {
TransparencyLevel = EnableBlur(transparencyLevel); TransparencyLevel = EnableBlur(transparencyLevel);
@ -521,8 +537,13 @@ namespace Avalonia.Win32
} }
} }
public IEnumerable<object> Surfaces => new object[] { (IPlatformNativeSurfaceHandle)Handle, _gl, _framebuffer }; /// <inheritdoc />
public IEnumerable<object> Surfaces
=> _gl is null ?
new object[] { Handle, _framebuffer } :
new object[] { Handle, _gl, _framebuffer };
/// <inheritdoc />
public PixelPoint Position public PixelPoint Position
{ {
get get
@ -568,17 +589,21 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public void Move(PixelPoint point) => Position = point; public void Move(PixelPoint point) => Position = point;
/// <inheritdoc />
public void SetMinMaxSize(Size minSize, Size maxSize) public void SetMinMaxSize(Size minSize, Size maxSize)
{ {
_minSize = minSize; _minSize = minSize;
_maxSize = maxSize; _maxSize = maxSize;
} }
/// <inheritdoc />
public IRenderer CreateRenderer(IRenderRoot root) => public IRenderer CreateRenderer(IRenderRoot root) =>
new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces); new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces);
/// <inheritdoc />
public void Resize(Size value, PlatformResizeReason reason) public void Resize(Size value, PlatformResizeReason reason)
{ {
if (WindowState != WindowState.Normal) if (WindowState != WindowState.Normal)
@ -606,13 +631,16 @@ namespace Avalonia.Win32
} }
} }
/// <inheritdoc />
public void Activate() public void Activate()
{ {
SetForegroundWindow(_hwnd); SetForegroundWindow(_hwnd);
} }
public IPopupImpl CreatePopup() => Win32Platform.UseOverlayPopups ? null : new PopupImpl(this); /// <inheritdoc />
public IPopupImpl? CreatePopup() => Win32Platform.UseOverlayPopups ? null : new PopupImpl(this);
/// <inheritdoc />
public void Dispose() public void Dispose()
{ {
(_gl as IDisposable)?.Dispose(); (_gl as IDisposable)?.Dispose();
@ -654,6 +682,7 @@ namespace Avalonia.Win32
InvalidateRect(_hwnd, ref r, false); InvalidateRect(_hwnd, ref r, false);
} }
/// <inheritdoc />
public Point PointToClient(PixelPoint point) public Point PointToClient(PixelPoint point)
{ {
var p = new POINT { X = point.X, Y = point.Y }; var p = new POINT { X = point.X, Y = point.Y };
@ -661,6 +690,7 @@ namespace Avalonia.Win32
return new Point(p.X, p.Y) / RenderScaling; return new Point(p.X, p.Y) / RenderScaling;
} }
/// <inheritdoc />
public PixelPoint PointToScreen(Point point) public PixelPoint PointToScreen(Point point)
{ {
point *= RenderScaling; point *= RenderScaling;
@ -669,29 +699,34 @@ namespace Avalonia.Win32
return new PixelPoint(p.X, p.Y); return new PixelPoint(p.X, p.Y);
} }
/// <inheritdoc />
public void SetInputRoot(IInputRoot inputRoot) public void SetInputRoot(IInputRoot inputRoot)
{ {
_owner = inputRoot; _owner = inputRoot;
CreateDropTarget(); CreateDropTarget(inputRoot);
} }
/// <inheritdoc />
public void Hide() public void Hide()
{ {
UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide);
_shown = false; _shown = false;
} }
/// <inheritdoc />
public virtual void Show(bool activate, bool isDialog) public virtual void Show(bool activate, bool isDialog)
{ {
SetParent(_parent); SetParent(_parent);
ShowWindow(_showWindowState, activate); ShowWindow(_showWindowState, activate);
} }
public Action GotInputWhenDisabled { get; set; } /// <inheritdoc />
public Action? GotInputWhenDisabled { get; set; }
public void SetParent(IWindowImpl parent) /// <inheritdoc />
public void SetParent(IWindowImpl? parent)
{ {
_parent = (WindowImpl)parent; _parent = parent as WindowImpl;
var parentHwnd = _parent?._hwnd ?? IntPtr.Zero; var parentHwnd = _parent?._hwnd ?? IntPtr.Zero;
@ -704,8 +739,10 @@ namespace Avalonia.Win32
SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd); SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd);
} }
/// <inheritdoc />
public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable); public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);
/// <inheritdoc />
public void BeginMoveDrag(PointerPressedEventArgs e) public void BeginMoveDrag(PointerPressedEventArgs e)
{ {
e.Pointer.Capture(null); e.Pointer.Capture(null);
@ -713,6 +750,7 @@ namespace Avalonia.Win32
new IntPtr((int)HitTestValues.HTCAPTION), IntPtr.Zero); new IntPtr((int)HitTestValues.HTCAPTION), IntPtr.Zero);
} }
/// <inheritdoc />
public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
{ {
if (_windowProperties.IsResizable) if (_windowProperties.IsResizable)
@ -727,35 +765,37 @@ namespace Avalonia.Win32
} }
} }
public void SetTitle(string title) /// <inheritdoc />
public void SetTitle(string? title)
{ {
SetWindowText(_hwnd, title); SetWindowText(_hwnd, title);
} }
public void SetCursor(ICursorImpl cursor) /// <inheritdoc />
public void SetCursor(ICursorImpl? cursor)
{ {
var impl = cursor as CursorImpl; var impl = cursor as CursorImpl;
if (cursor is null || impl is object) var hCursor = impl?.Handle ?? s_defaultCursor;
{ SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
var hCursor = impl?.Handle ?? DefaultCursor;
SetClassLong(_hwnd, ClassLongIndex.GCLP_HCURSOR, hCursor);
if (_owner.IsPointerOver) if (_owner?.IsPointerOver == true)
{ {
UnmanagedMethods.SetCursor(hCursor); UnmanagedMethods.SetCursor(hCursor);
}
} }
} }
public void SetIcon(IWindowIconImpl icon) /// <inheritdoc />
public void SetIcon(IWindowIconImpl? icon)
{ {
var impl = (IconImpl)icon; var impl = icon as IconImpl;
var hIcon = impl?.HIcon ?? IntPtr.Zero; var hIcon = impl?.HIcon ?? IntPtr.Zero;
PostMessage(_hwnd, (int)WindowsMessage.WM_SETICON, PostMessage(_hwnd, (int)WindowsMessage.WM_SETICON,
new IntPtr((int)Icons.ICON_BIG), hIcon); new IntPtr((int)Icons.ICON_BIG), hIcon);
} }
/// <inheritdoc />
public void ShowTaskbarIcon(bool value) public void ShowTaskbarIcon(bool value)
{ {
var newWindowProperties = _windowProperties; var newWindowProperties = _windowProperties;
@ -765,6 +805,7 @@ namespace Avalonia.Win32
UpdateWindowProperties(newWindowProperties); UpdateWindowProperties(newWindowProperties);
} }
/// <inheritdoc />
public void CanResize(bool value) public void CanResize(bool value)
{ {
var newWindowProperties = _windowProperties; var newWindowProperties = _windowProperties;
@ -774,6 +815,7 @@ namespace Avalonia.Win32
UpdateWindowProperties(newWindowProperties); UpdateWindowProperties(newWindowProperties);
} }
/// <inheritdoc />
public void SetSystemDecorations(SystemDecorations value) public void SetSystemDecorations(SystemDecorations value)
{ {
var newWindowProperties = _windowProperties; var newWindowProperties = _windowProperties;
@ -783,6 +825,7 @@ namespace Avalonia.Win32
UpdateWindowProperties(newWindowProperties); UpdateWindowProperties(newWindowProperties);
} }
/// <inheritdoc />
public void SetTopmost(bool value) public void SetTopmost(bool value)
{ {
if (value == _topmost) if (value == _topmost)
@ -799,6 +842,7 @@ namespace Avalonia.Win32
_topmost = value; _topmost = value;
} }
/// <inheritdoc />
public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant) public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
{ {
if (Win32Platform.WindowsVersion.Build >= 22000) if (Win32Platform.WindowsVersion.Build >= 22000)
@ -829,6 +873,9 @@ namespace Avalonia.Win32
IntPtr.Zero); IntPtr.Zero);
} }
[MemberNotNull(nameof(_wndProcDelegate))]
[MemberNotNull(nameof(_className))]
[MemberNotNull(nameof(Handle))]
private void CreateWindow() private void CreateWindow()
{ {
// Ensure that the delegate doesn't get garbage collected by storing it as a field. // Ensure that the delegate doesn't get garbage collected by storing it as a field.
@ -845,7 +892,7 @@ namespace Avalonia.Win32
style = (int)windowClassStyle, style = (int)windowClassStyle,
lpfnWndProc = _wndProcDelegate, lpfnWndProc = _wndProcDelegate,
hInstance = GetModuleHandle(null), hInstance = GetModuleHandle(null),
hCursor = DefaultCursor, hCursor = s_defaultCursor,
hbrBackground = IntPtr.Zero, hbrBackground = IntPtr.Zero,
lpszClassName = _className lpszClassName = _className
}; };
@ -878,20 +925,23 @@ namespace Avalonia.Win32
monitor, monitor,
MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
out var dpix, out var dpix,
out var dpiy) == 0) out _) == 0)
{ {
_scaling = dpix / 96.0; _scaling = dpix / 96.0;
} }
} }
} }
private void CreateDropTarget() private void CreateDropTarget(IInputRoot inputRoot)
{ {
var odt = new OleDropTarget(this, _owner); if (AvaloniaLocator.Current.GetService<IDragDropDevice>() is { } dragDropDevice)
if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false)
{ {
_dropTarget = odt; var odt = new OleDropTarget(this, inputRoot, dragDropDevice);
if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false)
{
_dropTarget = odt;
}
} }
} }
@ -1105,7 +1155,7 @@ namespace Avalonia.Win32
case WindowState.FullScreen: case WindowState.FullScreen:
newWindowProperties.IsFullScreen = true; newWindowProperties.IsFullScreen = true;
command = IsWindowVisible(_hwnd) ? (ShowWindowCommand?)null : ShowWindowCommand.Restore; command = IsWindowVisible(_hwnd) ? null : ShowWindowCommand.Restore;
break; break;
default: default:
@ -1370,13 +1420,13 @@ namespace Avalonia.Win32
private const int MF_DISABLED = 0x2; private const int MF_DISABLED = 0x2;
private const int SC_CLOSE = 0xF060; private const int SC_CLOSE = 0xF060;
static void DisableCloseButton(IntPtr hwnd) private static void DisableCloseButton(IntPtr hwnd)
{ {
EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE,
MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
} }
static void EnableCloseButton(IntPtr hwnd) private static void EnableCloseButton(IntPtr hwnd)
{ {
EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE,
MF_BYCOMMAND | MF_ENABLED); MF_BYCOMMAND | MF_ENABLED);
@ -1407,6 +1457,7 @@ namespace Avalonia.Win32
IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
/// <inheritdoc />
public void SetExtendClientAreaToDecorationsHint(bool hint) public void SetExtendClientAreaToDecorationsHint(bool hint)
{ {
_isClientAreaExtended = hint; _isClientAreaExtended = hint;
@ -1414,6 +1465,7 @@ namespace Avalonia.Win32
ExtendClientArea(); ExtendClientArea();
} }
/// <inheritdoc />
public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
{ {
_extendChromeHints = hints; _extendChromeHints = hints;
@ -1433,7 +1485,7 @@ namespace Avalonia.Win32
public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended; public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended;
/// <inheritdoc/> /// <inheritdoc/>
public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; } public Action<bool>? ExtendClientAreaToDecorationsChanged { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome); public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome);

13
src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Avalonia.Reactive;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Threading; using Avalonia.Threading;
@ -12,12 +11,12 @@ namespace Avalonia.Win32
internal class WindowsMountedVolumeInfoListener : IDisposable internal class WindowsMountedVolumeInfoListener : IDisposable
{ {
private readonly IDisposable _disposable; private readonly IDisposable _disposable;
private bool _beenDisposed = false; private bool _beenDisposed;
private ObservableCollection<MountedVolumeInfo> mountedDrives; private readonly ObservableCollection<MountedVolumeInfo> _mountedDrives;
public WindowsMountedVolumeInfoListener(ObservableCollection<MountedVolumeInfo> mountedDrives) public WindowsMountedVolumeInfoListener(ObservableCollection<MountedVolumeInfo> mountedDrives)
{ {
this.mountedDrives = mountedDrives; _mountedDrives = mountedDrives;
_disposable = DispatcherTimer.Run(Poll, TimeSpan.FromSeconds(1)); _disposable = DispatcherTimer.Run(Poll, TimeSpan.FromSeconds(1));
@ -51,14 +50,14 @@ namespace Avalonia.Win32
}) })
.ToArray(); .ToArray();
if (mountedDrives.SequenceEqual(mountVolInfos)) if (_mountedDrives.SequenceEqual(mountVolInfos))
return true; return true;
else else
{ {
mountedDrives.Clear(); _mountedDrives.Clear();
foreach (var i in mountVolInfos) foreach (var i in mountVolInfos)
mountedDrives.Add(i); _mountedDrives.Add(i);
return true; return true;
} }
} }

11
src/tools/DevGenerators/Helpers.cs

@ -6,16 +6,21 @@ namespace Generator;
static class Helpers static class Helpers
{ {
private static readonly SymbolDisplayFormat s_symbolDisplayFormat =
SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(
SymbolDisplayFormat.FullyQualifiedFormat.MiscellaneousOptions |
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
public static StringBuilder Pad(this StringBuilder sb, int count) => sb.Append(' ', count * 4); public static StringBuilder Pad(this StringBuilder sb, int count) => sb.Append(' ', count * 4);
public static string GetFullyQualifiedName(this ISymbol symbol) public static string GetFullyQualifiedName(this ISymbol symbol)
{ {
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); return symbol.ToDisplayString(s_symbolDisplayFormat);
} }
public static bool HasFullyQualifiedName(this ISymbol symbol, string name) public static bool HasFullyQualifiedName(this ISymbol symbol, string name)
{ {
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == name; return symbol.ToDisplayString(s_symbolDisplayFormat) == name;
} }
public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name) public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name)
@ -28,4 +33,4 @@ static class Helpers
return false; return false;
} }
} }

Loading…
Cancel
Save