Browse Source

Added basic Pen support for X11 (#19910)

* Added surface dispose to DrawingSurfaceDemoBase

* Added pen support for x11

* Fixed intermediate points defaulting properties

* Detect the current device being an eraser

* Formatting fixes

* Removed spaces

---------

Co-authored-by: flabbet <flabbet@fedora>
Co-authored-by: Nikita Tsukanov <keks9n@gmail.com>
pull/20295/head
Krzysztof Krysiński 2 months ago
committed by GitHub
parent
commit
79be86d923
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      src/Avalonia.X11/X11Window.cs
  2. 302
      src/Avalonia.X11/XI2Manager.cs
  3. 8
      src/Avalonia.X11/XIStructs.cs
  4. 2
      src/Shared/RawEventGrouping.cs

4
src/Avalonia.X11/X11Window.cs

@ -44,6 +44,7 @@ namespace Avalonia.X11
private bool _triggeredExpose; private bool _triggeredExpose;
private IInputRoot? _inputRoot; private IInputRoot? _inputRoot;
private readonly MouseDevice _mouse; private readonly MouseDevice _mouse;
private readonly PenDevice _pen;
private readonly TouchDevice _touch; private readonly TouchDevice _touch;
private readonly IKeyboardDevice _keyboard; private readonly IKeyboardDevice _keyboard;
private readonly ITopLevelNativeMenuExporter? _nativeMenuExporter; private readonly ITopLevelNativeMenuExporter? _nativeMenuExporter;
@ -97,6 +98,7 @@ namespace Avalonia.X11
_overrideRedirect = _popup || overrideRedirect; _overrideRedirect = _popup || overrideRedirect;
_x11 = platform.Info; _x11 = platform.Info;
_mouse = Avalonia.Input.MouseDevice.Primary; _mouse = Avalonia.Input.MouseDevice.Primary;
_pen = new PenDevice();
_touch = new TouchDevice(); _touch = new TouchDevice();
_keyboard = platform.KeyboardDevice; _keyboard = platform.KeyboardDevice;
@ -1072,6 +1074,7 @@ namespace Avalonia.X11
_platform.XI2?.OnWindowDestroyed(_handle); _platform.XI2?.OnWindowDestroyed(_handle);
var handle = _handle; var handle = _handle;
_handle = IntPtr.Zero; _handle = IntPtr.Zero;
_pen.Dispose();
_touch.Dispose(); _touch.Dispose();
if (!fromDestroyNotification) if (!fromDestroyNotification)
XDestroyWindow(_x11.Display, handle); XDestroyWindow(_x11.Display, handle);
@ -1238,6 +1241,7 @@ namespace Avalonia.X11
} }
public IMouseDevice MouseDevice => _mouse; public IMouseDevice MouseDevice => _mouse;
public IPenDevice PenDevice => _pen;
public TouchDevice TouchDevice => _touch; public TouchDevice TouchDevice => _touch;
public IPopupImpl? CreatePopup() public IPopupImpl? CreatePopup()

302
src/Avalonia.X11/XI2Manager.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
@ -16,7 +17,6 @@ namespace Avalonia.X11
XiEventType.XI_ButtonRelease, XiEventType.XI_ButtonRelease,
XiEventType.XI_Leave, XiEventType.XI_Leave,
XiEventType.XI_Enter, XiEventType.XI_Enter,
}; };
private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[] private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[]
@ -35,13 +35,14 @@ namespace Avalonia.X11
public int Id { get; } public int Id { get; }
public XIValuatorClassInfo[] Valuators { get; private set; } = []; public XIValuatorClassInfo[] Valuators { get; private set; } = [];
public XIScrollClassInfo[] Scrollers { get; private set; } = []; public XIScrollClassInfo[] Scrollers { get; private set; } = [];
public DeviceInfo(XIDeviceInfo info) public DeviceInfo(XIDeviceInfo info)
{ {
Id = info.Deviceid; Id = info.Deviceid;
UpdateCore(info.Classes, info.NumClasses); UpdateCore(info.Classes, info.NumClasses);
} }
public virtual void Update(XIAnyClassInfo** classes, int num) public virtual void Update(XIAnyClassInfo** classes, int num, int? slaveId)
{ {
UpdateCore(classes, num); UpdateCore(classes, num);
} }
@ -66,7 +67,7 @@ namespace Avalonia.X11
{ {
foreach (var v in valuators) foreach (var v in valuators)
{ {
if (Valuators.Length > v.Key) if (Valuators.Length > v.Key)
Valuators[v.Key].Value = v.Value; Valuators[v.Key].Value = v.Value;
} }
} }
@ -74,13 +75,16 @@ namespace Avalonia.X11
private class PointerDeviceInfo : DeviceInfo private class PointerDeviceInfo : DeviceInfo
{ {
private string? _currentSlaveName = null;
private bool _currentSlaveIsEraser = false;
public PointerDeviceInfo(XIDeviceInfo info, X11Info x11Info) : base(info) public PointerDeviceInfo(XIDeviceInfo info, X11Info x11Info) : base(info)
{ {
_x11 = x11Info; _x11 = x11Info;
UpdateKnownValuator(); UpdateKnownValuator();
} }
private readonly X11Info _x11; private readonly X11Info _x11;
private void UpdateKnownValuator() private void UpdateKnownValuator()
@ -91,6 +95,9 @@ namespace Avalonia.X11
var touchMinorAtom = XInternAtom(_x11.Display, "Abs MT Touch Minor", false); var touchMinorAtom = XInternAtom(_x11.Display, "Abs MT Touch Minor", false);
var pressureAtom = XInternAtom(_x11.Display, "Abs MT Pressure", false); var pressureAtom = XInternAtom(_x11.Display, "Abs MT Pressure", false);
var pressureAtomPen = XInternAtom(_x11.Display, "Abs Pressure", false);
var absTiltXAtom = XInternAtom(_x11.Display, "Abs Tilt X", false);
var absTiltYAtom = XInternAtom(_x11.Display, "Abs Tilt Y", false);
PressureXIValuatorClassInfo = null; PressureXIValuatorClassInfo = null;
TouchMajorXIValuatorClassInfo = null; TouchMajorXIValuatorClassInfo = null;
@ -98,7 +105,8 @@ namespace Avalonia.X11
foreach (var xiValuatorClassInfo in Valuators) foreach (var xiValuatorClassInfo in Valuators)
{ {
if (xiValuatorClassInfo.Label == pressureAtom) if (xiValuatorClassInfo.Label == pressureAtom ||
xiValuatorClassInfo.Label == pressureAtomPen)
{ {
PressureXIValuatorClassInfo = xiValuatorClassInfo; PressureXIValuatorClassInfo = xiValuatorClassInfo;
} }
@ -110,15 +118,52 @@ namespace Avalonia.X11
{ {
TouchMinorXIValuatorClassInfo = xiValuatorClassInfo; TouchMinorXIValuatorClassInfo = xiValuatorClassInfo;
} }
else if (xiValuatorClassInfo.Label == absTiltXAtom)
{
TiltXXIValuatorClassInfo = xiValuatorClassInfo;
}
else if (xiValuatorClassInfo.Label == absTiltYAtom)
{
TiltYXIValuatorClassInfo = xiValuatorClassInfo;
}
} }
} }
public override void Update(XIAnyClassInfo** classes, int num) public override void Update(XIAnyClassInfo** classes, int num, int? slaveId)
{ {
base.Update(classes, num); base.Update(classes, num, slaveId);
if (slaveId != null)
{
_currentSlaveName = null;
_currentSlaveIsEraser = false;
var devices = (XIDeviceInfo*)XIQueryDevice(_x11.Display,
(int)XiPredefinedDeviceId.XIAllDevices, out int deviceNum);
for (var c = 0; c < deviceNum; c++)
{
if (devices[c].Deviceid == slaveId)
{
_currentSlaveName = Marshal.PtrToStringAnsi(devices[c].Name);
_currentSlaveIsEraser =
_currentSlaveName?.IndexOf("eraser", StringComparison.OrdinalIgnoreCase) >= 0;
break;
}
}
XIFreeDeviceInfo(devices);
}
UpdateKnownValuator(); UpdateKnownValuator();
} }
public bool HasPressureValuator()
{
return PressureXIValuatorClassInfo is not null;
}
public bool IsEraser => _currentSlaveIsEraser;
public string? Name => _currentSlaveName;
public bool HasScroll(ParsedDeviceEvent ev) public bool HasScroll(ParsedDeviceEvent ev)
{ {
foreach (var val in ev.Valuators) foreach (var val in ev.Valuators)
@ -127,7 +172,7 @@ namespace Avalonia.X11
return false; return false;
} }
public bool HasMotion(ParsedDeviceEvent ev) public bool HasMotion(ParsedDeviceEvent ev)
{ {
foreach (var val in ev.Valuators) foreach (var val in ev.Valuators)
@ -140,8 +185,10 @@ namespace Avalonia.X11
public XIValuatorClassInfo? PressureXIValuatorClassInfo { get; private set; } public XIValuatorClassInfo? PressureXIValuatorClassInfo { get; private set; }
public XIValuatorClassInfo? TouchMajorXIValuatorClassInfo { get; private set; } public XIValuatorClassInfo? TouchMajorXIValuatorClassInfo { get; private set; }
public XIValuatorClassInfo? TouchMinorXIValuatorClassInfo { get; private set; } public XIValuatorClassInfo? TouchMinorXIValuatorClassInfo { get; private set; }
public XIValuatorClassInfo? TiltXXIValuatorClassInfo { get; private set; }
public XIValuatorClassInfo? TiltYXIValuatorClassInfo { get; private set; }
} }
private readonly PointerDeviceInfo _pointerDevice; private readonly PointerDeviceInfo _pointerDevice;
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
@ -157,7 +204,7 @@ namespace Avalonia.X11
{ {
var x11 = platform.Info; var x11 = platform.Info;
var devices = (XIDeviceInfo*) XIQueryDevice(x11.Display, var devices = (XIDeviceInfo*)XIQueryDevice(x11.Display,
(int)XiPredefinedDeviceId.XIAllMasterDevices, out int num); (int)XiPredefinedDeviceId.XIAllMasterDevices, out int num);
PointerDeviceInfo? pointerDevice = null; PointerDeviceInfo? pointerDevice = null;
@ -202,8 +249,8 @@ namespace Avalonia.X11
events.AddRange(MultiTouchEventTypes); events.AddRange(MultiTouchEventTypes);
XiSelectEvents(_x11.Display, xid, XiSelectEvents(_x11.Display, xid,
new Dictionary<int, List<XiEventType>> {[_pointerDevice.Id] = events}); new Dictionary<int, List<XiEventType>> { [_pointerDevice.Id] = events });
// We are taking over mouse input handling from here // We are taking over mouse input handling from here
return XEventMask.PointerMotionMask return XEventMask.PointerMotionMask
| XEventMask.ButtonMotionMask | XEventMask.ButtonMotionMask
@ -219,16 +266,15 @@ namespace Avalonia.X11
} }
public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid); public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid);
public void OnEvent(XIEvent* xev) public void OnEvent(XIEvent* xev)
{ {
if (xev->evtype == XiEventType.XI_DeviceChanged) if (xev->evtype == XiEventType.XI_DeviceChanged)
{ {
var changed = (XIDeviceChangedEvent*)xev; var changed = (XIDeviceChangedEvent*)xev;
_pointerDevice.Update(changed->Classes, changed->NumClasses); _pointerDevice.Update(changed->Classes, changed->NumClasses, changed->Reason == XiDeviceChangeReason.XISlaveSwitch ? changed->Sourceid : null);
} }
if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion) 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_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd))
{ {
@ -258,9 +304,9 @@ namespace Avalonia.X11
{ {
foreach (var scroller in _pointerDevice.Scrollers) foreach (var scroller in _pointerDevice.Scrollers)
{ {
_pointerDevice.Valuators[scroller.Number].Value = 0; _pointerDevice.Valuators[scroller.Number].Value = 0;
} }
client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, (ulong)ev.time.ToInt64(), client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, (ulong)ev.time.ToInt64(),
client.InputRoot, client.InputRoot,
RawPointerEventType.LeaveWindow, new Point(ev.event_x, ev.event_y), buttons)); RawPointerEventType.LeaveWindow, new Point(ev.event_x, ev.event_y), buttons));
@ -270,8 +316,8 @@ namespace Avalonia.X11
private void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev) private void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev)
{ {
if (ev.Type == XiEventType.XI_TouchBegin if (ev.Type == XiEventType.XI_TouchBegin
|| ev.Type == XiEventType.XI_TouchUpdate || ev.Type == XiEventType.XI_TouchUpdate
|| ev.Type == XiEventType.XI_TouchEnd) || ev.Type == XiEventType.XI_TouchEnd)
{ {
var type = ev.Type == XiEventType.XI_TouchBegin ? var type = ev.Type == XiEventType.XI_TouchBegin ?
@ -280,22 +326,20 @@ namespace Avalonia.X11
RawPointerEventType.TouchUpdate : RawPointerEventType.TouchUpdate :
RawPointerEventType.TouchEnd); RawPointerEventType.TouchEnd);
var rawPointerPoint = new RawPointerPoint() var rawPointerPoint = new RawPointerPoint() { Position = ev.Position };
{
Position = ev.Position
};
if (_pointerDevice.PressureXIValuatorClassInfo is {} valuatorClassInfo) if (_pointerDevice.PressureXIValuatorClassInfo is { } valuatorClassInfo)
{ {
if (ev.Valuators.TryGetValue(valuatorClassInfo.Number, out var pressureValue)) if (ev.Valuators.TryGetValue(valuatorClassInfo.Number, out var pressureValue))
{ {
// In our API we use range from 0.0 to 1.0. // In our API we use range from 0.0 to 1.0.
var pressure = (pressureValue - valuatorClassInfo.Min) / (valuatorClassInfo.Max - valuatorClassInfo.Min); var pressure = (pressureValue - valuatorClassInfo.Min) /
(valuatorClassInfo.Max - valuatorClassInfo.Min);
rawPointerPoint.Pressure = (float)pressure; rawPointerPoint.Pressure = (float)pressure;
} }
} }
if(_pointerDevice.TouchMajorXIValuatorClassInfo is {} touchMajorXIValuatorClassInfo) if (_pointerDevice.TouchMajorXIValuatorClassInfo is { } touchMajorXIValuatorClassInfo)
{ {
double? touchMajor = null; double? touchMajor = null;
double? touchMinor = null; double? touchMinor = null;
@ -310,18 +354,20 @@ namespace Avalonia.X11
// As https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html says, using `screenBounds.Width` is not accurate enough. // As https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html says, using `screenBounds.Width` is not accurate enough.
touchMajor = (touchMajorValue - touchMajorXIValuatorClassInfo.Min) / touchMajor = (touchMajorValue - touchMajorXIValuatorClassInfo.Min) /
(touchMajorXIValuatorClassInfo.Max - touchMajorXIValuatorClassInfo.Min) * screenBounds.Width; (touchMajorXIValuatorClassInfo.Max - touchMajorXIValuatorClassInfo.Min) *
screenBounds.Width;
} }
} }
if (touchMajor != null) if (touchMajor != null)
{ {
if(_pointerDevice.TouchMinorXIValuatorClassInfo is {} touchMinorXIValuatorClassInfo) if (_pointerDevice.TouchMinorXIValuatorClassInfo is { } touchMinorXIValuatorClassInfo)
{ {
if (ev.Valuators.TryGetValue(touchMinorXIValuatorClassInfo.Number, out var touchMinorValue)) if (ev.Valuators.TryGetValue(touchMinorXIValuatorClassInfo.Number, out var touchMinorValue))
{ {
touchMinor = (touchMinorValue - touchMinorXIValuatorClassInfo.Min) / touchMinor = (touchMinorValue - touchMinorXIValuatorClassInfo.Min) /
(touchMinorXIValuatorClassInfo.Max - touchMinorXIValuatorClassInfo.Min) * screenBounds.Height; (touchMinorXIValuatorClassInfo.Max - touchMinorXIValuatorClassInfo.Min) *
screenBounds.Height;
} }
} }
@ -351,10 +397,17 @@ namespace Avalonia.X11
if (!client.IsEnabled || (_multitouch && ev.Emulated)) if (!client.IsEnabled || (_multitouch && ev.Emulated))
return; return;
var eventModifiers = ev.Modifiers;
if (_pointerDevice.IsEraser)
eventModifiers |= RawInputModifiers.PenEraser;
if (ev.Type == XiEventType.XI_Motion) if (ev.Type == XiEventType.XI_Motion)
{ {
Vector scrollDelta = default; Vector scrollDelta = default;
var rawPointerPoint = new RawPointerPoint() { Position = ev.Position };
IInputDevice device = _pointerDevice.HasPressureValuator() ? client.PenDevice : client.MouseDevice;
foreach (var v in ev.Valuators) foreach (var v in ev.Valuators)
{ {
foreach (var scroller in _pointerDevice.Scrollers) foreach (var scroller in _pointerDevice.Scrollers)
@ -374,15 +427,15 @@ namespace Avalonia.X11
} }
} }
SetPenSpecificValues(v, ref rawPointerPoint);
} }
if (scrollDelta != default) if (scrollDelta != default)
client.ScheduleXI2Input(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp, client.ScheduleXI2Input(new RawMouseWheelEventArgs(device, ev.Timestamp,
client.InputRoot, ev.Position, scrollDelta, ev.Modifiers)); client.InputRoot, ev.Position, scrollDelta, eventModifiers));
if (_pointerDevice.HasMotion(ev)) if (_pointerDevice.HasMotion(ev))
client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, client.ScheduleXI2Input(new RawPointerEventArgs(device, ev.Timestamp, client.InputRoot,
RawPointerEventType.Move, ev.Position, ev.Modifiers)); RawPointerEventType.Move, rawPointerPoint, eventModifiers));
} }
if (ev.Type == XiEventType.XI_ButtonPress && ev.Button >= 4 && ev.Button <= 7 && !ev.Emulated) if (ev.Type == XiEventType.XI_ButtonPress && ev.Button >= 4 && ev.Button <= 7 && !ev.Emulated)
@ -395,10 +448,10 @@ namespace Avalonia.X11
7 => new Vector(-1, 0), 7 => new Vector(-1, 0),
_ => (Vector?)null _ => (Vector?)null
}; };
if (scrollDelta.HasValue) if (scrollDelta.HasValue)
client.ScheduleXI2Input(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp, client.ScheduleXI2Input(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp,
client.InputRoot, ev.Position, scrollDelta.Value, ev.Modifiers)); client.InputRoot, ev.Position, scrollDelta.Value, eventModifiers));
} }
if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease) if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease)
@ -413,87 +466,134 @@ namespace Avalonia.X11
9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up, 9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up,
_ => (RawPointerEventType?)null _ => (RawPointerEventType?)null
}; };
if (type.HasValue) if (type.HasValue)
client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, {
type.Value, ev.Position, ev.Modifiers)); IInputDevice device = _pointerDevice.HasPressureValuator() ? client.PenDevice : client.MouseDevice;
var pointerPoint = new RawPointerPoint() { Position = ev.Position };
SetPenSpecificValues(ev, ref pointerPoint);
client.ScheduleXI2Input(new RawPointerEventArgs(device, ev.Timestamp, client.InputRoot,
type.Value, pointerPoint, eventModifiers));
}
} }
_pointerDevice.UpdateValuators(ev.Valuators); _pointerDevice.UpdateValuators(ev.Valuators);
} }
}
private void SetPenSpecificValues(ParsedDeviceEvent ev, ref RawPointerPoint pointerPoint)
{
foreach (var evValuator in ev.Valuators)
{
SetPenSpecificValues(evValuator, ref pointerPoint);
}
}
internal unsafe class ParsedDeviceEvent private void SetPenSpecificValues(KeyValuePair<int, double> item, ref RawPointerPoint rawPointerPoint)
{
public XiEventType Type { get; }
public RawInputModifiers Modifiers { get; }
public ulong Timestamp { get; }
public Point Position { get; }
public Point RootPosition { get; }
public int Button { get; set; }
public int Detail { get; set; }
public bool Emulated { get; set; }
public Dictionary<int, double> Valuators { get; }
public static RawInputModifiers ParseButtonState(int len, byte* buttons)
{ {
RawInputModifiers rv = default; if (_pointerDevice.PressureXIValuatorClassInfo is { } valuatorClassInfo)
if (len > 0) {
if (item.Key == valuatorClassInfo.Number)
{
var pressure = (item.Value - valuatorClassInfo.Min) /
(valuatorClassInfo.Max - valuatorClassInfo.Min);
rawPointerPoint.Pressure = (float)pressure;
}
}
if (_pointerDevice.TiltXXIValuatorClassInfo is { } tiltXValuatorClassInfo)
{
if (item.Key == tiltXValuatorClassInfo.Number)
{
rawPointerPoint.XTilt = (float)item.Value;
}
}
if (_pointerDevice.TiltYXIValuatorClassInfo is { } tiltYValuatorClassInfo)
{ {
if (XIMaskIsSet(buttons, 1)) if (item.Key == tiltYValuatorClassInfo.Number)
rv |= RawInputModifiers.LeftMouseButton;
if (XIMaskIsSet(buttons, 2))
rv |= RawInputModifiers.MiddleMouseButton;
if (XIMaskIsSet(buttons, 3))
rv |= RawInputModifiers.RightMouseButton;
if (len > 1)
{ {
if (XIMaskIsSet(buttons, 8)) rawPointerPoint.YTilt = (float)item.Value;
rv |= RawInputModifiers.XButton1MouseButton;
if (XIMaskIsSet(buttons, 9))
rv |= RawInputModifiers.XButton2MouseButton;
} }
} }
return rv;
} }
public ParsedDeviceEvent(XIDeviceEvent* ev) internal unsafe class ParsedDeviceEvent
{ {
Type = ev->evtype; public XiEventType Type { get; }
Timestamp = (ulong)ev->time.ToInt64(); public RawInputModifiers Modifiers { get; }
var state = (XModifierMask)ev->mods.Effective; public ulong Timestamp { get; }
if (state.HasAllFlags(XModifierMask.ShiftMask)) public Point Position { get; }
Modifiers |= RawInputModifiers.Shift; public Point RootPosition { get; }
if (state.HasAllFlags(XModifierMask.ControlMask)) public int Button { get; set; }
Modifiers |= RawInputModifiers.Control; public int Detail { get; set; }
if (state.HasAllFlags(XModifierMask.Mod1Mask)) public bool Emulated { get; set; }
Modifiers |= RawInputModifiers.Alt; public Dictionary<int, double> Valuators { get; }
if (state.HasAllFlags(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta; public static RawInputModifiers ParseButtonState(int len, byte* buttons)
{
Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); RawInputModifiers rv = default;
if (len > 0)
Valuators = new Dictionary<int, double>(); {
Position = new Point(ev->event_x, ev->event_y); if (XIMaskIsSet(buttons, 1))
RootPosition = new Point(ev->root_x, ev->root_y); rv |= RawInputModifiers.LeftMouseButton;
var values = ev->valuators.Values; if (XIMaskIsSet(buttons, 2))
if(ev->valuators.Mask != null) rv |= RawInputModifiers.MiddleMouseButton;
for (var c = 0; c < ev->valuators.MaskLen * 8; c++) if (XIMaskIsSet(buttons, 3))
if (XIMaskIsSet(ev->valuators.Mask, c)) rv |= RawInputModifiers.RightMouseButton;
Valuators[c] = *values++; if (len > 1)
{
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease) if (XIMaskIsSet(buttons, 8))
Button = ev->detail; rv |= RawInputModifiers.XButton1MouseButton;
Detail = ev->detail; if (XIMaskIsSet(buttons, 9))
Emulated = ev->flags.HasAllFlags(XiDeviceEventFlags.XIPointerEmulated); rv |= RawInputModifiers.XButton2MouseButton;
}
}
return rv;
}
public ParsedDeviceEvent(XIDeviceEvent* ev)
{
Type = ev->evtype;
Timestamp = (ulong)ev->time.ToInt64();
var state = (XModifierMask)ev->mods.Effective;
if (state.HasAllFlags(XModifierMask.ShiftMask))
Modifiers |= RawInputModifiers.Shift;
if (state.HasAllFlags(XModifierMask.ControlMask))
Modifiers |= RawInputModifiers.Control;
if (state.HasAllFlags(XModifierMask.Mod1Mask))
Modifiers |= RawInputModifiers.Alt;
if (state.HasAllFlags(XModifierMask.Mod4Mask))
Modifiers |= RawInputModifiers.Meta;
Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask);
Valuators = new Dictionary<int, double>();
Position = new Point(ev->event_x, ev->event_y);
RootPosition = new Point(ev->root_x, ev->root_y);
var values = ev->valuators.Values;
if (ev->valuators.Mask != null)
for (var c = 0; c < ev->valuators.MaskLen * 8; c++)
if (XIMaskIsSet(ev->valuators.Mask, c))
Valuators[c] = *values++;
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease)
Button = ev->detail;
Detail = ev->detail;
Emulated = ev->flags.HasAllFlags(XiDeviceEventFlags.XIPointerEmulated);
}
} }
} }
internal interface IXI2Client internal interface IXI2Client
{ {
bool IsEnabled { get; } bool IsEnabled { get; }
IInputRoot InputRoot { get; } IInputRoot InputRoot { get; }
void ScheduleXI2Input(RawInputEventArgs args); void ScheduleXI2Input(RawInputEventArgs args);
IMouseDevice MouseDevice { get; } IMouseDevice MouseDevice { get; }
TouchDevice TouchDevice { get; } IPenDevice PenDevice { get; }
TouchDevice TouchDevice { get; }
} }
} }

8
src/Avalonia.X11/XIStructs.cs

@ -205,7 +205,7 @@ namespace Avalonia.X11
public IntPtr Time; public IntPtr Time;
public int Deviceid; /* id of the device that changed */ public int Deviceid; /* id of the device that changed */
public int Sourceid; /* Source for the new classes. */ public int Sourceid; /* Source for the new classes. */
public int Reason; /* Reason for the change */ public XiDeviceChangeReason Reason; /* Reason for the change */
public int NumClasses; public int NumClasses;
public XIAnyClassInfo** Classes; /* same as in XIDeviceInfo */ public XIAnyClassInfo** Classes; /* same as in XIDeviceInfo */
} }
@ -272,6 +272,12 @@ namespace Avalonia.X11
XIPointerEmulated = (1 << 16) XIPointerEmulated = (1 << 16)
} }
internal enum XiDeviceChangeReason : int
{
XISlaveSwitch = 1,
XIDeviceChange = 2
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal unsafe struct XIEvent internal unsafe struct XIEvent
{ {

2
src/Shared/RawEventGrouping.cs

@ -180,7 +180,7 @@ internal class RawEventGrouper : IDisposable
{ {
last.IntermediatePoints ??= new Lazy<IReadOnlyList<RawPointerPoint>?>(s_getPooledListDelegate); last.IntermediatePoints ??= new Lazy<IReadOnlyList<RawPointerPoint>?>(s_getPooledListDelegate);
((PooledList<RawPointerPoint>)last.IntermediatePoints.Value!).Add(new RawPointerPoint { Position = last.Position }); ((PooledList<RawPointerPoint>)last.IntermediatePoints.Value!).Add(new RawPointerPoint { Position = last.Position, Pressure = last.Point.Pressure, ContactRect = last.Point.ContactRect, Twist = last.Point.Twist, XTilt = last.Point.XTilt, YTilt = last.Point.YTilt });
last.Position = current.Position; last.Position = current.Position;
last.Timestamp = current.Timestamp; last.Timestamp = current.Timestamp;
last.InputModifiers = current.InputModifiers; last.InputModifiers = current.InputModifiers;

Loading…
Cancel
Save