Browse Source

Xdnd: add global event hook instead of grabbing pointer

pull/20926/head
Julien Lebosquain 6 months ago
parent
commit
fa922c122c
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 10
      src/Avalonia.X11/Dispatching/X11EventDispatcher.cs
  2. 136
      src/Avalonia.X11/Selections/DragDrop/X11DragSource.cs

10
src/Avalonia.X11/Dispatching/X11EventDispatcher.cs

@ -22,6 +22,8 @@ internal class X11EventDispatcher
} }
public bool IsPending => XPending(_display) != 0; public bool IsPending => XPending(_display) != 0;
public IEventHook? EventHook { get; set; }
public unsafe void DispatchX11Events(CancellationToken cancellationToken) public unsafe void DispatchX11Events(CancellationToken cancellationToken)
{ {
@ -38,6 +40,9 @@ internal class X11EventDispatcher
XGetEventData(_display, &xev.GenericEventCookie); XGetEventData(_display, &xev.GenericEventCookie);
try try
{ {
if (EventHook?.TryHandleEvent(in xev) == true)
continue;
if (xev.type == XEventName.GenericEvent) if (xev.type == XEventName.GenericEvent)
{ {
if (_platform.XI2 != null && _platform.Info.XInputOpcode == if (_platform.XI2 != null && _platform.Info.XInputOpcode ==
@ -59,4 +64,9 @@ internal class X11EventDispatcher
} }
public void Flush() => XFlush(_display); public void Flush() => XFlush(_display);
public interface IEventHook
{
bool TryHandleEvent(in XEvent evt);
}
} }

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

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
@ -43,7 +44,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
return await handler.Completion; return await handler.Completion;
} }
private sealed class Handler : IDisposable private sealed class Handler : X11EventDispatcher.IEventHook, IDisposable
{ {
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
private readonly IntPtr _sourceWindow; private readonly IntPtr _sourceWindow;
@ -73,7 +74,6 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_dataTransfer = dataTransfer; _dataTransfer = dataTransfer;
_allowedEffects = allowedEffects; _allowedEffects = allowedEffects;
_cursorFactory = cursorFactory; _cursorFactory = cursorFactory;
_currentEffects = allowedEffects;
_logger = Logger.TryGet(LogEventLevel.Verbose, LogArea.X11Platform); _logger = Logger.TryGet(LogEventLevel.Verbose, LogArea.X11Platform);
_dataProvider = new DragDropDataProvider(platform, dataTransfer.ToAsynchronous()); _dataProvider = new DragDropDataProvider(platform, dataTransfer.ToAsynchronous());
@ -84,31 +84,14 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
return; return;
} }
// Note: in the standard case (starting a drop-drop operation on pointer pressed), X11 already has an // We're assuming we have an implicit grab here.
// implicit capture. However, the capture is from our child render window when GL is used, instead of
// the parent window. For now, release any existing capture and start our own.
// TODO: make the render window invisible from input using XShape.
XUngrabPointer(platform.Display, 0);
var grabResult = XGrabPointer(
platform.Display,
_sourceWindow,
false,
EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask,
GrabMode.GrabModeAsync,
GrabMode.GrabModeAsync,
0,
GetCursor(GetEffectiveAllowedEffects(initialKeyModifiers)),
0);
if (grabResult != GrabResult.GrabSuccess)
{
_formatAtoms = [];
Complete(DragDropEffects.None);
return;
}
_pointerGrabbed = true; _pointerGrabbed = true;
UpdateCurrentEffects(GetEffectiveAllowedEffects(initialKeyModifiers));
// Install our global event hook.
var eventDispatcher = platform.DispatcherImpl.EventDispatcher;
Debug.Assert(eventDispatcher.EventHook is null);
eventDispatcher.EventHook = this;
// Replace the window event handler with our own during the drag operation. // Replace the window event handler with our own during the drag operation.
_originalSourceWindowInfo = sourceWindowInfo; _originalSourceWindowInfo = sourceWindowInfo;
@ -136,22 +119,86 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private DragDropTimeoutManager TimeoutManager private DragDropTimeoutManager TimeoutManager
=> _timeoutManager ??= new DragDropTimeoutManager(SelectionHelper.Timeout, OnTimeout); => _timeoutManager ??= new DragDropTimeoutManager(SelectionHelper.Timeout, OnTimeout);
private void OnEvent(ref XEvent evt) public bool TryHandleEvent(in XEvent evt)
{ {
if (!_pointerGrabbed)
return false;
switch (evt.type) switch (evt.type)
{ {
case XEventName.MotionNotify when _pointerGrabbed: case XEventName.MotionNotify:
OnMotionNotify(in evt.MotionEvent); OnPointerMoved(
break; new PixelPoint(evt.MotionEvent.x_root, evt.MotionEvent.y_root),
evt.MotionEvent.time,
evt.MotionEvent.state.ToRawInputModifiers());
return true;
case XEventName.ButtonPress:
return true;
case XEventName.ButtonRelease:
OnPointerReleased(
new PixelPoint(evt.ButtonEvent.x_root, evt.ButtonEvent.y_root),
evt.ButtonEvent.time,
evt.ButtonEvent.state.ToRawInputModifiers());
return true;
case XEventName.KeyPress or XEventName.KeyRelease:
OnKey(in evt.KeyEvent);
return true;
case XEventName.ButtonRelease when _pointerGrabbed: case XEventName.GenericEvent:
OnButtonRelease(in evt.ButtonEvent); if (_platform.XI2 is null || _platform.Info.XInputOpcode != evt.GenericEventCookie.extension)
break; return false;
case XEventName.KeyPress or XEventName.KeyRelease when _pointerGrabbed: unsafe
OnKey(in evt.KeyEvent); {
break; var xiEvent = (XIEvent*)evt.GenericEventCookie.data;
switch (xiEvent->evtype)
{
case XiEventType.XI_Motion:
case XiEventType.XI_TouchUpdate:
{
var deviceEvent = (XIDeviceEvent*)xiEvent;
var modifiers = ((XModifierMask)deviceEvent->mods.Effective).ToRawInputModifiers();
OnPointerMoved(
new PixelPoint((int)deviceEvent->root_x, (int)deviceEvent->root_y),
deviceEvent->time,
modifiers);
return true;
}
case XiEventType.XI_ButtonPress:
case XiEventType.XI_TouchBegin:
return true;
case XiEventType.XI_ButtonRelease:
case XiEventType.XI_TouchEnd:
{
var deviceEvent = (XIDeviceEvent*)xiEvent;
var modifiers = ((XModifierMask)deviceEvent->mods.Effective).ToRawInputModifiers();
OnPointerReleased(
new PixelPoint((int)deviceEvent->root_x, (int)deviceEvent->root_y),
deviceEvent->time,
modifiers);
return true;
}
default:
return false;
}
}
default:
return false;
}
}
private void OnEvent(ref XEvent evt)
{
switch (evt.type)
{
case XEventName.ClientMessage: case XEventName.ClientMessage:
ref var message = ref evt.ClientMessageEvent; ref var message = ref evt.ClientMessageEvent;
var atoms = _platform.Info.Atoms; var atoms = _platform.Info.Atoms;
@ -174,15 +221,13 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
} }
} }
private void OnMotionNotify(in XMotionEvent motion) private void OnPointerMoved(PixelPoint rootPosition, IntPtr timestamp, RawInputModifiers modifiers)
{ {
// Drop pending, ignore any queued position change. // Drop pending, ignore any queued position change.
if (_targetState.PendingDrop is not null) if (_targetState.PendingDrop is not null)
return; return;
var rootPosition = new PixelPoint(motion.x_root, motion.y_root);
var target = FindXdndTarget(rootPosition); var target = FindXdndTarget(rootPosition);
var modifiers = motion.state.ToRawInputModifiers();
// Handle new target. // Handle new target.
if (_targetState.Target != target) if (_targetState.Target != target)
@ -221,7 +266,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
// Update current target. // Update current target.
if (target is { } currentTarget) if (target is { } currentTarget)
UpdateTargetPosition(currentTarget, rootPosition, modifiers, motion.time); UpdateTargetPosition(currentTarget, rootPosition, modifiers, timestamp);
} }
private void UpdateTargetPosition( private void UpdateTargetPosition(
@ -290,7 +335,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
UpdateTargetPosition(target, position, modifiers.ToRawInputModifiers(), key.time); UpdateTargetPosition(target, position, modifiers.ToRawInputModifiers(), key.time);
} }
private void OnButtonRelease(in XButtonEvent button) private void OnPointerReleased(PixelPoint rootPosition, IntPtr timestamp, RawInputModifiers modifiers)
{ {
UngrabPointer(); UngrabPointer();
@ -299,9 +344,6 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
if (target.InProcessWindow is not null) if (target.InProcessWindow is not null)
{ {
var rootPosition = new PixelPoint(button.x_root, button.y_root);
var modifiers = button.state.ToRawInputModifiers();
if (_targetState.AllowsDrop) if (_targetState.AllowsDrop)
{ {
var effects = ProcessRawDragEvent(target.InProcessWindow, RawDragEventType.Drop, rootPosition, modifiers); var effects = ProcessRawDragEvent(target.InProcessWindow, RawDragEventType.Drop, rootPosition, modifiers);
@ -318,7 +360,7 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
} }
else else
{ {
var dropRequest = new DropRequest(button.time); var dropRequest = new DropRequest(timestamp);
if (_targetState.IsWaitingForStatus) if (_targetState.IsWaitingForStatus)
{ {
@ -670,6 +712,10 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_originalSourceWindowInfo = null; _originalSourceWindowInfo = null;
} }
var eventDispatcher = _platform.DispatcherImpl.EventDispatcher;
if (eventDispatcher.EventHook == this)
eventDispatcher.EventHook = null;
_targetState = default; _targetState = default;
_currentEffects = DragDropEffects.None; _currentEffects = DragDropEffects.None;

Loading…
Cancel
Save