Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
AvaloniaTouchInputAdapter.cs
1using System.Collections.Concurrent;
2using System.Drawing;
3using Avalonia.Controls;
4using Avalonia.Input;
6using Microsoft.Extensions.Logging;
7
9
28public sealed class AvaloniaTouchInputAdapter : ITouchAdapter, IDisposable
29{
30 private readonly Control _control;
31 private readonly bool _emulateMouse;
32 private readonly Dictionary<int, TouchPoint> _activeTouches = new();
33 private TouchPoint[] _activeTouchesSnapshot = Array.Empty<TouchPoint>();
34 private readonly ConcurrentQueue<TouchPoint> _pendingBegins = new();
35 private readonly ConcurrentQueue<TouchPoint> _pendingEnds = new();
36 private bool _isDisposed;
37
39 public IReadOnlyList<TouchPoint> ActiveTouches => _activeTouchesSnapshot;
40
56 public AvaloniaTouchInputAdapter(Control control, bool emulateMouse = false)
57 {
58 _control = control ?? throw new ArgumentNullException(nameof(control));
59 _emulateMouse = emulateMouse;
60
61 _control.PointerPressed += OnPointerPressed;
62 _control.PointerMoved += OnPointerMoved;
63 _control.PointerReleased += OnPointerReleased;
64 _control.PointerCaptureLost += OnPointerCaptureLost;
65
66 Engine.Logger.LogInformation("AvaloniaTouchInputAdapter initialized.");
67 }
68
69 private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
70 {
71 if (e.Pointer.Type == PointerType.Mouse && !_emulateMouse)
72 return;
73
74 // For mouse pointers, only emulate touch for the primary (left) button.
75 // Right- and middle-clicks should not generate touch events.
76 if (e.Pointer.Type == PointerType.Mouse &&
77 !e.GetCurrentPoint(sender as Control).Properties.IsLeftButtonPressed)
78 return;
79
80 var id = GetTouchId(e.Pointer);
81 var pos = GetPosition(e, sender as Control);
82 var point = new TouchPoint(id, pos, TouchPhase.Began);
83
84 _activeTouches[id] = point;
85 RebuildSnapshot();
86 _pendingBegins.Enqueue(point);
87 }
88
89 private void OnPointerMoved(object? sender, PointerEventArgs e)
90 {
91 if (e.Pointer.Type == PointerType.Mouse && !_emulateMouse)
92 return;
93
94 var id = GetTouchId(e.Pointer);
95
96 // Only track movement for contacts that are already active (i.e., button/finger is held).
97 // On desktop, PointerMoved fires even without a button down; skip those.
98 if (!_activeTouches.ContainsKey(id))
99 return;
100
101 var pos = GetPosition(e, sender as Control);
102 var point = new TouchPoint(id, pos, TouchPhase.Moved);
103
104 _activeTouches[id] = point;
105 RebuildSnapshot();
106 }
107
108 private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
109 {
110 if (e.Pointer.Type == PointerType.Mouse && !_emulateMouse)
111 return;
112
113 var id = GetTouchId(e.Pointer);
114
115 if (!_activeTouches.ContainsKey(id))
116 return;
117
118 var pos = GetPosition(e, sender as Control);
119 var point = new TouchPoint(id, pos, TouchPhase.Ended);
120
121 _activeTouches.Remove(id);
122 RebuildSnapshot();
123 _pendingEnds.Enqueue(point);
124 }
125
126 private void OnPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
127 {
128 if (e.Pointer.Type == PointerType.Mouse && !_emulateMouse)
129 return;
130
131 // The system cancelled the pointer (e.g. incoming call on Android).
132 var id = GetTouchId(e.Pointer);
133
134 if (!_activeTouches.TryGetValue(id, out var existing))
135 return;
136
137 var point = new TouchPoint(existing.Id, existing.Position, TouchPhase.Cancelled);
138 _activeTouches.Remove(id);
139 RebuildSnapshot();
140 _pendingEnds.Enqueue(point);
141 }
142
144 public IReadOnlyList<TouchPoint> ConsumeBeganTouches()
145 {
146 if (_pendingBegins.IsEmpty)
147 return Array.Empty<TouchPoint>();
148
149 var snapshot = new List<TouchPoint>(_pendingBegins.Count);
150 while (_pendingBegins.TryDequeue(out var point))
151 snapshot.Add(point);
152 return snapshot;
153 }
154
156 public IReadOnlyList<TouchPoint> ConsumeEndedTouches()
157 {
158 if (_pendingEnds.IsEmpty)
159 return Array.Empty<TouchPoint>();
160
161 var snapshot = new List<TouchPoint>(_pendingEnds.Count);
162 while (_pendingEnds.TryDequeue(out var point))
163 snapshot.Add(point);
164 return snapshot;
165 }
166
167 private void RebuildSnapshot()
168 {
169 _activeTouchesSnapshot = _activeTouches.Values.ToArray();
170 }
171
179 private static int GetTouchId(IPointer pointer)
180 => pointer.Type == PointerType.Mouse ? 0 : (int)pointer.Id;
181
182 private static Point GetPosition(PointerEventArgs e, Control? relativeTo)
183 {
184 var pos = e.GetPosition(relativeTo);
185 return PointerCoordinates.ToScreenPx(relativeTo, new PointF((float)pos.X, (float)pos.Y));
186 }
187
192 public void Dispose()
193 {
194 if (_isDisposed) return;
195 _isDisposed = true;
196
197 _control.PointerPressed -= OnPointerPressed;
198 _control.PointerMoved -= OnPointerMoved;
199 _control.PointerReleased -= OnPointerReleased;
200 _control.PointerCaptureLost -= OnPointerCaptureLost;
201
202 Engine.Logger.LogInformation("AvaloniaTouchInputAdapter disposed.");
203 }
204}
IReadOnlyList< TouchPoint > ActiveTouches
Gets the list of touch contact points that are currently active (in contact with the surface)....
IReadOnlyList< TouchPoint > ConsumeEndedTouches()
Drains and returns all touch contacts that ended (lifted or cancelled) since the last call....
void Dispose()
Releases all resources held by this adapter, unsubscribing from all Avalonia pointer events....
IReadOnlyList< TouchPoint > ConsumeBeganTouches()
Drains and returns all touch contacts that began since the last call. Keeping beginnings in a queue p...
AvaloniaTouchInputAdapter(Control control, bool emulateMouse=false)
Initializes a new instance of the AvaloniaTouchInputAdapter class, attaching pointer event handlers t...
Represents the core game engine singleton responsible for managing the main update loop,...
Definition Engine.cs:36
static ILogger< Engine > Logger
Gets the logger instance used by the engine for diagnostic and informational messages.
Definition Engine.cs:59
Defines a passive adapter interface for accessing touch device state. Implementations update their in...
readonly record struct TouchPoint(int Id, Point Position, TouchPhase Phase)
Represents a single touch contact point, capturing its unique identifier, screen position,...
TouchPhase
Describes the current phase of a single touch contact point in its lifecycle.
Definition TouchPhase.cs:7