Browse Source

Implement DirectComposition support, as well as return Win8+ window transparency (#13382)

* Init DirectComposition support

* Use Win8.1 as a minimum dcomp target, avoid hacks with render timer

* Refactor transparency supprot a little

* Cleanups

* Restore old hack as I don't know how to remove it yet
pull/13394/head
Max Katz 3 years ago
committed by GitHub
parent
commit
da523ffcaf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  2. 54
      src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs
  3. 173
      src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs
  4. 129
      src/Windows/Avalonia.Win32/DComposition/DirectCompositionConnection.cs
  5. 24
      src/Windows/Avalonia.Win32/DComposition/DirectCompositionShared.cs
  6. 14
      src/Windows/Avalonia.Win32/DComposition/NativeMethods.cs
  7. 21
      src/Windows/Avalonia.Win32/DComposition/NativeStructs.cs
  8. 164
      src/Windows/Avalonia.Win32/DComposition/dcomp.idl
  9. 8
      src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
  10. 17
      src/Windows/Avalonia.Win32/IBlurHost.cs
  11. 11
      src/Windows/Avalonia.Win32/IWindowsSurfaceFactory.cs
  12. 31
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  13. 1
      src/Windows/Avalonia.Win32/PlatformConstants.cs
  14. 8
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  15. 20
      src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
  16. 11
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
  17. 7
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
  18. 15
      src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs
  19. 2
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  20. 233
      src/Windows/Avalonia.Win32/WindowImpl.cs

1
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -13,6 +13,7 @@
<MicroComIdl Include="WinRT\winrt.idl" CSharpInteropPath="WinRT\WinRT.Generated.cs" />
<MicroComIdl Include="Win32Com\win32.idl" CSharpInteropPath="Win32Com\Win32.Generated.cs" />
<MicroComIdl Include="DirectX\directx.idl" CSharpInteropPath="DirectX\directx.Generated.cs" />
<MicroComIdl Include="DComposition\dcomp.idl" CSharpInteropPath="DComposition\DComp.Generated.cs" />
</ItemGroup>
<Import Project="..\..\..\build\System.Drawing.Common.props" />
<Import Project="..\..\..\build\NullableEnable.props" />

54
src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs

@ -0,0 +1,54 @@
using System;
using System.Numerics;
using System.Threading;
using Avalonia.OpenGL.Egl;
using Avalonia.Reactive;
using MicroCom.Runtime;
namespace Avalonia.Win32.DComposition;
internal class DirectCompositedWindow : IDisposable
{
private readonly DirectCompositionShared _shared;
public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; }
private readonly IDCompositionVisual _container;
private readonly IDCompositionTarget _target;
private readonly IDCompositionDevice2 _device;
public void Dispose()
{
lock (_shared.SyncRoot)
{
_container.Dispose();
_target.Dispose();
_device.Dispose();
}
}
public DirectCompositedWindow(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, DirectCompositionShared shared)
{
WindowInfo = info;
_shared = shared;
_device = shared.Device.CloneReference();
using var desktopTarget = shared.Device.CreateTargetForHwnd(WindowInfo.Handle, false);
_target = desktopTarget.QueryInterface<IDCompositionTarget>();
using var container = shared.Device.CreateVisual();
_container = container.CloneReference();
_target.SetRoot(container);
}
public void SetSurface(IDCompositionSurface surface) => _container.SetContent(surface);
public IDisposable BeginTransaction()
{
Monitor.Enter(_shared.SyncRoot);
return Disposable.Create(() =>
{
_device.Commit();
Monitor.Exit(_shared.SyncRoot);
});
}
}

173
src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs

@ -0,0 +1,173 @@
using System;
using System.ComponentModel;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.Interop;
using MicroCom.Runtime;
namespace Avalonia.Win32.DComposition;
internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, ICompositionEffectsSurface
{
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
private readonly DirectCompositionShared _shared;
private DirectCompositedWindow? _window;
private BlurEffect _blurEffect;
public DirectCompositedWindowSurface(DirectCompositionShared shared, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
_shared = shared;
_info = info;
}
public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice)
{
_window ??= new DirectCompositedWindow(_info, _shared);
SetBlur(_blurEffect);
return new DirectCompositedWindowRenderTarget(context, d3dDevice, _shared, _window);
}
public void Dispose()
{
_window?.Dispose();
_window = null;
}
// TODO: we can implement BlurEffect.GaussianBlur in with IDCompositionDevice3.CreateGaussianBlurEffect.
public bool IsBlurSupported(BlurEffect effect) => effect == BlurEffect.None;
public void SetBlur(BlurEffect enable)
{
_blurEffect = enable;
// _window?.SetBlur(enable);
}
}
internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget
{
private static readonly Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
private readonly IPlatformGraphicsContext _context;
private readonly DirectCompositedWindow _window;
private readonly IDCompositionVirtualSurface _surface;
private bool _lost;
private PixelSize _size;
private readonly IUnknown _d3dDevice;
public DirectCompositedWindowRenderTarget(
IPlatformGraphicsContext context, IntPtr d3dDevice,
DirectCompositionShared shared, DirectCompositedWindow window)
{
_d3dDevice = MicroComRuntime.CreateProxyFor<IUnknown>(d3dDevice, false).CloneReference();
_context = context;
_window = window;
using (var surfaceFactory = shared.Device.CreateSurfaceFactory(_d3dDevice))
{
_surface = surfaceFactory.CreateVirtualSurface(1, 1, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED);
}
}
public void Dispose()
{
_surface.Dispose();
_d3dDevice.Dispose();
}
public bool IsCorrupted => _context.IsLost || _lost;
public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw()
{
if (IsCorrupted)
throw new RenderTargetCorruptedException();
var transaction = _window.BeginTransaction();
bool needsEndDraw = false;
try
{
var size = _window.WindowInfo.Size;
var scale = _window.WindowInfo.Scaling;
if (_size != size)
{
_surface.Resize((ushort)size.Width, (ushort)size.Height);
_size = size;
}
_window.SetSurface(_surface);
void* pTexture;
UnmanagedMethods.POINT off;
try
{
var rect = new UnmanagedMethods.RECT { right = size.Width, bottom = size.Height };
var iid = IID_ID3D11Texture2D;
off = _surface.BeginDraw(&rect, &iid, &pTexture);
}
catch (Exception e)
{
_lost = true;
throw new RenderTargetCorruptedException(e);
}
needsEndDraw = true;
var offset = new PixelPoint(off.X, off.Y);
using var texture = MicroComRuntime.CreateProxyFor<IUnknown>(pTexture, true);
var session = new Session(_surface, texture, transaction, size, offset, scale);
transaction = null;
return session;
}
finally
{
if (transaction != null)
{
if (needsEndDraw)
_surface.EndDraw();
transaction.Dispose();
}
}
}
private class Session : IDirect3D11TextureRenderTargetRenderSession
{
private readonly IDisposable _transaction;
private readonly PixelSize _size;
private readonly PixelPoint _offset;
private readonly double _scaling;
private readonly IDCompositionSurface _surfaceInterop;
private readonly IUnknown _texture;
public Session(IDCompositionSurface surfaceInterop, IUnknown texture, IDisposable transaction,
PixelSize size, PixelPoint offset, double scaling)
{
_transaction = transaction;
_size = size;
_offset = offset;
_scaling = scaling;
_surfaceInterop = surfaceInterop.CloneReference();
_texture = texture.CloneReference();
}
public void Dispose()
{
try
{
_texture.Dispose();
_surfaceInterop.EndDraw();
_surfaceInterop.Dispose();
}
finally
{
_transaction.Dispose();
}
}
public IntPtr D3D11Texture2D => _texture.GetNativeIntPtr();
public PixelSize Size => _size;
public PixelPoint Offset => _offset;
public double Scaling => _scaling;
}
}

129
src/Windows/Avalonia.Win32/DComposition/DirectCompositionConnection.cs

@ -0,0 +1,129 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.MicroCom;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
using Avalonia.Win32.Interop;
using Avalonia.Win32.WinRT;
using Avalonia.Win32.WinRT.Composition;
using MicroCom.Runtime;
namespace Avalonia.Win32.DComposition;
internal class DirectCompositionConnection : IRenderTimer, IWindowsSurfaceFactory
{
private static readonly Guid IID_IDCompositionDesktopDevice = Guid.Parse("5f4633fe-1e08-4cb8-8c75-ce24333f5602");
public event Action<TimeSpan>? Tick;
public bool RunsInBackground => true;
private readonly DirectCompositionShared _shared;
public DirectCompositionConnection(DirectCompositionShared shared)
{
_shared = shared;
}
private static bool TryCreateAndRegisterCore()
{
var tcs = new TaskCompletionSource<bool>();
var th = new Thread(() =>
{
DirectCompositionConnection connect;
try
{
var result = NativeMethods.DCompositionCreateDevice2(default, IID_IDCompositionDesktopDevice, out var cDevice);
if (result != UnmanagedMethods.HRESULT.S_OK)
{
throw new Win32Exception((int)result);
}
using (var device = MicroComRuntime.CreateProxyFor<IDCompositionDesktopDevice>(cDevice, false))
{
var shared = new DirectCompositionShared(device);
connect = new DirectCompositionConnection(shared);
}
AvaloniaLocator.CurrentMutable.Bind<IWindowsSurfaceFactory>().ToConstant(connect);
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connect);
tcs.SetResult(true);
}
catch (Exception e)
{
tcs.SetException(e);
return;
}
connect.RunLoop();
}) { IsBackground = true, Name = "DwmRenderTimerLoop" };
th.SetApartmentState(ApartmentState.STA);
th.Start();
return tcs.Task.Result;
}
private void RunLoop()
{
var cts = new CancellationTokenSource();
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
cts.Cancel();
var _stopwatch = Stopwatch.StartNew();
var device = _shared.Device.CloneReference();
while (!cts.IsCancellationRequested)
{
try
{
device.WaitForCommitCompletion();
Tick?.Invoke(_stopwatch.Elapsed);
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Win32Platform)
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
}
}
device?.Dispose();
}
public static bool IsSupported()
{
return Win32Platform.WindowsVersion >= PlatformConstants.Windows8_1;
}
public static bool TryCreateAndRegister()
{
if (IsSupported())
{
try
{
TryCreateAndRegisterCore();
return true;
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Win32Platform)
?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
}
}
else
{
var osVersionNotice =
$"Windows {PlatformConstants.Windows8_1} is required. Your machine has Windows {Win32Platform.WindowsVersion} installed.";
Logger.TryGet(LogEventLevel.Warning, LogArea.Win32Platform)?.Log(null,
$"Unable to initialize WinUI compositor: {osVersionNotice}");
}
return false;
}
public bool RequiresNoRedirectionBitmap => true;
public object CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new DirectCompositedWindowSurface(_shared, info);
}

24
src/Windows/Avalonia.Win32/DComposition/DirectCompositionShared.cs

@ -0,0 +1,24 @@
using System;
using System.ComponentModel;
using System.Threading;
using Avalonia.Win32.Interop;
using MicroCom.Runtime;
namespace Avalonia.Win32.DComposition;
internal class DirectCompositionShared : IDisposable
{
public object SyncRoot { get; } = new();
public DirectCompositionShared(IDCompositionDesktopDevice device)
{
Device = device.CloneReference();
}
public IDCompositionDesktopDevice Device { get; }
public void Dispose()
{
Device.Dispose();
}
}

14
src/Windows/Avalonia.Win32/DComposition/NativeMethods.cs

@ -0,0 +1,14 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32.DComposition;
internal class NativeMethods
{
[DllImport("dcomp", ExactSpelling = true)]
public static extern UnmanagedMethods.HRESULT DCompositionCreateDevice2( /* _In_opt_ */
IntPtr renderingDevice, /* _In_ */
[MarshalAs(UnmanagedType.LPStruct)] Guid iid, /* _Outptr_ */ out IntPtr dcompositionDevice);
}

21
src/Windows/Avalonia.Win32/DComposition/NativeStructs.cs

@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
using Avalonia.Win32.DirectX;
namespace Avalonia.Win32.DComposition;
[StructLayout(LayoutKind.Sequential)]
public struct DXGI_RATIONAL
{
public uint Numerator;
public uint Denominator;
}
[StructLayout(LayoutKind.Sequential)]
public struct DCOMPOSITION_FRAME_STATISTICS
{
public long lastFrameTime;
public DXGI_RATIONAL currentCompositionRate;
public long currentTime;
public long timeFrequency;
public long nextEstimatedFrameTime;
}

164
src/Windows/Avalonia.Win32/DComposition/dcomp.idl

@ -0,0 +1,164 @@
@clr-namespace Avalonia.Win32.DComposition
@clr-access internal
@clr-map FLOAT float
@clr-map HSTRING IntPtr
@clr-map Vector2 System.Numerics.Vector2
@clr-map Vector3 System.Numerics.Vector3
@clr-map Quaternion System.Numerics.Quaternion
@clr-map Matrix4x4 System.Numerics.Matrix4x4
@clr-map RECT Avalonia.Win32.Interop.UnmanagedMethods.RECT
@clr-map SIZE Avalonia.Win32.Interop.UnmanagedMethods.SIZE
@clr-map POINT Avalonia.Win32.Interop.UnmanagedMethods.POINT
@clr-map DXGI_FORMAT Avalonia.Win32.DirectX.DXGI_FORMAT
@clr-map DXGI_ALPHA_MODE Avalonia.Win32.DirectX.DXGI_ALPHA_MODE
@clr-map HWND IntPtr
@clr-map HANDLE IntPtr
@clr-map BOOL int
@clr-map DWORD int
@clr-map SIZE_T IntPtr
@clr-map boolean int
@clr-map BYTE byte
@clr-map INT16 short
@clr-map INT32 int
@clr-map INT64 long
@clr-map UINT ushort
@clr-map UINT16 ushort
@clr-map ULONG uint
@clr-map UINT32 uint
@clr-map UINT64 ulong
@clr-map DOUBLE double
@clr-map GUID System.Guid
@clr-map REFGUID System.Guid*
@clr-map REFIID System.Guid*
@clr-map WCHAR System.Char
@clr-map HMODULE void*
@clr-map DXGI_USAGE uint
@clr-map LARGE_INTEGER ulong
@clr-map INT int
@clr-map DXGI_SHARED_RESOURCE void*
@clr-map LUID ulong
@clr-map LPSTR ushort*
@clr-map LPWSTR ushort*
@clr-map LPCWSTR ushort*
[uuid(c37ea93a-e7aa-450d-b16f-9746cb0407f3)]
interface IDCompositionDevice : IUnknown
{
HRESULT Commit();
HRESULT WaitForCommitCompletion();
HRESULT GetFrameStatistics(DCOMPOSITION_FRAME_STATISTICS* statistics);
HRESULT CreateTargetForHwnd(IntPtr hwnd, bool topmost, [out] IDCompositionVisual** target);
HRESULT CreateVisual([out] IDCompositionVisual** visual);
HRESULT CreateSurface(UINT width, UINT height, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionSurface** surface);
HRESULT CreateVirtualSurface(UINT initialWidth, UINT initialHeight, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionVirtualSurface** virtualSurface);
HRESULT CreateSurfaceFromHandle(IntPtr handle, [out] IUnknown** surface);
HRESULT CreateSurfaceFromHwnd(IntPtr hwnd, [out] IUnknown** surface);
HRESULT CreateTranslateTransform([out] void** translateTransform);
HRESULT CreateScaleTransform([out] void** scaleTransform);
HRESULT CreateRotateTransform([out] void** rotateTransform);
HRESULT CreateSkewTransform([out] void** skewTransform);
HRESULT CreateMatrixTransform([out] void** matrixTransform);
HRESULT CreateTransformGroup(void* transforms, int elements, [out] void** transformGroup);
HRESULT CreateTranslateTransform3D([out] void** translateTransform3D);
HRESULT CreateScaleTransform3D([out] void** scaleTransform3D);
HRESULT CreateRotateTransform3D([out] void** rotateTransform3D);
HRESULT CreateMatrixTransform3D([out] void** matrixTransform3D);
HRESULT CreateTransform3DGroup(void* transforms3D, int elements, [out] void** transform3DGroup);
HRESULT CreateEffectGroup([out] void** effectGroup);
HRESULT CreateRectangleClip([out] void** clip);
HRESULT CreateAnimation([out] void** animation);
HRESULT CheckDeviceState([out] BOOL* pfValid);
}
// IDCompositionDevice2 DOES NOT inherit IDCompositionDevice, it's a completely different interface in DComp as it doesn't have desktop only APIs.
[uuid(75F6468D-1B8E-447C-9BC6-75FEA80B5B25)]
interface IDCompositionDevice2 : IUnknown
{
HRESULT Commit();
HRESULT WaitForCommitCompletion();
HRESULT GetFrameStatistics(DCOMPOSITION_FRAME_STATISTICS* statistics);
HRESULT CreateVisual([out] IDCompositionVisual** visual);
HRESULT CreateSurfaceFactory(IUnknown* renderingDevice, [out] IDCompositionSurfaceFactory** surfaceFactory);
HRESULT CreateSurface(UINT width, UINT height, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionSurface** surface);
HRESULT CreateVirtualSurface(UINT initialWidth, UINT initialHeight, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionVirtualSurface** virtualSurface);
HRESULT CreateTranslateTransform([out] void** translateTransform);
HRESULT CreateScaleTransform([out] void** scaleTransform);
HRESULT CreateRotateTransform([out] void** rotateTransform);
HRESULT CreateSkewTransform([out] void** skewTransform);
HRESULT CreateMatrixTransform([out] void** matrixTransform);
HRESULT CreateTransformGroup(void* transforms, int elements, [out] void** transformGroup);
HRESULT CreateTranslateTransform3D([out] void** translateTransform3D);
HRESULT CreateScaleTransform3D([out] void** scaleTransform3D);
HRESULT CreateRotateTransform3D([out] void** rotateTransform3D);
HRESULT CreateMatrixTransform3D([out] void** matrixTransform3D);
HRESULT CreateTransform3DGroup(void* transforms3D, int elements, [out] void** transform3DGroup);
HRESULT CreateEffectGroup([out] void** effectGroup);
HRESULT CreateRectangleClip([out] void** clip);
HRESULT CreateAnimation([out] void** animation);
}
[uuid(0987cb06-f916-48bf-8d35-ce7641781bd9)]
interface IDCompositionDevice3 : IDCompositionDevice2
{
HRESULT CreateGaussianBlurEffect([out] void** gaussianBlurEffect);
}
[uuid(5f4633fe-1e08-4cb8-8c75-ce24333f5602)]
interface IDCompositionDesktopDevice : IDCompositionDevice2
{
HRESULT CreateTargetForHwnd(IntPtr hwnd, bool topmost, [out] IDCompositionVisual** target);
HRESULT CreateSurfaceFromHandle(IntPtr handle, [out] IUnknown** surface);
HRESULT CreateSurfaceFromHwnd(IntPtr hwnd, [out] IUnknown** surface);
}
[uuid(4d93059d-097b-4651-9a60-f0f25116e2f3)]
interface IDCompositionVisual : IUnknown
{
HRESULT SetOffsetX_IDCompositionAnimation(void* animation);
HRESULT SetOffsetX(float offsetX);
HRESULT SetOffsetY_IDCompositionAnimation(void* animation);
HRESULT SetOffsetY(float offsetY);
HRESULT SetTransform_IDCompositionTransform(void* transform);
HRESULT SetTransform(/* D2D_MATRIX_3X2_F */ void* matrix);
HRESULT SetTransformParent(IDCompositionVisual* visual);
HRESULT SetEffect(void* effect);
HRESULT SetBitmapInterpolationMode(/* DCOMPOSITION_BITMAP_INTERPOLATION_MODE */ int interpolationMode);
HRESULT SetBorderMode(/* DCOMPOSITION_BORDER_MODE */ int borderMode);
HRESULT SetClip_IDCompositionClip(void* clip);
HRESULT SetClip(/* D2D_RECT_F */ void* rect);
HRESULT SetContent(IUnknown* content);
HRESULT AddVisual(IDCompositionVisual* visual, BOOL insertAbove, IDCompositionVisual* referenceVisual);
HRESULT RemoveVisual(IDCompositionVisual* visual);
HRESULT RemoveAllVisuals();
HRESULT SetCompositeMode(/* DCOMPOSITION_COMPOSITE_MODE */ int compositeMode);
}
[uuid(eacdd04c-117e-4e17-88f4-d1b12b0e3d89)]
interface IDCompositionTarget : IUnknown
{
HRESULT SetRoot(IDCompositionVisual* visual);
}
[uuid(e334bc12-3937-4e02-85eb-fcf4eb30d2c8)]
interface IDCompositionSurfaceFactory : IUnknown
{
HRESULT CreateSurface(UINT width, UINT height, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionSurface** surface);
HRESULT CreateVirtualSurface(UINT initialWidth, UINT initialHeight, DXGI_FORMAT pixelFormat, DXGI_ALPHA_MODE alphaMode, [out] IDCompositionVirtualSurface** virtualSurface);
}
[uuid(bb8a4953-2c99-4f5a-96f5-4819027fa3ac)]
interface IDCompositionSurface : IUnknown
{
HRESULT BeginDraw(RECT* updateRect, GUID* iid, [out] void** updateObject, [out] POINT* updateOffset);
HRESULT EndDraw();
HRESULT SuspendDraw();
HRESULT ResumeDraw();
HRESULT Scroll(RECT* scrollRect, RECT* clipRect, int offsetX, int offsetY);
}
[uuid(ae471c51-5f53-4a24-8d3e-d0c39c30b3f0)]
interface IDCompositionVirtualSurface : IDCompositionSurface
{
HRESULT Resize(UINT width, UINT height);
HRESULT Trim(/* RECT* */ void* rectangles, INT count);
}

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

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods;
@ -9,7 +10,7 @@ using MicroCom.Runtime;
namespace Avalonia.Win32.DirectX
{
internal unsafe class DxgiConnection : IRenderTimer
internal unsafe class DxgiConnection : IRenderTimer, IWindowsSurfaceFactory
{
public const uint ENUM_CURRENT_SETTINGS = unchecked((uint)(-1));
@ -164,7 +165,7 @@ namespace Avalonia.Win32.DirectX
{
var connection = new DxgiConnection(pumpLock);
AvaloniaLocator.CurrentMutable.BindToSelf(connection);
AvaloniaLocator.CurrentMutable.Bind<IWindowsSurfaceFactory>().ToConstant(connection);
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connection);
tcs.SetResult(true);
connection.RunLoop();
@ -180,5 +181,8 @@ namespace Avalonia.Win32.DirectX
// block until
return tcs.Task.Result;
}
public bool RequiresNoRedirectionBitmap => false;
public object CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new DxgiSwapchainWindow(this, info);
}
}

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

@ -0,0 +1,17 @@
namespace Avalonia.Win32;
internal enum BlurEffect
{
None,
GaussianBlur,
Acrylic,
MicaLight,
MicaDark
}
internal interface ICompositionEffectsSurface
{
bool IsBlurSupported(BlurEffect effect);
void SetBlur(BlurEffect enable);
}

11
src/Windows/Avalonia.Win32/IWindowsSurfaceFactory.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
using Avalonia.OpenGL.Egl;
namespace Avalonia.Win32;
internal interface IWindowsSurfaceFactory
{
bool RequiresNoRedirectionBitmap { get; }
object CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info);
}

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

@ -1151,6 +1151,9 @@ namespace Avalonia.Win32.Interop
uint uStartScan, uint cScanLines,
IntPtr lpvBits, [In] ref BITMAPINFO lpbmi, uint fuColorUse);
[DllImport("gdi32.dll", SetLastError = false, ExactSpelling = true)]
public static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
[DllImport("user32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
@ -1704,8 +1707,8 @@ namespace Avalonia.Win32.Interop
[DllImport("dwmapi.dll")]
public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult);
[DllImport("dwmapi.dll")]
public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
[DllImport("dwmapi.dll", SetLastError = false)]
public static extern int DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
[Flags]
public enum LayeredWindowFlags
@ -1732,14 +1735,6 @@ namespace Avalonia.Win32.Interop
public bool fEnable;
public IntPtr hRgnBlur;
public bool fTransitionOnMaximized;
public DWM_BLURBEHIND(bool enabled)
{
fEnable = enabled;
hRgnBlur = IntPtr.Zero;
fTransitionOnMaximized = false;
dwFlags = DWM_BB.Enable;
}
}
[StructLayout(LayoutKind.Sequential)]
@ -1830,22 +1825,6 @@ namespace Avalonia.Win32.Interop
return result;
}
internal static int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data)
{
var user32 = LoadLibrary("user32.dll");
var pfnSetWindowCompositionAttribute = (delegate* unmanaged[Stdcall]<IntPtr, WindowCompositionAttributeData*, int>)GetProcAddress(user32, nameof(SetWindowCompositionAttribute));
if (pfnSetWindowCompositionAttribute == null)
{
// This preserves the same behavior as using the DllImport attribute.
throw new EntryPointNotFoundException("The unsupported SetWindowCompositionAttribute-function has been removed from the operating system.");
}
fixed (WindowCompositionAttributeData* pData = &data)
{
return pfnSetWindowCompositionAttribute(hwnd, pData);
}
}
[Flags]
public enum GCS : uint
{

1
src/Windows/Avalonia.Win32/PlatformConstants.cs

@ -9,6 +9,7 @@ namespace Avalonia.Win32
public static readonly Version Windows10 = new Version(10, 0);
public static readonly Version Windows8 = new Version(6, 2);
public static readonly Version Windows8_1 = new Version(6, 3);
public static readonly Version Windows7 = new Version(6, 1);
}
}

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

@ -3,6 +3,7 @@ using System.Diagnostics.Tracing;
using System.Linq;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Win32.DComposition;
using Avalonia.Win32.DirectX;
using Avalonia.Win32.OpenGl;
using Avalonia.Win32.OpenGl.Angle;
@ -87,6 +88,13 @@ static class Win32GlManager
return;
}
if (compositionMode == Win32CompositionMode.DirectComposition
&& DirectCompositionConnection.IsSupported()
&& DirectCompositionConnection.TryCreateAndRegister())
{
return;
}
if (compositionMode == Win32CompositionMode.LowLatencyDxgiSwapChain
&& DxgiConnection.TryCreateAndRegister())
{

20
src/Windows/Avalonia.Win32/Win32PlatformOptions.cs

@ -61,14 +61,14 @@ public enum Win32CompositionMode
/// </remarks>
WinUIComposition = 1,
// /// <summary>
// /// Render Avalonia to a texture inside the DirectComposition tree.
// /// </summary>
// /// <remarks>
// /// Supported on Windows 8 and above. Ignored on other versions.<br/>
// /// Can only be applied with <see cref="Win32PlatformOptions.RenderingMode"/>=<see cref="Win32RenderingMode.AngleEgl"/>.
// /// </remarks>
// DirectComposition = 2,
/// <summary>
/// Render Avalonia to a texture inside the DirectComposition tree.
/// </summary>
/// <remarks>
/// Supported on Windows 8 and above. Ignored on other versions.<br/>
/// Can only be applied with <see cref="Win32PlatformOptions.RenderingMode"/>=<see cref="Win32RenderingMode.AngleEgl"/>.
/// </remarks>
DirectComposition = 2,
/// <summary>
/// When <see cref="LowLatencyDxgiSwapChain"/> is active, renders Avalonia through a low-latency Dxgi Swapchain.
@ -117,7 +117,7 @@ public class Win32PlatformOptions
/// <summary>
/// Gets or sets Avalonia composition modes with fallbacks.
/// The first element in the array has the highest priority.
/// The default value is: <see cref="Win32CompositionMode.WinUIComposition"/>, <see cref="Win32CompositionMode.RedirectionSurface"/>.
/// The default value is: <see cref="Win32CompositionMode.WinUIComposition"/>, <see cref="Win32CompositionMode.DirectComposition"/>, <see cref="Win32CompositionMode.RedirectionSurface"/>.
/// </summary>
/// <remarks>
/// If application should work on as wide range of devices as possible, at least add <see cref="Win32CompositionMode.RedirectionSurface"/> as a fallback value.
@ -125,7 +125,7 @@ public class Win32PlatformOptions
/// <exception cref="System.InvalidOperationException">Thrown if no values were matched.</exception>
public IReadOnlyList<Win32CompositionMode> CompositionMode { get; set; } = new[]
{
Win32CompositionMode.WinUIComposition, Win32CompositionMode.RedirectionSurface
Win32CompositionMode.WinUIComposition, Win32CompositionMode.DirectComposition, Win32CompositionMode.RedirectionSurface
};
/// <summary>

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

@ -7,7 +7,7 @@ using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
internal class WinUiCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, IBlurHost
internal class WinUiCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, ICompositionEffectsSurface
{
private readonly WinUiCompositionShared _shared;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
@ -36,6 +36,15 @@ namespace Avalonia.Win32.WinRT.Composition
_window = null;
}
public bool IsBlurSupported(BlurEffect effect) => effect switch
{
BlurEffect.None => true,
BlurEffect.Acrylic => Win32Platform.WindowsVersion >= WinUiCompositionShared.MinAcrylicVersion,
BlurEffect.MicaLight => Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion,
BlurEffect.MicaDark => Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion,
_ => false
};
public void SetBlur(BlurEffect enable)
{
_blurEffect = enable;

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

@ -11,7 +11,7 @@ using Avalonia.Win32.Interop;
namespace Avalonia.Win32.WinRT.Composition;
internal class WinUiCompositorConnection : IRenderTimer
internal class WinUiCompositorConnection : IRenderTimer, Win32.IWindowsSurfaceFactory
{
private readonly WinUiCompositionShared _shared;
public event Action<TimeSpan>? Tick;
@ -54,7 +54,7 @@ internal class WinUiCompositorConnection : IRenderTimer
threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT
});
connect = new WinUiCompositorConnection();
AvaloniaLocator.CurrentMutable.BindToSelf(connect);
AvaloniaLocator.CurrentMutable.Bind<IWindowsSurfaceFactory>().ToConstant(connect);
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connect);
tcs.SetResult(true);
@ -144,5 +144,6 @@ internal class WinUiCompositorConnection : IRenderTimer
return false;
}
public WinUiCompositedWindowSurface CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new(_shared, info);
public bool RequiresNoRedirectionBitmap => true;
public object CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new WinUiCompositedWindowSurface(_shared, info);
}

15
src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs

@ -1,15 +0,0 @@
namespace Avalonia.Win32.WinRT
{
internal enum BlurEffect
{
None,
Acrylic,
MicaLight,
MicaDark
}
internal interface IBlurHost
{
void SetBlur(BlurEffect enable);
}
}

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

@ -100,7 +100,7 @@ namespace Avalonia.Win32
}
// Cleanup render targets
(_gl as IDisposable)?.Dispose();
(_glSurface as IDisposable)?.Dispose();
if (_dropTarget != null)
{

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

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@ -60,8 +60,6 @@ namespace Avalonia.Win32
private Thickness _extendedMargins;
private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1;
private readonly bool _isUsingComposition;
private readonly IBlurHost? _blurHost;
private WindowResizeReason _resizeReason;
private MOUSEMOVEPOINT _lastWmMousePoint;
@ -74,7 +72,7 @@ namespace Avalonia.Win32
private readonly WindowsMouseDevice _mouseDevice;
private readonly PenDevice _penDevice;
private readonly FramebufferManager _framebuffer;
private readonly object? _gl;
private readonly object? _glSurface;
private readonly bool _wmPointerEnabled;
private readonly Win32NativeControlHost _nativeControlHost;
@ -101,6 +99,7 @@ namespace Avalonia.Win32
private uint _langid;
internal bool _ignoreWmChar;
private WindowTransparencyLevel _transparencyLevel;
private readonly WindowTransparencyLevel _defaultTransparencyLevel;
private const int MaxPointerHistorySize = 512;
private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new();
@ -133,20 +132,10 @@ namespace Avalonia.Win32
Decorations = SystemDecorations.Full
};
var surfaceFactory = AvaloniaLocator.Current.GetService<IWindowsSurfaceFactory>();
var glPlatform = AvaloniaLocator.Current.GetService<IPlatformGraphics>();
var compositionConnector = AvaloniaLocator.Current.GetService<WinUiCompositorConnection>();
var isUsingAngleDX11 = glPlatform is D3D11AngleWin32PlatformGraphics;
_isUsingComposition = compositionConnector is { } && isUsingAngleDX11;
DxgiConnection? dxgiConnection = null;
var isUsingDxgiSwapchain = false;
if (!_isUsingComposition)
{
dxgiConnection = AvaloniaLocator.Current.GetService<DxgiConnection>();
isUsingDxgiSwapchain = dxgiConnection is { } && isUsingAngleDX11;
}
UseRedirectionBitmap = surfaceFactory is null || glPlatform is null ||
!surfaceFactory.RequiresNoRedirectionBitmap;
_wmPointerEnabled = Win32Platform.WindowsVersion >= PlatformConstants.Windows8;
@ -157,34 +146,28 @@ namespace Avalonia.Win32
{
UpdateInputMethod(GetKeyboardLayout(0));
}
if (glPlatform != null)
{
if (_isUsingComposition)
if (surfaceFactory is not null)
{
var cgl = compositionConnector!.CreateSurface(this);
_blurHost = cgl;
_gl = cgl;
}
else if (isUsingDxgiSwapchain)
{
var dxgigl = new DxgiSwapchainWindow(dxgiConnection!, this);
_gl = dxgigl;
_glSurface = surfaceFactory.CreateSurface(this);
}
else
{
if (glPlatform is D3D11AngleWin32PlatformGraphics or D3D9AngleWin32PlatformGraphics)
_gl = new EglGlPlatformSurface(this);
_glSurface = new EglGlPlatformSurface(this);
else if (glPlatform is WglPlatformOpenGlInterface)
_gl = new WglGlPlatformSurface(this);
_glSurface = new WglGlPlatformSurface(this);
}
}
Screen = new ScreenImpl();
_storageProvider = new Win32StorageProvider(this);
_nativeControlHost = new Win32NativeControlHost(this, _isUsingComposition);
_transparencyLevel = _isUsingComposition ? WindowTransparencyLevel.Transparent : WindowTransparencyLevel.None;
_nativeControlHost = new Win32NativeControlHost(this, !UseRedirectionBitmap);
_defaultTransparencyLevel = UseRedirectionBitmap ? WindowTransparencyLevel.None : WindowTransparencyLevel.Transparent;
_transparencyLevel = _defaultTransparencyLevel;
s_instances.Add(this);
}
@ -244,6 +227,9 @@ namespace Avalonia.Win32
private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.IsPrimary)?.Scaling ?? 1;
private ICompositionEffectsSurface? CompositionEffectsSurface => _glSurface as ICompositionEffectsSurface;
private bool UseRedirectionBitmap { get; }
public double RenderScaling => _scaling;
public double DesktopScaling => RenderScaling;
@ -362,164 +348,143 @@ namespace Avalonia.Win32
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels)
{
var windowsVersion = Win32Platform.WindowsVersion;
foreach (var level in transparencyLevels)
{
if (!IsSupported(level, windowsVersion))
if (!IsSupported(level))
continue;
if (level == TransparencyLevel)
{
return;
}
if (level == WindowTransparencyLevel.Transparent)
SetTransparencyTransparent(windowsVersion);
else if (level == WindowTransparencyLevel.Blur)
SetTransparencyBlur(windowsVersion);
{
if (!SetTransparencyTransparent())
continue;
}
else if (level == WindowTransparencyLevel.AcrylicBlur)
SetTransparencyAcrylicBlur(windowsVersion);
{
if (!SetTransparencyAcrylicBlur())
continue;
}
else if (level == WindowTransparencyLevel.Mica)
SetTransparencyMica(windowsVersion);
{
if (!SetTransparencyMica())
continue;
}
TransparencyLevel = level;
return;
}
// If we get here, we didn't find a supported level. Use the defualt of Transparent or
// None, depending on whether composition is enabled.
if (_isUsingComposition)
{
SetTransparencyTransparent(windowsVersion);
TransparencyLevel = WindowTransparencyLevel.Transparent;
}
else
{
TransparencyLevel = WindowTransparencyLevel.None;
}
// If we get here, we didn't find a supported level. Report the default.
TransparencyLevel = _defaultTransparencyLevel;
}
private bool IsSupported(WindowTransparencyLevel level, Version windowsVersion)
private bool IsSupported(WindowTransparencyLevel level)
{
// Only None is suppported when composition is disabled.
if (!_isUsingComposition)
return level == WindowTransparencyLevel.None;
// When composition is enabled, None is not supported because the backing visual always
// has an alpha channel
// None is only supported with redirection bitmap.
// Note, it's still possible to have non-transparent window with a fallback background brush.
if (level == WindowTransparencyLevel.None)
return false;
return UseRedirectionBitmap;
// Transparent only supported on Windows 8+.
// Transparent is supported either with DwmEnableBlurBehindWindow (win8+) or with NoRedirectionBitmap.
if (level == WindowTransparencyLevel.Transparent)
return windowsVersion >= PlatformConstants.Windows8;
return !UseRedirectionBitmap || Win32Platform.WindowsVersion >= PlatformConstants.Windows8;
// Blur only supported on Windows 8 and lower.
if (level == WindowTransparencyLevel.Blur)
return windowsVersion < PlatformConstants.Windows10;
return CompositionEffectsSurface?.IsBlurSupported(BlurEffect.GaussianBlur) ?? false;
// Acrylic is supported on Windows >= 10.0.15063.
if (level == WindowTransparencyLevel.AcrylicBlur)
return windowsVersion >= WinUiCompositionShared.MinAcrylicVersion;
return CompositionEffectsSurface?.IsBlurSupported(BlurEffect.Acrylic) ?? false;
// Mica is supported on Windows >= 10.0.22000.
if (level == WindowTransparencyLevel.Mica)
return windowsVersion >= WinUiCompositionShared.MinHostBackdropVersion;
return CompositionEffectsSurface?.IsBlurSupported(BlurEffect.MicaDark) ?? false;
return false;
}
private void SetTransparencyTransparent(Version windowsVersion)
private bool SetTransparencyTransparent()
{
// Transparent only supported with composition on Windows 8+.
if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8)
return;
if (windowsVersion < PlatformConstants.Windows10)
if (CompositionEffectsSurface is {} surface)
{
// Some of the AccentState Enum's values have different meanings on Windows 8.x than on
// Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯.
// Hey, I'm just porting what was here before.
SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND);
var blurInfo = new DWM_BLURBEHIND(false);
DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
surface.SetBlur(BlurEffect.None);
return true;
}
else
{
return SetLegacyTransparency(true);
}
SetUseHostBackdropBrush(false);
_blurHost?.SetBlur(BlurEffect.None);
}
private void SetTransparencyBlur(Version windowsVersion)
{
// Blur only supported with composition on Windows 8 and lower.
if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10)
return;
// Some of the AccentState Enum's values have different meanings on Windows 8.x than on
// Windows 10.
SetAccentState(AccentState.ACCENT_DISABLED);
var blurInfo = new DWM_BLURBEHIND(true);
DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
}
private void SetTransparencyAcrylicBlur(Version windowsVersion)
private bool SetTransparencyAcrylicBlur()
{
// Acrylic blur only supported with composition on Windows >= 10.0.15063.
if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinAcrylicVersion)
return;
SetUseHostBackdropBrush(true);
_blurHost?.SetBlur(BlurEffect.Acrylic);
SetLegacyTransparency(false);
CompositionEffectsSurface!.SetBlur(BlurEffect.Acrylic);
return true;
}
private void SetTransparencyMica(Version windowsVersion)
/// <summary>
/// Sets the transparency mica
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
private bool SetTransparencyMica()
{
// Mica only supported with composition on Windows >= 10.0.22000.
if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion)
return;
SetUseHostBackdropBrush(false);
_blurHost?.SetBlur(_currentThemeVariant switch
SetLegacyTransparency(false);
CompositionEffectsSurface!.SetBlur(_currentThemeVariant switch
{
PlatformThemeVariant.Light => BlurEffect.MicaLight,
PlatformThemeVariant.Dark => BlurEffect.MicaDark,
_ => throw new ArgumentOutOfRangeException()
});
return true;
}
private void SetAccentState(AccentState state)
private bool SetLegacyTransparency(bool enabled)
{
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
//Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10
accent.AccentState = state;
if (Win32Platform.WindowsVersion < PlatformConstants.Windows8 || !UseRedirectionBitmap)
return false;
// On pre-Win8 this method was blurring a window, which is a different from desired behavior.
// On win8+ we use this method as a fallback, when WinUI/DComp composition with true transparency isn't available.
// Note: there is no guarantee that this behavior won't be changed back to true blur in Win12.
// See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow#remarks
// Also https://github.com/qt/qtbase/blob/fd300f143fd30947bba60a03d614acd2711b635f/src/plugins/platforms/windows/qwindowswindow.cpp#L519
var blurInfo = new DWM_BLURBEHIND();
blurInfo.fEnable = enabled;
blurInfo.dwFlags = DWM_BB.Enable | DWM_BB.BlurRegion;
blurInfo.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var result = DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
var data = new WindowCompositionAttributeData();
data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.SizeOfData = accentStructSize;
data.Data = accentPtr;
if (blurInfo.hRgnBlur != default)
{
DeleteObject(blurInfo.hRgnBlur);
}
SetWindowCompositionAttribute(_hwnd, ref data);
Marshal.FreeHGlobal(accentPtr);
return result == 0;
}
private void SetUseHostBackdropBrush(bool useHostBackdropBrush)
private unsafe bool SetUseHostBackdropBrush(bool useHostBackdropBrush)
{
if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion)
return;
return false;
unsafe
{
var pvUseBackdropBrush = useHostBackdropBrush ? 1 : 0;
DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
}
// AcrylicBlur requires window to set DWMWA_USE_HOSTBACKDROPBRUSH flag on Win11+.
// It's not necessary on older versions and it's not necessary with Mica brush.
var pvUseBackdropBrush = useHostBackdropBrush ? 1 : 0;
var result = DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
return result == 0;
}
public IEnumerable<object> Surfaces
=> _gl is null ?
=> _glSurface is null ?
new object[] { Handle, _framebuffer } :
new object[] { Handle, _gl, _framebuffer };
new object[] { Handle, _glSurface, _framebuffer };
public PixelPoint Position
{
@ -797,7 +762,7 @@ namespace Avalonia.Win32
sizeof(int));
if (TransparencyLevel == WindowTransparencyLevel.Mica)
{
SetTransparencyMica(Win32Platform.WindowsVersion);
SetTransparencyMica();
}
}
}
@ -805,7 +770,7 @@ namespace Avalonia.Win32
protected virtual IntPtr CreateWindowOverride(ushort atom)
{
return CreateWindowEx(
_isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0,
UseRedirectionBitmap ? 0 : (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP,
atom,
null,
(int)WindowStyles.WS_OVERLAPPEDWINDOW | (int)WindowStyles.WS_CLIPCHILDREN,
@ -992,7 +957,7 @@ namespace Avalonia.Win32
}
//using a default margin of 0 when using WinUiComp removes artefacts when resizing. See issue #8316
var defaultMargin = _isUsingComposition ? 0 : 1;
var defaultMargin = UseRedirectionBitmap ? 1 : 0;
MARGINS margins = new MARGINS();
margins.cxLeftWidth = defaultMargin;
@ -1274,7 +1239,7 @@ namespace Avalonia.Win32
if (oldProperties.IsFullScreen == newProperties.IsFullScreen)
{
var exStyle = WindowStyles.WS_EX_WINDOWEDGE | (_isUsingComposition ? WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0);
var exStyle = WindowStyles.WS_EX_WINDOWEDGE | (UseRedirectionBitmap ? 0 : WindowStyles.WS_EX_NOREDIRECTIONBITMAP);
if ((oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar) || forceChanges)
{

Loading…
Cancel
Save