|
|
|
@ -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); |
|
|
|
|