committed by
GitHub
64 changed files with 2155 additions and 1619 deletions
@ -1,188 +1,190 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:CompileBindings="True"> |
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:CompileBindings="True"> |
|||
|
|||
<Style Selector="Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="BorderThickness" Value="0" /> |
|||
<ControlTheme x:Key="ColorSliderThumbTheme" |
|||
TargetType="Thumb"> |
|||
<Setter Property="Background" Value="Transparent" /> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
|||
<Setter Property="BorderThickness" Value="3" /> |
|||
<Setter Property="CornerRadius" Value="10" /> |
|||
<Setter Property="Template"> |
|||
<Setter.Value> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
BorderThickness="{TemplateBinding BorderThickness}" |
|||
CornerRadius="10" /> |
|||
CornerRadius="{TemplateBinding CornerRadius}" /> |
|||
</ControlTemplate> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
</ControlTheme> |
|||
|
|||
<Style Selector="ColorSlider:horizontal"> |
|||
<Setter Property="BorderThickness" Value="0" /> |
|||
<Setter Property="CornerRadius" Value="10" /> |
|||
<Setter Property="Height" Value="20" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate TargetType="{x:Type ColorSlider}"> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
CornerRadius="{TemplateBinding CornerRadius}"> |
|||
<Grid Margin="{TemplateBinding Padding}"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{StaticResource ColorControlCheckeredBackgroundBrush}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding Background}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Track Name="PART_Track" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Minimum="{TemplateBinding Minimum}" |
|||
Maximum="{TemplateBinding Maximum}" |
|||
Value="{TemplateBinding Value, Mode=TwoWay}" |
|||
IsDirectionReversed="{TemplateBinding IsDirectionReversed}" |
|||
Orientation="Horizontal"> |
|||
<Track.DecreaseButton> |
|||
<RepeatButton Name="PART_DecreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.DecreaseButton> |
|||
<Track.IncreaseButton> |
|||
<RepeatButton Name="PART_IncreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.IncreaseButton> |
|||
<Thumb Classes="ColorSliderThumbStyle" |
|||
Name="ColorSliderThumb" |
|||
Margin="0" |
|||
Padding="0" |
|||
DataContext="{TemplateBinding Value}" |
|||
Height="{TemplateBinding Height}" |
|||
Width="{TemplateBinding Height}" /> |
|||
</Track> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<ControlTheme x:Key="{x:Type ColorSlider}" |
|||
TargetType="ColorSlider"> |
|||
|
|||
<Style Selector="ColorSlider:vertical"> |
|||
<Setter Property="BorderThickness" Value="0" /> |
|||
<Setter Property="CornerRadius" Value="10" /> |
|||
<Setter Property="Width" Value="20" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate TargetType="{x:Type ColorSlider}"> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
CornerRadius="{TemplateBinding CornerRadius}"> |
|||
<Grid Margin="{TemplateBinding Padding}"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{StaticResource ColorControlCheckeredBackgroundBrush}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding Background}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Track Name="PART_Track" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Minimum="{TemplateBinding Minimum}" |
|||
Maximum="{TemplateBinding Maximum}" |
|||
Value="{TemplateBinding Value, Mode=TwoWay}" |
|||
IsDirectionReversed="{TemplateBinding IsDirectionReversed}" |
|||
Orientation="Vertical"> |
|||
<Track.DecreaseButton> |
|||
<RepeatButton Name="PART_DecreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.DecreaseButton> |
|||
<Track.IncreaseButton> |
|||
<RepeatButton Name="PART_IncreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.IncreaseButton> |
|||
<Thumb Classes="ColorSliderThumbStyle" |
|||
Name="ColorSliderThumb" |
|||
Margin="0" |
|||
Padding="0" |
|||
DataContext="{TemplateBinding Value}" |
|||
Height="{TemplateBinding Width}" |
|||
Width="{TemplateBinding Width}" /> |
|||
</Track> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="^:horizontal"> |
|||
<Setter Property="BorderThickness" Value="0" /> |
|||
<Setter Property="CornerRadius" Value="10" /> |
|||
<Setter Property="Height" Value="20" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate TargetType="{x:Type ColorSlider}"> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
CornerRadius="{TemplateBinding CornerRadius}"> |
|||
<Grid Margin="{TemplateBinding Padding}"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{StaticResource ColorControlCheckeredBackgroundBrush}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding Background}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Track Name="PART_Track" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Minimum="{TemplateBinding Minimum}" |
|||
Maximum="{TemplateBinding Maximum}" |
|||
Value="{TemplateBinding Value, Mode=TwoWay}" |
|||
IsDirectionReversed="{TemplateBinding IsDirectionReversed}" |
|||
Orientation="Horizontal"> |
|||
<Track.DecreaseButton> |
|||
<RepeatButton Name="PART_DecreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.DecreaseButton> |
|||
<Track.IncreaseButton> |
|||
<RepeatButton Name="PART_IncreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.IncreaseButton> |
|||
<Thumb Name="ColorSliderThumb" |
|||
Theme="{StaticResource ColorSliderThumbTheme}" |
|||
Margin="0" |
|||
Padding="0" |
|||
DataContext="{TemplateBinding Value}" |
|||
Height="{TemplateBinding Height}" |
|||
Width="{TemplateBinding Height}" /> |
|||
</Track> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<!-- Normal State --> |
|||
<Style Selector="ColorSlider /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="Background" Value="Transparent" /> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
|||
<Setter Property="BorderThickness" Value="3" /> |
|||
</Style> |
|||
<Style Selector="^:vertical"> |
|||
<Setter Property="BorderThickness" Value="0" /> |
|||
<Setter Property="CornerRadius" Value="10" /> |
|||
<Setter Property="Width" Value="20" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate TargetType="{x:Type ColorSlider}"> |
|||
<Border BorderThickness="{TemplateBinding BorderThickness}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
CornerRadius="{TemplateBinding CornerRadius}"> |
|||
<Grid Margin="{TemplateBinding Padding}"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{StaticResource ColorControlCheckeredBackgroundBrush}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding Background}" |
|||
RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}" |
|||
RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" /> |
|||
<Track Name="PART_Track" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Minimum="{TemplateBinding Minimum}" |
|||
Maximum="{TemplateBinding Maximum}" |
|||
Value="{TemplateBinding Value, Mode=TwoWay}" |
|||
IsDirectionReversed="{TemplateBinding IsDirectionReversed}" |
|||
Orientation="Vertical"> |
|||
<Track.DecreaseButton> |
|||
<RepeatButton Name="PART_DecreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.DecreaseButton> |
|||
<Track.IncreaseButton> |
|||
<RepeatButton Name="PART_IncreaseButton" |
|||
Background="Transparent" |
|||
Focusable="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<RepeatButton.Template> |
|||
<ControlTemplate> |
|||
<Border Name="FocusTarget" |
|||
Background="Transparent" |
|||
Margin="0,-10" /> |
|||
</ControlTemplate> |
|||
</RepeatButton.Template> |
|||
</RepeatButton> |
|||
</Track.IncreaseButton> |
|||
<Thumb Name="ColorSliderThumb" |
|||
Theme="{StaticResource ColorSliderThumbTheme}" |
|||
Margin="0" |
|||
Padding="0" |
|||
DataContext="{TemplateBinding Value}" |
|||
Height="{TemplateBinding Width}" |
|||
Width="{TemplateBinding Width}" /> |
|||
</Track> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<!-- Selector/Thumb Color --> |
|||
<Style Selector="^:pointerover /template/ Thumb#ColorSliderThumb"> |
|||
<Setter Property="Opacity" Value="0.75" /> |
|||
</Style> |
|||
<Style Selector="^:pointerover:dark-selector /template/ Thumb#ColorSliderThumb"> |
|||
<Setter Property="Opacity" Value="0.7" /> |
|||
</Style> |
|||
<Style Selector="^:pointerover:light-selector /template/ Thumb#ColorSliderThumb"> |
|||
<Setter Property="Opacity" Value="0.8" /> |
|||
</Style> |
|||
|
|||
<!-- Selector/Thumb Color --> |
|||
<Style Selector="ColorSlider:pointerover /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="Opacity" Value="0.75" /> |
|||
</Style> |
|||
<Style Selector="ColorSlider:pointerover:dark-selector /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="Opacity" Value="0.7" /> |
|||
</Style> |
|||
<Style Selector="ColorSlider:pointerover:light-selector /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="Opacity" Value="0.8" /> |
|||
</Style> |
|||
<Style Selector="^:dark-selector /template/ Thumb#ColorSliderThumb"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundChromeBlackHighBrush}" /> |
|||
</Style> |
|||
<Style Selector="^:light-selector /template/ Thumb#ColorSliderThumb"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundChromeWhiteBrush}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="ColorSlider:dark-selector /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundChromeBlackHighBrush}" /> |
|||
</Style> |
|||
<Style Selector="ColorSlider:light-selector /template/ Thumb.ColorSliderThumbStyle"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundChromeWhiteBrush}" /> |
|||
</Style> |
|||
</ControlTheme> |
|||
|
|||
</Styles> |
|||
</ResourceDictionary> |
|||
|
|||
@ -1,79 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Platform.Interop; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public class GlInterfaceBase : GlInterfaceBase<object> |
|||
{ |
|||
public GlInterfaceBase(Func<string, IntPtr> getProcAddress) : base(getProcAddress, null) |
|||
{ |
|||
} |
|||
|
|||
public GlInterfaceBase(Func<Utf8Buffer, IntPtr> nativeGetProcAddress) : base(nativeGetProcAddress, null) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public class GlInterfaceBase<TContext> |
|||
{ |
|||
private readonly Func<string, IntPtr> _getProcAddress; |
|||
public GlInterfaceBase(Func<string, IntPtr> getProcAddress, TContext context) |
|||
{ |
|||
_getProcAddress = getProcAddress; |
|||
foreach (var prop in this.GetType().GetProperties()) |
|||
{ |
|||
var attrs = prop.GetCustomAttributes() |
|||
.Where(a => |
|||
a is IGlEntryPointAttribute || a is IGlEntryPointAttribute<TContext>) |
|||
.ToList(); |
|||
if(attrs.Count == 0) |
|||
continue; |
|||
|
|||
var isOptional = prop.GetCustomAttribute<GlOptionalEntryPoint>() != null; |
|||
|
|||
var fieldName = $"<{prop.Name}>k__BackingField"; |
|||
var field = prop.DeclaringType.GetField(fieldName, |
|||
BindingFlags.Instance | BindingFlags.NonPublic); |
|||
if (field == null) |
|||
throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}"); |
|||
|
|||
|
|||
IntPtr proc = IntPtr.Zero; |
|||
foreach (var attr in attrs) |
|||
{ |
|||
if (attr is IGlEntryPointAttribute<TContext> typed) |
|||
proc = typed.GetProcAddress(context, getProcAddress); |
|||
else if (attr is IGlEntryPointAttribute untyped) |
|||
proc = untyped.GetProcAddress(getProcAddress); |
|||
if (proc != IntPtr.Zero) |
|||
break; |
|||
} |
|||
|
|||
if (proc != IntPtr.Zero) |
|||
field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType)); |
|||
else if (!isOptional) |
|||
throw new OpenGlException("Unable to find a suitable GL function for " + prop.Name); |
|||
} |
|||
} |
|||
|
|||
protected static Func<string, IntPtr> ConvertNative(Func<Utf8Buffer, IntPtr> func) => |
|||
(proc) => |
|||
{ |
|||
using (var u = new Utf8Buffer(proc)) |
|||
{ |
|||
var rv = func(u); |
|||
return rv; |
|||
} |
|||
}; |
|||
|
|||
public GlInterfaceBase(Func<Utf8Buffer, IntPtr> nativeGetProcAddress, TContext context) : this(ConvertNative(nativeGetProcAddress), context) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public IntPtr GetProcAddress(string proc) => _getProcAddress(proc); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Design.PreviewWith> |
|||
<RichTextBlock IsTextSelectionEnabled="True" Text="Preview"/> |
|||
</Design.PreviewWith> |
|||
|
|||
<Style Selector="RichTextBlock[IsTextSelectionEnabled=true]"> |
|||
<Setter Property="Cursor" Value="IBeam" /> |
|||
</Style> |
|||
</Styles> |
|||
@ -0,0 +1,14 @@ |
|||
<ResourceDictionary xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Design.PreviewWith> |
|||
<RichTextBlock IsTextSelectionEnabled="True" Text="Preview"/> |
|||
</Design.PreviewWith> |
|||
|
|||
<ControlTheme x:Key="{x:Type RichTextBlock}" TargetType="RichTextBlock"> |
|||
<Style Selector="^[IsTextSelectionEnabled=True]"> |
|||
<Setter Property="Cursor" Value="IBeam" /> |
|||
</Style> |
|||
</ControlTheme> |
|||
|
|||
|
|||
</ResourceDictionary> |
|||
@ -0,0 +1,27 @@ |
|||
using Avalonia.LinuxFramebuffer.Output; |
|||
using Avalonia.Media; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Avalonia.LinuxFramebuffer |
|||
{ |
|||
public class DrmOutputOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Scaling factor.
|
|||
/// Default: 1.0
|
|||
/// </summary>
|
|||
public double Scaling { get; set; } = 1.0; |
|||
|
|||
/// <summary>
|
|||
/// If true an two cycle buffer swapping is processed at init.
|
|||
/// Default: True
|
|||
/// </summary>
|
|||
public bool EnableInitialBufferSwapping { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// Color for <see cref="EnableInitialBufferSwapping"/>
|
|||
/// Default: R0 G0 B0 A0
|
|||
/// </summary>
|
|||
public Color InitialBufferSwappingColor { get; set; } = new Color(0, 0, 0, 0); |
|||
} |
|||
} |
|||
@ -0,0 +1,338 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using Microsoft.CodeAnalysis; |
|||
using Microsoft.CodeAnalysis.CSharp; |
|||
using Microsoft.CodeAnalysis.CSharp.Syntax; |
|||
|
|||
namespace Generator; |
|||
|
|||
[Generator(LanguageNames.CSharp)] |
|||
public class GetProcAddressInitializationGenerator : IIncrementalGenerator |
|||
{ |
|||
const string GetProcAddressFullName = "global::Avalonia.SourceGenerator.GetProcAddressAttribute"; |
|||
|
|||
public void Initialize(IncrementalGeneratorInitializationContext context) |
|||
{ |
|||
var allMethodsWithAttributes = context.SyntaxProvider |
|||
.CreateSyntaxProvider( |
|||
static (s, _) => s is MethodDeclarationSyntax |
|||
{ |
|||
AttributeLists.Count: > 0, |
|||
} md && md.Modifiers.Any(m=>m.IsKind(SyntaxKind.PartialKeyword)), |
|||
static (context, _) => |
|||
(IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!); |
|||
|
|||
var fieldsWithAttribute = allMethodsWithAttributes |
|||
.Where(s => s.HasAttributeWithFullyQualifiedName(GetProcAddressFullName)); |
|||
|
|||
var all = fieldsWithAttribute.Collect(); |
|||
context.RegisterSourceOutput(all, static (context, methods) => |
|||
{ |
|||
foreach (var typeGroup in methods.GroupBy(f => f.ContainingType)) |
|||
{ |
|||
var nextContext = 0; |
|||
var contexts = new Dictionary<string, int>(); |
|||
|
|||
string GetContextNameFromIndex(int c) => "context" + (c == 0 ? "" : c); |
|||
string GetContextName(string type) |
|||
{ |
|||
if (contexts.TryGetValue(type, out var idx)) |
|||
return GetContextNameFromIndex(idx); |
|||
if (nextContext != 0) |
|||
idx += nextContext; |
|||
nextContext++; |
|||
return GetContextNameFromIndex(contexts[type] = idx); |
|||
} |
|||
|
|||
var classBuilder = new StringBuilder(); |
|||
if (typeGroup.Key.ContainingNamespace != null) |
|||
classBuilder |
|||
.AppendLine("using System;") |
|||
.Append("namespace ") |
|||
.Append(typeGroup.Key.ContainingNamespace) |
|||
.AppendLine(";"); |
|||
classBuilder |
|||
.Append("unsafe partial class ") |
|||
.AppendLine(typeGroup.Key.Name) |
|||
.AppendLine("{"); |
|||
var initializeBody = new StringBuilder() |
|||
.Pad(2) |
|||
.AppendLine("var addr = IntPtr.Zero;"); |
|||
|
|||
foreach (var method in typeGroup) |
|||
{ |
|||
var isOptional = false; |
|||
var first = true; |
|||
var fieldName = "_addr_" + method.Name; |
|||
var delegateType = BuildDelegateType(method); |
|||
|
|||
void AppendNextAddr() |
|||
{ |
|||
if (first) |
|||
{ |
|||
first = false; |
|||
initializeBody.Pad(2); |
|||
} |
|||
else |
|||
initializeBody |
|||
.Pad(2) |
|||
.Append("if(addr == IntPtr.Zero) "); |
|||
} |
|||
|
|||
initializeBody |
|||
.Pad(2).Append("// Initializing ").AppendLine(method.Name) |
|||
.Pad(2) |
|||
.AppendLine("addr = IntPtr.Zero;"); |
|||
foreach (var attr in method.GetAttributes()) |
|||
{ |
|||
if (attr.AttributeClass?.HasFullyQualifiedName(GetProcAddressFullName) == true) |
|||
{ |
|||
string? primaryName = null; |
|||
foreach (var arg in attr.ConstructorArguments) |
|||
{ |
|||
if (arg.Value is string name) |
|||
primaryName = name; |
|||
if (arg.Value is bool opt) |
|||
isOptional = opt; |
|||
} |
|||
|
|||
if (primaryName != null) |
|||
{ |
|||
AppendNextAddr(); |
|||
initializeBody |
|||
.Append("addr = getProcAddress(\"") |
|||
.Append(primaryName) |
|||
.AppendLine("\");"); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (attr.AttributeClass != null |
|||
&& attr.AttributeClass.MemberNames.Contains("GetProcAddress")) |
|||
{ |
|||
var getProcMethod = attr.AttributeClass.GetMembers() |
|||
.FirstOrDefault(m => m.Name == "GetProcAddress") as IMethodSymbol; |
|||
if (getProcMethod == null || !getProcMethod.IsStatic || getProcMethod.Parameters.Length < 2) |
|||
continue; |
|||
var contextName = |
|||
GetContextName(getProcMethod |
|||
.Parameters[1].Type |
|||
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); |
|||
AppendNextAddr(); |
|||
initializeBody |
|||
.Append("addr = ") |
|||
.Append(attr.AttributeClass.GetFullyQualifiedName()) |
|||
.Append(".GetProcAddress(") |
|||
.Append("getProcAddress, ") |
|||
.Append(contextName); |
|||
|
|||
var syntaxNode = (AttributeSyntax)attr.ApplicationSyntaxReference.GetSyntax(); |
|||
foreach (var arg in syntaxNode.ArgumentList.Arguments) |
|||
initializeBody.Append(", ").Append(arg.GetText()); |
|||
initializeBody.AppendLine(");"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!isOptional) |
|||
{ |
|||
initializeBody |
|||
.Pad(2) |
|||
.Append("if (addr == IntPtr.Zero) throw new System.EntryPointNotFoundException(\"") |
|||
.Append(fieldName).AppendLine("\");"); |
|||
} |
|||
|
|||
initializeBody |
|||
.Pad(2) |
|||
.Append(fieldName) |
|||
.Append(" = (") |
|||
.Append(delegateType) |
|||
.AppendLine(")addr;"); |
|||
|
|||
classBuilder |
|||
.Pad(1) |
|||
.Append(delegateType); |
|||
classBuilder |
|||
.Append(fieldName) |
|||
.AppendLine(";"); |
|||
|
|||
classBuilder |
|||
.Pad(1) |
|||
.Append("public partial ") |
|||
.Append(method.ReturnType.GetFullyQualifiedName()) |
|||
.Append(" ") |
|||
.Append(method.Name) |
|||
.Append("("); |
|||
var firstArg = true; |
|||
foreach (var p in method.Parameters) |
|||
{ |
|||
if (firstArg) |
|||
firstArg = false; |
|||
else |
|||
classBuilder.Append(", "); |
|||
AppendRefKind(classBuilder, p.RefKind); |
|||
classBuilder |
|||
.Append(p.Type.GetFullyQualifiedName()) |
|||
.Append(" @") |
|||
.Append(p.Name); |
|||
} |
|||
classBuilder |
|||
.AppendLine(")") |
|||
.Pad(1) |
|||
.AppendLine("{"); |
|||
if (isOptional) |
|||
classBuilder |
|||
.Pad(2) |
|||
.Append("if (") |
|||
.Append(fieldName) |
|||
.Append(" == null) throw new System.EntryPointNotFoundException(\"") |
|||
.Append(method.Name) |
|||
.AppendLine("\");"); |
|||
|
|||
foreach(var p in method.Parameters) |
|||
if (NeedsPin(p.Type)) |
|||
classBuilder.Pad(2) |
|||
.Append("fixed(") |
|||
.Append(MapToNative(p.Type)) |
|||
.Append(" @__p_") |
|||
.Append(p.Name) |
|||
.Append(" = ") |
|||
.Append(p.Name) |
|||
.AppendLine(")"); |
|||
|
|||
classBuilder.Pad(2); |
|||
if (!method.ReturnsVoid) |
|||
classBuilder.Append("return "); |
|||
|
|||
var invokeBuilder = new StringBuilder(); |
|||
|
|||
invokeBuilder |
|||
.Append(fieldName) |
|||
.Append("("); |
|||
firstArg = true; |
|||
foreach (var p in method.Parameters) |
|||
{ |
|||
if (firstArg) |
|||
firstArg = false; |
|||
else |
|||
invokeBuilder.Append(", "); |
|||
AppendRefKind(invokeBuilder, p.RefKind); |
|||
invokeBuilder |
|||
.Append("@") |
|||
.Append(ConvertToNative(p.Name, p.Type)); |
|||
} |
|||
|
|||
invokeBuilder.Append(")"); |
|||
classBuilder.Append(ConvertToManaged(method.ReturnType, invokeBuilder.ToString())); |
|||
|
|||
classBuilder.AppendLine(";").Pad(1).AppendLine("}"); |
|||
if (isOptional) |
|||
classBuilder |
|||
.Pad(1) |
|||
.Append("public bool Is") |
|||
.Append(method.Name) |
|||
.Append("Available => ") |
|||
.Append(fieldName) |
|||
.AppendLine(" != null;"); |
|||
} |
|||
|
|||
classBuilder |
|||
.Pad(1) |
|||
.Append("void Initialize(Func<string, IntPtr> getProcAddress"); |
|||
foreach (var kv in contexts.OrderBy(x => x.Value)) |
|||
{ |
|||
classBuilder |
|||
.Append(", ") |
|||
.Append(kv.Key) |
|||
.Append(" ") |
|||
.Append(GetContextNameFromIndex(kv.Value)); |
|||
} |
|||
|
|||
classBuilder.AppendLine(")").Pad(1).AppendLine("{"); |
|||
classBuilder.Append(initializeBody.ToString()); |
|||
classBuilder.Append("}\n}"); |
|||
|
|||
|
|||
context.AddSource(typeGroup.Key.GetFullyQualifiedName().Replace(":", ""), classBuilder.ToString()); |
|||
} |
|||
}); |
|||
|
|||
|
|||
} |
|||
|
|||
static StringBuilder AppendRefKind(StringBuilder sb, RefKind kind) |
|||
{ |
|||
if (kind == RefKind.Ref) |
|||
sb.Append("ref "); |
|||
if (kind == RefKind.Out) |
|||
sb.Append("out "); |
|||
return sb; |
|||
} |
|||
|
|||
static bool NeedsPin(ITypeSymbol type) |
|||
{ |
|||
if (type.TypeKind == TypeKind.Array) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
static string ConvertToNative(string name, ITypeSymbol type) |
|||
{ |
|||
if (NeedsPin(type)) |
|||
return "__p_" + name; |
|||
if (IsBool(type)) |
|||
return $"{name} ? 1 : 0"; |
|||
return name; |
|||
} |
|||
|
|||
static string ConvertToManaged(ITypeSymbol type, string expr) |
|||
{ |
|||
if (IsBool(type)) |
|||
return expr + " != 0"; |
|||
return expr; |
|||
} |
|||
|
|||
static bool IsBool(ITypeSymbol type) => type.GetFullyQualifiedName() == "global::System.Boolean" || |
|||
type.GetFullyQualifiedName() == "bool"; |
|||
|
|||
static string MapToNative(ITypeSymbol type) |
|||
{ |
|||
if (type.TypeKind == TypeKind.Array) |
|||
return ((IArrayTypeSymbol)type).ElementType.GetFullyQualifiedName() + "*"; |
|||
if (IsBool(type)) |
|||
return "int"; |
|||
return type.GetFullyQualifiedName(); |
|||
} |
|||
|
|||
static string BuildDelegateType(IMethodSymbol method) |
|||
{ |
|||
StringBuilder name = new("delegate* unmanaged[Stdcall]<"); |
|||
var firstArg = true; |
|||
|
|||
void AppendArg(string a, RefKind kind) |
|||
{ |
|||
if (firstArg) |
|||
firstArg = false; |
|||
else |
|||
name.Append(","); |
|||
AppendRefKind(name, kind); |
|||
name.Append(a); |
|||
} |
|||
|
|||
foreach (var p in method.Parameters) |
|||
{ |
|||
AppendArg(MapToNative(p.Type), p.RefKind); |
|||
} |
|||
|
|||
AppendArg(MapToNative(method.ReturnType), RefKind.None); |
|||
name.Append(">"); |
|||
return name.ToString(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System.Collections.Immutable; |
|||
using System.Text; |
|||
using Microsoft.CodeAnalysis; |
|||
|
|||
namespace Generator; |
|||
|
|||
static class Helpers |
|||
{ |
|||
public static StringBuilder Pad(this StringBuilder sb, int count) => sb.Append(' ', count * 4); |
|||
|
|||
public static string GetFullyQualifiedName(this ISymbol symbol) |
|||
{ |
|||
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); |
|||
} |
|||
|
|||
public static bool HasFullyQualifiedName(this ISymbol symbol, string name) |
|||
{ |
|||
return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == name; |
|||
} |
|||
|
|||
public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name) |
|||
{ |
|||
ImmutableArray<AttributeData> attributes = symbol.GetAttributes(); |
|||
|
|||
foreach (AttributeData attribute in attributes) |
|||
if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
Binary file not shown.
Loading…
Reference in new issue