Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X11 clipboard incr proto #12357

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7ef6152
code format
viordash Jul 23, 2023
5ffb53e
X11Clipboard.OnEvent code refactoring
viordash Jul 23, 2023
341984f
fix potential memleak. (XGetWindowProperty always allocates one extra…
viordash Jul 23, 2023
94225f2
no need to call XGetWindowProperty for an empty property
viordash Jul 23, 2023
7a85d21
X11Clipboard. handle PropertyNotify events
viordash Jul 23, 2023
8d86c4f
X11Clipboard. implement receive INCR data
viordash Jul 23, 2023
41e5ff1
X11Clipboard. unify the process of changing properties (text and bina…
viordash Jul 23, 2023
de6cc22
wip. X11Clipboard. implement transmit INCR data
viordash Jul 24, 2023
5dcacd7
X11Clipboard. get MaxRequestSize from X11
viordash Jul 25, 2023
46faae7
Clipboard X11. wait for the data to be stored
viordash Jul 25, 2023
09f1d6c
Revert "X11Clipboard. get MaxRequestSize from X11"
viordash Jul 25, 2023
4058759
X11Clipboard. for big data don't use CLIPBOARD_MANAGER
viordash Jul 25, 2023
0f89be8
X11Clipboard. fix waiting for INCR store to complete
viordash Jul 26, 2023
8e5f7db
X11Clipboard. fix UseIncrProtocol
viordash Jul 26, 2023
decd3a6
X11Clipboard. INCR for foreign windows only
viordash Jul 26, 2023
edfac01
use byte array
viordash Jul 27, 2023
0c9946e
X11Clipboard. support parallel INCR reading
viordash Jul 29, 2023
bbfbe50
X11Clipboard. support parallel INCR writing
viordash Jul 29, 2023
a977d3d
Merge remote-tracking branch 'origin/master' into x11-clipboard-incr-…
viordash Jul 29, 2023
8c41a77
X11Clipboard. fix ClearAsync completion
viordash Jul 29, 2023
6670284
Merge branch 'master' into x11-clipboard-incr-proto
viordash Jul 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
X11Clipboard. support parallel INCR reading
  • Loading branch information
viordash committed Jul 29, 2023
commit 0c9946e86660287d66a84cca8d3cda77f74caf06
116 changes: 71 additions & 45 deletions src/Avalonia.X11/X11Clipboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,56 @@ namespace Avalonia.X11
{
internal class X11Clipboard : IClipboard
{
#region inner classes
private class IncrDataReader
{
private readonly X11Info _x11;
public readonly IntPtr Property;
private readonly int _total;
private readonly Action<IntPtr, object> _onCompleted;
private readonly List<byte> _readData;

public IncrDataReader(X11Info x11, IntPtr property, int total, Action<IntPtr, object> onCompleted)
{
_x11 = x11;
Property = property;
_total = total;
_onCompleted = onCompleted;
_readData = new List<byte>();
}

public void Append(IntPtr data, int size)
{
if (size > 0)
{
var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(size);
Marshal.Copy(data, buffer, 0, size);
_readData.AddRange(buffer.Take(size));
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
return;
}

if (_readData.Count != _total)
{
_onCompleted(Property, null);
return;
}

var textEnc = GetStringEncoding(_x11.Atoms, Property);
var bytes = _readData.ToArray();
if (textEnc != null)
{
_onCompleted(Property, textEnc.GetString(bytes));
}
else
{
_onCompleted(Property, bytes);
}
}
}

#endregion

private readonly AvaloniaX11Platform _platform;
private readonly X11Info _x11;
private IDataObject _storedDataObject;
Expand All @@ -20,14 +70,15 @@ internal class X11Clipboard : IClipboard
private TaskCompletionSource<object> _requestedDataTcs;
private readonly IntPtr[] _textAtoms;
private readonly IntPtr _avaloniaSaveTargetsAtom;
private IntPtr _incrReadTargetAtom;
private readonly List<byte> _incrReadData;

private IntPtr _incrWriteTargetAtom;
private IntPtr _incrWriteWindow;
private IntPtr _incrWriteProperty;
private byte[] _incrWriteData;
private const int MaxRequestSize = 0x40000;

private readonly Dictionary<IntPtr, IncrDataReader> _incrDataReaders;

public X11Clipboard(AvaloniaX11Platform platform)
{
_platform = platform;
Expand All @@ -44,8 +95,7 @@ public X11Clipboard(AvaloniaX11Platform platform)
_x11.Atoms.UTF16_STRING
}.Where(a => a != IntPtr.Zero).ToArray();

_incrReadTargetAtom = IntPtr.Zero;
_incrReadData = new List<byte>();
_incrDataReaders = new();
_incrWriteTargetAtom = IntPtr.Zero;
_incrWriteWindow = IntPtr.Zero;
_incrWriteProperty = IntPtr.Zero;
Expand All @@ -57,14 +107,14 @@ private bool IsStringAtom(IntPtr atom)
return _textAtoms.Contains(atom);
}

private Encoding GetStringEncoding(IntPtr atom)
private static Encoding GetStringEncoding(X11Atoms atoms, IntPtr atom)
{
return (atom == _x11.Atoms.XA_STRING
|| atom == _x11.Atoms.OEMTEXT)
return (atom == atoms.XA_STRING
|| atom == atoms.OEMTEXT)
? Encoding.ASCII
: atom == _x11.Atoms.UTF8_STRING
: atom == atoms.UTF8_STRING
? Encoding.UTF8
: atom == _x11.Atoms.UTF16_STRING
: atom == atoms.UTF16_STRING
? Encoding.Unicode
: null;
}
Expand Down Expand Up @@ -127,7 +177,7 @@ private unsafe void OnEvent(ref XEvent ev)
_requestedFormatsTcs?.TrySetResult(formats);
}
}
else if ((textEnc = GetStringEncoding(actualTypeAtom)) != null)
else if ((textEnc = GetStringEncoding(_x11.Atoms, actualTypeAtom)) != null)
{
var text = textEnc.GetString((byte*)prop.ToPointer(), nitems.ToInt32());
_requestedDataTcs?.TrySetResult(text);
Expand All @@ -140,9 +190,12 @@ private unsafe void OnEvent(ref XEvent ev)
_requestedDataTcs?.TrySetResult(null);
else
{
_incrReadTargetAtom = sel.property;
var total = *((int*)prop.ToPointer());
_incrReadData.Capacity = total;
_incrDataReaders[sel.property] = new IncrDataReader(_x11, sel.property, *(int*)prop.ToPointer(),
(property, obj) =>
{
_incrDataReaders.Remove(property);
_requestedDataTcs?.TrySetResult(obj);
});
}
}
else
Expand All @@ -160,39 +213,12 @@ private unsafe void OnEvent(ref XEvent ev)

if (ev.type == XEventName.PropertyNotify)
{
if (_incrReadTargetAtom == ev.PropertyEvent.atom && (PropertyState)ev.PropertyEvent.state == PropertyState.NewValue)
if ((PropertyState)ev.PropertyEvent.state == PropertyState.NewValue && _incrDataReaders.TryGetValue(ev.PropertyEvent.atom, out var incrDataReader))
{
XGetWindowProperty(_x11.Display, _handle, _incrReadTargetAtom, 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);
XGetWindowProperty(_x11.Display, _handle, incrDataReader.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);
incrDataReader.Append(prop, (int)nitems * (actualFormat / 8));

if (_incrReadTargetAtom == actualTypeAtom && (int)nitems > 0)
{
var chunkSize = (int)nitems * (actualFormat / 8);
var buffer = new byte[chunkSize];
Marshal.Copy(prop, buffer, 0, chunkSize);
_incrReadData.AddRange(buffer);
}
else
{
var bytes = _incrReadData.ToArray();
var textEnc = GetStringEncoding(_incrReadTargetAtom);
_incrReadData.Clear();
_incrReadTargetAtom = IntPtr.Zero;

if (bytes.Length == 0)
{
_requestedDataTcs?.TrySetResult(null);
}
else if (textEnc != null)
{
var text = textEnc.GetString(bytes);
_requestedDataTcs?.TrySetResult(text);
}
else
{
_requestedDataTcs?.TrySetResult(bytes);
}
}
XFree(prop);
return;
}
Expand Down Expand Up @@ -270,7 +296,7 @@ private unsafe IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr
{
if (objValue is string s)
{
var textEnc = GetStringEncoding(target) ?? Encoding.UTF8;
var textEnc = GetStringEncoding(_x11.Atoms, target) ?? Encoding.UTF8;
bytes = textEnc.GetBytes(s);
}
else
Expand Down