refactor(input): own pointer and callback lifetime
Move camera pointer, framebuffer resize, and retained/devtools input edges behind focused reversible owners. Preserve input priority while making shutdown deactivate callbacks before live-session retirement and retry physical detach without stranding transport teardown.
This commit is contained in:
parent
d09e246d3a
commit
8b8afeefa3
42 changed files with 4029 additions and 461 deletions
|
|
@ -49,15 +49,27 @@ public sealed class UiHost : System.IDisposable
|
|||
public IKeyboard? Keyboard { get; private set; }
|
||||
|
||||
private long _startTicks = System.Environment.TickCount64;
|
||||
private readonly List<System.Action> _inputUnsubscribers = new();
|
||||
private readonly HostQuiescenceGate _quiescence;
|
||||
private readonly List<IRetainedUiInputBinding> _inputBindings = new();
|
||||
private ResourceShutdownTransaction? _inputShutdown;
|
||||
private ResourceShutdownTransaction? _shutdown;
|
||||
private bool _disposeRequested;
|
||||
private bool _disposed;
|
||||
|
||||
public UiHost(GL gl, string shaderDir, BitmapFont? defaultFont = null)
|
||||
: this(gl, shaderDir, defaultFont, new HostQuiescenceGate())
|
||||
{
|
||||
}
|
||||
|
||||
internal UiHost(
|
||||
GL gl,
|
||||
string shaderDir,
|
||||
BitmapFont? defaultFont,
|
||||
HostQuiescenceGate quiescence)
|
||||
{
|
||||
TextRenderer = new TextRenderer(gl, shaderDir);
|
||||
DefaultFont = defaultFont;
|
||||
_quiescence = quiescence ?? throw new ArgumentNullException(nameof(quiescence));
|
||||
}
|
||||
|
||||
// ── Per-frame ──────────────────────────────────────────────────────
|
||||
|
|
@ -86,25 +98,21 @@ public sealed class UiHost : System.IDisposable
|
|||
System.ObjectDisposedException.ThrowIf(_disposeRequested || _disposed, this);
|
||||
System.ArgumentNullException.ThrowIfNull(mouse);
|
||||
|
||||
void OnMouseDown(IMouse sender, MouseButton button) =>
|
||||
Root.OnMouseDown(MapButton(button), (int)sender.Position.X, (int)sender.Position.Y);
|
||||
void OnMouseUp(IMouse sender, MouseButton button) =>
|
||||
Root.OnMouseUp(MapButton(button), (int)sender.Position.X, (int)sender.Position.Y);
|
||||
void OnMouseMove(IMouse sender, Vector2 position) =>
|
||||
Root.OnMouseMove((int)position.X, (int)position.Y);
|
||||
void OnScroll(IMouse sender, ScrollWheel scroll) => Root.OnScroll((int)scroll.Y);
|
||||
|
||||
mouse.MouseDown += OnMouseDown;
|
||||
mouse.MouseUp += OnMouseUp;
|
||||
mouse.MouseMove += OnMouseMove;
|
||||
mouse.Scroll += OnScroll;
|
||||
_inputUnsubscribers.Add(() =>
|
||||
var binding = new RetainedMouseInputBinding(
|
||||
new SilkRetainedMouseSurface(mouse),
|
||||
Root,
|
||||
_quiescence);
|
||||
_inputBindings.Add(binding);
|
||||
try
|
||||
{
|
||||
mouse.MouseDown -= OnMouseDown;
|
||||
mouse.MouseUp -= OnMouseUp;
|
||||
mouse.MouseMove -= OnMouseMove;
|
||||
mouse.Scroll -= OnScroll;
|
||||
});
|
||||
binding.Attach();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (binding.IsDisposalComplete)
|
||||
_inputBindings.Remove(binding);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void WireKeyboard(IKeyboard kb)
|
||||
|
|
@ -112,28 +120,54 @@ public sealed class UiHost : System.IDisposable
|
|||
System.ObjectDisposedException.ThrowIf(_disposeRequested || _disposed, this);
|
||||
System.ArgumentNullException.ThrowIfNull(kb);
|
||||
Keyboard = kb; // last wired keyboard wins (one-keyboard desktop)
|
||||
void OnKeyDown(IKeyboard sender, Key key, int scanCode) => Root.OnKeyDown((int)key);
|
||||
void OnKeyUp(IKeyboard sender, Key key, int scanCode) => Root.OnKeyUp((int)key);
|
||||
void OnKeyChar(IKeyboard sender, char value) => Root.OnChar(value);
|
||||
|
||||
kb.KeyDown += OnKeyDown;
|
||||
kb.KeyUp += OnKeyUp;
|
||||
kb.KeyChar += OnKeyChar;
|
||||
_inputUnsubscribers.Add(() =>
|
||||
var binding = new RetainedKeyboardInputBinding(
|
||||
new SilkRetainedKeyboardSurface(kb),
|
||||
Root,
|
||||
_quiescence);
|
||||
_inputBindings.Add(binding);
|
||||
try
|
||||
{
|
||||
kb.KeyDown -= OnKeyDown;
|
||||
kb.KeyUp -= OnKeyUp;
|
||||
kb.KeyChar -= OnKeyChar;
|
||||
});
|
||||
binding.Attach();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (binding.IsDisposalComplete)
|
||||
_inputBindings.Remove(binding);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static UiMouseButton MapButton(MouseButton b) => b switch
|
||||
/// <summary>
|
||||
/// Stops retained-device delivery without retiring the window tree or GL
|
||||
/// renderer. Safe to call before a potentially long live-session close.
|
||||
/// </summary>
|
||||
public void QuiesceInput()
|
||||
{
|
||||
MouseButton.Left => UiMouseButton.Left,
|
||||
MouseButton.Right => UiMouseButton.Right,
|
||||
MouseButton.Middle => UiMouseButton.Middle,
|
||||
_ => UiMouseButton.Left,
|
||||
};
|
||||
_disposeRequested = true;
|
||||
foreach (IRetainedUiInputBinding binding in _inputBindings)
|
||||
binding.Deactivate();
|
||||
Keyboard = null;
|
||||
}
|
||||
|
||||
/// <summary>Physically removes every retained input edge after quiescence.</summary>
|
||||
public void DeactivateInput()
|
||||
{
|
||||
QuiesceInput();
|
||||
|
||||
_inputShutdown ??= new ResourceShutdownTransaction(
|
||||
new ResourceShutdownStage(
|
||||
"retained UI input subscriptions",
|
||||
_inputBindings
|
||||
.AsEnumerable()
|
||||
.Reverse()
|
||||
.Select((binding, index) => new ResourceShutdownOperation(
|
||||
$"input binding {index}",
|
||||
binding.Dispose))
|
||||
.ToArray()));
|
||||
_inputShutdown.CompleteOrThrow();
|
||||
if (_inputShutdown.IsComplete)
|
||||
_inputBindings.Clear();
|
||||
}
|
||||
|
||||
// ── Window manager forwarders (delegate to UiRoot) ─────────────────
|
||||
|
||||
|
|
@ -169,12 +203,8 @@ public sealed class UiHost : System.IDisposable
|
|||
|
||||
_disposeRequested = true;
|
||||
_shutdown ??= CreateShutdownTransaction(
|
||||
_inputUnsubscribers.AsEnumerable().Reverse().ToArray(),
|
||||
() =>
|
||||
{
|
||||
_inputUnsubscribers.Clear();
|
||||
Keyboard = null;
|
||||
},
|
||||
[DeactivateInput],
|
||||
() => { },
|
||||
WindowManager.Dispose,
|
||||
TextRenderer.Dispose);
|
||||
_shutdown.CompleteOrThrow();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue