committed by
GitHub
166 changed files with 10949 additions and 484 deletions
@ -1,16 +1,31 @@ |
|||
This template is not intended to be prescriptive, but to help us review pull requests it would be useful if you included as much of the following information as possible: |
|||
## What does the pull request do? |
|||
|
|||
- What does the pull request do? |
|||
- What is the current behavior? |
|||
- What is the updated/expected behavior with this PR? |
|||
- How was the solution implemented (if it's not obvious)? |
|||
Give a bit of background on the PR here, together with links to with related issues etc. |
|||
|
|||
Checklist: |
|||
## What is the current behavior? |
|||
|
|||
If the PR is a fix, describe the current incorrect behavior, otherwise delete this section. |
|||
|
|||
## What is the updated/expected behavior with this PR? |
|||
|
|||
Describe how to test the PR. |
|||
|
|||
## How was the solution implemented (if it's not obvious)? |
|||
|
|||
Include any information that might be of use to a reviewer here. |
|||
|
|||
## Checklist |
|||
|
|||
- [ ] Added unit tests (if possible)? |
|||
- [ ] Added XML documentation to any related classes? |
|||
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation |
|||
|
|||
## Breaking changes |
|||
|
|||
List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes |
|||
|
|||
## Fixed issues |
|||
|
|||
If the pull request fixes issue(s) list them like this: |
|||
|
|||
Fixes #123 |
|||
|
|||
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -1,6 +1,6 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SkiaSharp" Version="1.68.0" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="Avalonia.Skia.Linux.Natives" Version="1.68.0.1" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,6 @@ |
|||
<Application xmlns="https://github.com/avaloniaui"> |
|||
<Application.Styles> |
|||
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/> |
|||
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,13 @@ |
|||
using Avalonia; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace PlatformSanityChecks |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp2.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,132 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Reactive.Disposables; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading; |
|||
using Avalonia; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Threading; |
|||
using Avalonia.X11; |
|||
|
|||
namespace PlatformSanityChecks |
|||
{ |
|||
public class Program |
|||
{ |
|||
static Thread UiThread; |
|||
|
|||
static void Main(string[] args) |
|||
{ |
|||
UiThread = Thread.CurrentThread; |
|||
AppBuilder.Configure<App>().RuntimePlatformServicesInitializer(); |
|||
var app = new App(); |
|||
|
|||
AvaloniaX11PlatformExtensions.InitializeX11Platform(); |
|||
|
|||
CheckPlatformThreading(); |
|||
} |
|||
|
|||
static bool CheckAccess() => UiThread == Thread.CurrentThread; |
|||
|
|||
static void VerifyAccess() |
|||
{ |
|||
if (!CheckAccess()) |
|||
Die("Call from invalid thread"); |
|||
} |
|||
|
|||
static Exception Die(string error) |
|||
{ |
|||
Console.Error.WriteLine(error); |
|||
Console.Error.WriteLine(Environment.StackTrace); |
|||
Process.GetCurrentProcess().Kill(); |
|||
throw new Exception(error); |
|||
} |
|||
|
|||
static IDisposable Enter([CallerMemberName] string caller = null) |
|||
{ |
|||
Console.WriteLine("Entering " + caller); |
|||
return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); }); |
|||
} |
|||
|
|||
static void EnterLoop(Action<CancellationTokenSource> cb, [CallerMemberName] string caller = null) |
|||
{ |
|||
using (Enter(caller)) |
|||
{ |
|||
var cts = new CancellationTokenSource(); |
|||
cb(cts); |
|||
Dispatcher.UIThread.MainLoop(cts.Token); |
|||
if (!cts.IsCancellationRequested) |
|||
Die("Unexpected loop exit"); |
|||
} |
|||
} |
|||
|
|||
static void CheckTimerOrdering() => EnterLoop(cts => |
|||
{ |
|||
bool firstFired = false, secondFired = false; |
|||
DispatcherTimer.Run(() => |
|||
{ |
|||
Console.WriteLine("Second tick"); |
|||
VerifyAccess(); |
|||
if (!firstFired) |
|||
throw Die("Invalid timer ordering"); |
|||
if (secondFired) |
|||
throw Die("Invocation of finished timer"); |
|||
secondFired = true; |
|||
cts.Cancel(); |
|||
return false; |
|||
}, TimeSpan.FromSeconds(2)); |
|||
DispatcherTimer.Run(() => |
|||
{ |
|||
Console.WriteLine("First tick"); |
|||
VerifyAccess(); |
|||
if (secondFired) |
|||
throw Die("Invalid timer ordering"); |
|||
if (firstFired) |
|||
throw Die("Invocation of finished timer"); |
|||
firstFired = true; |
|||
return false; |
|||
}, TimeSpan.FromSeconds(1)); |
|||
}); |
|||
|
|||
static void CheckTimerTicking() => EnterLoop(cts => |
|||
{ |
|||
int ticks = 0; |
|||
var st = Stopwatch.StartNew(); |
|||
DispatcherTimer.Run(() => |
|||
{ |
|||
ticks++; |
|||
Console.WriteLine($"Tick {ticks} at {st.Elapsed}"); |
|||
if (ticks == 5) |
|||
{ |
|||
if (st.Elapsed.TotalSeconds < 4.5) |
|||
Die("Timer is too fast"); |
|||
if (st.Elapsed.TotalSeconds > 6) |
|||
Die("Timer is too slow"); |
|||
cts.Cancel(); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
}, TimeSpan.FromSeconds(1)); |
|||
}); |
|||
|
|||
static void CheckSignaling() => EnterLoop(cts => |
|||
{ |
|||
ThreadPool.QueueUserWorkItem(_ => |
|||
{ |
|||
Thread.Sleep(100); |
|||
Dispatcher.UIThread.Post(() => |
|||
{ |
|||
VerifyAccess(); |
|||
cts.Cancel(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
static void CheckPlatformThreading() |
|||
{ |
|||
CheckSignaling(); |
|||
CheckTimerOrdering(); |
|||
CheckTimerTicking(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
|
|||
namespace Avalonia.Data.Converters |
|||
{ |
|||
/// <summary>
|
|||
/// Provides a set of useful <see cref="IValueConverter"/>s for working with objects.
|
|||
/// </summary>
|
|||
public static class ObjectConverters |
|||
{ |
|||
/// <summary>
|
|||
/// A value converter that returns true if the input object is a null reference.
|
|||
/// </summary>
|
|||
public static readonly IValueConverter IsNull = |
|||
new FuncValueConverter<object, bool>(x => x is null); |
|||
|
|||
/// <summary>
|
|||
/// A value converter that returns true if the input object is not null.
|
|||
/// </summary>
|
|||
public static readonly IValueConverter IsNotNull = |
|||
new FuncValueConverter<object, bool>(x => !(x is null)); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides <see cref="PixelPoint"/> data for events.
|
|||
/// </summary>
|
|||
public class PixelPointEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelPointEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="point">The <see cref=PixelPoint"/> data.</param>
|
|||
public PixelPointEventArgs(PixelPoint point) |
|||
{ |
|||
Point = point; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="PixelPoint"/> data.
|
|||
/// </summary>
|
|||
public PixelPoint Point { get; } |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides <see cref="Point"/> data for events.
|
|||
/// </summary>
|
|||
public class PointEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PointEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="point">The <see cref="Point"/> data.</param>
|
|||
public PointEventArgs(Point point) |
|||
{ |
|||
Point = point; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="Point"/> data.
|
|||
/// </summary>
|
|||
public Point Point { get; } |
|||
} |
|||
} |
|||
@ -1,14 +1,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0</TargetFrameworks> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="../../src/Windows/Avalonia.Win32/Avalonia.Win32.csproj" /> |
|||
<ProjectReference Include="../../src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj" /> |
|||
<ProjectReference Include="../../src/Skia/Avalonia.Skia/Avalonia.Skia.csproj" /> |
|||
<ProjectReference Include="../../src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj" /> |
|||
<ProjectReference Include="../../src/Avalonia.Native/Avalonia.Native.csproj" /> |
|||
<ProjectReference Include="../../packages/Avalonia/Avalonia.csproj" /> |
|||
<ProjectReference Include="../Avalonia.X11/Avalonia.X11.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public enum EglErrors |
|||
{ |
|||
EGL_SUCCESS = EglConsts.EGL_SUCCESS, |
|||
EGL_NOT_INITIALIZED = EglConsts.EGL_NOT_INITIALIZED, |
|||
EGL_BAD_ACCESS = EglConsts.EGL_BAD_ACCESS, |
|||
EGL_BAD_ALLOC = EglConsts.EGL_BAD_ALLOC, |
|||
EGL_BAD_ATTRIBUTE = EglConsts.EGL_BAD_ATTRIBUTE, |
|||
EGL_BAD_CONTEXT = EglConsts.EGL_BAD_CONTEXT, |
|||
EGL_BAD_CONFIG = EglConsts.EGL_BAD_CONFIG, |
|||
EGL_BAD_CURRENT_SURFACE = EglConsts.EGL_BAD_CURRENT_SURFACE, |
|||
EGL_BAD_DISPLAY = EglConsts.EGL_BAD_DISPLAY, |
|||
EGL_BAD_SURFACE = EglConsts.EGL_BAD_SURFACE, |
|||
EGL_BAD_MATCH = EglConsts.EGL_BAD_MATCH, |
|||
EGL_BAD_PARAMETER = EglConsts.EGL_BAD_PARAMETER, |
|||
EGL_BAD_NATIVE_PIXMAP = EglConsts.EGL_BAD_NATIVE_PIXMAP, |
|||
EGL_BAD_NATIVE_WINDOW = EglConsts.EGL_BAD_NATIVE_WINDOW, |
|||
EGL_CONTEXT_LOST = EglConsts.EGL_CONTEXT_LOST |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public enum GlErrors |
|||
{ |
|||
GL_NO_ERROR = GlConsts.GL_NO_ERROR, |
|||
GL_INVALID_ENUM = GlConsts.GL_INVALID_ENUM, |
|||
GL_INVALID_VALUE = GlConsts.GL_INVALID_VALUE, |
|||
GL_INVALID_OPERATION = GlConsts.GL_INVALID_OPERATION, |
|||
GL_INVALID_FRAMEBUFFER_OPERATION = GlConsts.GL_INVALID_FRAMEBUFFER_OPERATION, |
|||
GL_STACK_OVERFLOW = GlConsts.GL_STACK_OVERFLOW, |
|||
GL_STACK_UNDERFLOW = GlConsts.GL_STACK_UNDERFLOW, |
|||
GL_OUT_OF_MEMORY = GlConsts.GL_OUT_OF_MEMORY, |
|||
GL_CONTEXT_LOST = GlConsts.GL_CONTEXT_LOST |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive.Linq; |
|||
|
|||
namespace Avalonia.Styling |
|||
{ |
|||
/// <summary>
|
|||
/// The `:not()` style selector.
|
|||
/// </summary>
|
|||
internal class NotSelector : Selector |
|||
{ |
|||
private readonly Selector _previous; |
|||
private readonly Selector _argument; |
|||
private string _selectorString; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NotSelector"/> class.
|
|||
/// </summary>
|
|||
/// <param name="previous">The previous selector.</param>
|
|||
/// <param name="argument">The selector to be not-ed.</param>
|
|||
public NotSelector(Selector previous, Selector argument) |
|||
{ |
|||
_previous = previous; |
|||
_argument = argument ?? throw new InvalidOperationException("Not selector must have a selector argument."); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool InTemplate => _argument.InTemplate; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool IsCombinator => false; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Type TargetType => _previous?.TargetType; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (_selectorString == null) |
|||
{ |
|||
_selectorString = ":not(" + _argument.ToString() + ")"; |
|||
} |
|||
|
|||
return _selectorString; |
|||
} |
|||
|
|||
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) |
|||
{ |
|||
var innerResult = _argument.Match(control, subscribe); |
|||
|
|||
switch (innerResult.Result) |
|||
{ |
|||
case SelectorMatchResult.AlwaysThisInstance: |
|||
return SelectorMatch.NeverThisInstance; |
|||
case SelectorMatchResult.AlwaysThisType: |
|||
return SelectorMatch.NeverThisType; |
|||
case SelectorMatchResult.NeverThisInstance: |
|||
return SelectorMatch.AlwaysThisInstance; |
|||
case SelectorMatchResult.NeverThisType: |
|||
return SelectorMatch.AlwaysThisType; |
|||
case SelectorMatchResult.Sometimes: |
|||
return new SelectorMatch(innerResult.Activator.Select(x => !x)); |
|||
default: |
|||
throw new InvalidOperationException("Invalid SelectorMatchResult."); |
|||
} |
|||
} |
|||
|
|||
protected override Selector MovePrevious() => _previous; |
|||
} |
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a point in device pixels.
|
|||
/// </summary>
|
|||
public readonly struct PixelPoint |
|||
{ |
|||
/// <summary>
|
|||
/// A point representing 0,0.
|
|||
/// </summary>
|
|||
public static readonly PixelPoint Origin = new PixelPoint(0, 0); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelPoint"/> structure.
|
|||
/// </summary>
|
|||
/// <param name="x">The X co-ordinate.</param>
|
|||
/// <param name="y">The Y co-ordinate.</param>
|
|||
public PixelPoint(int x, int y) |
|||
{ |
|||
X = x; |
|||
Y = y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the X co-ordinate.
|
|||
/// </summary>
|
|||
public int X { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the Y co-ordinate.
|
|||
/// </summary>
|
|||
public int Y { get; } |
|||
|
|||
/// <summary>
|
|||
/// Checks for equality between two <see cref="PixelPoint"/>s.
|
|||
/// </summary>
|
|||
/// <param name="left">The first point.</param>
|
|||
/// <param name="right">The second point.</param>
|
|||
/// <returns>True if the points are equal; otherwise false.</returns>
|
|||
public static bool operator ==(PixelPoint left, PixelPoint right) |
|||
{ |
|||
return left.X == right.X && left.Y == right.Y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks for inequality between two <see cref="PixelPoint"/>s.
|
|||
/// </summary>
|
|||
/// <param name="left">The first point.</param>
|
|||
/// <param name="right">The second point.</param>
|
|||
/// <returns>True if the points are unequal; otherwise false.</returns>
|
|||
public static bool operator !=(PixelPoint left, PixelPoint right) |
|||
{ |
|||
return !(left == right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a <see cref="PixelPoint"/> string.
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The <see cref="PixelPoint"/>.</returns>
|
|||
public static PixelPoint Parse(string s) |
|||
{ |
|||
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelPoint")) |
|||
{ |
|||
return new PixelPoint( |
|||
tokenizer.ReadInt32(), |
|||
tokenizer.ReadInt32()); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks for equality between a point and an object.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object.</param>
|
|||
/// <returns>
|
|||
/// True if <paramref name="obj"/> is a point that equals the current point.
|
|||
/// </returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is PixelPoint other) |
|||
{ |
|||
return this == other; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a hash code for a <see cref="PixelPoint"/>.
|
|||
/// </summary>
|
|||
/// <returns>The hash code.</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
int hash = 17; |
|||
hash = (hash * 23) + X.GetHashCode(); |
|||
hash = (hash * 23) + Y.GetHashCode(); |
|||
return hash; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelPoint"/> with the same Y co-ordinate and the specified X co-ordinate.
|
|||
/// </summary>
|
|||
/// <param name="x">The X co-ordinate.</param>
|
|||
/// <returns>The new <see cref="PixelPoint"/>.</returns>
|
|||
public PixelPoint WithX(int x) => new PixelPoint(x, Y); |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelPoint"/> with the same X co-ordinate and the specified Y co-ordinate.
|
|||
/// </summary>
|
|||
/// <param name="y">The Y co-ordinate.</param>
|
|||
/// <returns>The new <see cref="PixelPoint"/>.</returns>
|
|||
public PixelPoint WithY(int y) => new PixelPoint(X, y); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
|
|||
/// specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public Point ToPoint(double scale) => new Point(X / scale, Y / scale); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
|
|||
/// specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public Point ToPoint(Vector scale) => new Point(X / scale.X, Y / scale.Y); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
|
|||
/// specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public Point ToPointWithDpi(double dpi) => ToPoint(dpi / 96); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelPoint"/> to a device-independent <see cref="Point"/> using the
|
|||
/// specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public Point ToPointWithDpi(Vector dpi) => ToPoint(new Vector(dpi.X / 96, dpi.Y / 96)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Point"/> to device pixels using the specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="point">The point.</param>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelPoint FromPoint(Point point, double scale) => new PixelPoint( |
|||
(int)(point.X * scale), |
|||
(int)(point.Y * scale)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Point"/> to device pixels using the specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="point">The point.</param>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelPoint FromPoint(Point point, Vector scale) => new PixelPoint( |
|||
(int)(point.X * scale.X), |
|||
(int)(point.Y * scale.Y)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Point"/> to device pixels using the specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="point">The point.</param>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelPoint FromPointWithDpi(Point point, double dpi) => FromPoint(point, dpi / 96); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Point"/> to device pixels using the specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="point">The point.</param>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelPoint FromPointWithDpi(Point point, Vector dpi) => FromPoint(point, new Vector(dpi.X / 96, dpi.Y / 96)); |
|||
|
|||
/// <summary>
|
|||
/// Returns the string representation of the point.
|
|||
/// </summary>
|
|||
/// <returns>The string representation of the point.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", X, Y); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,436 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a rectangle in device pixels.
|
|||
/// </summary>
|
|||
public readonly struct PixelRect |
|||
{ |
|||
/// <summary>
|
|||
/// An empty rectangle.
|
|||
/// </summary>
|
|||
public static readonly PixelRect Empty = default; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
|
|||
/// </summary>
|
|||
/// <param name="x">The X position.</param>
|
|||
/// <param name="y">The Y position.</param>
|
|||
/// <param name="width">The width.</param>
|
|||
/// <param name="height">The height.</param>
|
|||
public PixelRect(int x, int y, int width, int height) |
|||
{ |
|||
X = x; |
|||
Y = y; |
|||
Width = width; |
|||
Height = height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
|
|||
/// </summary>
|
|||
/// <param name="size">The size of the rectangle.</param>
|
|||
public PixelRect(PixelSize size) |
|||
{ |
|||
X = 0; |
|||
Y = 0; |
|||
Width = size.Width; |
|||
Height = size.Height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
|
|||
/// </summary>
|
|||
/// <param name="position">The position of the rectangle.</param>
|
|||
/// <param name="size">The size of the rectangle.</param>
|
|||
public PixelRect(PixelPoint position, PixelSize size) |
|||
{ |
|||
X = position.X; |
|||
Y = position.Y; |
|||
Width = size.Width; |
|||
Height = size.Height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelRect"/> structure.
|
|||
/// </summary>
|
|||
/// <param name="topLeft">The top left position of the rectangle.</param>
|
|||
/// <param name="bottomRight">The bottom right position of the rectangle.</param>
|
|||
public PixelRect(PixelPoint topLeft, PixelPoint bottomRight) |
|||
{ |
|||
X = topLeft.X; |
|||
Y = topLeft.Y; |
|||
Width = bottomRight.X - topLeft.X; |
|||
Height = bottomRight.Y - topLeft.Y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the X position.
|
|||
/// </summary>
|
|||
public int X { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the Y position.
|
|||
/// </summary>
|
|||
public int Y { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the width.
|
|||
/// </summary>
|
|||
public int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height.
|
|||
/// </summary>
|
|||
public int Height { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the position of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint Position => new PixelPoint(X, Y); |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the rectangle.
|
|||
/// </summary>
|
|||
public PixelSize Size => new PixelSize(Width, Height); |
|||
|
|||
/// <summary>
|
|||
/// Gets the right position of the rectangle.
|
|||
/// </summary>
|
|||
public int Right => X + Width; |
|||
|
|||
/// <summary>
|
|||
/// Gets the bottom position of the rectangle.
|
|||
/// </summary>
|
|||
public int Bottom => Y + Height; |
|||
|
|||
/// <summary>
|
|||
/// Gets the top left point of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint TopLeft => new PixelPoint(X, Y); |
|||
|
|||
/// <summary>
|
|||
/// Gets the top right point of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint TopRight => new PixelPoint(Right, Y); |
|||
|
|||
/// <summary>
|
|||
/// Gets the bottom left point of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint BottomLeft => new PixelPoint(X, Bottom); |
|||
|
|||
/// <summary>
|
|||
/// Gets the bottom right point of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint BottomRight => new PixelPoint(Right, Bottom); |
|||
|
|||
/// <summary>
|
|||
/// Gets the center point of the rectangle.
|
|||
/// </summary>
|
|||
public PixelPoint Center => new PixelPoint(X + (Width / 2), Y + (Height / 2)); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the rectangle is empty.
|
|||
/// </summary>
|
|||
public bool IsEmpty => Width == 0 && Height == 0; |
|||
|
|||
/// <summary>
|
|||
/// Checks for equality between two <see cref="PixelRect"/>s.
|
|||
/// </summary>
|
|||
/// <param name="left">The first rect.</param>
|
|||
/// <param name="right">The second rect.</param>
|
|||
/// <returns>True if the rects are equal; otherwise false.</returns>
|
|||
public static bool operator ==(PixelRect left, PixelRect right) |
|||
{ |
|||
return left.Position == right.Position && left.Size == right.Size; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks for inequality between two <see cref="PixelRect"/>s.
|
|||
/// </summary>
|
|||
/// <param name="left">The first rect.</param>
|
|||
/// <param name="right">The second rect.</param>
|
|||
/// <returns>True if the rects are unequal; otherwise false.</returns>
|
|||
public static bool operator !=(PixelRect left, PixelRect right) |
|||
{ |
|||
return !(left == right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether a point in in the bounds of the rectangle.
|
|||
/// </summary>
|
|||
/// <param name="p">The point.</param>
|
|||
/// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>
|
|||
public bool Contains(PixelPoint p) |
|||
{ |
|||
return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the rectangle fully contains another rectangle.
|
|||
/// </summary>
|
|||
/// <param name="r">The rectangle.</param>
|
|||
/// <returns>true if the rectangle is fully contained; otherwise false.</returns>
|
|||
public bool Contains(PixelRect r) |
|||
{ |
|||
return Contains(r.TopLeft) && Contains(r.BottomRight); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Centers another rectangle in this rectangle.
|
|||
/// </summary>
|
|||
/// <param name="rect">The rectangle to center.</param>
|
|||
/// <returns>The centered rectangle.</returns>
|
|||
public PixelRect CenterRect(PixelRect rect) |
|||
{ |
|||
return new PixelRect( |
|||
X + ((Width - rect.Width) / 2), |
|||
Y + ((Height - rect.Height) / 2), |
|||
rect.Width, |
|||
rect.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a boolean indicating whether the given object is equal to this rectangle.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to compare against.</param>
|
|||
/// <returns>True if the object is equal to this rectangle; false otherwise.</returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is PixelRect other) |
|||
{ |
|||
return this == other; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <returns>The hash code.</returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
unchecked |
|||
{ |
|||
int hash = 17; |
|||
hash = (hash * 23) + X.GetHashCode(); |
|||
hash = (hash * 23) + Y.GetHashCode(); |
|||
hash = (hash * 23) + Width.GetHashCode(); |
|||
hash = (hash * 23) + Height.GetHashCode(); |
|||
return hash; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the intersection of two rectangles.
|
|||
/// </summary>
|
|||
/// <param name="rect">The other rectangle.</param>
|
|||
/// <returns>The intersection.</returns>
|
|||
public PixelRect Intersect(PixelRect rect) |
|||
{ |
|||
var newLeft = (rect.X > X) ? rect.X : X; |
|||
var newTop = (rect.Y > Y) ? rect.Y : Y; |
|||
var newRight = (rect.Right < Right) ? rect.Right : Right; |
|||
var newBottom = (rect.Bottom < Bottom) ? rect.Bottom : Bottom; |
|||
|
|||
if ((newRight > newLeft) && (newBottom > newTop)) |
|||
{ |
|||
return new PixelRect(newLeft, newTop, newRight - newLeft, newBottom - newTop); |
|||
} |
|||
else |
|||
{ |
|||
return Empty; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether a rectangle intersects with this rectangle.
|
|||
/// </summary>
|
|||
/// <param name="rect">The other rectangle.</param>
|
|||
/// <returns>
|
|||
/// True if the specified rectangle intersects with this one; otherwise false.
|
|||
/// </returns>
|
|||
public bool Intersects(PixelRect rect) |
|||
{ |
|||
return (rect.X < Right) && (X < rect.Right) && (rect.Y < Bottom) && (Y < rect.Bottom); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the union of two rectangles.
|
|||
/// </summary>
|
|||
/// <param name="rect">The other rectangle.</param>
|
|||
/// <returns>The union.</returns>
|
|||
public PixelRect Union(PixelRect rect) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return rect; |
|||
} |
|||
else if (rect.IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
else |
|||
{ |
|||
var x1 = Math.Min(X, rect.X); |
|||
var x2 = Math.Max(Right, rect.Right); |
|||
var y1 = Math.Min(Y, rect.Y); |
|||
var y2 = Math.Max(Bottom, rect.Bottom); |
|||
|
|||
return new PixelRect(new PixelPoint(x1, y1), new PixelPoint(x2, y2)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelRect"/> with the specified X position.
|
|||
/// </summary>
|
|||
/// <param name="x">The x position.</param>
|
|||
/// <returns>The new <see cref="PixelRect"/>.</returns>
|
|||
public PixelRect WithX(int x) |
|||
{ |
|||
return new PixelRect(x, Y, Width, Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelRect"/> with the specified Y position.
|
|||
/// </summary>
|
|||
/// <param name="y">The y position.</param>
|
|||
/// <returns>The new <see cref="PixelRect"/>.</returns>
|
|||
public PixelRect WithY(int y) |
|||
{ |
|||
return new PixelRect(X, y, Width, Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelRect"/> with the specified width.
|
|||
/// </summary>
|
|||
/// <param name="width">The width.</param>
|
|||
/// <returns>The new <see cref="PixelRect"/>.</returns>
|
|||
public PixelRect WithWidth(int width) |
|||
{ |
|||
return new PixelRect(X, Y, width, Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a new <see cref="PixelRect"/> with the specified height.
|
|||
/// </summary>
|
|||
/// <param name="height">The height.</param>
|
|||
/// <returns>The new <see cref="PixelRect"/>.</returns>
|
|||
public PixelRect WithHeight(int height) |
|||
{ |
|||
return new PixelRect(X, Y, Width, Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
|
|||
/// specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent rect.</returns>
|
|||
public Rect ToRect(double scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale)); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
|
|||
/// specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent rect.</returns>
|
|||
public Rect ToRect(Vector scale) => new Rect(Position.ToPoint(scale), Size.ToSize(scale)); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
|
|||
/// specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent rect.</returns>
|
|||
public Rect ToRectWithDpi(double dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi)); |
|||
|
|||
/// <summary>
|
|||
/// Converts the <see cref="PixelRect"/> to a device-independent <see cref="Rect"/> using the
|
|||
/// specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent rect.</returns>
|
|||
public Rect ToRectWithDpi(Vector dpi) => new Rect(Position.ToPointWithDpi(dpi), Size.ToSizeWithDpi(dpi)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Rect"/> to device pixels using the specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="rect">The rect.</param>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent rect.</returns>
|
|||
public static PixelRect FromRect(Rect rect, double scale) => new PixelRect( |
|||
PixelPoint.FromPoint(rect.Position, scale), |
|||
PixelSize.FromSize(rect.Size, scale)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Rect"/> to device pixels using the specified scaling factor.
|
|||
/// </summary>
|
|||
/// <param name="rect">The rect.</param>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelRect FromRect(Rect rect, Vector scale) => new PixelRect( |
|||
PixelPoint.FromPoint(rect.Position, scale), |
|||
PixelSize.FromSize(rect.Size, scale)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Rect"/> to device pixels using the specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="rect">The rect.</param>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelRect FromRectWithDpi(Rect rect, double dpi) => new PixelRect( |
|||
PixelPoint.FromPointWithDpi(rect.Position, dpi), |
|||
PixelSize.FromSizeWithDpi(rect.Size, dpi)); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Rect"/> to device pixels using the specified dots per inch (DPI).
|
|||
/// </summary>
|
|||
/// <param name="rect">The rect.</param>
|
|||
/// <param name="dpi">The dots per inch of the device.</param>
|
|||
/// <returns>The device-independent point.</returns>
|
|||
public static PixelRect FromRectWithDpi(Rect rect, Vector dpi) => new PixelRect( |
|||
PixelPoint.FromPointWithDpi(rect.Position, dpi), |
|||
PixelSize.FromSizeWithDpi(rect.Size, dpi)); |
|||
|
|||
/// <summary>
|
|||
/// Returns the string representation of the rectangle.
|
|||
/// </summary>
|
|||
/// <returns>The string representation of the rectangle.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format( |
|||
CultureInfo.InvariantCulture, |
|||
"{0}, {1}, {2}, {3}", |
|||
X, |
|||
Y, |
|||
Width, |
|||
Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a <see cref="PixelRect"/> string.
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The parsed <see cref="PixelRect"/>.</returns>
|
|||
public static PixelRect Parse(string s) |
|||
{ |
|||
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid PixelRect")) |
|||
{ |
|||
return new PixelRect( |
|||
tokenizer.ReadInt32(), |
|||
tokenizer.ReadInt32(), |
|||
tokenizer.ReadInt32(), |
|||
tokenizer.ReadInt32() |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Rendering |
|||
{ |
|||
/// <summary>
|
|||
/// Provides data for the <see cref="IRenderer.SceneInvalidated"/> event.
|
|||
/// </summary>
|
|||
public class SceneInvalidatedEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SceneInvalidatedEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="root">The render root that has been updated.</param>
|
|||
/// <param name="dirtyRect">The updated area.</param>
|
|||
public SceneInvalidatedEventArgs( |
|||
IRenderRoot root, |
|||
Rect dirtyRect) |
|||
{ |
|||
RenderRoot = root; |
|||
DirtyRect = dirtyRect; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the invalidated area.
|
|||
/// </summary>
|
|||
public Rect DirtyRect { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the render root that has been invalidated.
|
|||
/// </summary>
|
|||
public IRenderRoot RenderRoot { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" /> |
|||
<ProjectReference Include="..\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj" /> |
|||
<ProjectReference Include="..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,91 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.Platform.Interop; |
|||
// ReSharper disable UnassignedGetOnlyAutoProperty
|
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
unsafe class GlxInterface : GlInterfaceBase |
|||
{ |
|||
private const string libGL = "libGL.so.1"; |
|||
[GlEntryPointAttribute("glXMakeContextCurrent")] |
|||
public GlxMakeContextCurrent MakeContextCurrent { get; } |
|||
public delegate bool GlxMakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); |
|||
|
|||
[GlEntryPoint("glXCreatePbuffer")] |
|||
public GlxCreatePbuffer CreatePbuffer { get; } |
|||
|
|||
public delegate IntPtr GlxCreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list); |
|||
|
|||
[GlEntryPointAttribute("glXChooseVisual")] |
|||
public GlxChooseVisual ChooseVisual { get; } |
|||
public delegate XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXCreateContext")] |
|||
public GlxCreateContext CreateContext { get; } |
|||
public delegate IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXCreateContextAttribsARB")] |
|||
public GlxCreateContextAttribsARB CreateContextAttribsARB { get; } |
|||
public delegate IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, |
|||
bool direct, int[] attribs); |
|||
|
|||
|
|||
[DllImport(libGL, EntryPoint = "glXGetProcAddress")] |
|||
public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXDestroyContext")] |
|||
public GlxDestroyContext DestroyContext { get; } |
|||
public delegate void GlxDestroyContext(IntPtr dpy, IntPtr ctx); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXChooseFBConfig")] |
|||
public GlxChooseFBConfig ChooseFBConfig { get; } |
|||
public delegate IntPtr* GlxChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements); |
|||
|
|||
|
|||
public IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable<int> attribs, out int nelements) |
|||
{ |
|||
var arr = attribs.Concat(new[]{0}).ToArray(); |
|||
return ChooseFBConfig(dpy, screen, arr, out nelements); |
|||
} |
|||
|
|||
[GlEntryPointAttribute("glXGetVisualFromFBConfig")] |
|||
public GlxGetVisualFromFBConfig GetVisualFromFBConfig { get; } |
|||
public delegate XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXGetFBConfigAttrib")] |
|||
public GlxGetFBConfigAttrib GetFBConfigAttrib { get; } |
|||
public delegate int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXSwapBuffers")] |
|||
public GlxSwapBuffers SwapBuffers { get; } |
|||
public delegate void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXWaitX")] |
|||
public GlxWaitX WaitX { get; } |
|||
public delegate void GlxWaitX(); |
|||
|
|||
|
|||
[GlEntryPointAttribute("glXWaitGL")] |
|||
public GlxWaitGL WaitGL { get; } |
|||
public delegate void GlxWaitGL(); |
|||
|
|||
public delegate int GlGetError(); |
|||
[GlEntryPoint("glGetError")] |
|||
public GlGetError GetError { get; } |
|||
|
|||
public GlxInterface() : base(GlxGetProcAddress) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
// ReSharper disable IdentifierTypo
|
|||
// ReSharper disable UnusedMember.Global
|
|||
#pragma warning disable 414
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxConsts |
|||
{ |
|||
public const int GLX_USE_GL = 1; |
|||
public const int GLX_BUFFER_SIZE = 2; |
|||
public const int GLX_LEVEL = 3; |
|||
public const int GLX_RGBA = 4; |
|||
public const int GLX_DOUBLEBUFFER = 5; |
|||
public const int GLX_STEREO = 6; |
|||
public const int GLX_AUX_BUFFERS = 7; |
|||
public const int GLX_RED_SIZE = 8; |
|||
public const int GLX_GREEN_SIZE = 9; |
|||
public const int GLX_BLUE_SIZE = 10; |
|||
public const int GLX_ALPHA_SIZE = 11; |
|||
public const int GLX_DEPTH_SIZE = 12; |
|||
public const int GLX_STENCIL_SIZE = 13; |
|||
public const int GLX_ACCUM_RED_SIZE = 14; |
|||
public const int GLX_ACCUM_GREEN_SIZE = 15; |
|||
public const int GLX_ACCUM_BLUE_SIZE = 16; |
|||
public const int GLX_ACCUM_ALPHA_SIZE = 17; |
|||
public const int GLX_BAD_SCREEN = 1; |
|||
public const int GLX_BAD_ATTRIBUTE = 2; |
|||
public const int GLX_NO_EXTENSION = 3; |
|||
public const int GLX_BAD_VISUAL = 4; |
|||
public const int GLX_BAD_CONTEXT = 5; |
|||
public const int GLX_BAD_VALUE = 6; |
|||
public const int GLX_BAD_ENUM = 7; |
|||
public const int GLX_VENDOR = 1; |
|||
public const int GLX_VERSION = 2; |
|||
public const int GLX_EXTENSIONS= 3; |
|||
public const int GLX_CONFIG_CAVEAT = 0x20; |
|||
public const int GLX_DONT_CARE = unchecked((int)0xFFFFFFFF); |
|||
public const int GLX_X_VISUAL_TYPE = 0x22; |
|||
public const int GLX_TRANSPARENT_TYPE = 0x23; |
|||
public const int GLX_TRANSPARENT_INDEX_VALUE = 0x24; |
|||
public const int GLX_TRANSPARENT_RED_VALUE = 0x25; |
|||
public const int GLX_TRANSPARENT_GREEN_VALUE = 0x26; |
|||
public const int GLX_TRANSPARENT_BLUE_VALUE = 0x27; |
|||
public const int GLX_TRANSPARENT_ALPHA_VALUE = 0x28; |
|||
public const int GLX_WINDOW_BIT = 0x00000001; |
|||
public const int GLX_PIXMAP_BIT = 0x00000002; |
|||
public const int GLX_PBUFFER_BIT = 0x00000004; |
|||
public const int GLX_AUX_BUFFERS_BIT = 0x00000010; |
|||
public const int GLX_FRONT_LEFT_BUFFER_BIT = 0x00000001; |
|||
public const int GLX_FRONT_RIGHT_BUFFER_BIT = 0x00000002; |
|||
public const int GLX_BACK_LEFT_BUFFER_BIT = 0x00000004; |
|||
public const int GLX_BACK_RIGHT_BUFFER_BIT = 0x00000008; |
|||
public const int GLX_DEPTH_BUFFER_BIT = 0x00000020; |
|||
public const int GLX_STENCIL_BUFFER_BIT = 0x00000040; |
|||
public const int GLX_ACCUM_BUFFER_BIT = 0x00000080; |
|||
public const int GLX_NONE = 0x8000; |
|||
public const int GLX_SLOW_CONFIG = 0x8001; |
|||
public const int GLX_TRUE_COLOR = 0x8002; |
|||
public const int GLX_DIRECT_COLOR = 0x8003; |
|||
public const int GLX_PSEUDO_COLOR = 0x8004; |
|||
public const int GLX_STATIC_COLOR = 0x8005; |
|||
public const int GLX_GRAY_SCALE = 0x8006; |
|||
public const int GLX_STATIC_GRAY = 0x8007; |
|||
public const int GLX_TRANSPARENT_RGB = 0x8008; |
|||
public const int GLX_TRANSPARENT_INDEX = 0x8009; |
|||
public const int GLX_VISUAL_ID = 0x800B; |
|||
public const int GLX_SCREEN = 0x800C; |
|||
public const int GLX_NON_CONFORMANT_CONFIG = 0x800D; |
|||
public const int GLX_DRAWABLE_TYPE = 0x8010; |
|||
public const int GLX_RENDER_TYPE = 0x8011; |
|||
public const int GLX_X_RENDERABLE = 0x8012; |
|||
public const int GLX_FBCONFIG_ID = 0x8013; |
|||
public const int GLX_RGBA_TYPE = 0x8014; |
|||
public const int GLX_COLOR_INDEX_TYPE = 0x8015; |
|||
public const int GLX_MAX_PBUFFER_WIDTH = 0x8016; |
|||
public const int GLX_MAX_PBUFFER_HEIGHT = 0x8017; |
|||
public const int GLX_MAX_PBUFFER_PIXELS = 0x8018; |
|||
public const int GLX_PRESERVED_CONTENTS = 0x801B; |
|||
public const int GLX_LARGEST_PBUFFER = 0x801C; |
|||
public const int GLX_WIDTH = 0x801D; |
|||
public const int GLX_HEIGHT = 0x801E; |
|||
public const int GLX_EVENT_MASK = 0x801F; |
|||
public const int GLX_DAMAGED = 0x8020; |
|||
public const int GLX_SAVED = 0x8021; |
|||
public const int GLX_WINDOW = 0x8022; |
|||
public const int GLX_PBUFFER = 0x8023; |
|||
public const int GLX_PBUFFER_HEIGHT = 0x8040; |
|||
public const int GLX_PBUFFER_WIDTH = 0x8041; |
|||
public const int GLX_RGBA_BIT = 0x00000001; |
|||
public const int GLX_COLOR_INDEX_BIT = 0x00000002; |
|||
public const int GLX_PBUFFER_CLOBBER_MASK = 0x08000000; |
|||
public const int GLX_SAMPLE_BUFFERS = 0x186a0 /*100000*/; |
|||
public const int GLX_SAMPLES = 0x186a1 /*100001*/; |
|||
public const int GLX_PbufferClobber = 0; |
|||
public const int GLX_BufferSwapComplete = 1; |
|||
public const int GLX_CONTEXT_DEBUG_BIT_ARB = 0x00000001; |
|||
public const int GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x00000002; |
|||
public const int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091; |
|||
public const int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092; |
|||
public const int GLX_CONTEXT_FLAGS_ARB = 0x2094; |
|||
public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; |
|||
public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; |
|||
public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126; |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Reactive.Disposables; |
|||
using System.Threading; |
|||
using Avalonia.OpenGL; |
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxContext : IGlContext |
|||
{ |
|||
public IntPtr Handle { get; } |
|||
public GlxInterface Glx { get; } |
|||
private readonly X11Info _x11; |
|||
private readonly IntPtr _defaultXid; |
|||
private readonly object _lock = new object(); |
|||
|
|||
public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid) |
|||
{ |
|||
Handle = handle; |
|||
Glx = glx; |
|||
_x11 = x11; |
|||
_defaultXid = defaultXid; |
|||
Display = display; |
|||
} |
|||
|
|||
public GlxDisplay Display { get; } |
|||
IGlDisplay IGlContext.Display => Display; |
|||
|
|||
public IDisposable Lock() |
|||
{ |
|||
Monitor.Enter(_lock); |
|||
return Disposable.Create(() => Monitor.Exit(_lock)); |
|||
} |
|||
|
|||
public void MakeCurrent() => MakeCurrent(_defaultXid); |
|||
|
|||
public void MakeCurrent(IntPtr xid) |
|||
{ |
|||
if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle)) |
|||
throw new OpenGlException("glXMakeContextCurrent failed "); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Avalonia.OpenGL; |
|||
using static Avalonia.X11.Glx.GlxConsts; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
unsafe class GlxDisplay : IGlDisplay |
|||
{ |
|||
private readonly X11Info _x11; |
|||
private readonly IntPtr _fbconfig; |
|||
private readonly XVisualInfo* _visual; |
|||
public GlDisplayType Type => GlDisplayType.OpenGL2; |
|||
public GlInterface GlInterface { get; } |
|||
|
|||
public XVisualInfo* VisualInfo => _visual; |
|||
public int SampleCount { get; } |
|||
public int StencilSize { get; } |
|||
|
|||
public GlxContext ImmediateContext { get; } |
|||
public GlxContext DeferredContext { get; } |
|||
public GlxInterface Glx { get; } = new GlxInterface(); |
|||
public GlxDisplay(X11Info x11) |
|||
{ |
|||
_x11 = x11; |
|||
|
|||
var baseAttribs = new[] |
|||
{ |
|||
GLX_X_RENDERABLE, 1, |
|||
GLX_RENDER_TYPE, GLX_RGBA_BIT, |
|||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT, |
|||
GLX_DOUBLEBUFFER, 1, |
|||
GLX_RED_SIZE, 8, |
|||
GLX_GREEN_SIZE, 8, |
|||
GLX_BLUE_SIZE, 8, |
|||
GLX_ALPHA_SIZE, 8, |
|||
GLX_DEPTH_SIZE, 1, |
|||
GLX_STENCIL_SIZE, 8, |
|||
|
|||
}; |
|||
|
|||
foreach (var attribs in new[] |
|||
{ |
|||
//baseAttribs.Concat(multiattribs),
|
|||
baseAttribs, |
|||
}) |
|||
{ |
|||
var ptr = Glx.ChooseFBConfig(_x11.Display, x11.DefaultScreen, |
|||
attribs, out var count); |
|||
for (var c = 0 ; c < count; c++) |
|||
{ |
|||
|
|||
var visual = Glx.GetVisualFromFBConfig(_x11.Display, ptr[c]); |
|||
// We prefer 32 bit visuals
|
|||
if (_fbconfig == IntPtr.Zero || visual->depth == 32) |
|||
{ |
|||
_fbconfig = ptr[c]; |
|||
_visual = visual; |
|||
if(visual->depth == 32) |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (_fbconfig != IntPtr.Zero) |
|||
break; |
|||
} |
|||
|
|||
if (_fbconfig == IntPtr.Zero) |
|||
throw new OpenGlException("Unable to choose FBConfig"); |
|||
|
|||
if (_visual == null) |
|||
throw new OpenGlException("Unable to get visual info from FBConfig"); |
|||
if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0) |
|||
SampleCount = samples; |
|||
if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0) |
|||
StencilSize = stencil; |
|||
|
|||
var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] |
|||
{ |
|||
GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 |
|||
})).ToList(); |
|||
|
|||
XLib.XFlush(_x11.Display); |
|||
|
|||
ImmediateContext = CreateContext(pbuffers[0],null); |
|||
DeferredContext = CreateContext(pbuffers[1], ImmediateContext); |
|||
ImmediateContext.MakeCurrent(); |
|||
var err = Glx.GetError(); |
|||
|
|||
GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress); |
|||
if (GlInterface.Version == null) |
|||
throw new OpenGlException("GL version string is null, aborting"); |
|||
} |
|||
|
|||
public void ClearContext() => Glx.MakeContextCurrent(_x11.Display, |
|||
IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); |
|||
|
|||
public GlxContext CreateContext(IGlContext share) => CreateContext(IntPtr.Zero, share); |
|||
public GlxContext CreateContext(IntPtr defaultXid, IGlContext share) |
|||
{ |
|||
var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; |
|||
IntPtr handle = default; |
|||
foreach (var ver in new[] |
|||
{ |
|||
new Version(4, 0), new Version(3, 2), |
|||
new Version(3, 0), new Version(2, 0) |
|||
}) |
|||
{ |
|||
|
|||
var attrs = new[] |
|||
{ |
|||
GLX_CONTEXT_MAJOR_VERSION_ARB, ver.Major, |
|||
GLX_CONTEXT_MINOR_VERSION_ARB, ver.Minor, |
|||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, |
|||
0 |
|||
}; |
|||
try |
|||
{ |
|||
handle = Glx.CreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs); |
|||
if (handle != IntPtr.Zero) |
|||
break; |
|||
} |
|||
catch |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (handle == IntPtr.Zero) |
|||
throw new OpenGlException("Unable to create direct GLX context"); |
|||
return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid); |
|||
} |
|||
|
|||
public void SwapBuffers(IntPtr xid) => Glx.SwapBuffers(_x11.Display, xid); |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System; |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxGlPlatformSurface: IGlPlatformSurface |
|||
{ |
|||
|
|||
private readonly GlxDisplay _display; |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public GlxGlPlatformSurface(GlxDisplay display, GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_display = display; |
|||
_context = context; |
|||
_info = info; |
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
|||
{ |
|||
return new RenderTarget(_context, _info); |
|||
} |
|||
|
|||
class RenderTarget : IGlPlatformSurfaceRenderTarget |
|||
{ |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public RenderTarget(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_context = context; |
|||
_info = info; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
// No-op
|
|||
} |
|||
|
|||
public IGlPlatformSurfaceRenderingSession BeginDraw() |
|||
{ |
|||
var l = _context.Lock(); |
|||
try |
|||
{ |
|||
_context.MakeCurrent(_info.Handle); |
|||
return new Session(_context, _info, l); |
|||
} |
|||
catch |
|||
{ |
|||
l.Dispose(); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
class Session : IGlPlatformSurfaceRenderingSession |
|||
{ |
|||
private readonly GlxContext _context; |
|||
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
|||
private IDisposable _lock; |
|||
|
|||
public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, |
|||
IDisposable @lock) |
|||
{ |
|||
_context = context; |
|||
_info = info; |
|||
_lock = @lock; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_context.Display.GlInterface.Flush(); |
|||
_context.Glx.WaitGL(); |
|||
_context.Display.SwapBuffers(_info.Handle); |
|||
_context.Glx.WaitX(); |
|||
_context.Display.ClearContext(); |
|||
_lock.Dispose(); |
|||
} |
|||
|
|||
public IGlDisplay Display => _context.Display; |
|||
public PixelSize Size => _info.Size; |
|||
public double Scaling => _info.Scaling; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.X11.Glx |
|||
{ |
|||
class GlxGlPlatformFeature : IWindowingPlatformGlFeature |
|||
{ |
|||
public GlxDisplay Display { get; private set; } |
|||
public IGlContext ImmediateContext { get; private set; } |
|||
public GlxContext DeferredContext { get; private set; } |
|||
|
|||
public static bool TryInitialize(X11Info x11) |
|||
{ |
|||
var feature = TryCreate(x11); |
|||
if (feature != null) |
|||
{ |
|||
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public static GlxGlPlatformFeature TryCreate(X11Info x11) |
|||
{ |
|||
try |
|||
{ |
|||
var disp = new GlxDisplay(x11); |
|||
return new GlxGlPlatformFeature |
|||
{ |
|||
Display = disp, |
|||
ImmediateContext = disp.ImmediateContext, |
|||
DeferredContext = disp.DeferredContext |
|||
}; |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.Error("OpenGL", null, "Unable to initialize GLX-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Platform; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Platform; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.X11 |
|||
{ |
|||
class PlatformSettingsStub : IPlatformSettings |
|||
{ |
|||
public Size DoubleClickSize { get; } = new Size(2, 2); |
|||
public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500); |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
// Permission is hereby granted, free of charge, to any person obtaining
|
|||
// a copy of this software and associated documentation files (the
|
|||
// "Software"), to deal in the Software without restriction, including
|
|||
// without limitation the rights to use, copy, modify, merge, publish,
|
|||
// distribute, sublicense, and/or sell copies of the Software, and to
|
|||
// permit persons to whom the Software is furnished to do so, subject to
|
|||
// the following conditions:
|
|||
//
|
|||
// The above copyright notice and this permission notice shall be
|
|||
// included in all copies or substantial portions of the Software.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
//
|
|||
// Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
|
|||
//
|
|||
//
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using static Avalonia.X11.XLib; |
|||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
|||
// ReSharper disable IdentifierTypo
|
|||
// ReSharper disable MemberCanBePrivate.Global
|
|||
// ReSharper disable UnusedMember.Global
|
|||
// ReSharper disable CommentTypo
|
|||
// ReSharper disable ArrangeThisQualifier
|
|||
// ReSharper disable NotAccessedField.Global
|
|||
// ReSharper disable InconsistentNaming
|
|||
// ReSharper disable StringLiteralTypo
|
|||
#pragma warning disable 649
|
|||
|
|||
namespace Avalonia.X11 |
|||
{ |
|||
|
|||
internal class X11Atoms |
|||
{ |
|||
|
|||
// Our atoms
|
|||
public readonly IntPtr AnyPropertyType = (IntPtr)0; |
|||
public readonly IntPtr XA_PRIMARY = (IntPtr)1; |
|||
public readonly IntPtr XA_SECONDARY = (IntPtr)2; |
|||
public readonly IntPtr XA_ARC = (IntPtr)3; |
|||
public readonly IntPtr XA_ATOM = (IntPtr)4; |
|||
public readonly IntPtr XA_BITMAP = (IntPtr)5; |
|||
public readonly IntPtr XA_CARDINAL = (IntPtr)6; |
|||
public readonly IntPtr XA_COLORMAP = (IntPtr)7; |
|||
public readonly IntPtr XA_CURSOR = (IntPtr)8; |
|||
public readonly IntPtr XA_CUT_BUFFER0 = (IntPtr)9; |
|||
public readonly IntPtr XA_CUT_BUFFER1 = (IntPtr)10; |
|||
public readonly IntPtr XA_CUT_BUFFER2 = (IntPtr)11; |
|||
public readonly IntPtr XA_CUT_BUFFER3 = (IntPtr)12; |
|||
public readonly IntPtr XA_CUT_BUFFER4 = (IntPtr)13; |
|||
public readonly IntPtr XA_CUT_BUFFER5 = (IntPtr)14; |
|||
public readonly IntPtr XA_CUT_BUFFER6 = (IntPtr)15; |
|||
public readonly IntPtr XA_CUT_BUFFER7 = (IntPtr)16; |
|||
public readonly IntPtr XA_DRAWABLE = (IntPtr)17; |
|||
public readonly IntPtr XA_FONT = (IntPtr)18; |
|||
public readonly IntPtr XA_INTEGER = (IntPtr)19; |
|||
public readonly IntPtr XA_PIXMAP = (IntPtr)20; |
|||
public readonly IntPtr XA_POINT = (IntPtr)21; |
|||
public readonly IntPtr XA_RECTANGLE = (IntPtr)22; |
|||
public readonly IntPtr XA_RESOURCE_MANAGER = (IntPtr)23; |
|||
public readonly IntPtr XA_RGB_COLOR_MAP = (IntPtr)24; |
|||
public readonly IntPtr XA_RGB_BEST_MAP = (IntPtr)25; |
|||
public readonly IntPtr XA_RGB_BLUE_MAP = (IntPtr)26; |
|||
public readonly IntPtr XA_RGB_DEFAULT_MAP = (IntPtr)27; |
|||
public readonly IntPtr XA_RGB_GRAY_MAP = (IntPtr)28; |
|||
public readonly IntPtr XA_RGB_GREEN_MAP = (IntPtr)29; |
|||
public readonly IntPtr XA_RGB_RED_MAP = (IntPtr)30; |
|||
public readonly IntPtr XA_STRING = (IntPtr)31; |
|||
public readonly IntPtr XA_VISUALID = (IntPtr)32; |
|||
public readonly IntPtr XA_WINDOW = (IntPtr)33; |
|||
public readonly IntPtr XA_WM_COMMAND = (IntPtr)34; |
|||
public readonly IntPtr XA_WM_HINTS = (IntPtr)35; |
|||
public readonly IntPtr XA_WM_CLIENT_MACHINE = (IntPtr)36; |
|||
public readonly IntPtr XA_WM_ICON_NAME = (IntPtr)37; |
|||
public readonly IntPtr XA_WM_ICON_SIZE = (IntPtr)38; |
|||
public readonly IntPtr XA_WM_NAME = (IntPtr)39; |
|||
public readonly IntPtr XA_WM_NORMAL_HINTS = (IntPtr)40; |
|||
public readonly IntPtr XA_WM_SIZE_HINTS = (IntPtr)41; |
|||
public readonly IntPtr XA_WM_ZOOM_HINTS = (IntPtr)42; |
|||
public readonly IntPtr XA_MIN_SPACE = (IntPtr)43; |
|||
public readonly IntPtr XA_NORM_SPACE = (IntPtr)44; |
|||
public readonly IntPtr XA_MAX_SPACE = (IntPtr)45; |
|||
public readonly IntPtr XA_END_SPACE = (IntPtr)46; |
|||
public readonly IntPtr XA_SUPERSCRIPT_X = (IntPtr)47; |
|||
public readonly IntPtr XA_SUPERSCRIPT_Y = (IntPtr)48; |
|||
public readonly IntPtr XA_SUBSCRIPT_X = (IntPtr)49; |
|||
public readonly IntPtr XA_SUBSCRIPT_Y = (IntPtr)50; |
|||
public readonly IntPtr XA_UNDERLINE_POSITION = (IntPtr)51; |
|||
public readonly IntPtr XA_UNDERLINE_THICKNESS = (IntPtr)52; |
|||
public readonly IntPtr XA_STRIKEOUT_ASCENT = (IntPtr)53; |
|||
public readonly IntPtr XA_STRIKEOUT_DESCENT = (IntPtr)54; |
|||
public readonly IntPtr XA_ITALIC_ANGLE = (IntPtr)55; |
|||
public readonly IntPtr XA_X_HEIGHT = (IntPtr)56; |
|||
public readonly IntPtr XA_QUAD_WIDTH = (IntPtr)57; |
|||
public readonly IntPtr XA_WEIGHT = (IntPtr)58; |
|||
public readonly IntPtr XA_POINT_SIZE = (IntPtr)59; |
|||
public readonly IntPtr XA_RESOLUTION = (IntPtr)60; |
|||
public readonly IntPtr XA_COPYRIGHT = (IntPtr)61; |
|||
public readonly IntPtr XA_NOTICE = (IntPtr)62; |
|||
public readonly IntPtr XA_FONT_NAME = (IntPtr)63; |
|||
public readonly IntPtr XA_FAMILY_NAME = (IntPtr)64; |
|||
public readonly IntPtr XA_FULL_NAME = (IntPtr)65; |
|||
public readonly IntPtr XA_CAP_HEIGHT = (IntPtr)66; |
|||
public readonly IntPtr XA_WM_CLASS = (IntPtr)67; |
|||
public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68; |
|||
|
|||
public readonly IntPtr WM_PROTOCOLS; |
|||
public readonly IntPtr WM_DELETE_WINDOW; |
|||
public readonly IntPtr WM_TAKE_FOCUS; |
|||
public readonly IntPtr _NET_SUPPORTED; |
|||
public readonly IntPtr _NET_CLIENT_LIST; |
|||
public readonly IntPtr _NET_NUMBER_OF_DESKTOPS; |
|||
public readonly IntPtr _NET_DESKTOP_GEOMETRY; |
|||
public readonly IntPtr _NET_DESKTOP_VIEWPORT; |
|||
public readonly IntPtr _NET_CURRENT_DESKTOP; |
|||
public readonly IntPtr _NET_DESKTOP_NAMES; |
|||
public readonly IntPtr _NET_ACTIVE_WINDOW; |
|||
public readonly IntPtr _NET_WORKAREA; |
|||
public readonly IntPtr _NET_SUPPORTING_WM_CHECK; |
|||
public readonly IntPtr _NET_VIRTUAL_ROOTS; |
|||
public readonly IntPtr _NET_DESKTOP_LAYOUT; |
|||
public readonly IntPtr _NET_SHOWING_DESKTOP; |
|||
public readonly IntPtr _NET_CLOSE_WINDOW; |
|||
public readonly IntPtr _NET_MOVERESIZE_WINDOW; |
|||
public readonly IntPtr _NET_WM_MOVERESIZE; |
|||
public readonly IntPtr _NET_RESTACK_WINDOW; |
|||
public readonly IntPtr _NET_REQUEST_FRAME_EXTENTS; |
|||
public readonly IntPtr _NET_WM_NAME; |
|||
public readonly IntPtr _NET_WM_VISIBLE_NAME; |
|||
public readonly IntPtr _NET_WM_ICON_NAME; |
|||
public readonly IntPtr _NET_WM_VISIBLE_ICON_NAME; |
|||
public readonly IntPtr _NET_WM_DESKTOP; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE; |
|||
public readonly IntPtr _NET_WM_STATE; |
|||
public readonly IntPtr _NET_WM_ALLOWED_ACTIONS; |
|||
public readonly IntPtr _NET_WM_STRUT; |
|||
public readonly IntPtr _NET_WM_STRUT_PARTIAL; |
|||
public readonly IntPtr _NET_WM_ICON_GEOMETRY; |
|||
public readonly IntPtr _NET_WM_ICON; |
|||
public readonly IntPtr _NET_WM_PID; |
|||
public readonly IntPtr _NET_WM_HANDLED_ICONS; |
|||
public readonly IntPtr _NET_WM_USER_TIME; |
|||
public readonly IntPtr _NET_FRAME_EXTENTS; |
|||
public readonly IntPtr _NET_WM_PING; |
|||
public readonly IntPtr _NET_WM_SYNC_REQUEST; |
|||
public readonly IntPtr _NET_SYSTEM_TRAY_S; |
|||
public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION; |
|||
public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; |
|||
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ; |
|||
public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT; |
|||
public readonly IntPtr _XEMBED; |
|||
public readonly IntPtr _XEMBED_INFO; |
|||
public readonly IntPtr _MOTIF_WM_HINTS; |
|||
public readonly IntPtr _NET_WM_STATE_SKIP_TASKBAR; |
|||
public readonly IntPtr _NET_WM_STATE_ABOVE; |
|||
public readonly IntPtr _NET_WM_STATE_MODAL; |
|||
public readonly IntPtr _NET_WM_STATE_HIDDEN; |
|||
public readonly IntPtr _NET_WM_CONTEXT_HELP; |
|||
public readonly IntPtr _NET_WM_WINDOW_OPACITY; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_DESKTOP; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_DOCK; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_TOOLBAR; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_MENU; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_UTILITY; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_SPLASH; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_DIALOG; |
|||
public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL; |
|||
public readonly IntPtr CLIPBOARD; |
|||
public readonly IntPtr CLIPBOARD_MANAGER; |
|||
public readonly IntPtr SAVE_TARGETS; |
|||
public readonly IntPtr MULTIPLE; |
|||
public readonly IntPtr PRIMARY; |
|||
public readonly IntPtr OEMTEXT; |
|||
public readonly IntPtr UNICODETEXT; |
|||
public readonly IntPtr TARGETS; |
|||
public readonly IntPtr UTF8_STRING; |
|||
public readonly IntPtr UTF16_STRING; |
|||
public readonly IntPtr ATOM_PAIR; |
|||
|
|||
|
|||
public X11Atoms(IntPtr display) |
|||
{ |
|||
|
|||
// make sure this array stays in sync with the statements below
|
|||
|
|||
var fields = typeof(X11Atoms).GetFields() |
|||
.Where(f => f.FieldType == typeof(IntPtr) && (IntPtr)f.GetValue(this) == IntPtr.Zero).ToArray(); |
|||
var atomNames = fields.Select(f => f.Name).ToArray(); |
|||
|
|||
IntPtr[] atoms = new IntPtr [atomNames.Length]; |
|||
; |
|||
|
|||
XInternAtoms(display, atomNames, atomNames.Length, true, atoms); |
|||
|
|||
for (var c = 0; c < fields.Length; c++) |
|||
fields[c].SetValue(this, atoms[c]); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,234 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Input.Platform; |
|||
using static Avalonia.X11.XLib; |
|||
namespace Avalonia.X11 |
|||
{ |
|||
class X11Clipboard : IClipboard |
|||
{ |
|||
private readonly X11Info _x11; |
|||
private string _storedString; |
|||
private IntPtr _handle; |
|||
private TaskCompletionSource<IntPtr[]> _requestedFormatsTcs; |
|||
private TaskCompletionSource<string> _requestedTextTcs; |
|||
private readonly IntPtr[] _textAtoms; |
|||
private readonly IntPtr _avaloniaSaveTargetsAtom; |
|||
|
|||
public X11Clipboard(AvaloniaX11Platform platform) |
|||
{ |
|||
_x11 = platform.Info; |
|||
_handle = CreateEventWindow(platform, OnEvent); |
|||
_avaloniaSaveTargetsAtom = XInternAtom(_x11.Display, "AVALONIA_SAVE_TARGETS_PROPERTY_ATOM", false); |
|||
_textAtoms = new[] |
|||
{ |
|||
_x11.Atoms.XA_STRING, |
|||
_x11.Atoms.OEMTEXT, |
|||
_x11.Atoms.UTF8_STRING, |
|||
_x11.Atoms.UTF16_STRING |
|||
}.Where(a => a != IntPtr.Zero).ToArray(); |
|||
} |
|||
|
|||
Encoding GetStringEncoding(IntPtr atom) |
|||
{ |
|||
return (atom == _x11.Atoms.XA_STRING |
|||
|| atom == _x11.Atoms.OEMTEXT) |
|||
? Encoding.ASCII |
|||
: atom == _x11.Atoms.UTF8_STRING |
|||
? Encoding.UTF8 |
|||
: atom == _x11.Atoms.UTF16_STRING |
|||
? Encoding.Unicode |
|||
: null; |
|||
} |
|||
|
|||
private unsafe void OnEvent(XEvent ev) |
|||
{ |
|||
if (ev.type == XEventName.SelectionRequest) |
|||
{ |
|||
var sel = ev.SelectionRequestEvent; |
|||
var resp = new XEvent |
|||
{ |
|||
SelectionEvent = |
|||
{ |
|||
type = XEventName.SelectionNotify, |
|||
send_event = true, |
|||
display = _x11.Display, |
|||
selection = sel.selection, |
|||
target = sel.target, |
|||
requestor = sel.requestor, |
|||
time = sel.time, |
|||
property = IntPtr.Zero |
|||
} |
|||
}; |
|||
if (sel.selection == _x11.Atoms.CLIPBOARD) |
|||
{ |
|||
resp.SelectionEvent.property = WriteTargetToProperty(sel.target, sel.requestor, sel.property); |
|||
} |
|||
|
|||
XSendEvent(_x11.Display, sel.requestor, false, new IntPtr((int)EventMask.NoEventMask), ref resp); |
|||
} |
|||
|
|||
IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr property) |
|||
{ |
|||
Encoding textEnc; |
|||
if (target == _x11.Atoms.TARGETS) |
|||
{ |
|||
var atoms = _textAtoms; |
|||
atoms = atoms.Concat(new[] {_x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE}) |
|||
.ToArray(); |
|||
XChangeProperty(_x11.Display, window, property, |
|||
target, 32, PropertyMode.Replace, atoms, atoms.Length); |
|||
return property; |
|||
} |
|||
else if(target == _x11.Atoms.SAVE_TARGETS && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) |
|||
{ |
|||
return property; |
|||
} |
|||
else if ((textEnc = GetStringEncoding(target)) != null) |
|||
{ |
|||
|
|||
var data = textEnc.GetBytes(_storedString ?? ""); |
|||
fixed (void* pdata = data) |
|||
XChangeProperty(_x11.Display, window, property, target, 8, |
|||
PropertyMode.Replace, |
|||
pdata, data.Length); |
|||
return property; |
|||
} |
|||
else if (target == _x11.Atoms.MULTIPLE && _x11.Atoms.MULTIPLE != IntPtr.Zero) |
|||
{ |
|||
XGetWindowProperty(_x11.Display, window, property, IntPtr.Zero, new IntPtr(0x7fffffff), false, |
|||
_x11.Atoms.ATOM_PAIR, out _, out var actualFormat, out var nitems, out _, out var prop); |
|||
if (nitems == IntPtr.Zero) |
|||
return IntPtr.Zero; |
|||
if (actualFormat == 32) |
|||
{ |
|||
var data = (IntPtr*)prop.ToPointer(); |
|||
for (var c = 0; c < nitems.ToInt32(); c += 2) |
|||
{ |
|||
var subTarget = data[c]; |
|||
var subProp = data[c + 1]; |
|||
var converted = WriteTargetToProperty(subTarget, window, subProp); |
|||
data[c + 1] = converted; |
|||
} |
|||
|
|||
XChangeProperty(_x11.Display, window, property, _x11.Atoms.ATOM_PAIR, 32, PropertyMode.Replace, |
|||
prop.ToPointer(), nitems.ToInt32()); |
|||
} |
|||
|
|||
XFree(prop); |
|||
|
|||
return property; |
|||
} |
|||
else |
|||
return IntPtr.Zero; |
|||
} |
|||
|
|||
if (ev.type == XEventName.SelectionNotify && ev.SelectionEvent.selection == _x11.Atoms.CLIPBOARD) |
|||
{ |
|||
var sel = ev.SelectionEvent; |
|||
if (sel.property == IntPtr.Zero) |
|||
{ |
|||
_requestedFormatsTcs?.TrySetResult(null); |
|||
_requestedTextTcs?.TrySetResult(null); |
|||
} |
|||
XGetWindowProperty(_x11.Display, _handle, sel.property, IntPtr.Zero, new IntPtr (0x7fffffff), true, (IntPtr)Atom.AnyPropertyType, |
|||
out var actualAtom, out var actualFormat, out var nitems, out var bytes_after, out var prop); |
|||
Encoding textEnc = null; |
|||
if (nitems == IntPtr.Zero) |
|||
{ |
|||
_requestedFormatsTcs?.TrySetResult(null); |
|||
_requestedTextTcs?.TrySetResult(null); |
|||
} |
|||
else |
|||
{ |
|||
if (sel.property == _x11.Atoms.TARGETS) |
|||
{ |
|||
if (actualFormat != 32) |
|||
_requestedFormatsTcs?.TrySetResult(null); |
|||
else |
|||
{ |
|||
var formats = new IntPtr[nitems.ToInt32()]; |
|||
Marshal.Copy(prop, formats, 0, formats.Length); |
|||
_requestedFormatsTcs?.TrySetResult(formats); |
|||
} |
|||
} |
|||
else if ((textEnc = GetStringEncoding(sel.property)) != null) |
|||
{ |
|||
var text = textEnc.GetString((byte*)prop.ToPointer(), nitems.ToInt32()); |
|||
_requestedTextTcs?.TrySetResult(text); |
|||
} |
|||
} |
|||
|
|||
XFree(prop); |
|||
} |
|||
} |
|||
|
|||
Task<IntPtr[]> SendFormatRequest() |
|||
{ |
|||
if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted) |
|||
_requestedFormatsTcs = new TaskCompletionSource<IntPtr[]>(); |
|||
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, _x11.Atoms.TARGETS, _x11.Atoms.TARGETS, _handle, |
|||
IntPtr.Zero); |
|||
return _requestedFormatsTcs.Task; |
|||
} |
|||
|
|||
Task<string> SendTextRequest(IntPtr format) |
|||
{ |
|||
if (_requestedTextTcs == null || _requestedFormatsTcs.Task.IsCompleted) |
|||
_requestedTextTcs = new TaskCompletionSource<string>(); |
|||
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, format, format, _handle, IntPtr.Zero); |
|||
return _requestedTextTcs.Task; |
|||
} |
|||
|
|||
public async Task<string> GetTextAsync() |
|||
{ |
|||
if (XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) == IntPtr.Zero) |
|||
return null; |
|||
var res = await SendFormatRequest(); |
|||
var target = _x11.Atoms.UTF8_STRING; |
|||
if (res != null) |
|||
{ |
|||
var preferredFormats = new[] {_x11.Atoms.UTF16_STRING, _x11.Atoms.UTF8_STRING, _x11.Atoms.XA_STRING}; |
|||
foreach (var pf in preferredFormats) |
|||
if (res.Contains(pf)) |
|||
{ |
|||
target = pf; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return await SendTextRequest(target); |
|||
} |
|||
|
|||
void StoreAtomsInClipboardManager(IntPtr[] atoms) |
|||
{ |
|||
if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) |
|||
{ |
|||
var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); |
|||
if (clipboardManager != IntPtr.Zero) |
|||
{ |
|||
XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, |
|||
PropertyMode.Replace, |
|||
atoms, atoms.Length); |
|||
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER, _x11.Atoms.SAVE_TARGETS, |
|||
_avaloniaSaveTargetsAtom, _handle, IntPtr.Zero); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Task SetTextAsync(string text) |
|||
{ |
|||
_storedString = text; |
|||
XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); |
|||
StoreAtomsInClipboardManager(_textAtoms); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task ClearAsync() |
|||
{ |
|||
return SetTextAsync(null); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue