Browse Source

Xdnd: handle in process DnD

pull/20926/head
Julien Lebosquain 7 months ago
parent
commit
7e069a15c0
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 112
      src/Avalonia.X11/Selections/DragDrop/X11DragSource.cs
  2. 12
      src/Avalonia.X11/Selections/DragDrop/X11DropTarget.cs
  3. 9
      src/Avalonia.X11/X11Window.cs

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

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Threading;
using static Avalonia.X11.Selections.DragDrop.XdndConstants;
@ -30,7 +31,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
var cursorFactory = AvaloniaLocator.Current.GetService<ICursorFactory>();
using var handler = new Handler(platform, window.Handle, dataTransfer.ToAsynchronous(), allowedEffects, cursorFactory);
using var handler = new Handler(platform, window.Handle, dataTransfer, allowedEffects, cursorFactory);
await handler.Completion;
return DragDropEffects.None;
@ -40,6 +41,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
{
private readonly AvaloniaX11Platform _platform;
private readonly IntPtr _sourceWindow;
private readonly IDataTransfer _dataTransfer;
private readonly DragDropEffects _allowedEffects;
private readonly TaskCompletionSource<DragDropEffects> _completionSource = new();
private readonly DragDropDataProvider _dataProvider;
@ -47,19 +49,21 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private X11WindowInfo? _originalSourceWindowInfo;
private bool _pointerGrabbed;
private XdndTargetInfo? _lastTarget;
private IntPtr _lastStatusAction;
private DragDropEffects _lastEffects;
public Handler(
AvaloniaX11Platform platform,
IntPtr sourceWindow,
IAsyncDataTransfer dataTransfer,
IDataTransfer dataTransfer,
DragDropEffects allowedEffects,
ICursorFactory? cursorFactory)
{
_platform = platform;
_sourceWindow = sourceWindow;
_dataTransfer = dataTransfer;
_allowedEffects = allowedEffects;
_dataProvider = new DragDropDataProvider(platform, dataTransfer);
_lastEffects = allowedEffects;
_dataProvider = new DragDropDataProvider(platform, dataTransfer.ToAsynchronous());
if (!platform.Windows.TryGetValue(sourceWindow, out var sourceWindowInfo))
{
@ -145,23 +149,40 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private void OnMotionNotify(in XMotionEvent motion)
{
var target = FindXdndTarget(motion.x_root, motion.y_root);
var rootPosition = new PixelPoint(motion.x_root, motion.y_root);
var target = FindXdndTarget(rootPosition);
if (_lastTarget != target)
{
if (_lastTarget is { } lastTarget)
SendXdndLeave(lastTarget);
{
if (lastTarget.InProcessWindow is not null)
ProcessRawDragEvent(lastTarget.InProcessWindow, RawDragEventType.DragLeave, rootPosition);
else
SendXdndLeave(lastTarget);
}
_lastTarget = target;
_lastEffects = _allowedEffects;
if (target is { } newTarget)
SendXdndEnter(newTarget);
{
if (newTarget.InProcessWindow is not null)
ProcessRawDragEvent(newTarget.InProcessWindow, RawDragEventType.DragEnter, rootPosition);
else
SendXdndEnter(newTarget);
}
}
if (target is { } currentTarget)
{
var action = XdndActionHelper.EffectsToAction(_allowedEffects, _platform.Info.Atoms);
SendXdndPosition(currentTarget, motion.x_root, motion.y_root, motion.time, action);
if (currentTarget.InProcessWindow is not null)
ProcessRawDragEvent(currentTarget.InProcessWindow, RawDragEventType.DragOver, rootPosition);
else
{
var action = XdndActionHelper.EffectsToAction(_allowedEffects, _platform.Info.Atoms);
SendXdndPosition(currentTarget, rootPosition, motion.time, action);
}
}
}
@ -172,8 +193,16 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
if (_lastTarget is not { } lastTarget)
return;
_dataProvider.SetOwner(_sourceWindow);
SendXdndDrop(lastTarget, button.time);
if (lastTarget.InProcessWindow is not null)
{
var rootPosition = new PixelPoint(button.x_root, button.y_root);
ProcessRawDragEvent(lastTarget.InProcessWindow, RawDragEventType.Drop, rootPosition);
}
else
{
_dataProvider.SetOwner(_sourceWindow);
SendXdndDrop(lastTarget, button.time);
}
}
private void OnXdndStatus(in XClientMessageEvent message)
@ -182,7 +211,8 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
return;
var accepted = (message.ptr2 & 1) == 1;
_lastStatusAction = accepted ? message.ptr5 : 0;
var action = accepted ? message.ptr5 : 0;
_lastEffects = XdndActionHelper.ActionToEffects(action, _platform.Info.Atoms);
}
private void OnXdndFinished(in XClientMessageEvent message)
@ -192,20 +222,17 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_lastTarget = null;
IntPtr action;
if (lastTarget.Version >= 5)
{
var accepted = (message.ptr2 & 1) == 1;
action = accepted ? message.ptr3 : 0;
var action = accepted ? message.ptr5 : 0;
_lastEffects = XdndActionHelper.ActionToEffects(action, _platform.Info.Atoms);
}
else
action = _lastStatusAction;
var effects = XdndActionHelper.ActionToEffects(action, _platform.Info.Atoms);
_completionSource.TrySetResult(effects);
_completionSource.TrySetResult(_lastEffects);
}
private XdndTargetInfo? FindXdndTarget(int x, int y)
private XdndTargetInfo? FindXdndTarget(PixelPoint rootPosition)
{
var display = _platform.Display;
var rootWindow = _platform.Info.RootWindow;
@ -216,8 +243,11 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
if (TryGetXdndTargetInfo(currentWindow) is { } info)
return info;
if (!XTranslateCoordinates(display, rootWindow, currentWindow, x, y, out _, out _, out var childWindow))
if (!XTranslateCoordinates(display, rootWindow, currentWindow, rootPosition.X, rootPosition.Y,
out _, out _, out var childWindow))
{
return null;
}
currentWindow = childWindow;
}
@ -227,11 +257,18 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private XdndTargetInfo? TryGetXdndTargetInfo(IntPtr window)
{
// Special case our own windows: we don't need to go through X for them.
if (_platform.Windows.TryGetValue(window, out var windowInfo)
&& windowInfo.Window is { } inProcessWindow)
{
return new XdndTargetInfo(XdndVersion, window, window, inProcessWindow);
}
var proxyWindow = GetXdndProxyWindow(window);
var messageWindow = proxyWindow != 0 ? proxyWindow : window;
var version = GetXdndVersion(messageWindow);
return version != 0 ? new XdndTargetInfo(version, window, messageWindow) : null;
return version != 0 ? new XdndTargetInfo(version, window, messageWindow, null) : null;
}
private IntPtr GetXdndProxyWindow(IntPtr window)
@ -277,13 +314,13 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_formatAtoms.Length >= 3 ? _formatAtoms[2] : 0);
}
private void SendXdndPosition(XdndTargetInfo target, int x, int y, IntPtr timestamp, IntPtr action)
private void SendXdndPosition(XdndTargetInfo target, PixelPoint rootPosition, IntPtr timestamp, IntPtr action)
{
SendXdndMessage(
_platform.Info.Atoms.XdndPosition,
target.MessageWindow,
0,
(x << 16) | y,
(rootPosition.X << 16) | rootPosition.Y,
timestamp,
action);
}
@ -327,6 +364,30 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
XFlush(_platform.Display);
}
private void ProcessRawDragEvent(X11Window targetWindow, RawDragEventType eventType, PixelPoint rootPosition)
{
if (targetWindow.DragDropDevice is not { } dragDropDevice)
{
_lastEffects = DragDropEffects.None;
return;
}
var localPosition = targetWindow.PointToClient(rootPosition);
var dragEvent = new RawDragEvent(
dragDropDevice,
eventType,
targetWindow.InputRoot,
localPosition,
_dataTransfer,
_allowedEffects,
RawInputModifiers.None);
dragDropDevice.ProcessRawEvent(dragEvent);
_lastEffects = dragEvent.Effects;
}
private void UngrabPointer()
{
_pointerGrabbed = false;
@ -348,7 +409,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
}
_lastTarget = null;
_lastStatusAction = 0;
_lastEffects = DragDropEffects.None;
if (_dataProvider.GetOwner() == _sourceWindow)
_dataProvider.SetOwner(0);
@ -359,6 +420,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private readonly record struct XdndTargetInfo(
byte Version,
IntPtr TargetWindow,
IntPtr MessageWindow);
IntPtr MessageWindow,
X11Window? InProcessWindow);
}
}

12
src/Avalonia.X11/Selections/DragDrop/X11DropTarget.cs

@ -19,6 +19,7 @@ internal sealed class X11DropTarget
private readonly IntPtr _display;
private readonly X11Atoms _atoms;
private DragDropDataTransfer? _currentDrag;
private DragDropEffects _resultEffects;
public X11DropTarget(IDragDropDevice dragDropDevice, IXdndWindow window, IntPtr _display, X11Atoms atoms)
{
@ -102,7 +103,7 @@ internal sealed class X11DropTarget
_currentDrag.LastPosition = position;
_currentDrag.LastTimestamp = message.ptr4;
var dragEnter = new RawDragEvent(
var dragEvent = new RawDragEvent(
_dragDropDevice,
eventType,
_currentDrag.InputRoot,
@ -111,9 +112,10 @@ internal sealed class X11DropTarget
requestedEffects,
RawInputModifiers.None);
_dragDropDevice.ProcessRawEvent(dragEnter);
_dragDropDevice.ProcessRawEvent(dragEvent);
var resultAction = XdndActionHelper.EffectsToAction(dragEnter.Effects, _atoms);
_resultEffects = dragEvent.Effects;
var resultAction = XdndActionHelper.EffectsToAction(_resultEffects, _atoms);
var evt = new XEvent
{
@ -145,6 +147,8 @@ internal sealed class X11DropTarget
if (sourceWindow != _currentDrag.SourceWindow)
return;
_resultEffects = DragDropEffects.None;
var dragLeave = new RawDragEvent(
_dragDropDevice,
RawDragEventType.DragLeave,
@ -174,7 +178,7 @@ internal sealed class X11DropTarget
_currentDrag.InputRoot,
_currentDrag.LastPosition ?? default,
_currentDrag,
DragDropEffects.None,
_resultEffects,
RawInputModifiers.None);
_dragDropDevice.ProcessRawEvent(drop);

9
src/Avalonia.X11/X11Window.cs

@ -280,7 +280,10 @@ namespace Avalonia.X11
});
if (AvaloniaLocator.Current.GetService<IDragDropDevice>() is { } dragDropDevice)
{
DragDropDevice = dragDropDevice;
_dropTarget = new X11DropTarget(dragDropDevice, this, _x11.Display, _x11.Atoms);
}
platform.X11Screens.Changed += OnScreensChanged;
}
@ -523,6 +526,8 @@ namespace Avalonia.X11
public Compositor Compositor => _platform.Compositor;
public IDragDropDevice? DragDropDevice { get; }
private PlatformAllowedWindowActions GetAllowedActions(IntPtr[]? netSupported)
{
if (netSupported == null)
@ -545,7 +550,7 @@ namespace Avalonia.X11
private void OnNetSupportedChanged() =>
AllowedWindowActionsChanged?.Invoke(AllowedWindowActions);
private void OnEvent(ref XEvent ev)
{
if (_inputRoot is null)
@ -1174,7 +1179,7 @@ namespace Avalonia.X11
_platform.X11Screens.Changed -= OnScreensChanged;
_platform.Globals.NetSupportedChanged -= OnNetSupportedChanged;
if (_useRenderWindow && _renderHandle != IntPtr.Zero)
{
_renderHandle = IntPtr.Zero;

Loading…
Cancel
Save