in src/Avalonia.X11/XI2Manager.cs [271:422]
private void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev)
{
if (ev.Type == XiEventType.XI_TouchBegin
|| ev.Type == XiEventType.XI_TouchUpdate
|| ev.Type == XiEventType.XI_TouchEnd)
{
var type = ev.Type == XiEventType.XI_TouchBegin ?
RawPointerEventType.TouchBegin :
(ev.Type == XiEventType.XI_TouchUpdate ?
RawPointerEventType.TouchUpdate :
RawPointerEventType.TouchEnd);
var rawPointerPoint = new RawPointerPoint()
{
Position = ev.Position
};
if (_pointerDevice.PressureXIValuatorClassInfo is {} valuatorClassInfo)
{
if (ev.Valuators.TryGetValue(valuatorClassInfo.Number, out var pressureValue))
{
// In our API we use range from 0.0 to 1.0.
var pressure = (pressureValue - valuatorClassInfo.Min) / (valuatorClassInfo.Max - valuatorClassInfo.Min);
rawPointerPoint.Pressure = (float)pressure;
}
}
if(_pointerDevice.TouchMajorXIValuatorClassInfo is {} touchMajorXIValuatorClassInfo)
{
double? touchMajor = null;
double? touchMinor = null;
PixelRect screenBounds = default;
if (ev.Valuators.TryGetValue(touchMajorXIValuatorClassInfo.Number, out var touchMajorValue))
{
var pixelPoint = new PixelPoint((int)ev.RootPosition.X, (int)ev.RootPosition.Y);
var screen = _platform.Screens.ScreenFromPoint(pixelPoint);
if (screen?.Bounds is { } screenBoundsFromPoint)
{
screenBounds = screenBoundsFromPoint;
// 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) /
(touchMajorXIValuatorClassInfo.Max - touchMajorXIValuatorClassInfo.Min) * screenBounds.Width;
}
}
if (touchMajor != null)
{
if(_pointerDevice.TouchMinorXIValuatorClassInfo is {} touchMinorXIValuatorClassInfo)
{
if (ev.Valuators.TryGetValue(touchMinorXIValuatorClassInfo.Number, out var touchMinorValue))
{
touchMinor = (touchMinorValue - touchMinorXIValuatorClassInfo.Min) /
(touchMinorXIValuatorClassInfo.Max - touchMinorXIValuatorClassInfo.Min) * screenBounds.Height;
}
}
if (touchMinor == null)
{
touchMinor = touchMajor;
}
var center = ev.Position;
var leftX = center.X - touchMajor.Value / 2;
var topY = center.Y - touchMinor.Value / 2;
rawPointerPoint.ContactRect = new Rect
(
leftX,
topY,
touchMajor.Value,
touchMinor.Value
);
}
}
client.ScheduleXI2Input(new RawTouchEventArgs(client.TouchDevice,
ev.Timestamp, client.InputRoot, type, rawPointerPoint, ev.Modifiers, ev.Detail));
return;
}
if (!client.IsEnabled || (_multitouch && ev.Emulated))
return;
if (ev.Type == XiEventType.XI_Motion)
{
Vector scrollDelta = default;
foreach (var v in ev.Valuators)
{
foreach (var scroller in _pointerDevice.Scrollers)
{
if (scroller.Number == v.Key)
{
var old = _pointerDevice.Valuators[scroller.Number].Value;
// Value was zero after reset, ignore the event and use it as a reference next time
if (old == 0)
continue;
var diff = (old - v.Value) / scroller.Increment;
if (scroller.ScrollType == XiScrollType.Horizontal)
scrollDelta = scrollDelta.WithX(scrollDelta.X + diff);
else
scrollDelta = scrollDelta.WithY(scrollDelta.Y + diff);
}
}
}
if (scrollDelta != default)
client.ScheduleXI2Input(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp,
client.InputRoot, ev.Position, scrollDelta, ev.Modifiers));
if (_pointerDevice.HasMotion(ev))
client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
RawPointerEventType.Move, ev.Position, ev.Modifiers));
}
if (ev.Type == XiEventType.XI_ButtonPress && ev.Button >= 4 && ev.Button <= 7 && !ev.Emulated)
{
var scrollDelta = ev.Button switch
{
4 => new Vector(0, 1),
5 => new Vector(0, -1),
6 => new Vector(1, 0),
7 => new Vector(-1, 0),
_ => (Vector?)null
};
if (scrollDelta.HasValue)
client.ScheduleXI2Input(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp,
client.InputRoot, ev.Position, scrollDelta.Value, ev.Modifiers));
}
if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease)
{
var down = ev.Type == XiEventType.XI_ButtonPress;
var type = ev.Button switch
{
1 => down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp,
2 => down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp,
3 => down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp,
8 => down ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Up,
9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up,
_ => (RawPointerEventType?)null
};
if (type.HasValue)
client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
type.Value, ev.Position, ev.Modifiers));
}
_pointerDevice.UpdateValuators(ev.Valuators);
}