Browse Source

Nullable annotations for X11 (#17814)

* Nullable annotations for X11

* Address review
pull/17948/head
Julien Lebosquain 2 years 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> </ItemGroup>
<Import Project="..\..\build\SourceGenerators.props" /> <Import Project="..\..\build\SourceGenerators.props" />
<Import Project="..\..\build\TrimmingEnable.props" /> <Import Project="..\..\build\TrimmingEnable.props" />
<Import Project="..\..\build\NullableEnable.props" />
<ItemGroup> <ItemGroup>
<Compile Remove="..\Shared\SourceGeneratorAttributes.cs"/> <Compile Remove="..\Shared\SourceGeneratorAttributes.cs"/>

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

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -312,4 +311,4 @@ internal class GlibDispatcherImpl :
} }
public X11EventDispatcher EventDispatcher => _x11Events; 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 bool CurrentThreadIsLoopThread => Thread.CurrentThread == _mainThread;
public event Action Signaled; public event Action? Signaled;
public event Action Timer; public event Action? Timer;
public void UpdateTimer(long? dueTimeInMs) 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) public string[] GetExtensions(IntPtr display)
{ {
var s = Marshal.PtrToStringAnsi(QueryExtensionsString(display, 0)); var s = Marshal.PtrToStringAnsi(QueryExtensionsString(display, 0));
if (string.IsNullOrEmpty(s))
return [];
return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim()).ToArray(); .Select(x => x.Trim()).ToArray();

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

@ -1,5 +1,3 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; 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, public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true); 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) int sampleCount, int stencilSize, bool ownsPBuffer)
{ {
var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; var sharelist = ((GlxContext?)share)?.Handle ?? IntPtr.Zero;
IntPtr handle = default; IntPtr handle = default;
GlxContext Create(GlVersion profile) GlxContext? Create(GlVersion profile)
{ {
var profileMask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; var profileMask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
if (profile.Type == GlProfileType.OpenGL && if (profile.Type == GlProfileType.OpenGL &&
@ -149,7 +149,7 @@ namespace Avalonia.X11.Glx
if (handle != IntPtr.Zero) if (handle != IntPtr.Zero)
{ {
_version = profile; _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); sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer);
} }
@ -162,7 +162,7 @@ namespace Avalonia.X11.Glx
return null; return null;
} }
GlxContext rv = null; GlxContext? rv = null;
if (_version.HasValue) if (_version.HasValue)
{ {
rv = Create(_version.Value); 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 PixelSize Size => _size ?? _info.Size;
public double Scaling => _info.Scaling; 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 internal class GlxPlatformGraphics : IPlatformGraphics
{ {
public GlxDisplay Display { get; private set; } public GlxDisplay Display { get; }
public bool CanCreateContexts => true; public bool CanCreateContexts => true;
public bool CanShareContexts => true; public bool CanShareContexts => true;
public bool UsesSharedContext => false; public bool UsesSharedContext => false;
IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext(); IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext();
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); 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 try
{ {
var disp = new GlxDisplay(x11, glProfiles); var disp = new GlxDisplay(x11, glProfiles);
return new GlxPlatformGraphics return new GlxPlatformGraphics(disp);
{
Display = disp
};
} }
catch(Exception e) catch(Exception e)
{ {

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

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

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

@ -1,10 +1,10 @@
#nullable enable
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
using Avalonia.Threading;
using Avalonia.X11.Dispatching;
using Avalonia.X11.Interop; using Avalonia.X11.Interop;
// ReSharper disable IdentifierTypo // 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); public static IntPtr GetForeignWindow(IntPtr xid) => gdk_x11_window_foreign_new_for_display(s_display, xid);
static object s_startGtkLock = new(); static object s_startGtkLock = new();
static Task<bool> s_startGtkTask; static Task<bool>? s_startGtkTask;
public static Task<bool> StartGtk() 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.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;

1
src/Avalonia.X11/SMLib.cs

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

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

@ -1,10 +1,10 @@
#nullable enable
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Platform; using Avalonia.Platform;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11.Screens; namespace Avalonia.X11.Screens;
internal partial class X11Screens internal partial class X11Screens
@ -44,7 +44,7 @@ internal partial class X11Screens
private unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput) private unsafe Size? GetPhysicalMonitorSizeFromEDID(IntPtr rrOutput)
{ {
if (rrOutput == IntPtr.Zero || x11 == null) if (rrOutput == IntPtr.Zero)
return null; return null;
var properties = XRRListOutputProperties(x11.Display, rrOutput, out int propertyCount); var properties = XRRListOutputProperties(x11.Display, rrOutput, out int propertyCount);
var hasEDID = false; var hasEDID = false;
@ -130,7 +130,7 @@ internal partial class X11Screens
{ {
nint[] ScreenKeys { get; } nint[] ScreenKeys { get; }
event Action? Changed; event Action? Changed;
X11Screen? CreateScreenFromKey(nint key); X11Screen CreateScreenFromKey(nint key);
} }
internal unsafe struct MonitorInfo 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; var infos = MonitorInfos;
for (var i = 0; i < infos.Length; i++) for (var i = 0; i < infos.Length; i++)
{ {
if (infos[i].Name == key) 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); 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); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;

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

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

2
src/Avalonia.X11/TransparencyHelper.cs

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

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

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

2
src/Avalonia.X11/X11Atoms.cs

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

75
src/Avalonia.X11/X11Clipboard.cs

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

2
src/Avalonia.X11/X11CursorFactory.cs

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

2
src/Avalonia.X11/X11FramebufferSurface.cs

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

17
src/Avalonia.X11/X11Globals.cs

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

4
src/Avalonia.X11/X11Info.cs

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

26
src/Avalonia.X11/X11NativeControlHost.cs

@ -1,8 +1,9 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
// TODO: Actually implement XEmbed instead of simply using XReparentWindow // TODO: Actually implement XEmbed instead of simply using XReparentWindow
@ -25,7 +26,7 @@ namespace Avalonia.X11
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create)
{ {
var holder = new DumbWindow(_platform.Info, true, Window.Handle.Handle); var holder = new DumbWindow(_platform.Info, true, Window.Handle.Handle);
Attachment attachment = null; Attachment? attachment = null;
try try
{ {
var child = create(holder); var child = create(holder);
@ -38,7 +39,7 @@ namespace Avalonia.X11
catch catch
{ {
attachment?.Dispose(); attachment?.Dispose();
holder?.Destroy(); holder.Destroy();
throw; throw;
} }
} }
@ -101,9 +102,9 @@ namespace Avalonia.X11
{ {
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _orphanedWindow; private readonly IntPtr _orphanedWindow;
private DumbWindow _holder; private DumbWindow? _holder;
private IPlatformHandle _child; private IPlatformHandle? _child;
private X11NativeControlHost _attachedTo; private X11NativeControlHost? _attachedTo;
private bool _mapped; private bool _mapped;
public Attachment(IntPtr display, DumbWindow holder, IntPtr orphanedWindow, IPlatformHandle child) public Attachment(IntPtr display, DumbWindow holder, IntPtr orphanedWindow, IPlatformHandle child)
@ -129,20 +130,22 @@ namespace Avalonia.X11
_attachedTo = null; _attachedTo = null;
} }
[MemberNotNull(nameof(_child))]
[MemberNotNull(nameof(_holder))]
private void CheckDisposed() private void CheckDisposed()
{ {
if (_child == null) if (_child is null || _holder is null)
throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment"); throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment");
} }
public INativeControlHostImpl AttachedTo public INativeControlHostImpl? AttachedTo
{ {
get => _attachedTo; get => _attachedTo;
set set
{ {
CheckDisposed(); CheckDisposed();
_attachedTo = (X11NativeControlHost)value; _attachedTo = (X11NativeControlHost?)value;
if (value == null) if (_attachedTo is null)
{ {
_mapped = false; _mapped = false;
XUnmapWindow(_display, _holder.Handle); XUnmapWindow(_display, _holder.Handle);
@ -164,7 +167,8 @@ namespace Avalonia.X11
if (_mapped) if (_mapped)
{ {
_mapped = false; _mapped = false;
XUnmapWindow(_display, _holder.Handle); if (_holder is not null)
XUnmapWindow(_display, _holder.Handle);
} }
size *= _attachedTo.Window.RenderScaling; 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()); private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice());
public KeyboardDevice KeyboardDevice => _keyboardDevice.Value; public KeyboardDevice KeyboardDevice => _keyboardDevice.Value;
public Dictionary<IntPtr, X11EventDispatcher.EventHandler> Windows { get; } = new (); public Dictionary<IntPtr, X11EventDispatcher.EventHandler> Windows { get; } = new ();
public XI2Manager XI2 { get; private set; } public XI2Manager? XI2 { get; private set; }
public X11Info Info { get; private set; } public X11Info Info { get; private set; } = null!;
public X11Screens X11Screens { get; private set; } public X11Screens X11Screens { get; private set; } = null!;
public Compositor Compositor { get; private set; } public Compositor Compositor { get; private set; } = null!;
public IScreenImpl Screens { get; private set; } public IScreenImpl Screens { get; private set; } = null!;
public X11PlatformOptions Options { get; private set; } public X11PlatformOptions Options { get; private set; } = null!;
public IntPtr OrphanedWindow { get; private set; } public IntPtr OrphanedWindow { get; private set; }
public X11Globals Globals { get; private set; } public X11Globals Globals { get; private set; } = null!;
public XResources Resources { get; private set; } public XResources Resources { get; private set; } = null!;
public ManualRawEventGrouperDispatchQueue EventGrouperDispatchQueue { get; } = new(); public ManualRawEventGrouperDispatchQueue EventGrouperDispatchQueue { get; } = new();
public void Initialize(X11PlatformOptions options) public void Initialize(X11PlatformOptions options)
@ -90,9 +90,7 @@ namespace Avalonia.X11
Screens = X11Screens = new X11Screens(this); Screens = X11Screens = new X11Screens(this);
if (Info.XInputVersion != null) if (Info.XInputVersion != null)
{ {
var xi2 = new XI2Manager(); XI2 = XI2Manager.TryCreate(this);
if (xi2.Init(this))
XI2 = xi2;
} }
var graphics = InitializeGraphics(options, Info); var graphics = InitializeGraphics(options, Info);
@ -108,7 +106,7 @@ namespace Avalonia.X11
public IntPtr DeferredDisplay { get; set; } public IntPtr DeferredDisplay { get; set; }
public IntPtr Display { get; set; } public IntPtr Display { get; set; }
private static uint[] X11IconConverter(IWindowIconImpl icon) private static uint[] X11IconConverter(IWindowIconImpl? icon)
{ {
if (!(icon is X11IconData x11icon)) if (!(icon is X11IconData x11icon))
return Array.Empty<uint>(); return Array.Empty<uint>();
@ -187,7 +185,7 @@ namespace Avalonia.X11
return false; 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()) 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> /// <summary>
/// Enables multitouch support. The default value is true. /// Enables multitouch support. The default value is true.
@ -392,7 +390,7 @@ namespace Avalonia
{ {
try try
{ {
WmClass = Assembly.GetEntryAssembly()?.GetName()?.Name; WmClass = Assembly.GetEntryAssembly()?.GetName().Name;
} }
catch catch
{ {
@ -412,7 +410,7 @@ namespace Avalonia
return builder; return builder;
} }
public static void InitializeX11Platform(X11PlatformOptions options = null) => public static void InitializeX11Platform(X11PlatformOptions? options = null) =>
new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions()); new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions());
} }

3
src/Avalonia.X11/X11PlatformLifetimeEvents.cs

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

2
src/Avalonia.X11/X11Structs.cs

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

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

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

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

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

6
src/Avalonia.X11/X11Window.cs

@ -30,8 +30,6 @@ using Avalonia.Platform.Storage.FileIO;
// ReSharper disable IdentifierTypo // ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo // ReSharper disable StringLiteralTypo
#nullable enable
namespace Avalonia.X11 namespace Avalonia.X11
{ {
internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client, internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
@ -95,7 +93,7 @@ namespace Avalonia.X11
_mode = mode; _mode = mode;
_mode.Init(this); _mode.Init(this);
_popup = popupParent != null; _popup = popupParent != null;
_overrideRedirect = _popup || overrideRedirect; _overrideRedirect = _popup || overrideRedirect;
_x11 = platform.Info; _x11 = platform.Info;
_mouse = new MouseDevice(); _mouse = new MouseDevice();
_touch = new TouchDevice(); _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 // 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] // 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 public class InputProxyWindowMode : DefaultTopLevelWindowMode
{ {
private X11FocusProxy _focusProxy; private X11FocusProxy? _focusProxy;
public override void OnHandleCreated(IntPtr handle) public override void OnHandleCreated(IntPtr handle)
{ {
@ -52,4 +52,4 @@ partial class X11Window
base.AppendWmProtocols(data); base.AppendWmProtocols(data);
} }
} }
} }

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

@ -7,10 +7,10 @@ partial class X11Window
{ {
public abstract class X11WindowMode public abstract class X11WindowMode
{ {
public X11Window Window { get; private set; } public X11Window Window { get; private set; } = null!;
protected IntPtr Display; protected IntPtr Display;
protected X11Info X11; protected X11Info X11 = null!;
protected AvaloniaX11Platform Platform; protected AvaloniaX11Platform Platform = null!;
protected IntPtr Handle => Window._handle; protected IntPtr Handle => Window._handle;
protected IntPtr RenderHandle => Window._renderHandle; protected IntPtr RenderHandle => Window._renderHandle;
public virtual bool BlockInput => false; 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;
using System.ComponentModel;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Embedding; using Avalonia.Controls.Embedding;
using Avalonia.Input; using Avalonia.Input;
@ -73,7 +71,10 @@ partial class X11Window
static XEmbedClientWindowMode() static XEmbedClientWindowMode()
{ {
KeyboardDevice.Instance.PropertyChanged += (_, args) => if (KeyboardDevice.Instance is not { } keyboardDevice)
return;
keyboardDevice.PropertyChanged += (_, args) =>
{ {
if (args.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement)) if (args.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
{ {
@ -204,4 +205,4 @@ partial class X11Window
(int)(point.Y * Window.RenderScaling)) (int)(point.Y * Window.RenderScaling))
+ GetWindowOffset(); + GetWindowOffset();
} }
} }

19
src/Avalonia.X11/XEmbedPlug.cs

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

8
src/Avalonia.X11/XEmbedTrayIconImpl.cs

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

73
src/Avalonia.X11/XI2Manager.cs

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

6
src/Avalonia.X11/XLib.cs

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

2
src/Avalonia.X11/XResources.cs

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

Loading…
Cancel
Save