From 1f06e4af50ae9ff56d4af8869cf0cdf0298e98ba Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 13:31:33 +0300 Subject: [PATCH 1/3] use real data formats while storing atoms early --- src/Avalonia.X11/X11Clipboard.cs | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index 6e18151fae..bed0a8cebf 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -82,18 +82,9 @@ namespace Avalonia.X11 Encoding textEnc; if (target == _x11.Atoms.TARGETS) { - var atoms = new HashSet { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE }; - foreach (var fmt in _storedDataObject.GetDataFormats()) - { - if (fmt == DataFormats.Text) - foreach (var ta in _textAtoms) - atoms.Add(ta); - else - atoms.Add(_x11.Atoms.GetAtom(fmt)); - } - + var atoms = ConvertDataObject(_storedDataObject); XChangeProperty(_x11.Display, window, property, - _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms.ToArray(), atoms.Count); + _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); return property; } else if(target == _x11.Atoms.SAVE_TARGETS && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) @@ -252,13 +243,29 @@ namespace Avalonia.X11 return (string)await SendDataRequest(target); } - private void StoreAtomsInClipboardManager(IntPtr[] atoms) + + private IntPtr[] ConvertDataObject(IDataObject data) + { + var atoms = new HashSet { _x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE }; + 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(); + } + + private void StoreAtomsInClipboardManager(IDataObject data) { if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) { var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); if (clipboardManager != IntPtr.Zero) - { + { + var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); @@ -283,8 +290,8 @@ namespace Avalonia.X11 public Task SetDataObjectAsync(IDataObject data) { _storedDataObject = data; - XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); - StoreAtomsInClipboardManager(_textAtoms); + XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); + StoreAtomsInClipboardManager(data); return Task.CompletedTask; } From 0d1f7fac56936312c2d4249eb182ea6ae137dd35 Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 14:19:10 +0300 Subject: [PATCH 2/3] await for completion when storing atoms early --- src/Avalonia.X11/X11Clipboard.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index bed0a8cebf..53190b0f3e 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -14,6 +14,7 @@ namespace Avalonia.X11 private readonly X11Info _x11; private IDataObject _storedDataObject; private IntPtr _handle; + private TaskCompletionSource _storeAtomTcs; private TaskCompletionSource _requestedFormatsTcs; private TaskCompletionSource _requestedDataTcs; private readonly IntPtr[] _textAtoms; @@ -52,6 +53,12 @@ namespace Avalonia.X11 private unsafe void OnEvent(ref XEvent ev) { + if (ev.type == XEventName.SelectionClear) + { + _storeAtomTcs?.TrySetResult(); + return; + } + if (ev.type == XEventName.SelectionRequest) { var sel = ev.SelectionRequestEvent; @@ -258,21 +265,26 @@ namespace Avalonia.X11 return atoms.ToArray(); } - private void StoreAtomsInClipboardManager(IDataObject data) + private Task StoreAtomsInClipboardManager(IDataObject data) { if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) { var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); if (clipboardManager != IntPtr.Zero) - { + { + if (_storeAtomTcs == null || _storeAtomTcs.Task.IsCompleted) + _storeAtomTcs = new TaskCompletionSource(); + var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, PropertyMode.Replace, atoms, atoms.Length); XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER, _x11.Atoms.SAVE_TARGETS, _avaloniaSaveTargetsAtom, _handle, IntPtr.Zero); + return _storeAtomTcs.Task; } } + return Task.CompletedTask; } public Task SetTextAsync(string text) @@ -291,8 +303,7 @@ namespace Avalonia.X11 { _storedDataObject = data; XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); - StoreAtomsInClipboardManager(data); - return Task.CompletedTask; + return StoreAtomsInClipboardManager(data); } public async Task GetFormatsAsync() From 22f68362b6af22a1ebdb39373a2aa142dd468f1c Mon Sep 17 00:00:00 2001 From: Vitalii Orazov Date: Sun, 2 Jul 2023 19:47:19 +0300 Subject: [PATCH 3/3] fix error CS0305: Using the generic type 'TaskCompletionSource' requires 1 type arguments --- src/Avalonia.X11/X11Clipboard.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs index 53190b0f3e..637d44d617 100644 --- a/src/Avalonia.X11/X11Clipboard.cs +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -14,7 +14,7 @@ namespace Avalonia.X11 private readonly X11Info _x11; private IDataObject _storedDataObject; private IntPtr _handle; - private TaskCompletionSource _storeAtomTcs; + private TaskCompletionSource _storeAtomTcs; private TaskCompletionSource _requestedFormatsTcs; private TaskCompletionSource _requestedDataTcs; private readonly IntPtr[] _textAtoms; @@ -55,7 +55,7 @@ namespace Avalonia.X11 { if (ev.type == XEventName.SelectionClear) { - _storeAtomTcs?.TrySetResult(); + _storeAtomTcs?.TrySetResult(true); return; } @@ -273,7 +273,7 @@ namespace Avalonia.X11 if (clipboardManager != IntPtr.Zero) { if (_storeAtomTcs == null || _storeAtomTcs.Task.IsCompleted) - _storeAtomTcs = new TaskCompletionSource(); + _storeAtomTcs = new TaskCompletionSource(); var atoms = ConvertDataObject(data); XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32,