|
|
@ -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; } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|