Browse Source

Xdnd: add timeout waiting for XdndFinished

pull/20926/head
Julien Lebosquain 7 months ago
parent
commit
8da5c1f7ec
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 11
      src/Avalonia.X11/Selections/DragDrop/DragDropDataProvider.cs
  2. 35
      src/Avalonia.X11/Selections/DragDrop/DragDropTimeoutManager.cs
  3. 34
      src/Avalonia.X11/Selections/DragDrop/X11DragSource.cs
  4. 16
      src/Avalonia.X11/Selections/SelectionDataProvider.cs
  5. 8
      src/Avalonia.X11/Selections/SelectionHelper.cs
  6. 4
      src/Avalonia.X11/Selections/SelectionReadSession.cs

11
src/Avalonia.X11/Selections/DragDrop/DragDropDataProvider.cs

@ -5,11 +5,11 @@ namespace Avalonia.X11.Selections.DragDrop;
internal sealed class DragDropDataProvider : SelectionDataProvider
{
public Action? Activity { get; set; }
public DragDropDataProvider(AvaloniaX11Platform platform, IAsyncDataTransfer dataTransfer)
: base(platform, platform.Info.Atoms.XdndSelection)
{
DataTransfer = dataTransfer;
}
=> DataTransfer = dataTransfer;
public new IntPtr GetOwner()
=> base.GetOwner();
@ -17,8 +17,13 @@ internal sealed class DragDropDataProvider : SelectionDataProvider
public new void SetOwner(IntPtr window)
=> base.SetOwner(window);
protected override void OnActivity()
=> Activity?.Invoke();
public override void Dispose()
{
Activity = null;
DataTransfer?.Dispose();
DataTransfer = null;
}

35
src/Avalonia.X11/Selections/DragDrop/DragDropTimeoutManager.cs

@ -0,0 +1,35 @@
using System;
using System.Threading;
namespace Avalonia.X11.Selections.DragDrop;
internal sealed class DragDropTimeoutManager : IDisposable
{
private readonly TimeSpan _timeout;
private readonly Timer _timer;
public DragDropTimeoutManager(TimeSpan timeout, Action onTimeout)
{
_timeout = timeout;
_timer = new Timer(
static state => ((Action)state!)(),
onTimeout,
Timeout.InfiniteTimeSpan,
Timeout.InfiniteTimeSpan);
}
public void Restart()
{
try
{
_timer.Change(_timeout, Timeout.InfiniteTimeSpan);
}
catch (ObjectDisposedException)
{
}
}
public void Dispose()
=> _timer.Dispose();
}

34
src/Avalonia.X11/Selections/DragDrop/X11DragSource.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
@ -49,6 +50,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private bool _pointerGrabbed;
private XdndTargetInfo? _lastTarget;
private DragDropEffects _currentEffects;
private DragDropTimeoutManager? _timeoutManager;
public Handler(
AvaloniaX11Platform platform,
@ -68,7 +70,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
if (!platform.Windows.TryGetValue(sourceWindow, out var sourceWindowInfo))
{
_formatAtoms = [];
_completionSource.TrySetResult(DragDropEffects.None);
Complete(DragDropEffects.None);
return;
}
@ -92,7 +94,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
if (grabResult != GrabResult.GrabSuccess)
{
_formatAtoms = [];
_completionSource.TrySetResult(DragDropEffects.None);
Complete(DragDropEffects.None);
return;
}
@ -198,11 +200,22 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
var rootPosition = new PixelPoint(button.x_root, button.y_root);
ProcessRawDragEvent(lastTarget.InProcessWindow, RawDragEventType.Drop, rootPosition);
_lastTarget = null;
_completionSource.TrySetResult(_currentEffects);
Complete(_currentEffects);
}
else
{
_dataProvider.SetOwner(_sourceWindow);
Debug.Assert(_timeoutManager is null);
_timeoutManager = new DragDropTimeoutManager(SelectionHelper.Timeout, () =>
{
DisposeTimeoutManager();
Complete(DragDropEffects.None);
});
_dataProvider.Activity = _timeoutManager.Restart;
SendXdndDrop(lastTarget, button.time);
}
}
@ -224,6 +237,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
return;
_lastTarget = null;
DisposeTimeoutManager();
if (lastTarget.Version >= 5)
{
@ -233,7 +247,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
UpdateCurrentEffects(effects & _allowedEffects);
}
_completionSource.TrySetResult(_currentEffects);
Complete(_currentEffects);
}
private XdndTargetInfo? FindXdndTarget(PixelPoint rootPosition)
@ -433,6 +447,16 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
return _platform.Info.DefaultCursor;
}
private void Complete(DragDropEffects resultEffects)
=> _completionSource.TrySetResult(resultEffects);
private void DisposeTimeoutManager()
{
_dataProvider.Activity = null;
_timeoutManager?.Dispose();
_timeoutManager = null;
}
public void Dispose()
{
if (_pointerGrabbed)
@ -448,6 +472,8 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_lastTarget = null;
_currentEffects = DragDropEffects.None;
DisposeTimeoutManager();
if (_dataProvider.GetOwner() == _sourceWindow)
_dataProvider.SetOwner(0);

16
src/Avalonia.X11/Selections/SelectionDataProvider.cs

@ -63,6 +63,7 @@ internal abstract class SelectionDataProvider : IDisposable
}
XSendEvent(Platform.Display, request.requestor, false, new IntPtr((int)EventMask.NoEventMask), ref response);
OnActivity();
IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr property)
{
@ -196,6 +197,7 @@ internal abstract class SelectionDataProvider : IDisposable
ref size, 1);
var buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(_maximumPropertySize, data.Length));
var gotTimeout = false;
while (true)
{
@ -204,22 +206,29 @@ internal abstract class SelectionDataProvider : IDisposable
x.type == XEventName.PropertyNotify &&
x.PropertyEvent.atom == property &&
x.PropertyEvent.state == 1,
TimeSpan.FromMinutes(1));
SelectionHelper.Timeout);
if (evt is null)
{
gotTimeout = true;
break;
}
var read = await data.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
break;
XChangeProperty(Platform.Display, window, property, target, 8, PropertyMode.Replace, buffer, read);
OnActivity();
}
ArrayPool<byte>.Shared.Return(buffer);
// Finish the transfer
XChangeProperty(Platform.Display, window, property, target, 8, PropertyMode.Replace, 0, 0);
if (!gotTimeout)
OnActivity();
}
private Task SendDataToClientAsync(IntPtr window, IntPtr property, IntPtr target, byte[] bytes)
@ -228,6 +237,7 @@ internal abstract class SelectionDataProvider : IDisposable
{
XChangeProperty(Platform.Display, window, property, target, 8, PropertyMode.Replace,
bytes, bytes.Length);
OnActivity();
return Task.CompletedTask;
}
@ -242,5 +252,9 @@ internal abstract class SelectionDataProvider : IDisposable
return [atoms.TARGETS, atoms.MULTIPLE, ..formatAtoms];
}
protected virtual void OnActivity()
{
}
public abstract void Dispose();
}

8
src/Avalonia.X11/Selections/SelectionHelper.cs

@ -0,0 +1,8 @@
using System;
namespace Avalonia.X11.Selections;
internal static class SelectionHelper
{
public static TimeSpan Timeout { get; } = TimeSpan.FromSeconds(5);
}

4
src/Avalonia.X11/Selections/SelectionReadSession.cs

@ -42,7 +42,7 @@ internal sealed class SelectionReadSession(
ev.SelectionEvent.requestor == window &&
ev.SelectionEvent.selection == selection &&
ev.SelectionEvent.property == property,
TimeSpan.FromSeconds(5));
SelectionHelper.Timeout);
if (ev == null)
return null;
@ -108,7 +108,7 @@ internal sealed class SelectionReadSession(
x => x is { type: XEventName.PropertyNotify, PropertyEvent.state: 0 } &&
x.PropertyEvent.window == window &&
x.PropertyEvent.atom == property,
TimeSpan.FromSeconds(5));
SelectionHelper.Timeout);
if (ev == null)
return null;

Loading…
Cancel
Save