Browse Source

Nullable annotations for X11 (#17814)

* Nullable annotations for X11

* Address review
pull/17948/head
Julien Lebosquain 1 year ago
committed by GitHub
parent
commit
f2f017c9e1
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      src/Avalonia.X11/Avalonia.X11.csproj
  2. 3
      src/Avalonia.X11/Dispatching/GLibDispatcherImpl.cs
  3. 4
      src/Avalonia.X11/Dispatching/X11PlatformThreading.cs
  4. 3
      src/Avalonia.X11/Glx/Glx.cs
  5. 2
      src/Avalonia.X11/Glx/GlxContext.cs
  6. 10
      src/Avalonia.X11/Glx/GlxDisplay.cs
  7. 2
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  8. 16
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  9. 10
      src/Avalonia.X11/Interop/Glib.cs
  10. 6
      src/Avalonia.X11/NativeDialogs/Gtk.cs
  11. 5
      src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
  12. 1
      src/Avalonia.X11/SMLib.cs
  13. 18
      src/Avalonia.X11/Screens/X11Screen.Providers.cs
  14. 1
      src/Avalonia.X11/Screens/X11Screens.Scaling.cs
  15. 5
      src/Avalonia.X11/Screens/X11Screens.cs
  16. 2
      src/Avalonia.X11/TransparencyHelper.cs
  17. 2
      src/Avalonia.X11/Vulkan/VulkanSupport.cs
  18. 2
      src/Avalonia.X11/X11Atoms.cs
  19. 75
      src/Avalonia.X11/X11Clipboard.cs
  20. 2
      src/Avalonia.X11/X11CursorFactory.cs
  21. 2
      src/Avalonia.X11/X11FramebufferSurface.cs
  22. 17
      src/Avalonia.X11/X11Globals.cs
  23. 2
      src/Avalonia.X11/X11IconLoader.cs
  24. 4
      src/Avalonia.X11/X11Info.cs
  25. 26
      src/Avalonia.X11/X11NativeControlHost.cs
  26. 30
      src/Avalonia.X11/X11Platform.cs
  27. 3
      src/Avalonia.X11/X11PlatformLifetimeEvents.cs
  28. 2
      src/Avalonia.X11/X11Structs.cs
  29. 2
      src/Avalonia.X11/X11Window.Ime.cs
  30. 2
      src/Avalonia.X11/X11Window.Xim.cs
  31. 6
      src/Avalonia.X11/X11Window.cs
  32. 4
      src/Avalonia.X11/X11WindowModes/InputProxyWindowMode.cs
  33. 8
      src/Avalonia.X11/X11WindowModes/WindowMode.cs
  34. 9
      src/Avalonia.X11/X11WindowModes/XEmbedClientWindowMode.cs
  35. 19
      src/Avalonia.X11/XEmbedPlug.cs
  36. 8
      src/Avalonia.X11/XEmbedTrayIconImpl.cs
  37. 73
      src/Avalonia.X11/XI2Manager.cs
  38. 6
      src/Avalonia.X11/XLib.cs
  39. 2
      src/Avalonia.X11/XResources.cs

1
src/Avalonia.X11/Avalonia.X11.csproj

@ -13,6 +13,7 @@
</ItemGroup>
<Import Project="..\..\build\SourceGenerators.props" />
<Import Project="..\..\build\TrimmingEnable.props" />
<Import Project="..\..\build\NullableEnable.props" />
<ItemGroup>
<Compile Remove="..\Shared\SourceGeneratorAttributes.cs"/>

3
src/Avalonia.X11/Dispatching/GLibDispatcherImpl.cs

@ -1,4 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -312,4 +311,4 @@ internal class GlibDispatcherImpl :
}
public X11EventDispatcher EventDispatcher => _x11Events;
}
}

4
src/Avalonia.X11/Dispatching/X11PlatformThreading.cs

@ -186,8 +186,8 @@ namespace Avalonia.X11
public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _mainThread;
public event Action Signaled;
public event Action Timer;
public event Action? Signaled;
public event Action? Timer;
public void UpdateTimer(long? dueTimeInMs)
{

3
src/Avalonia.X11/Glx/Glx.cs

@ -115,6 +115,9 @@ namespace Avalonia.X11.Glx
public string[] GetExtensions(IntPtr display)
{
var s = Marshal.PtrToStringAnsi(QueryExtensionsString(display, 0));
if (string.IsNullOrEmpty(s))
return [];
return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim()).ToArray();

2
src/Avalonia.X11/Glx/GlxContext.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;

10
src/Avalonia.X11/Glx/GlxDisplay.cs

@ -119,13 +119,13 @@ namespace Avalonia.X11.Glx
public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true);
private GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
private GlxContext CreateContext(IntPtr defaultXid, IGlContext? share,
int sampleCount, int stencilSize, bool ownsPBuffer)
{
var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero;
var sharelist = ((GlxContext?)share)?.Handle ?? IntPtr.Zero;
IntPtr handle = default;
GlxContext Create(GlVersion profile)
GlxContext? Create(GlVersion profile)
{
var profileMask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
if (profile.Type == GlProfileType.OpenGL &&
@ -149,7 +149,7 @@ namespace Avalonia.X11.Glx
if (handle != IntPtr.Zero)
{
_version = profile;
return new GlxContext(new GlxInterface(), handle, this, (GlxContext)share, profile,
return new GlxContext(new GlxInterface(), handle, this, (GlxContext?)share, profile,
sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer);
}
@ -162,7 +162,7 @@ namespace Avalonia.X11.Glx
return null;
}
GlxContext rv = null;
GlxContext? rv = null;
if (_version.HasValue)
{
rv = Create(_version.Value);

2
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@ -96,7 +96,7 @@ namespace Avalonia.X11.Glx
public PixelSize Size => _size ?? _info.Size;
public double Scaling => _info.Scaling;
public bool IsYFlipped { get; }
public bool IsYFlipped => false;
}
}
}

16
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@ -8,23 +8,25 @@ namespace Avalonia.X11.Glx
{
internal class GlxPlatformGraphics : IPlatformGraphics
{
public GlxDisplay Display { get; private set; }
public GlxDisplay Display { get; }
public bool CanCreateContexts => true;
public bool CanShareContexts => true;
public bool UsesSharedContext => false;
IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext();
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
public static GlxPlatformGraphics TryCreate(X11Info x11, IList<GlVersion> glProfiles)
public GlxPlatformGraphics(GlxDisplay display)
{
Display = display;
}
public static GlxPlatformGraphics? TryCreate(X11Info x11, IList<GlVersion> glProfiles)
{
try
{
var disp = new GlxDisplay(x11, glProfiles);
return new GlxPlatformGraphics
{
Display = disp
};
return new GlxPlatformGraphics(disp);
}
catch(Exception e)
{

10
src/Avalonia.X11/Interop/Glib.cs

@ -1,9 +1,10 @@
#nullable enable
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Platform.Interop;
using static Avalonia.X11.Interop.Glib;
namespace Avalonia.X11.Interop;
internal static unsafe class Glib
@ -75,7 +76,7 @@ internal static unsafe class Glib
private static readonly GDestroyNotify s_gcHandleDestroyNotify = handle => GCHandle.FromIntPtr(handle).Free();
private static readonly GSourceFunc s_sourceFuncDispatchCallback =
handle => ((Func<bool>)GCHandle.FromIntPtr(handle).Target)() ? 1 : 0;
handle => ((Func<bool>)GCHandle.FromIntPtr(handle).Target!)() ? 1 : 0;
[DllImport(GlibName)]
private static extern uint g_idle_add_full (int priority, GSourceFunc function, IntPtr data, GDestroyNotify notify);
@ -108,7 +109,7 @@ internal static unsafe class Glib
public delegate int GUnixFDSourceFunc(int fd, GIOCondition condition, IntPtr user_data);
private static readonly GUnixFDSourceFunc s_unixFdSourceCallback = (fd, cond, handle) =>
((Func<int, GIOCondition, bool>)GCHandle.FromIntPtr(handle).Target)(fd, cond) ? 1 : 0;
((Func<int, GIOCondition, bool>)GCHandle.FromIntPtr(handle).Target!)(fd, cond) ? 1 : 0;
[DllImport(GlibName)]
public static extern uint g_unix_fd_add_full (int priority,
@ -152,6 +153,7 @@ internal static unsafe class Glib
}
public static IDisposable ConnectSignal<T>(IntPtr obj, string name, T handler)
where T : notnull
{
var handle = GCHandle.Alloc(handler);
var ptr = Marshal.GetFunctionPointerForDelegate(handler);
@ -187,4 +189,4 @@ internal unsafe struct GSList
{
public readonly IntPtr Data;
public readonly GSList* Next;
}
}

6
src/Avalonia.X11/NativeDialogs/Gtk.cs

@ -1,10 +1,10 @@
#nullable enable
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Platform.Interop;
using Avalonia.Threading;
using Avalonia.X11.Dispatching;
using Avalonia.X11.Interop;
// ReSharper disable IdentifierTypo
@ -142,7 +142,7 @@ namespace Avalonia.X11.NativeDialogs
public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid);
static object s_startGtkLock = new();
static Task<bool> s_startGtkTask;
static Task<bool>? s_startGtkTask;
public static Task<bool> StartGtk()
{

5
src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs

@ -1,11 +1,8 @@
#nullable enable
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
using Avalonia.Platform.Storage;

1
src/Avalonia.X11/SMLib.cs

@ -1,4 +1,3 @@
#nullable enable
using System;
using System.Runtime.InteropServices;

18
src/Avalonia.X11/Screens/X11Screen.Providers.cs

@ -1,10 +1,10 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Platform;
using static Avalonia.X11.XLib;
namespace Avalonia.X11.Screens;
internal partial class X11Screens
@ -44,7 +44,7 @@ internal partial class X11Screens
private unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput)
{
if (rrOutput == IntPtr.Zero || x11 == null)
if (rrOutput == IntPtr.Zero)
return null;
var properties = XRRListOutputProperties(x11.Display, rrOutput, out int propertyCount);
var hasEDID = false;
@ -130,7 +130,7 @@ internal partial class X11Screens
{
nint[] ScreenKeys { get; }
event Action? Changed;
X11Screen? CreateScreenFromKey(nint key);
X11Screen CreateScreenFromKey(nint key);
}
internal unsafe struct MonitorInfo
@ -211,20 +211,18 @@ internal partial class X11Screens
}
}
public X11Screen? CreateScreenFromKey(nint key)
public X11Screen CreateScreenFromKey(nint key)
{
var info = MonitorInfos.Where(x => x.Name == key).FirstOrDefault();
var infos = MonitorInfos;
for (var i = 0; i < infos.Length; i++)
{
if (infos[i].Name == key)
{
return new X11Screen(info, _x11, _scalingProvider, i);
return new X11Screen(infos[i], _x11, _scalingProvider, i);
}
}
return null;
throw new ArgumentOutOfRangeException(nameof(key));
}
}
@ -248,7 +246,7 @@ internal partial class X11Screens
private bool UpdateRootWindowGeometry() => XGetGeometry(_info.Display, _info.RootWindow, out _geo);
public X11Screen? CreateScreenFromKey(nint key)
public X11Screen CreateScreenFromKey(nint key)
{
return new FallBackScreen(new PixelRect(0, 0, _geo.width, _geo.height), _info);
}

1
src/Avalonia.X11/Screens/X11Screens.Scaling.cs

@ -1,4 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;

5
src/Avalonia.X11/Screens/X11Screens.cs

@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Platform;
using static Avalonia.X11.Screens.X11Screens;
using static Avalonia.X11.XLib;
namespace Avalonia.X11.Screens
{

2
src/Avalonia.X11/TransparencyHelper.cs

@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using Avalonia.Controls;
#nullable enable
namespace Avalonia.X11
{
internal class TransparencyHelper : IDisposable

2
src/Avalonia.X11/Vulkan/VulkanSupport.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

2
src/Avalonia.X11/X11Atoms.cs

@ -222,7 +222,7 @@ namespace Avalonia.X11
return atom;
}
public string GetAtomName(IntPtr atom)
public string? GetAtomName(IntPtr atom)
{
if (_atomsToNames.TryGetValue(atom, out var rv))
return rv;

75
src/Avalonia.X11/X11Clipboard.cs

@ -12,11 +12,11 @@ namespace Avalonia.X11
internal class X11Clipboard : IClipboard
{
private readonly X11Info _x11;
private IDataObject _storedDataObject;
private IDataObject? _storedDataObject;
private IntPtr _handle;
private TaskCompletionSource<bool> _storeAtomTcs;
private TaskCompletionSource<IntPtr[]> _requestedFormatsTcs;
private TaskCompletionSource<object> _requestedDataTcs;
private TaskCompletionSource<bool>? _storeAtomTcs;
private TaskCompletionSource<IntPtr[]?>? _requestedFormatsTcs;
private TaskCompletionSource<object?>? _requestedDataTcs;
private readonly IntPtr[] _textAtoms;
private readonly IntPtr _avaloniaSaveTargetsAtom;
@ -39,7 +39,7 @@ namespace Avalonia.X11
return _textAtoms.Contains(atom);
}
private Encoding GetStringEncoding(IntPtr atom)
private Encoding? GetStringEncoding(IntPtr atom)
{
return (atom == _x11.Atoms.XA_STRING
|| atom == _x11.Atoms.OEMTEXT)
@ -86,7 +86,7 @@ namespace Avalonia.X11
IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr property)
{
Encoding textEnc;
Encoding? textEnc;
if (target == _x11.Atoms.TARGETS)
{
var atoms = ConvertDataObject(_storedDataObject);
@ -136,9 +136,9 @@ namespace Avalonia.X11
return property;
}
else if(_storedDataObject?.Contains(_x11.Atoms.GetAtomName(target)) == true)
else if(_x11.Atoms.GetAtomName(target) is { } atomName && _storedDataObject?.Contains(atomName) == true)
{
var objValue = _storedDataObject.Get(_x11.Atoms.GetAtomName(target));
var objValue = _storedDataObject.Get(atomName);
if(!(objValue is byte[] bytes))
{
@ -167,7 +167,7 @@ namespace Avalonia.X11
}
XGetWindowProperty(_x11.Display, _handle, sel.property, IntPtr.Zero, new IntPtr (0x7fffffff), true, (IntPtr)Atom.AnyPropertyType,
out var actualTypeAtom, out var actualFormat, out var nitems, out var bytes_after, out var prop);
Encoding textEnc = null;
Encoding? textEnc;
if (nitems == IntPtr.Zero)
{
_requestedFormatsTcs?.TrySetResult(null);
@ -196,7 +196,7 @@ namespace Avalonia.X11
if (actualTypeAtom == _x11.Atoms.INCR)
{
// TODO: Actually implement that monstrosity
_requestedDataTcs.TrySetResult(null);
_requestedDataTcs?.TrySetResult(null);
}
else
{
@ -211,26 +211,26 @@ namespace Avalonia.X11
}
}
private Task<IntPtr[]> SendFormatRequest()
private Task<IntPtr[]?> SendFormatRequest()
{
if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted)
_requestedFormatsTcs = new TaskCompletionSource<IntPtr[]>();
_requestedFormatsTcs = new TaskCompletionSource<IntPtr[]?>();
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, _x11.Atoms.TARGETS, _x11.Atoms.TARGETS, _handle,
IntPtr.Zero);
return _requestedFormatsTcs.Task;
}
private Task<object> SendDataRequest(IntPtr format)
private Task<object?> SendDataRequest(IntPtr format)
{
if (_requestedDataTcs == null || _requestedDataTcs.Task.IsCompleted)
_requestedDataTcs = new TaskCompletionSource<object>();
_requestedDataTcs = new TaskCompletionSource<object?>();
XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, format, format, _handle, IntPtr.Zero);
return _requestedDataTcs.Task;
}
private bool HasOwner => XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) != IntPtr.Zero;
public async Task<string> GetTextAsync()
public async Task<string?> GetTextAsync()
{
if (!HasOwner)
return null;
@ -247,21 +247,26 @@ namespace Avalonia.X11
}
}
return (string)await SendDataRequest(target);
return (string?)await SendDataRequest(target);
}
private IntPtr[] ConvertDataObject(IDataObject data)
private IntPtr[] ConvertDataObject(IDataObject? data)
{
var atoms = new HashSet<IntPtr> { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE };
foreach (var fmt in data.GetDataFormats())
if (data is not null)
{
if (fmt == DataFormats.Text)
foreach (var ta in _textAtoms)
atoms.Add(ta);
else
atoms.Add(_x11.Atoms.GetAtom(fmt));
foreach (var fmt in data.GetDataFormats())
{
if (fmt == DataFormats.Text)
foreach (var ta in _textAtoms)
atoms.Add(ta);
else
atoms.Add(_x11.Atoms.GetAtom(fmt));
}
}
return atoms.ToArray();
}
@ -287,10 +292,13 @@ namespace Avalonia.X11
return Task.CompletedTask;
}
public Task SetTextAsync(string text)
public Task SetTextAsync(string? text)
{
var data = new DataObject();
data.Set(DataFormats.Text, text);
if (text is not null)
data.Set(DataFormats.Text, text);
return SetDataObjectAsync(data);
}
@ -309,19 +317,26 @@ namespace Avalonia.X11
public async Task<string[]> GetFormatsAsync()
{
if (!HasOwner)
return null;
return [];
var res = await SendFormatRequest();
if (res == null)
return null;
return [];
var rv = new List<string>();
if (_textAtoms.Any(res.Contains))
rv.Add(DataFormats.Text);
foreach (var t in res)
rv.Add(_x11.Atoms.GetAtomName(t));
{
if (_x11.Atoms.GetAtomName(t) is { } atomName)
rv.Add(atomName);
}
return rv.ToArray();
}
public async Task<object> GetDataAsync(string format)
public async Task<object?> GetDataAsync(string format)
{
if (!HasOwner)
return null;
@ -330,7 +345,7 @@ namespace Avalonia.X11
var formatAtom = _x11.Atoms.GetAtom(format);
var res = await SendFormatRequest();
if (!res.Contains(formatAtom))
if (res is null || !res.Contains(formatAtom))
return null;
return await SendDataRequest(formatAtom);

2
src/Avalonia.X11/X11CursorFactory.cs

@ -6,8 +6,6 @@ using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Platform.Internal;
#nullable enable
namespace Avalonia.X11
{
internal partial class X11CursorFactory : ICursorFactory

2
src/Avalonia.X11/X11FramebufferSurface.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;

17
src/Avalonia.X11/X11Globals.cs

@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
{
internal unsafe class X11Globals
@ -13,14 +12,14 @@ namespace Avalonia.X11
private readonly IntPtr _rootWindow;
private readonly IntPtr _compositingAtom;
private string _wmName;
private string? _wmName;
private IntPtr _compositionAtomOwner;
private bool _isCompositionEnabled;
public event Action WindowManagerChanged;
public event Action CompositionChanged;
public event Action<IntPtr> RootPropertyChanged;
public event Action RootGeometryChangedChanged;
public event Action? WindowManagerChanged;
public event Action? CompositionChanged;
public event Action<IntPtr>? RootPropertyChanged;
public event Action? RootGeometryChangedChanged;
public X11Globals(AvaloniaX11Platform plat)
{
@ -36,7 +35,7 @@ namespace Avalonia.X11
UpdateCompositingAtomOwner();
}
public string WmName
public string? WmName
{
get => _wmName;
private set
@ -131,7 +130,7 @@ namespace Avalonia.X11
private void UpdateWmName() => WmName = GetWmName();
private string GetWmName()
private string? GetWmName()
{
var wm = GetSupportingWmCheck(_rootWindow);
if (wm == IntPtr.Zero || wm != GetSupportingWmCheck(wm))

2
src/Avalonia.X11/X11IconLoader.cs

@ -33,7 +33,7 @@ namespace Avalonia.X11
{
private int _width;
private int _height;
private uint[] _bdata;
private uint[]? _bdata;
public UIntPtr[] Data { get; }
public X11IconData(Bitmap bitmap)

4
src/Avalonia.X11/X11Info.cs

@ -19,13 +19,13 @@ namespace Avalonia.X11
public int RandrEventBase { get; }
public int RandrErrorBase { get; }
public Version RandrVersion { get; }
public Version? RandrVersion { get; }
public int XInputOpcode { get; }
public int XInputEventBase { get; }
public int XInputErrorBase { get; }
public Version XInputVersion { get; }
public Version? XInputVersion { get; }
public IntPtr LastActivityTimestamp { get; set; }
public XVisualInfo? TransparentVisualInfo { get; }

26
src/Avalonia.X11/X11NativeControlHost.cs

@ -1,8 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.VisualTree;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
{
// TODO: Actually implement XEmbed instead of simply using XReparentWindow
@ -25,7 +26,7 @@ namespace Avalonia.X11
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create)
{
var holder = new DumbWindow(_platform.Info, true, Window.Handle.Handle);
Attachment attachment = null;
Attachment? attachment = null;
try
{
var child = create(holder);
@ -38,7 +39,7 @@ namespace Avalonia.X11
catch
{
attachment?.Dispose();
holder?.Destroy();
holder.Destroy();
throw;
}
}
@ -101,9 +102,9 @@ namespace Avalonia.X11
{
private readonly IntPtr _display;
private readonly IntPtr _orphanedWindow;
private DumbWindow _holder;
private IPlatformHandle _child;
private X11NativeControlHost _attachedTo;
private DumbWindow? _holder;
private IPlatformHandle? _child;
private X11NativeControlHost? _attachedTo;
private bool _mapped;
public Attachment(IntPtr display, DumbWindow holder, IntPtr orphanedWindow, IPlatformHandle child)
@ -129,20 +130,22 @@ namespace Avalonia.X11
_attachedTo = null;
}
[MemberNotNull(nameof(_child))]
[MemberNotNull(nameof(_holder))]
private void CheckDisposed()
{
if (_child == null)
if (_child is null || _holder is null)
throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment");
}
public INativeControlHostImpl AttachedTo
public INativeControlHostImpl? AttachedTo
{
get => _attachedTo;
set
{
CheckDisposed();
_attachedTo = (X11NativeControlHost)value;
if (value == null)
_attachedTo = (X11NativeControlHost?)value;
if (_attachedTo is null)
{
_mapped = false;
XUnmapWindow(_display, _holder.Handle);
@ -164,7 +167,8 @@ namespace Avalonia.X11
if (_mapped)
{
_mapped = false;
XUnmapWindow(_display, _holder.Handle);
if (_holder is not null)
XUnmapWindow(_display, _holder.Handle);
}
size *= _attachedTo.Window.RenderScaling;

30
src/Avalonia.X11/X11Platform.cs

@ -28,15 +28,15 @@ namespace Avalonia.X11
private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice());
public KeyboardDevice KeyboardDevice => _keyboardDevice.Value;
public Dictionary<IntPtr, X11EventDispatcher.EventHandler> Windows { get; } = new ();
public XI2Manager XI2 { get; private set; }
public X11Info Info { get; private set; }
public X11Screens X11Screens { get; private set; }
public Compositor Compositor { get; private set; }
public IScreenImpl Screens { get; private set; }
public X11PlatformOptions Options { get; private set; }
public XI2Manager? XI2 { get; private set; }
public X11Info Info { get; private set; } = null!;
public X11Screens X11Screens { get; private set; } = null!;
public Compositor Compositor { get; private set; } = null!;
public IScreenImpl Screens { get; private set; } = null!;
public X11PlatformOptions Options { get; private set; } = null!;
public IntPtr OrphanedWindow { get; private set; }
public X11Globals Globals { get; private set; }
public XResources Resources { get; private set; }
public X11Globals Globals { get; private set; } = null!;
public XResources Resources { get; private set; } = null!;
public ManualRawEventGrouperDispatchQueue EventGrouperDispatchQueue { get; } = new();
public void Initialize(X11PlatformOptions options)
@ -90,9 +90,7 @@ namespace Avalonia.X11
Screens = X11Screens = new X11Screens(this);
if (Info.XInputVersion != null)
{
var xi2 = new XI2Manager();
if (xi2.Init(this))
XI2 = xi2;
XI2 = XI2Manager.TryCreate(this);
}
var graphics = InitializeGraphics(options, Info);
@ -108,7 +106,7 @@ namespace Avalonia.X11
public IntPtr DeferredDisplay { get; set; }
public IntPtr Display { get; set; }
private static uint[] X11IconConverter(IWindowIconImpl icon)
private static uint[] X11IconConverter(IWindowIconImpl? icon)
{
if (!(icon is X11IconData x11icon))
return Array.Empty<uint>();
@ -187,7 +185,7 @@ namespace Avalonia.X11
return false;
}
private static IPlatformGraphics InitializeGraphics(X11PlatformOptions opts, X11Info info)
private static IPlatformGraphics? InitializeGraphics(X11PlatformOptions opts, X11Info info)
{
if (opts.RenderingMode is null || !opts.RenderingMode.Any())
{
@ -354,7 +352,7 @@ namespace Avalonia
};
public string WmClass { get; set; }
public string? WmClass { get; set; }
/// <summary>
/// Enables multitouch support. The default value is true.
@ -392,7 +390,7 @@ namespace Avalonia
{
try
{
WmClass = Assembly.GetEntryAssembly()?.GetName()?.Name;
WmClass = Assembly.GetEntryAssembly()?.GetName().Name;
}
catch
{
@ -412,7 +410,7 @@ namespace Avalonia
return builder;
}
public static void InitializeX11Platform(X11PlatformOptions options = null) =>
public static void InitializeX11Platform(X11PlatformOptions? options = null) =>
new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions());
}

3
src/Avalonia.X11/X11PlatformLifetimeEvents.cs

@ -1,4 +1,3 @@
#nullable enable
using System;
using System.Text;
using System.Collections.Concurrent;
@ -232,7 +231,7 @@ namespace Avalonia.X11
{
var e = new ShutdownRequestedEventArgs();
if (_platform.Options?.EnableSessionManagement ?? false)
if (_platform.Options.EnableSessionManagement)
{
ShutdownRequested?.Invoke(this, e);
}

2
src/Avalonia.X11/X11Structs.cs

@ -666,7 +666,7 @@ namespace Avalonia.X11 {
if (!string.IsNullOrEmpty(result)) {
result += ", ";
}
object value = fields [i].GetValue (ev);
var value = fields [i].GetValue(ev);
result += fields [i].Name + "=" + (value == null ? "<null>" : value.ToString ());
}
return type.Name + " (" + result + ")";

2
src/Avalonia.X11/X11Window.Ime.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

2
src/Avalonia.X11/X11Window.Xim.cs

@ -1,5 +1,3 @@
#nullable enable
using System;
using System.Threading.Tasks;
using Avalonia.FreeDesktop;

6
src/Avalonia.X11/X11Window.cs

@ -30,8 +30,6 @@ using Avalonia.Platform.Storage.FileIO;
// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo
#nullable enable
namespace Avalonia.X11
{
internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
@ -95,7 +93,7 @@ namespace Avalonia.X11
_mode = mode;
_mode.Init(this);
_popup = popupParent != null;
_overrideRedirect = _popup || overrideRedirect;
_overrideRedirect = _popup || overrideRedirect;
_x11 = platform.Info;
_mouse = new MouseDevice();
_touch = new TouchDevice();
@ -1256,7 +1254,7 @@ namespace Avalonia.X11
}
}
public void SetWmClass(IntPtr handle, string wmClass)
public void SetWmClass(IntPtr handle, string? wmClass)
{
// See https://tronche.com/gui/x/icccm/sec-4.html#WM_CLASS
// We don't actually parse the application's command line, so we only use RESOURCE_NAME and argv[0]

4
src/Avalonia.X11/X11WindowModes/InputProxyWindowMode.cs

@ -8,7 +8,7 @@ partial class X11Window
{
public class InputProxyWindowMode : DefaultTopLevelWindowMode
{
private X11FocusProxy _focusProxy;
private X11FocusProxy? _focusProxy;
public override void OnHandleCreated(IntPtr handle)
{
@ -52,4 +52,4 @@ partial class X11Window
base.AppendWmProtocols(data);
}
}
}
}

8
src/Avalonia.X11/X11WindowModes/WindowMode.cs

@ -7,10 +7,10 @@ partial class X11Window
{
public abstract class X11WindowMode
{
public X11Window Window { get; private set; }
public X11Window Window { get; private set; } = null!;
protected IntPtr Display;
protected X11Info X11;
protected AvaloniaX11Platform Platform;
protected X11Info X11 = null!;
protected AvaloniaX11Platform Platform = null!;
protected IntPtr Handle => Window._handle;
protected IntPtr RenderHandle => Window._renderHandle;
public virtual bool BlockInput => false;
@ -57,4 +57,4 @@ partial class X11Window
{
}
}
}
}

9
src/Avalonia.X11/X11WindowModes/XEmbedClientWindowMode.cs

@ -1,6 +1,4 @@
#nullable enable
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Input;
@ -73,7 +71,10 @@ partial class X11Window
static XEmbedClientWindowMode()
{
KeyboardDevice.Instance.PropertyChanged += (_, args) =>
if (KeyboardDevice.Instance is not { } keyboardDevice)
return;
keyboardDevice.PropertyChanged += (_, args) =>
{
if (args.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
{
@ -204,4 +205,4 @@ partial class X11Window
(int)(point.Y * Window.RenderScaling))
+ GetWindowOffset();
}
}
}

19
src/Avalonia.X11/XEmbedPlug.cs

@ -9,14 +9,14 @@ namespace Avalonia.X11;
public class XEmbedPlug : IDisposable
{
private EmbeddableControlRoot _root;
private EmbeddableControlRoot? _root;
private Color _backgroundColor;
private readonly X11Info _x11;
private readonly X11Window.XEmbedClientWindowMode _mode;
private XEmbedPlug(IntPtr? parentXid)
{
var platform = AvaloniaLocator.Current.GetService<AvaloniaX11Platform>();
var platform = AvaloniaLocator.Current.GetRequiredService<AvaloniaX11Platform>();
_mode = new X11Window.XEmbedClientWindowMode();
_root = new EmbeddableControlRoot(new X11Window(platform, null, _mode));
_root.Prepare();
@ -28,13 +28,16 @@ public class XEmbedPlug : IDisposable
XLib.XSync(platform.Display, false);
}
private EmbeddableControlRoot Root
=> _root ?? throw new ObjectDisposedException(nameof(XEmbedPlug));
public IntPtr Handle =>
_root?.PlatformImpl!.Handle!.Handle ?? throw new ObjectDisposedException(nameof(XEmbedPlug));
Root.PlatformImpl!.Handle!.Handle;
public object Content
public object? Content
{
get => _root.Content;
set => _root.Content = value;
get => Root.Content;
set => Root.Content = value;
}
public Color BackgroundColor
@ -58,7 +61,7 @@ public class XEmbedPlug : IDisposable
public void ProcessInteractiveResize(PixelSize size)
{
var events = (IX11PlatformDispatcher)AvaloniaLocator.Current.GetService<IDispatcherImpl>();
var events = (IX11PlatformDispatcher)AvaloniaLocator.Current.GetRequiredService<IDispatcherImpl>();
events.EventDispatcher.DispatchX11Events(CancellationToken.None);
_mode.ProcessInteractiveResize(size);
Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender);
@ -78,4 +81,4 @@ public class XEmbedPlug : IDisposable
public static XEmbedPlug Create(IntPtr embedderXid) =>
embedderXid == IntPtr.Zero ? throw new ArgumentException() : new XEmbedPlug(embedderXid);
}
}

8
src/Avalonia.X11/XEmbedTrayIconImpl.cs

@ -26,12 +26,12 @@ namespace Avalonia.X11
NotImplemented();
}
public void SetIcon(IWindowIconImpl icon)
public void SetIcon(IWindowIconImpl? icon)
{
NotImplemented();
}
public void SetToolTipText(string text)
public void SetToolTipText(string? text)
{
NotImplemented();
}
@ -41,7 +41,7 @@ namespace Avalonia.X11
NotImplemented();
}
public INativeMenuExporter MenuExporter { get; }
public Action OnClicked { get; set; }
public INativeMenuExporter? MenuExporter { get; }
public Action? OnClicked { get; set; }
}
}

73
src/Avalonia.X11/XI2Manager.cs

@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
@ -37,8 +33,8 @@ namespace Avalonia.X11
private class DeviceInfo
{
public int Id { get; }
public XIValuatorClassInfo[] Valuators { get; private set; }
public XIScrollClassInfo[] Scrollers { get; private set; }
public XIValuatorClassInfo[] Valuators { get; private set; } = [];
public XIScrollClassInfo[] Scrollers { get; private set; } = [];
public DeviceInfo(XIDeviceInfo info)
{
Id = info.Deviceid;
@ -146,48 +142,47 @@ namespace Avalonia.X11
public XIValuatorClassInfo? TouchMinorXIValuatorClassInfo { get; private set; }
}
private PointerDeviceInfo _pointerDevice;
private AvaloniaX11Platform _platform;
private readonly PointerDeviceInfo _pointerDevice;
private readonly AvaloniaX11Platform _platform;
public bool Init(AvaloniaX11Platform platform)
private XI2Manager(AvaloniaX11Platform platform, PointerDeviceInfo pointerDevice)
{
_platform = platform;
_x11 = platform.Info;
_multitouch = platform.Options?.EnableMultiTouch ?? true;
var devices = (XIDeviceInfo*) XIQueryDevice(_x11.Display,
_multitouch = platform.Options.EnableMultiTouch ?? true;
_pointerDevice = pointerDevice;
}
public static XI2Manager? TryCreate(AvaloniaX11Platform platform)
{
var x11 = platform.Info;
var devices = (XIDeviceInfo*) XIQueryDevice(x11.Display,
(int)XiPredefinedDeviceId.XIAllMasterDevices, out int num);
PointerDeviceInfo? pointerDevice = null;
for (var c = 0; c < num; c++)
{
if (devices[c].Use == XiDeviceType.XIMasterPointer)
{
_pointerDevice = new PointerDeviceInfo(devices[c], _x11);
pointerDevice = new PointerDeviceInfo(devices[c], x11);
break;
}
}
if(_pointerDevice == null)
return false;
/*
int mask = 0;
XISetMask(ref mask, XiEventType.XI_DeviceChanged);
var emask = new XIEventMask
{
Mask = &mask,
Deviceid = _pointerDevice.Id,
MaskLen = XiEventMaskLen
};
if (XISelectEvents(_x11.Display, _x11.RootWindow, &emask, 1) != Status.Success)
return false;
return true;
*/
return XiSelectEvents(_x11.Display, _x11.RootWindow, new Dictionary<int, List<XiEventType>>
{
[_pointerDevice.Id] = new List<XiEventType>
{
XiEventType.XI_DeviceChanged
}
}) == Status.Success;
if (pointerDevice is null)
return null;
var status = XiSelectEvents(
x11.Display,
x11.RootWindow,
new Dictionary<int, List<XiEventType>> { [pointerDevice.Id] = [XiEventType.XI_DeviceChanged] });
if (status != Status.Success)
return null;
return new XI2Manager(platform, pointerDevice);
}
public XEventMask AddWindow(IntPtr xid, IXI2Client window)
@ -309,11 +304,9 @@ namespace Avalonia.X11
{
var pixelPoint = new PixelPoint((int)ev.RootPosition.X, (int)ev.RootPosition.Y);
var screen = _platform.Screens.ScreenFromPoint(pixelPoint);
var screenBoundsFromPoint = screen?.Bounds;
Debug.Assert(screenBoundsFromPoint != null);
if (screenBoundsFromPoint != null)
if (screen?.Bounds is { } screenBoundsFromPoint)
{
screenBounds = screenBoundsFromPoint.Value;
screenBounds = screenBoundsFromPoint;
// As https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html says, using `screenBounds.Width` is not accurate enough.
touchMajor = (touchMajorValue - touchMajorXIValuatorClassInfo.Min) /

6
src/Avalonia.X11/XLib.cs

@ -148,7 +148,7 @@ namespace Avalonia.X11
[DllImport(libX11)]
public static extern IntPtr XGetAtomName(IntPtr display, IntPtr atom);
public static string GetAtomName(IntPtr display, IntPtr atom)
public static string? GetAtomName(IntPtr display, IntPtr atom)
{
var ptr = XGetAtomName(display, atom);
if (ptr == IntPtr.Zero)
@ -511,8 +511,8 @@ namespace Avalonia.X11
[DllImport(libX11)]
public static extern IntPtr XCreateIC(IntPtr xim, string xnClientWindow, IntPtr handle, string xnFocusWindow,
IntPtr value2, string xnInputStyle, IntPtr value3, string xnResourceName, string optionsWmClass,
string xnResourceClass, string wmClass, string xnPreeditAttributes, IntPtr list, IntPtr zero);
IntPtr value2, string xnInputStyle, IntPtr value3, string xnResourceName, string? optionsWmClass,
string xnResourceClass, string? wmClass, string xnPreeditAttributes, IntPtr list, IntPtr zero);
[DllImport(libX11)]
public static extern void XSetICFocus(IntPtr xic);

2
src/Avalonia.X11/XResources.cs

@ -1,8 +1,8 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using static Avalonia.X11.XLib;
namespace Avalonia.X11;
internal class XResources

Loading…
Cancel
Save