Browse Source

Xdnd: update target on key press/release

pull/20926/head
Julien Lebosquain 6 months ago
parent
commit
5b381b7096
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 142
      src/Avalonia.X11/Selections/DragDrop/X11DragSource.cs
  2. 3
      src/Avalonia.X11/X11Atoms.cs
  3. 3
      src/Avalonia.X11/XLib.cs

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

@ -138,30 +138,40 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
private void OnEvent(ref XEvent evt)
{
if (evt.type == XEventName.MotionNotify && _pointerGrabbed)
OnMotionNotify(in evt.MotionEvent);
else if (evt.type == XEventName.ButtonRelease && _pointerGrabbed)
OnButtonRelease(in evt.ButtonEvent);
else if (evt.type == XEventName.ClientMessage)
switch (evt.type)
{
ref var message = ref evt.ClientMessageEvent;
var atoms = _platform.Info.Atoms;
case XEventName.MotionNotify when _pointerGrabbed:
OnMotionNotify(in evt.MotionEvent);
break;
case XEventName.ButtonRelease when _pointerGrabbed:
OnButtonRelease(in evt.ButtonEvent);
break;
case XEventName.KeyPress or XEventName.KeyRelease when _pointerGrabbed:
OnKey(in evt.KeyEvent);
break;
case XEventName.ClientMessage:
ref var message = ref evt.ClientMessageEvent;
var atoms = _platform.Info.Atoms;
if (message.message_type == atoms.XdndStatus)
OnXdndStatus(in message);
else if (message.message_type == atoms.XdndFinished)
OnXdndFinished(in message);
else
_originalSourceWindowInfo?.EventHandler(ref evt);
break;
if (message.message_type == atoms.XdndStatus)
OnXdndStatus(in message);
else if (message.message_type == atoms.XdndFinished)
OnXdndFinished(in message);
else
case XEventName.SelectionRequest:
_dataProvider.OnSelectionRequest(in evt.SelectionRequestEvent);
break;
default:
_originalSourceWindowInfo?.EventHandler(ref evt);
break;
}
else if (evt.type == XEventName.SelectionRequest)
_dataProvider.OnSelectionRequest(in evt.SelectionRequestEvent);
else
_originalSourceWindowInfo?.EventHandler(ref evt);
}
private void OnMotionNotify(in XMotionEvent motion)
@ -173,7 +183,6 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
var rootPosition = new PixelPoint(motion.x_root, motion.y_root);
var target = FindXdndTarget(rootPosition);
var modifiers = motion.state.ToRawInputModifiers();
var effectiveAllowedEffects = GetEffectiveAllowedEffects(modifiers.ToKeyModifiers());
// Handle new target.
if (_targetState.Target != target)
@ -193,6 +202,8 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
}
_targetState = new TargetState(target);
var effectiveAllowedEffects = GetEffectiveAllowedEffects(modifiers.ToKeyModifiers());
UpdateCurrentEffects(effectiveAllowedEffects);
if (target is { } newTarget)
@ -210,37 +221,75 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
// Update current target.
if (target is { } currentTarget)
UpdateTargetPosition(currentTarget, rootPosition, modifiers, motion.time);
}
private void UpdateTargetPosition(
XdndTargetInfo target,
PixelPoint rootPosition,
RawInputModifiers modifiers,
IntPtr timestamp)
{
if (target.InProcessWindow is { } window)
{
var effects = ProcessRawDragEvent(window, RawDragEventType.DragOver, rootPosition, modifiers);
_targetState.AllowsDrop = effects != DragDropEffects.None;
UpdateCurrentEffects(effects);
}
else
{
if (currentTarget.InProcessWindow is { } window)
var effectiveAllowedEffects = GetEffectiveAllowedEffects(modifiers.ToKeyModifiers());
var action = XdndActionHelper.EffectsToAction(effectiveAllowedEffects, _platform.Info.Atoms);
var positionRequest = new PositionRequest(rootPosition, timestamp, action);
if (_targetState.IsWaitingForStatus)
{
var effects = ProcessRawDragEvent(window, RawDragEventType.DragOver, rootPosition, modifiers);
_targetState.AllowsDrop = effects != DragDropEffects.None;
UpdateCurrentEffects(effects);
// We already sent a position previously and are waiting for a response. Don't flood.
_targetState.PendingPosition = positionRequest;
_logger?.Log(
this,
"Pointer moved to point {Position} on window {Window} while waiting for XdndStatus. XdndPosition will be sent later.",
rootPosition,
target.TargetWindow);
}
else
{
var action = XdndActionHelper.EffectsToAction(effectiveAllowedEffects, _platform.Info.Atoms);
var positionRequest = new PositionRequest(rootPosition, motion.time, action);
if (_targetState.IsWaitingForStatus)
{
// We already sent a position previously and are waiting for a response. Don't flood.
_targetState.PendingPosition = positionRequest;
_logger?.Log(
this,
"Pointer moved to point {Position} on window {Window} while waiting for XdndStatus. XdndPosition will be sent later.",
rootPosition,
currentTarget.TargetWindow);
}
else
{
SendPositionRequest(positionRequest, currentTarget);
}
SendPositionRequest(positionRequest, target);
}
}
}
private void OnKey(in XKeyEvent key)
{
if (_targetState.Target is not { } target || _targetState.PendingDrop is not null)
return;
var keySym = XLookupKeysym(in key, 0);
// If Shift/Ctrl/Alt are pressed/released, effective effects might change: update the target.
var interestingModifiers = keySym switch
{
XKeySym.XK_Shift_L or XKeySym.XK_Shift_R => XModifierMask.ShiftMask,
XKeySym.XK_Control_L or XKeySym.XK_Control_R => XModifierMask.ControlMask,
XKeySym.XK_Alt_L or XKeySym.XK_Alt_R => XModifierMask.Mod1Mask,
_ => default
};
if (interestingModifiers == 0)
return;
var modifiers = key.state;
if (key.type == XEventName.KeyPress)
modifiers |= interestingModifiers;
else if (key.type == XEventName.KeyRelease)
modifiers &= ~interestingModifiers;
var position = new PixelPoint(key.x_root, key.y_root);
UpdateTargetPosition(target, position, modifiers.ToRawInputModifiers(), key.time);
}
private void OnButtonRelease(in XButtonEvent button)
{
UngrabPointer();
@ -313,8 +362,8 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
_logger?.Log(
this,
"Received XdndStatus with effects {Effects} for window {Window}.",
effects,
"Received XdndStatus with action {Action} for window {Window}.",
_platform.Info.Atoms.GetAtomName(action),
target.TargetWindow);
// If we have any pending XdndPosition or XdndDrop to send, now is the time.
@ -443,7 +492,8 @@ internal sealed class X11DragSource(AvaloniaX11Platform platform) : IPlatformDra
{
_logger?.Log(
this,
"Sending XdndPosition at point {Position} to window {Window}.",
"Sending XdndPosition with action {Action} at point {Position} to window {Window}.",
_platform.Info.Atoms.GetAtomName(action),
rootPosition,
target.TargetWindow);

3
src/Avalonia.X11/X11Atoms.cs

@ -259,6 +259,9 @@ namespace Avalonia.X11
public string? GetAtomName(IntPtr atom)
{
if (atom == 0)
return null;
if (_atomsToNames.TryGetValue(atom, out var rv))
return rv;
var name = XLib.GetAtomName(_display, atom);

3
src/Avalonia.X11/XLib.cs

@ -471,6 +471,9 @@ namespace Avalonia.X11
XLookupKeySym = 3,
XLookupBoth = 4
}
[LibraryImport(libX11)]
public static partial XKeySym XLookupKeysym(in XKeyEvent key_event, int index);
[DllImport (libX11)]
public static extern int XLookupString(ref XKeyEvent xevent, byte* buffer, int num_bytes, out nint keysym, IntPtr composeStatus);

Loading…
Cancel
Save