using XFEExtension.NetCore.InputSimulator.Native; namespace XFEExtension.NetCore.InputSimulator; /// 可重复播放的输入序列。构建时不要并发修改;播放时使用步骤快照。 /// 每个按键步骤独立管理释放,不会释放调用者在序列外持有的按键。 public sealed class InputSequence { private readonly List> steps = []; /// 步骤数。 public int Count => steps.Count; /// 添加物理按键步骤。 public InputSequence PressKey(ScanCode key, int holdTime = 30) { InputValidation.Key(key); InputValidation.Delay(holdTime, nameof(holdTime)); steps.Add((input, token) => input.PressKeyAsync(key, holdTime, token)); return this; } /// 添加组合键步骤;复制参数,后续修改原数组不会改变序列。 public InputSequence PressCombination(IEnumerable keys, int holdTime = 30) { ArgumentNullException.ThrowIfNull(keys); InputValidation.Delay(holdTime, nameof(holdTime)); var copy = keys.Distinct().ToArray(); if (copy.Length == 0) throw new ArgumentException("组合键不能为空。", nameof(keys)); foreach (var key in copy) InputValidation.Key(key); steps.Add((input, token) => input.PressCombinationAsync(copy, holdTime, token)); return this; } /// 添加 Unicode 文本输入步骤。 public InputSequence TypeText(string text, int delay = 0) { ArgumentNullException.ThrowIfNull(text); InputValidation.Delay(delay, nameof(delay)); steps.Add((input, token) => input.TypeTextAsync(text, delay, token)); return this; } /// 添加鼠标点击步骤。 public InputSequence MouseClick(MouseButton button, int holdTime = 30) { InputValidation.Button(button); InputValidation.Delay(holdTime, nameof(holdTime)); steps.Add((input, token) => input.MouseClickAsync(button, holdTime, token)); return this; } /// 添加相对鼠标移动步骤。 public InputSequence MouseMove(int x, int y) { steps.Add((input, _) => { input.MouseMove(x, y); return Task.CompletedTask; }); return this; } /// 添加垂直或水平滚动步骤;120 为一格。 public InputSequence MouseWheel(int delta, bool horizontal = false) { steps.Add((input, _) => { if (horizontal) input.MouseHorizontalWheelRoll(delta); else input.MouseWheelRoll(delta); return Task.CompletedTask; }); return this; } /// 添加毫秒延迟。 public InputSequence Wait(int milliseconds) { InputValidation.Delay(milliseconds, nameof(milliseconds)); steps.Add((_, token) => Task.Delay(milliseconds, token)); return this; } /// 顺序播放指定次数。取消或发送失败时停止,按键步骤负责释放自己持有的键。 public async Task PlayAsync(InputController input, int repeat = 1, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(input); ArgumentOutOfRangeException.ThrowIfLessThan(repeat, 1); cancellationToken.ThrowIfCancellationRequested(); var snapshot = steps.ToArray(); for (var iteration = 0; iteration < repeat; iteration++) { foreach (var step in snapshot) { cancellationToken.ThrowIfCancellationRequested(); await step(input, cancellationToken).ConfigureAwait(false); } if (iteration + 1 < repeat) { cancellationToken.ThrowIfCancellationRequested(); await Task.Yield(); } } } }