diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 5249a4fb41..5e3e301461 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -110,8 +110,7 @@ namespace ControlCatalog.NetCore
{
builder.With(new Win32PlatformOptions()
{
- UseLowLatencyDxgiSwapChain = true,
- UseWindowsUIComposition = false
+ CompositionMode = new [] { Win32CompositionMode.LowLatencyDxgiSwapChain }
});
return builder.StartWithClassicDesktopLifetime(args);
}
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index d5d5f211e9..144909db4b 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using Avalonia.Controls;
using Avalonia.Android;
using Avalonia.Android.Platform;
@@ -22,6 +24,39 @@ namespace Avalonia
.UseSkia();
}
}
+
+ ///
+ /// Represents the rendering mode for platform graphics.
+ ///
+ public enum AndroidRenderingMode
+ {
+ ///
+ /// Avalonia is rendered into a framebuffer.
+ ///
+ Software = 1,
+
+ ///
+ /// Enables android EGL rendering.
+ ///
+ Egl = 2
+ }
+
+ public sealed class AndroidPlatformOptions
+ {
+ ///
+ /// Gets or sets Avalonia rendering modes with fallbacks.
+ /// The first element in the array has the highest priority.
+ /// The default value is: , .
+ ///
+ ///
+ /// If application should work on as wide range of devices as possible, at least add as a fallback value.
+ ///
+ /// Thrown if no values were matched.
+ public IReadOnlyList RenderingMode { get; set; } = new[]
+ {
+ AndroidRenderingMode.Egl, AndroidRenderingMode.Software
+ };
+ }
}
namespace Avalonia.Android
@@ -47,18 +82,39 @@ namespace Avalonia.Android
.Bind().ToConstant(new ChoreographerTimer())
.Bind().ToSingleton();
- if (Options.UseGpu)
+ var graphics = InitializeGraphics(Options);
+ if (graphics is not null)
{
- EglPlatformGraphics.TryInitialize();
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics);
}
-
- Compositor = new Compositor(AvaloniaLocator.Current.GetService());
+
+ Compositor = new Compositor(graphics);
}
- }
+
+ private static IPlatformGraphics InitializeGraphics(AndroidPlatformOptions opts)
+ {
+ if (opts.RenderingMode is null || !opts.RenderingMode.Any())
+ {
+ throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} must not be empty or null");
+ }
- public sealed class AndroidPlatformOptions
- {
- public bool UseDeferredRendering { get; set; } = false;
- public bool UseGpu { get; set; } = true;
+ foreach (var renderingMode in opts.RenderingMode)
+ {
+ if (renderingMode == AndroidRenderingMode.Software)
+ {
+ return null;
+ }
+
+ if (renderingMode == AndroidRenderingMode.Egl)
+ {
+ if (EglPlatformGraphics.TryCreate() is { } egl)
+ {
+ return egl;
+ }
+ }
+ }
+
+ throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
+ }
}
}
diff --git a/src/Avalonia.Base/CombinedGeometry.cs b/src/Avalonia.Base/CombinedGeometry.cs
index 4b5866519b..a47e756c88 100644
--- a/src/Avalonia.Base/CombinedGeometry.cs
+++ b/src/Avalonia.Base/CombinedGeometry.cs
@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Text;
using Avalonia.Platform;
-#nullable enable
-
namespace Avalonia.Media
{
public enum GeometryCombineMode
@@ -147,7 +145,7 @@ namespace Avalonia.Media
return new CombinedGeometry(GeometryCombineMode, Geometry1, Geometry2, Transform);
}
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var g1 = Geometry1;
var g2 = Geometry2;
diff --git a/src/Avalonia.Base/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs
index 84d74e888e..bb1263d8d5 100644
--- a/src/Avalonia.Base/Media/EllipseGeometry.cs
+++ b/src/Avalonia.Base/Media/EllipseGeometry.cs
@@ -135,7 +135,7 @@ namespace Avalonia.Media
}
///
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService();
diff --git a/src/Avalonia.Base/Media/Geometry.cs b/src/Avalonia.Base/Media/Geometry.cs
index 0d2311eafc..a66cd616a3 100644
--- a/src/Avalonia.Base/Media/Geometry.cs
+++ b/src/Avalonia.Base/Media/Geometry.cs
@@ -28,6 +28,11 @@ namespace Avalonia.Media
TransformProperty.Changed.AddClassHandler((x,e) => x.TransformChanged(e));
}
+ internal Geometry()
+ {
+
+ }
+
///
/// Raised when the geometry changes.
///
@@ -134,7 +139,7 @@ namespace Avalonia.Media
/// Creates the platform implementation of the geometry, without the transform applied.
///
///
- protected abstract IGeometryImpl? CreateDefiningGeometry();
+ private protected abstract IGeometryImpl? CreateDefiningGeometry();
///
/// Invalidates the platform implementation of the geometry.
diff --git a/src/Avalonia.Base/Media/GeometryGroup.cs b/src/Avalonia.Base/Media/GeometryGroup.cs
index 3e61413919..20bd297fc1 100644
--- a/src/Avalonia.Base/Media/GeometryGroup.cs
+++ b/src/Avalonia.Base/Media/GeometryGroup.cs
@@ -1,8 +1,6 @@
using Avalonia.Metadata;
using Avalonia.Platform;
-#nullable enable
-
namespace Avalonia.Media
{
///
@@ -72,7 +70,7 @@ namespace Avalonia.Media
newChildren.Parent = this;
}
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
if (_children.Count > 0)
{
diff --git a/src/Avalonia.Base/Media/LineGeometry.cs b/src/Avalonia.Base/Media/LineGeometry.cs
index 6ac92ea33b..ced208e30b 100644
--- a/src/Avalonia.Base/Media/LineGeometry.cs
+++ b/src/Avalonia.Base/Media/LineGeometry.cs
@@ -68,7 +68,7 @@ namespace Avalonia.Media
}
///
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService();
diff --git a/src/Avalonia.Base/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs
index 8292afde7e..bdfbfadce4 100644
--- a/src/Avalonia.Base/Media/PathGeometry.cs
+++ b/src/Avalonia.Base/Media/PathGeometry.cs
@@ -43,7 +43,7 @@ namespace Avalonia.Media
///
/// The s.
///
- public static new PathGeometry Parse(string pathData)
+ public new static PathGeometry Parse(string pathData)
{
var pathGeometry = new PathGeometry();
@@ -81,7 +81,7 @@ namespace Avalonia.Media
set { SetValue(FillRuleProperty, value); }
}
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var figures = Figures;
diff --git a/src/Avalonia.Base/Media/PlatformGeometry.cs b/src/Avalonia.Base/Media/PlatformGeometry.cs
index f25a14540f..e1488a8229 100644
--- a/src/Avalonia.Base/Media/PlatformGeometry.cs
+++ b/src/Avalonia.Base/Media/PlatformGeometry.cs
@@ -2,7 +2,7 @@
namespace Avalonia.Media
{
- internal class PlatformGeometry : Geometry
+ internal sealed class PlatformGeometry : Geometry
{
private readonly IGeometryImpl _geometryImpl;
@@ -16,7 +16,7 @@ namespace Avalonia.Media
return new PlatformGeometry(_geometryImpl);
}
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected override IGeometryImpl? CreateDefiningGeometry()
{
return _geometryImpl;
}
diff --git a/src/Avalonia.Base/Media/PolylineGeometry.cs b/src/Avalonia.Base/Media/PolylineGeometry.cs
index b0229b6455..47cf2f48a4 100644
--- a/src/Avalonia.Base/Media/PolylineGeometry.cs
+++ b/src/Avalonia.Base/Media/PolylineGeometry.cs
@@ -74,7 +74,7 @@ namespace Avalonia.Media
return new PolylineGeometry(Points, IsFilled);
}
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService();
var geometry = factory.CreateStreamGeometry();
diff --git a/src/Avalonia.Base/Media/RectangleGeometry.cs b/src/Avalonia.Base/Media/RectangleGeometry.cs
index 0bf9eb5664..01771242a7 100644
--- a/src/Avalonia.Base/Media/RectangleGeometry.cs
+++ b/src/Avalonia.Base/Media/RectangleGeometry.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Media
///
public override Geometry Clone() => new RectangleGeometry(Rect);
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService();
diff --git a/src/Avalonia.Base/Media/StreamGeometry.cs b/src/Avalonia.Base/Media/StreamGeometry.cs
index fb79488e0f..9969376896 100644
--- a/src/Avalonia.Base/Media/StreamGeometry.cs
+++ b/src/Avalonia.Base/Media/StreamGeometry.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Media
///
/// The string.
/// A .
- public static new StreamGeometry Parse(string s)
+ public new static StreamGeometry Parse(string s)
{
var streamGeometry = new StreamGeometry();
@@ -62,7 +62,7 @@ namespace Avalonia.Media
}
///
- protected override IGeometryImpl? CreateDefiningGeometry()
+ private protected override IGeometryImpl? CreateDefiningGeometry()
{
if (_impl == null)
{
diff --git a/src/Avalonia.Base/Point.cs b/src/Avalonia.Base/Point.cs
index 331cce4a76..e3ea21d1eb 100644
--- a/src/Avalonia.Base/Point.cs
+++ b/src/Avalonia.Base/Point.cs
@@ -164,6 +164,19 @@ namespace Avalonia
/// The resulting point.
public static Point operator *(Point point, Matrix matrix) => matrix.Transform(point);
+ ///
+ /// Computes the Euclidean distance between the two given points.
+ ///
+ /// The first point.
+ /// The second point.
+ /// The Euclidean distance.
+ public static double Distance(Point value1, Point value2)
+ {
+ double distanceSquared = ((value2.X - value1.X) * (value2.X - value1.X)) +
+ ((value2.Y - value1.Y) * (value2.Y - value1.Y));
+ return Math.Sqrt(distanceSquared);
+ }
+
///
/// Parses a string.
///
diff --git a/src/Avalonia.Base/Vector.cs b/src/Avalonia.Base/Vector.cs
index 15722901a6..fffe206835 100644
--- a/src/Avalonia.Base/Vector.cs
+++ b/src/Avalonia.Base/Vector.cs
@@ -359,7 +359,7 @@ namespace Avalonia
internal Vector(Vector2 v) : this(v.X, v.Y)
{
-
+
}
///
@@ -379,21 +379,27 @@ namespace Avalonia
///
public static Vector Max(Vector left, Vector right) =>
new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y));
-
+
///
/// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
///
public static Vector Min(Vector left, Vector right) =>
new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y));
-
+
///
/// Computes the Euclidean distance between the two given points.
///
+ /// The first point.
+ /// The second point.
+ /// The Euclidean distance.
public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2));
-
+
///
/// Returns the Euclidean distance squared between two specified points
///
+ /// The first point.
+ /// The second point.
+ /// The Euclidean distance squared.
public static double DistanceSquared(Vector value1, Vector value2)
{
var difference = value1 - value2;
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index 5d5e17839f..03f0129200 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -123,20 +123,18 @@ namespace Avalonia.Native
hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers));
AvaloniaLocator.CurrentMutable.Bind().ToConstant(hotkeys);
-
- if (_options.UseGpu)
+
+ // TODO: add software and metal support via RenderingMode options param
+ try
+ {
+ _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
+ AvaloniaLocator.CurrentMutable
+ .Bind().ToConstant(_platformGl);
+
+ }
+ catch (Exception)
{
- try
- {
- _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
- AvaloniaLocator.CurrentMutable
- .Bind().ToConstant(_platformGl);
-
- }
- catch (Exception)
- {
- // ignored
- }
+ // ignored
}
Compositor = new Compositor(_platformGl, true);
diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
index 2b989ce733..7e01e03c9c 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Native;
@@ -30,11 +31,6 @@ namespace Avalonia
///
public class AvaloniaNativePlatformOptions
{
- ///
- /// Determines whether to use GPU for rendering in your project. The default value is true.
- ///
- public bool UseGpu { get; set; } = true;
-
///
/// Embeds popups to the window when set to true. The default value is false.
///
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index 6b7f7e8883..6cef7ea578 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Native
public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts,
AvaloniaNativeGlPlatformGraphics glFeature,
- IWindowBaseImpl parent) : base(factory, opts, glFeature)
+ IWindowBaseImpl parent) : base(factory, glFeature)
{
_opts = opts;
_glFeature = glFeature;
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index 817fe3d080..ba96dd401a 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Native
private bool _canResize = true;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
- AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature)
+ AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, glFeature)
{
_opts = opts;
_glFeature = glFeature;
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 760816643e..053957f89f 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -67,11 +67,10 @@ namespace Avalonia.Native
private PlatformBehaviorInhibition _platformBehaviorInhibition;
private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None;
- internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
- AvaloniaNativeGlPlatformGraphics glFeature)
+ internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativeGlPlatformGraphics glFeature)
{
_factory = factory;
- _gpu = opts.UseGpu && glFeature != null;
+ _gpu = glFeature != null;
_keyboard = AvaloniaLocator.Current.GetService();
_mouse = new MouseDevice();
diff --git a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs
index 07e304febe..cf81999095 100644
--- a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs
+++ b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs
@@ -16,13 +16,6 @@ namespace Avalonia.OpenGL.Egl
_display = display;
}
- public static void TryInitialize()
- {
- var feature = TryCreate();
- if (feature != null)
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature);
- }
-
public static EglPlatformGraphics? TryCreate() => TryCreate(() => new EglDisplay(new EglDisplayCreationOptions
{
Egl = new EglInterface(),
diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
index 06766e0963..58336522c1 100644
--- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
+++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
@@ -15,18 +15,6 @@ namespace Avalonia.X11.Glx
IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext();
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
-
- public static bool TryInitialize(X11Info x11, IList glProfiles)
- {
- var feature = TryCreate(x11, glProfiles);
- if (feature != null)
- {
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature);
- return true;
- }
-
- return false;
- }
public static GlxPlatformGraphics TryCreate(X11Info x11, IList glProfiles)
{
diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index a880e4ba1a..c70c17d523 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -93,17 +93,13 @@ namespace Avalonia.X11
XI2 = xi2;
}
- if (options.UseGpu)
+ var graphics = InitializeGraphics(options, Info);
+ if (graphics is not null)
{
- if (options.UseEGL)
- EglPlatformGraphics.TryInitialize();
- else
- GlxPlatformGraphics.TryInitialize(Info, Options.GlProfiles);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics);
}
- var gl = AvaloniaLocator.Current.GetService();
-
- Compositor = new Compositor(gl);
+ Compositor = new Compositor(graphics);
}
public IntPtr DeferredDisplay { get; set; }
@@ -185,25 +181,84 @@ namespace Avalonia.X11
return false;
}
+
+ private static IPlatformGraphics InitializeGraphics(X11PlatformOptions opts, X11Info info)
+ {
+ if (opts.RenderingMode is null || !opts.RenderingMode.Any())
+ {
+ throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} must not be empty or null");
+ }
+
+ foreach (var renderingMode in opts.RenderingMode)
+ {
+ if (renderingMode == X11RenderingMode.Software)
+ {
+ return null;
+ }
+
+ if (renderingMode == X11RenderingMode.Glx)
+ {
+ if (GlxPlatformGraphics.TryCreate(info, opts.GlProfiles) is { } glx)
+ {
+ return glx;
+ }
+ }
+
+ if (renderingMode == X11RenderingMode.Egl)
+ {
+ if (EglPlatformGraphics.TryCreate() is { } egl)
+ {
+ return egl;
+ }
+ }
+ }
+
+ throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
+ }
}
}
namespace Avalonia
{
///
- /// Platform-specific options which apply to Linux.
+ /// Represents the rendering mode for platform graphics.
///
- public class X11PlatformOptions
+ public enum X11RenderingMode
{
///
- /// Enables native Linux EGL when set to true. The default value is false.
+ /// Avalonia is rendered into a framebuffer.
///
- public bool UseEGL { get; set; }
+ Software = 1,
///
- /// Determines whether to use GPU for rendering in your project. The default value is true.
+ /// Enables Glx rendering.
///
- public bool UseGpu { get; set; } = true;
+ Glx = 2,
+
+ ///
+ /// Enables native Linux EGL rendering.
+ ///
+ Egl = 3
+ }
+
+ ///
+ /// Platform-specific options which apply to Linux.
+ ///
+ public class X11PlatformOptions
+ {
+ ///
+ /// Gets or sets Avalonia rendering modes with fallbacks.
+ /// The first element in the array has the highest priority.
+ /// The default value is: , .
+ ///
+ ///
+ /// If application should work on as wide range of devices as possible, at least add as a fallback value.
+ ///
+ /// Thrown if no values were matched.
+ public IReadOnlyList RenderingMode { get; set; } = new[]
+ {
+ X11RenderingMode.Glx, X11RenderingMode.Software
+ };
///
/// Embeds popups to the window when set to true. The default value is false.
diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
index 07fb3169cb..bac2a73dd9 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
@@ -28,16 +28,18 @@ namespace Avalonia.Win32.DirectX
_syncLock = syncLock;
}
- public static void TryCreateAndRegister()
+ public static bool TryCreateAndRegister()
{
try
{
TryCreateAndRegisterCore();
+ return true;
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, LogArea)
?.Log(null, "Unable to establish Dxgi: {0}", ex);
+ return false;
}
}
diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
index f36fb29107..6b283282bf 100644
--- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
+++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
@@ -324,6 +324,11 @@ namespace Avalonia.Win32.Input
if (IsActive)
{
Client.SetPreeditText(null);
+
+ if (Client.SupportsSurroundingText && Client.SurroundingText.AnchorOffset != Client.SurroundingText.CursorOffset)
+ {
+ KeyPress(Key.Delete);
+ }
}
IsComposing = true;
@@ -393,6 +398,19 @@ namespace Avalonia.Win32.Input
return (int)(ptr.ToInt64() & 0xffffffff);
}
+ private void KeyPress(Key key)
+ {
+ if (_parent?.Input != null)
+ {
+ _parent.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _parent.Owner,
+ RawKeyEventType.KeyDown, key, RawInputModifiers.None));
+
+ _parent.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _parent.Owner,
+ RawKeyEventType.KeyUp, key, RawInputModifiers.None));
+
+ }
+ }
+
~Imm32InputMethod()
{
_caretManager.TryDestroy();
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index c26ce5fd45..b3760a37e1 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Diagnostics.Tracing;
+using System.Linq;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Win32.DirectX;
@@ -5,56 +8,88 @@ using Avalonia.Win32.OpenGl;
using Avalonia.Win32.OpenGl.Angle;
using Avalonia.Win32.WinRT.Composition;
-namespace Avalonia.Win32
+namespace Avalonia.Win32;
+
+static class Win32GlManager
{
- static class Win32GlManager
+ public static IPlatformGraphics? Initialize()
{
- public static IPlatformGraphics? Initialize()
- {
- var gl = InitializeCore();
-
- if (gl is not null)
- {
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
- }
+ var gl = InitializeCore();
- return gl;
+ if (gl is not null)
+ {
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
}
+
+ return gl;
+ }
- private static IPlatformGraphics? InitializeCore()
+ private static IPlatformGraphics? InitializeCore()
+ {
+ var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions();
+ if (opts.RenderingMode is null || !opts.RenderingMode.Any())
{
+ throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} must not be empty or null");
+ }
- var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions();
- if (opts.UseWgl)
+ foreach (var renderingMode in opts.RenderingMode)
+ {
+ if (renderingMode == Win32RenderingMode.Software)
{
- var wgl = WglPlatformOpenGlInterface.TryCreate();
- return wgl;
+ return null;
}
-
- if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7)
+
+ if (renderingMode == Win32RenderingMode.AngleEgl)
{
- var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ??
- new());
+ var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ?? new());
if (egl != null && egl.PlatformApi == AngleOptions.PlatformApi.DirectX11)
{
- AvaloniaLocator.CurrentMutable.Bind()
- .ToConstant(egl);
-
- if (opts.UseWindowsUIComposition)
- {
- WinUiCompositorConnection.TryCreateAndRegister();
- }
- else if (opts.UseLowLatencyDxgiSwapChain)
- {
- DxgiConnection.TryCreateAndRegister();
- }
+ TryRegisterComposition(opts);
+ return egl;
+ }
+ }
+
+ if (renderingMode == Win32RenderingMode.Wgl)
+ {
+ if (WglPlatformOpenGlInterface.TryCreate() is { } wgl)
+ {
+ return wgl;
}
+ }
+ }
+
+ throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
+ }
+
+ private static void TryRegisterComposition(Win32PlatformOptions opts)
+ {
+ if (opts.CompositionMode is null || !opts.CompositionMode.Any())
+ {
+ throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} must not be empty or null");
+ }
+
+ foreach (var compositionMode in opts.CompositionMode)
+ {
+ if (compositionMode == Win32CompositionMode.RedirectionSurface)
+ {
+ return;
+ }
- return egl;
+ if (compositionMode == Win32CompositionMode.WinUIComposition
+ && WinUiCompositorConnection.IsSupported()
+ && WinUiCompositorConnection.TryCreateAndRegister())
+ {
+ return;
}
- return null;
+ if (compositionMode == Win32CompositionMode.LowLatencyDxgiSwapChain
+ && DxgiConnection.TryCreateAndRegister())
+ {
+ return;
+ }
}
+
+ throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} has a value of \"{string.Join(", ", opts.CompositionMode)}\", but no options were applied.");
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index e9019803be..6e727ba4d4 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -1,8 +1,8 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using Avalonia.Reactive;
using System.Runtime.InteropServices;
using System.Threading;
@@ -10,7 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
-using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
@@ -31,78 +30,6 @@ namespace Avalonia
"Win32");
}
}
-
- ///
- /// Platform-specific options which apply to Windows.
- ///
- public class Win32PlatformOptions
- {
- ///
- /// Enables ANGLE for Windows. For every Windows version that is above Windows 7, the default is true otherwise it's false.
- ///
- ///
- /// GPU rendering will not be enabled if this is set to false.
- ///
- public bool? AllowEglInitialization { get; set; }
-
- ///
- /// Embeds popups to the window when set to true. The default value is false.
- ///
- public bool OverlayPopups { get; set; }
-
- ///
- /// Avalonia would try to use native Widows OpenGL when set to true. The default value is false.
- ///
- public bool UseWgl { get; set; }
-
- public IList WglProfiles { get; set; } = new List
- {
- new GlVersion(GlProfileType.OpenGL, 4, 0),
- new GlVersion(GlProfileType.OpenGL, 3, 2),
- };
-
- ///
- /// Render Avalonia to a Texture inside the Windows.UI.Composition tree.
- /// This setting is true by default.
- ///
- ///
- /// Supported on Windows 10 build 16299 and above. Ignored on other versions.
- /// This is recommended if you need to use AcrylicBlur or acrylic in your applications.
- ///
- public bool UseWindowsUIComposition { get; set; } = true;
-
- ///
- /// When enabled, create rounded corner blur brushes
- /// If set to null the brushes will be created using default settings (sharp corners)
- /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app
- ///
- public float? CompositionBackdropCornerRadius { get; set; }
-
- ///
- /// When is active, renders Avalonia through a low-latency Dxgi Swapchain.
- /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion.
- /// This is only recommended if low input latency is desirable, and there is no need for the transparency
- /// and stylings / blurrings offered by
- /// This is mutually exclusive with
- /// which if active will override this setting.
- /// This setting is false by default.
- ///
- public bool UseLowLatencyDxgiSwapChain { get; set; }
-
- ///
- /// Render directly on the UI thread instead of using a dedicated render thread.
- /// Only applicable if both and
- /// are false.
- /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.
- /// This setting is false by default.
- ///
- public bool ShouldRenderOnUIThread { get; set; }
-
- ///
- /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu
- ///
- public IPlatformGraphics? CustomPlatformGraphics { get; set; }
- }
}
namespace Avalonia.Win32
@@ -173,9 +100,23 @@ namespace Avalonia.Win32
.Bind().ToConstant(NonPumpingWaitHelperImpl.Instance)
.Bind().ToConstant(new WindowsMountedVolumeInfoProvider())
.Bind().ToConstant(s_instance);
-
- var platformGraphics = options.CustomPlatformGraphics
- ?? Win32GlManager.Initialize();
+
+ IPlatformGraphics? platformGraphics;
+ if (options.CustomPlatformGraphics is not null)
+ {
+ if (options.CompositionMode?.Contains(Win32CompositionMode.RedirectionSurface) == false)
+ {
+ throw new InvalidOperationException(
+ $"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CustomPlatformGraphics)} is only " +
+ $"compatible with {nameof(Win32CompositionMode)}.{nameof(Win32CompositionMode.RedirectionSurface)}");
+ }
+
+ platformGraphics = options.CustomPlatformGraphics;
+ }
+ else
+ {
+ platformGraphics = Win32GlManager.Initialize();
+ }
if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind().ToSingleton();
diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
new file mode 100644
index 0000000000..bbb4c37d7e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
@@ -0,0 +1,140 @@
+using System.Collections.Generic;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+
+namespace Avalonia;
+
+///
+/// Represents the rendering mode for platform graphics.
+///
+public enum Win32RenderingMode
+{
+ ///
+ /// Avalonia is rendered into a framebuffer.
+ ///
+ Software = 1,
+
+ ///
+ /// Enables ANGLE EGL for Windows with GPU rendering.
+ ///
+ AngleEgl = 2,
+
+ ///
+ /// Avalonia would try to use native Widows OpenGL with GPU rendering.
+ ///
+ Wgl = 3
+}
+
+///
+/// Represents the Win32 window composition mode.
+///
+public enum Win32CompositionMode
+{
+ ///
+ /// Render Avalonia to a texture inside the Windows.UI.Composition tree.
+ ///
+ ///
+ /// Supported on Windows 10 build 17134 and above. Ignored on other versions.
+ /// This is recommended option, as it allows window acrylic effects and high refresh rate rendering.
+ /// Can only be applied with =.
+ ///
+ WinUIComposition = 1,
+
+ // ///
+ // /// Render Avalonia to a texture inside the DirectComposition tree.
+ // ///
+ // ///
+ // /// Supported on Windows 8 and above. Ignored on other versions.
+ // /// Can only be applied with =.
+ // ///
+ // DirectComposition = 2,
+
+ ///
+ /// When is active, renders Avalonia through a low-latency Dxgi Swapchain.
+ ///
+ ///
+ /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion.
+ /// This is only recommended if low input latency is desirable, and there is no need for the transparency
+ /// and styling / blurring offered by .
+ /// Can only be applied with =.
+ ///
+ LowLatencyDxgiSwapChain = 3,
+
+ ///
+ /// The window renders to a redirection surface.
+ ///
+ ///
+ /// This option is kept only for compatibility with older systems. Some Avalonia features might not work.
+ ///
+ RedirectionSurface,
+}
+
+///
+/// Platform-specific options which apply to Windows.
+///
+public class Win32PlatformOptions
+{
+ ///
+ /// Embeds popups to the window when set to true. The default value is false.
+ ///
+ public bool OverlayPopups { get; set; }
+
+ ///
+ /// Gets or sets Avalonia rendering modes with fallbacks.
+ /// The first element in the array has the highest priority.
+ /// The default value is: , .
+ ///
+ ///
+ /// If application should work on as wide range of devices as possible, at least add as a fallback value.
+ ///
+ /// Thrown if no values were matched.
+ public IReadOnlyList RenderingMode { get; set; } = new[]
+ {
+ Win32RenderingMode.AngleEgl, Win32RenderingMode.Software
+ };
+
+ ///
+ /// Gets or sets Avalonia composition modes with fallbacks.
+ /// The first element in the array has the highest priority.
+ /// The default value is: , .
+ ///
+ ///
+ /// If application should work on as wide range of devices as possible, at least add as a fallback value.
+ ///
+ /// Thrown if no values were matched.
+ public IReadOnlyList CompositionMode { get; set; } = new[]
+ {
+ Win32CompositionMode.WinUIComposition, Win32CompositionMode.RedirectionSurface
+ };
+
+ ///
+ /// When is set to , create rounded corner blur brushes
+ /// If set to null the brushes will be created using default settings (sharp corners)
+ /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app.
+ ///
+ public float? WinUICompositionBackdropCornerRadius { get; set; }
+
+ ///
+ /// Render directly on the UI thread instead of using a dedicated render thread.
+ /// Only applicable if is set to .
+ /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.
+ /// This setting is false by default.
+ ///
+ public bool ShouldRenderOnUIThread { get; set; }
+
+ ///
+ /// Windows OpenGL profiles used when is set to .
+ /// This setting is 4.0 and 3.2 by default.
+ ///
+ public IList WglProfiles { get; set; } = new List
+ {
+ new(GlProfileType.OpenGL, 4, 0), new(GlProfileType.OpenGL, 3, 2)
+ };
+
+ ///
+ /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu.
+ /// When this property set is ignored
+ /// and only accepts null or .
+ ///
+ public IPlatformGraphics? CustomPlatformGraphics { get; set; }
+}
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
index 95ef338f08..8da154dc83 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Win32.WinRT.Composition
public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice)
{
var cornerRadius = AvaloniaLocator.Current.GetService()
- ?.CompositionBackdropCornerRadius;
+ ?.WinUICompositionBackdropCornerRadius;
_window ??= new WinUiCompositedWindow(_info, _shared, cornerRadius);
_window.SetBlur(_blurEffect);
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
index f17805fba3..b3a328d097 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
@@ -12,6 +12,7 @@ internal class WinUiCompositionShared : IDisposable
public ICompositionBrush? MicaBrush { get; }
public object SyncRoot { get; } = new();
+ public static readonly Version MinWinCompositionVersion = new(10, 0, 17134);
public static readonly Version MinAcrylicVersion = new(10, 0, 15063);
public static readonly Version MinHostBackdropVersion = new(10, 0, 22000);
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
index 754af86c06..596c94d30b 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
@@ -112,35 +112,36 @@ internal class WinUiCompositorConnection : IRenderTimer
}
}
- public static void TryCreateAndRegister()
+ public static bool IsSupported()
{
- const int majorRequired = 10;
- const int buildRequired = 17134;
-
- var majorInstalled = Win32Platform.WindowsVersion.Major;
- var buildInstalled = Win32Platform.WindowsVersion.Build;
-
- if (majorInstalled >= majorRequired &&
- buildInstalled >= buildRequired)
+ return Win32Platform.WindowsVersion >= WinUiCompositionShared.MinWinCompositionVersion;
+ }
+
+ public static bool TryCreateAndRegister()
+ {
+ if (IsSupported())
{
try
{
TryCreateAndRegisterCore();
- return;
+ return true;
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "WinUIComposition")
?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
-
}
}
+ else
+ {
+ var osVersionNotice =
+ $"Windows {WinUiCompositionShared.MinWinCompositionVersion} is required. Your machine has Windows {Win32Platform.WindowsVersion} installed.";
- var osVersionNotice =
- $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed.";
+ Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null,
+ $"Unable to initialize WinUI compositor: {osVersionNotice}");
+ }
- Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null,
- $"Unable to initialize WinUI compositor: {osVersionNotice}");
+ return false;
}
public WinUiCompositedWindowSurface CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new(_shared, info);
diff --git a/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs
index ae8dc9dad2..6d724c86b5 100644
--- a/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs
@@ -103,7 +103,7 @@ namespace Avalonia.Base.UnitTests.Media
throw new NotImplementedException();
}
- protected override IGeometryImpl CreateDefiningGeometry()
+ private protected sealed override IGeometryImpl CreateDefiningGeometry()
{
return Mock.Of(
x => x.WithTransform(It.IsAny()) ==