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
wip. X11Clipboard. implement transmit INCR data
  • Loading branch information
viordash committed Jul 27, 2023
commit de6cc22736334affb6fb3bc88a713ca81213923c
101 changes: 82 additions & 19 deletions src/Avalonia.X11/X11Clipboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Avalonia.X11
{
internal class X11Clipboard : IClipboard
{
private readonly AvaloniaX11Platform _platform;
private readonly X11Info _x11;
private IDataObject _storedDataObject;
private IntPtr _handle;
Expand All @@ -19,11 +20,17 @@ internal class X11Clipboard : IClipboard
private TaskCompletionSource<object> _requestedDataTcs;
private readonly IntPtr[] _textAtoms;
private readonly IntPtr _avaloniaSaveTargetsAtom;
private IntPtr _incrTargetAtom;
private readonly List<byte> _incrData;
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;

public X11Clipboard(AvaloniaX11Platform platform)
{
_platform = platform;
_x11 = platform.Info;
_handle = CreateEventWindow(platform, OnEvent);
XSelectInput(_x11.Display, _handle, new IntPtr((int)(EventMask.StructureNotifyMask | EventMask.PropertyChangeMask)));
Expand All @@ -37,8 +44,12 @@ public X11Clipboard(AvaloniaX11Platform platform)
_x11.Atoms.UTF16_STRING
}.Where(a => a != IntPtr.Zero).ToArray();

_incrTargetAtom = IntPtr.Zero;
_incrData = new List<byte>();
_incrReadTargetAtom = IntPtr.Zero;
_incrReadData = new List<byte>();
_incrWriteTargetAtom = IntPtr.Zero;
_incrWriteWindow = IntPtr.Zero;
_incrWriteProperty = IntPtr.Zero;
_incrWriteData = null;
}

private bool IsStringAtom(IntPtr atom)
Expand All @@ -62,7 +73,11 @@ private unsafe void OnEvent(ref XEvent ev)
{
if (ev.type == XEventName.SelectionClear)
{
_storeAtomTcs?.TrySetResult(true);

if (_incrWriteTargetAtom == IntPtr.Zero)
{
_storeAtomTcs?.TrySetResult(true);
}
return;
}

Expand Down Expand Up @@ -135,9 +150,9 @@ private unsafe void OnEvent(ref XEvent ev)
_requestedDataTcs?.TrySetResult(null);
else
{
_incrTargetAtom = sel.property;
_incrReadTargetAtom = sel.property;
var total = *((int*)prop.ToPointer());
_incrData.Capacity = total;
_incrReadData.Capacity = total;
}
}
else
Expand All @@ -153,28 +168,34 @@ private unsafe void OnEvent(ref XEvent ev)
return;
}

if (ev.type == XEventName.PropertyNotify && (PropertyState)ev.PropertyEvent.state == PropertyState.NewValue)
if (ev.type == XEventName.PropertyNotify)
{
if (_incrTargetAtom == ev.PropertyEvent.atom)
// System.Diagnostics.Debug.WriteLine($" --- OnEvent 10 XEventName.PropertyNotify atom:{ev.PropertyEvent.atom:X}, state:{(PropertyState)ev.PropertyEvent.state}");

if (_incrReadTargetAtom == ev.PropertyEvent.atom && (PropertyState)ev.PropertyEvent.state == PropertyState.NewValue)
{
XGetWindowProperty(_x11.Display, _handle, _incrTargetAtom, IntPtr.Zero, new IntPtr(0x7fffffff), true, (IntPtr)Atom.AnyPropertyType,
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);

if (_incrTargetAtom == actualTypeAtom && (int)nitems > 0)
// System.Diagnostics.Debug.WriteLine($" --- OnEvent 11 total: nitems:{nitems}, atom:{_incrReadTargetAtom:X}|{actualTypeAtom:X}, prop:{prop:X}");


if (_incrReadTargetAtom == actualTypeAtom && (int)nitems > 0)
{
var chunkSize = (int)nitems * (actualFormat / 8);
var arrayPool = System.Buffers.ArrayPool<byte>.Shared;
var buffer = arrayPool.Rent(chunkSize);
var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(chunkSize);
Marshal.Copy(prop, buffer, 0, chunkSize);
_incrData.AddRange(buffer.Take(chunkSize));
_incrReadData.AddRange(buffer.Take(chunkSize));
// System.Diagnostics.Debug.WriteLine($" ---------- OnEvent 11 nitems:{nitems}, size:{_incrReadData.Count}");
}
else
{
var bytes = _incrData.ToArray();
var textEnc = GetStringEncoding(_incrTargetAtom);
_incrData.Clear();
_incrTargetAtom = IntPtr.Zero;
var bytes = _incrReadData.ToArray();
var textEnc = GetStringEncoding(_incrReadTargetAtom);
_incrReadData.Clear();
_incrReadTargetAtom = IntPtr.Zero;

// System.Diagnostics.Debug.WriteLine($" ---------- OnEvent 12 _requestedDataTcs textEnc:{textEnc}, size:{bytes.Length}");
if (bytes.Length == 0)
{
_requestedDataTcs?.TrySetResult(null);
Expand All @@ -190,8 +211,33 @@ private unsafe void OnEvent(ref XEvent ev)
}
}
XFree(prop);
return;
}
}
}

private void OnIncrWritePropertyEvent(ref XEvent ev)
{
// System.Diagnostics.Debug.WriteLine($" ---- OnWritePropertyEvent 1 window:{ev.PropertyEvent.window:X}, atom:{ev.PropertyEvent.atom:X}|{_incrWriteTargetAtom:X}, state:{ev.PropertyEvent.state}");

if (ev.type == XEventName.PropertyNotify && (PropertyState)ev.PropertyEvent.state == PropertyState.Delete && ev.PropertyEvent.atom == _incrWriteTargetAtom)
{
if (_incrWriteData?.Length > 0)
{
var bytes = _incrWriteData.Take(MaxRequestSize).ToArray();
_incrWriteData = _incrWriteData.Skip(bytes.Length).ToArray();
XChangeProperty(_x11.Display, _incrWriteWindow, _incrWriteProperty, _incrWriteTargetAtom, 8, PropertyMode.Replace, bytes, bytes.Length);
// System.Diagnostics.Debug.WriteLine($" ---- OnWritePropertyEvent 2 INCR target:{_incrWriteTargetAtom:X}, window:{_incrWriteWindow:X}, property:{_incrWriteProperty:X}, size:{bytes.Length}");
}
else
{
_platform.Windows.Remove(_incrWriteWindow);
XChangeProperty(_x11.Display, _incrWriteWindow, _incrWriteProperty, _incrWriteTargetAtom, 8, PropertyMode.Replace, IntPtr.Zero, 0);
_incrWriteTargetAtom = IntPtr.Zero;
_incrWriteData = null;
_storeAtomTcs?.TrySetResult(true);
// System.Diagnostics.Debug.WriteLine($" ---- WriteProperty INCR stop target:{_incrWriteTargetAtom:X}");
}
}
}

Expand Down Expand Up @@ -224,6 +270,7 @@ private unsafe IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr
var subProp = data[c + 1];
var converted = WriteTargetToProperty(subTarget, window, subProp);
data[c + 1] = converted;
// System.Diagnostics.Debug.WriteLine($" --- WriteTargetToProperty MULTIPLE subTarget:{subTarget:X}, window:{window:X}, subProp:{subProp:X}, converted:{converted:X}");
}

XChangeProperty(_x11.Display, window, property, _x11.Atoms.ATOM_PAIR, 32, PropertyMode.Replace,
Expand All @@ -249,7 +296,23 @@ private unsafe IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr
return IntPtr.Zero;
}

XChangeProperty(_x11.Display, window, property, target, 8, PropertyMode.Replace, bytes, bytes.Length);
if (bytes.Length > MaxRequestSize)
{
_incrWriteTargetAtom = target;
_incrWriteWindow = window;
_incrWriteProperty = property;
_incrWriteData = bytes;
_platform.Windows[window] = OnIncrWritePropertyEvent;
XSelectInput(_x11.Display, window, new IntPtr((int)EventMask.PropertyChangeMask));
var total = new IntPtr[] { (IntPtr)bytes.Length };
XChangeProperty(_x11.Display, window, property, _x11.Atoms.INCR, 32, PropertyMode.Replace, total, total.Length);
// System.Diagnostics.Debug.WriteLine($" ---- WriteProperty INCR start target:{target:X}, window:{window:X} | {_handle:X}, property:{property:X}, total:{bytes.Length}");
}
else
{
XChangeProperty(_x11.Display, window, property, target, 8, PropertyMode.Replace, bytes, bytes.Length);
}

return property;
}

Expand Down