From 8f1e8590894d101dc57282c31a9759c5aad9d6ce Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 15:53:32 +0300 Subject: [PATCH 1/2] Fixed XI_Leave handling --- src/Avalonia.X11/XI2Manager.cs | 76 ++++++++++++++++++++++++---------- src/Avalonia.X11/XIStructs.cs | 43 ++++++++++++++++++- 2 files changed, 95 insertions(+), 24 deletions(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index d0655d05c1..b3a24e6c37 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -14,7 +14,9 @@ namespace Avalonia.X11 XiEventType.XI_Motion, XiEventType.XI_ButtonPress, XiEventType.XI_ButtonRelease, - XiEventType.XI_Leave + XiEventType.XI_Leave, + XiEventType.XI_Enter, + }; private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[] @@ -180,13 +182,37 @@ namespace Avalonia.X11 if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion) - || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd) - || xev->evtype == XiEventType.XI_Leave) + || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd)) { var dev = (XIDeviceEvent*)xev; if (_clients.TryGetValue(dev->EventWindow, out var client)) OnDeviceEvent(client, new ParsedDeviceEvent(dev)); } + + if (xev->evtype == XiEventType.XI_Leave || xev->evtype == XiEventType.XI_Enter) + { + var rev = (XIEnterLeaveEvent*)xev; + if (_clients.TryGetValue(rev->EventWindow, out var client)) + OnEnterLeaveEvent(client, ref *rev); + } + } + + void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev) + { + if (ev.evtype == XiEventType.XI_Leave) + { + var buttons = ParsedDeviceEvent.ParseButtonState(ev.buttons.MaskLen, ev.buttons.Mask); + var detail = ev.detail; + if ((detail == XiEnterLeaveDetail.XINotifyNonlinearVirtual || + detail == XiEnterLeaveDetail.XINotifyNonlinear || + detail == XiEnterLeaveDetail.XINotifyVirtual) + && buttons == default) + { + client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, (ulong)ev.time.ToInt64(), + client.InputRoot, + RawPointerEventType.LeaveWindow, new Point(ev.event_x, ev.event_y), buttons)); + } + } } void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev) @@ -205,12 +231,6 @@ namespace Avalonia.X11 return; } - if (ev.Type == XiEventType.XI_Leave) - { - client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, - RawPointerEventType.LeaveWindow, ev.Position, ev.Modifiers)); - } - if (_multitouch && ev.Emulated) return; @@ -294,6 +314,29 @@ namespace Avalonia.X11 public int Detail { get; set; } public bool Emulated { get; set; } public Dictionary Valuators { get; } + + public static RawInputModifiers ParseButtonState(int len, byte* buttons) + { + RawInputModifiers rv = default; + if (len > 0) + { + if (XIMaskIsSet(buttons, 1)) + rv |= RawInputModifiers.LeftMouseButton; + if (XIMaskIsSet(buttons, 2)) + rv |= RawInputModifiers.MiddleMouseButton; + if (XIMaskIsSet(buttons, 3)) + rv |= RawInputModifiers.RightMouseButton; + if (len > 1) + { + if (XIMaskIsSet(buttons, 8)) + rv |= RawInputModifiers.XButton1MouseButton; + if (XIMaskIsSet(buttons, 9)) + rv |= RawInputModifiers.XButton2MouseButton; + } + } + return rv; + } + public ParsedDeviceEvent(XIDeviceEvent* ev) { Type = ev->evtype; @@ -308,20 +351,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - if (ev->buttons.MaskLen > 1 && Type != XiEventType.XI_Leave) - { - var buttons = ev->buttons.Mask; - if (XIMaskIsSet(buttons, 1)) - Modifiers |= RawInputModifiers.LeftMouseButton; - if (XIMaskIsSet(buttons, 2)) - Modifiers |= RawInputModifiers.MiddleMouseButton; - if (XIMaskIsSet(buttons, 3)) - Modifiers |= RawInputModifiers.RightMouseButton; - if (XIMaskIsSet(buttons, 8)) - Modifiers |= RawInputModifiers.XButton1MouseButton; - if (XIMaskIsSet(buttons, 9)) - Modifiers |= RawInputModifiers.XButton2MouseButton; - } + Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); diff --git a/src/Avalonia.X11/XIStructs.cs b/src/Avalonia.X11/XIStructs.cs index 4675ef47f2..f4581a99ba 100644 --- a/src/Avalonia.X11/XIStructs.cs +++ b/src/Avalonia.X11/XIStructs.cs @@ -236,6 +236,34 @@ namespace Avalonia.X11 public XIModifierState mods; public XIModifierState group; } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct XIEnterLeaveEvent + { + public XEventName type; /* GenericEvent */ + public UIntPtr serial; /* # of last request processed by server */ + public Bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public int extension; /* XI extension offset */ + public XiEventType evtype; + public IntPtr time; + public int deviceid; + public int sourceid; + public XiEnterLeaveDetail detail; + public IntPtr RootWindow; + public IntPtr EventWindow; + public IntPtr ChildWindow; + public double root_x; + public double root_y; + public double event_x; + public double event_y; + public int mode; + public int focus; + public int same_screen; + public XIButtonState buttons; + public XIModifierState mods; + public XIModifierState group; + } [Flags] public enum XiDeviceEventFlags : int @@ -286,5 +314,18 @@ namespace Avalonia.X11 XI_BarrierLeave = 26, XI_LASTEVENT = XI_BarrierLeave, } - + + enum XiEnterLeaveDetail + { + XINotifyAncestor = 0, + XINotifyVirtual = 1, + XINotifyInferior = 2, + XINotifyNonlinear = 3, + XINotifyNonlinearVirtual = 4, + XINotifyPointer = 5, + XINotifyPointerRoot = 6, + XINotifyDetailNone = 7 + + } + } From 5d97e7fbf4f428f2578529de30d12a7152dd6c4d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 16:11:08 +0300 Subject: [PATCH 2/2] [DataGrid] Capture mouse pointer only when drag operation has actually started --- .../DataGridColumnHeader.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 856d1f6566..77f23392e9 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -340,8 +340,6 @@ namespace Avalonia.Controls if (OwningGrid != null && OwningGrid.ColumnHeaders != null) { - args.Pointer.Capture(this); - _dragMode = DragMode.MouseDown; _frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth(); _lastMousePositionHeaders = this.Translate(OwningGrid.ColumnHeaders, mousePosition); @@ -413,8 +411,9 @@ namespace Avalonia.Controls } //TODO DragEvents - internal void OnMouseMove(ref bool handled, Point mousePosition, Point mousePositionHeaders) + internal void OnMouseMove(PointerEventArgs args, Point mousePosition, Point mousePositionHeaders) { + var handled = args.Handled; if (handled || OwningGrid == null || OwningGrid.ColumnHeaders == null) { return; @@ -438,7 +437,10 @@ namespace Avalonia.Controls } _lastMousePositionHeaders = mousePositionHeaders; - + + if (args.Pointer.Captured != this) + args.Pointer.Capture(this); + SetDragCursor(mousePosition); } @@ -506,8 +508,7 @@ namespace Avalonia.Controls Point mousePosition = e.GetPosition(this); Point mousePositionHeaders = e.GetPosition(OwningGrid.ColumnHeaders); - bool handled = false; - OnMouseMove(ref handled, mousePosition, mousePositionHeaders); + OnMouseMove(e, mousePosition, mousePositionHeaders); } ///