-
Notifications
You must be signed in to change notification settings - Fork 6
/
Digitizer.cs
180 lines (158 loc) · 6.91 KB
/
Digitizer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using Avalonia.DfrFrameBuffer.Device.Hid;
using Avalonia.Input;
using Avalonia.Input.Raw;
using HidSharp;
using HidSharp.Reports;
using HidSharp.Reports.Input;
using System;
using System.Linq;
namespace Avalonia.DfrFrameBuffer
{
public class Digitizer
{
private readonly double _scale;
private readonly double _width;
private readonly double _height;
private readonly Vector _dpi;
private HidDevice _digitizer;
private ReportDescriptor _reportDescr;
private HidStream _hidStream;
private HidDeviceInputReceiver _hidDeviceInputReceiver;
private DeviceItemInputParser _hiddeviceInputParser;
// Pre-allocated slots
private TouchReport[] _prevReports;
private int _prevTappedSlotIndex;
public event Action<RawInputEventArgs> Event;
public Digitizer(double physicalWidth, double physicalHeight, Vector? dpi = null)
{
// 96 DPI is the standard 100% scale
_dpi = dpi ?? new Vector(192, 192);
_scale = 96 / _dpi.X;
_width = physicalWidth * _scale;
_height = physicalHeight * _scale;
_prevTappedSlotIndex = -1;
_prevReports = new TouchReport[11];
for (int i = 0; i < 11; i++)
{
_prevReports[i] = new TouchReport(0, 32767, false);
}
// Discover DFR digitizer device
_digitizer = DeviceList.Local.GetHidDeviceOrNull(0x05ac, 0x8302);
if (_digitizer == null)
{
throw new Exception("iBridge HID digitizer not found");
}
_reportDescr = _digitizer.GetReportDescriptor();
}
public void Start()
{
if (_digitizer.TryOpen(out _hidStream))
{
_hidDeviceInputReceiver = _reportDescr.CreateHidDeviceInputReceiver();
_hiddeviceInputParser = _reportDescr.DeviceItems[0].CreateDeviceItemInputParser();
_hidDeviceInputReceiver.Received += OnDigitizerInputReceived;
_hidDeviceInputReceiver.Start(_hidStream);
}
else
{
throw new Exception("Failed to open iBridge HID digitizer");
}
}
private void OnDigitizerInputReceived(object sender, EventArgs e)
{
var inputReportBuffer = new byte[_digitizer.GetMaxInputReportLength()];
while (_hidDeviceInputReceiver.TryRead(inputReportBuffer, 0, out Report report))
{
// Parse the report if possible.
// This will return false if (for example) the report applies to a different DeviceItem.
if (_hiddeviceInputParser.TryParseReport(inputReportBuffer, 0, report))
{
BridgeFrameBufferPlatform.Threading.Send(() => ProcessEvent());
}
}
}
private void ProcessEvent()
{
if (_hiddeviceInputParser.HasChanged)
{
int j = -1;
TouchReport[] currentReports = new TouchReport[11];
for (int i = 0; i < _hiddeviceInputParser.ValueCount; i++)
{
var data = _hiddeviceInputParser.GetValue(i);
if (data.Usages.FirstOrDefault() == VendorUsage.FingerIdentifier)
{
j++;
}
else
{
continue;
}
// Only 11 slots are statically allocated
if (j >= 11) break;
// This is defined by the descriptor, we just take the assumption
var fingerTapData1 = _hiddeviceInputParser.GetValue(i + 1);
var fingerTapData2 = _hiddeviceInputParser.GetValue(i + 2);
var xData = _hiddeviceInputParser.GetValue(i + 3);
// Y is discarded but being read anyway
var yData = _hiddeviceInputParser.GetValue(i + 4);
// Register this
currentReports[j] = new TouchReport(xData.GetPhysicalValue(),
xData.DataItem.PhysicalMaximum, fingerTapData1.GetPhysicalValue() != 0);
}
// Check if need to raise touch leave event for prev slot
if (_prevTappedSlotIndex >= 0)
{
var scaledX = currentReports[_prevTappedSlotIndex].GetXInPercentage() * _width;
if (!currentReports[_prevTappedSlotIndex].FingerStatus)
{
Event?.Invoke(new RawMouseEventArgs(
BridgeFrameBufferPlatform.MouseDevice,
BridgeFrameBufferPlatform.Timestamp,
BridgeFrameBufferPlatform.TopLevel.InputRoot,
RawMouseEventType.LeftButtonUp,
new Point(scaledX, _height / 2),
default));
_prevTappedSlotIndex = -1;
}
else
{
// Update cache, raise move event and complete routine
if (BridgeFrameBufferPlatform.MouseDevice.Captured != null)
{
Event?.Invoke(new RawMouseEventArgs(
BridgeFrameBufferPlatform.MouseDevice,
BridgeFrameBufferPlatform.Timestamp,
BridgeFrameBufferPlatform.TopLevel.InputRoot,
RawMouseEventType.Move,
new Point(scaledX, _height / 2),
InputModifiers.LeftMouseButton));
}
}
}
// Can raise new tap event
if (_prevTappedSlotIndex == -1)
{
for (int i = 0; i < 11; i++)
{
if (currentReports[i].FingerStatus)
{
var scaledX = currentReports[i].GetXInPercentage() * _width;
Event?.Invoke(new RawMouseEventArgs(
BridgeFrameBufferPlatform.MouseDevice,
BridgeFrameBufferPlatform.Timestamp,
BridgeFrameBufferPlatform.TopLevel.InputRoot,
RawMouseEventType.LeftButtonDown,
new Point(scaledX, _height / 2),
default));
_prevTappedSlotIndex = i;
break;
}
}
}
// Update cache
_prevReports = currentReports;
}
}
}
}