Browse Source
Added GetIntermediatePoints support for X11, libinput and evdev # Conflicts: # src/Avalonia.Base/Threading/JobRunner.csrelease/0.10.13
committed by
Dan Walmsley
15 changed files with 489 additions and 105 deletions
@ -0,0 +1,43 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Threading; |
|||
|
|||
namespace Avalonia.LinuxFramebuffer.Input; |
|||
|
|||
internal class RawEventGroupingThreadingHelper : IDisposable |
|||
{ |
|||
private readonly RawEventGrouper _grouper; |
|||
private readonly Queue<RawInputEventArgs> _rawQueue = new(); |
|||
private readonly Action _queueHandler; |
|||
|
|||
public RawEventGroupingThreadingHelper(Action<RawInputEventArgs> eventCallback) |
|||
{ |
|||
_grouper = new RawEventGrouper(eventCallback); |
|||
_queueHandler = QueueHandler; |
|||
} |
|||
|
|||
private void QueueHandler() |
|||
{ |
|||
lock (_rawQueue) |
|||
{ |
|||
while (_rawQueue.Count > 0) |
|||
_grouper.HandleEvent(_rawQueue.Dequeue()); |
|||
} |
|||
} |
|||
|
|||
public void OnEvent(RawInputEventArgs args) |
|||
{ |
|||
lock (_rawQueue) |
|||
{ |
|||
_rawQueue.Enqueue(args); |
|||
if (_rawQueue.Count == 1) |
|||
{ |
|||
Dispatcher.UIThread.Post(_queueHandler, DispatcherPriority.Input); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Dispose() => |
|||
Dispatcher.UIThread.Post(() => _grouper.Dispose(), DispatcherPriority.Input + 1); |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
#nullable enable |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Collections.Pooled; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Avalonia; |
|||
|
|||
/* |
|||
This helper maintains an input queue for backends that handle input asynchronously. |
|||
While doing that it groups Move and TouchUpdate events so we could provide GetIntermediatePoints API |
|||
*/ |
|||
|
|||
internal class RawEventGrouper : IDisposable |
|||
{ |
|||
private readonly Action<RawInputEventArgs> _eventCallback; |
|||
private readonly Queue<RawInputEventArgs> _inputQueue = new(); |
|||
private readonly Action _dispatchFromQueue; |
|||
readonly Dictionary<long, RawTouchEventArgs> _lastTouchPoints = new(); |
|||
RawInputEventArgs? _lastEvent; |
|||
|
|||
public RawEventGrouper(Action<RawInputEventArgs> eventCallback) |
|||
{ |
|||
_eventCallback = eventCallback; |
|||
_dispatchFromQueue = DispatchFromQueue; |
|||
} |
|||
|
|||
private void AddToQueue(RawInputEventArgs args) |
|||
{ |
|||
_lastEvent = args; |
|||
_inputQueue.Enqueue(args); |
|||
if (_inputQueue.Count == 1) |
|||
Dispatcher.UIThread.Post(_dispatchFromQueue, DispatcherPriority.Input); |
|||
} |
|||
|
|||
private void DispatchFromQueue() |
|||
{ |
|||
while (true) |
|||
{ |
|||
if(_inputQueue.Count == 0) |
|||
return; |
|||
|
|||
var ev = _inputQueue.Dequeue(); |
|||
|
|||
if (_lastEvent == ev) |
|||
_lastEvent = null; |
|||
|
|||
if (ev is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchUpdate) |
|||
_lastTouchPoints.Remove(touchUpdate.TouchPointId); |
|||
|
|||
_eventCallback?.Invoke(ev); |
|||
|
|||
if (ev is RawPointerEventArgs { IntermediatePoints: PooledList<Point> list }) |
|||
list.Dispose(); |
|||
|
|||
if (Dispatcher.UIThread.HasJobsWithPriority(DispatcherPriority.Input + 1)) |
|||
{ |
|||
Dispatcher.UIThread.Post(_dispatchFromQueue, DispatcherPriority.Input); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void HandleEvent(RawInputEventArgs args) |
|||
{ |
|||
/* |
|||
Try to update already enqueued events if |
|||
1) they are still not handled (_lastEvent and _lastTouchPoints shouldn't contain said event in that case) |
|||
2) previous event belongs to the same "event block", events in the same block: |
|||
- belong from the same device |
|||
- are pointer move events (Move/TouchUpdate) |
|||
- have the same type |
|||
- have same modifiers |
|||
|
|||
Even if nothing is updated and the event is actually enqueued, we need to update the relevant tracking info |
|||
*/ |
|||
if ( |
|||
args is RawPointerEventArgs pointerEvent |
|||
&& _lastEvent != null |
|||
&& _lastEvent.Device == args.Device |
|||
&& _lastEvent is RawPointerEventArgs lastPointerEvent |
|||
&& lastPointerEvent.InputModifiers == pointerEvent.InputModifiers |
|||
&& lastPointerEvent.Type == pointerEvent.Type |
|||
&& lastPointerEvent.Type is RawPointerEventType.Move or RawPointerEventType.TouchUpdate) |
|||
{ |
|||
if (args is RawTouchEventArgs touchEvent) |
|||
{ |
|||
if (_lastTouchPoints.TryGetValue(touchEvent.TouchPointId, out var lastTouchEvent)) |
|||
MergeEvents(lastTouchEvent, touchEvent); |
|||
else |
|||
{ |
|||
_lastTouchPoints[touchEvent.TouchPointId] = touchEvent; |
|||
AddToQueue(touchEvent); |
|||
} |
|||
} |
|||
else |
|||
MergeEvents(lastPointerEvent, pointerEvent); |
|||
|
|||
return; |
|||
} |
|||
else |
|||
{ |
|||
_lastTouchPoints.Clear(); |
|||
if (args is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchEvent) |
|||
_lastTouchPoints[touchEvent.TouchPointId] = touchEvent; |
|||
} |
|||
AddToQueue(args); |
|||
} |
|||
|
|||
private static void MergeEvents(RawPointerEventArgs last, RawPointerEventArgs current) |
|||
{ |
|||
last.IntermediatePoints ??= new PooledList<Point>(); |
|||
((PooledList<Point>)last.IntermediatePoints).Add(last.Position); |
|||
last.Position = current.Position; |
|||
last.Timestamp = current.Timestamp; |
|||
last.InputModifiers = current.InputModifiers; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_inputQueue.Clear(); |
|||
_lastEvent = null; |
|||
_lastTouchPoints.Clear(); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue