This commit is contained in:
Gregory Lirent 2026-01-08 21:35:06 +03:00
parent 37d4f290e3
commit 93016ca4d2
16 changed files with 1652 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "Lib/winipc-ua"]
path = Lib/winipc-ua
url = https://dev.lirent.ru/lirent/winipc-ua.git

View File

@ -1,12 +1,15 @@
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright © 2024-2025 Gregory Lirent */
using WinIPC.Config;
namespace WebmrAPI.Configuration
{
public class AppSettings
{
public MonitoringSettings Monitoring { get; set; } = new MonitoringSettings();
public WebServerSettings WebServer { get; set; } = new WebServerSettings();
public IPCServiceOptions IPCServiceOptions { get; set; } = new IPCServiceOptions();
}
public class MonitoringSettings

1
Lib/winipc-ua Submodule

@ -0,0 +1 @@
Subproject commit 838ab6852f7c3cdc6a4dab5c9fefecaa9feadc4b

View File

@ -0,0 +1,231 @@
using Google.Protobuf;
using Microsoft.Extensions.Options;
using WebmrAPI.Configuration;
using WebmrAPI.Utils;
using WebmrAPI.Utils.Sequences;
using WinIPC.Models;
using WinIPC.Services;
using WinIPC.Utils;
namespace WebmrAPI.Services
{
public sealed class IPCClientService : IPCBaseClientService
{
public IPCClientService(ILogger<IPCClientService> logger, IOptions<AppSettings> appConfigOptions)
: base(logger, appConfigOptions?.Value?.IPCServiceOptions.BasePipeName ?? throw new ArgumentNullException(nameof(appConfigOptions)))
{ }
public async Task<IEnumerable<WindowInfo>> GetWindowInfos(int sessionId, IEnumerable<IntPtr>? hwnds = null)
{
var req = new WindowsRequest();
if (hwnds != null)
{
foreach (var hwnd in hwnds)
{
req.Hwnds.Add((int)hwnd);
}
}
var resp = await SendRequest(sessionId, CommandType.GetWindowsInfo, req).GetResponseAsync();
if (resp != null && resp.Success)
{
return PayloadHandler.ExtractPayload<WindowsResponse>(resp).Data;
}
// TODO log Error
return new List<WindowInfo>();
}
public async Task<byte[]> GetScreenshot(int sessionId, IntPtr hwnd = 0, int x = 0, int y = 0, int width = 0, int height = 0)
{
var req = new ScreenshotRequest()
{
Hwnd = (int)hwnd,
CropPosition = new() { X = x, Y = y },
CropSize = new() { Width = width, Height = height }
};
var resp = await SendRequest(sessionId, CommandType.GetScreenshot, req).GetResponseAsync();
if (resp != null && resp.Success)
{
return PayloadHandler.ExtractPayload<ScreenshotResponse>(resp).Data.ToByteArray();
}
// TODO log Error
return Array.Empty<byte>();
}
public async Task<int> GetPixelColor(int sessionId, IntPtr hwnd = 0, int x = 0, int y = 0)
{
var req = new PixelRequest()
{
Hwnd = (int)hwnd,
PixelPosition = new() { X = x, Y = y }
};
var resp = await SendRequest(sessionId, CommandType.GetPixelColor, req).GetResponseAsync();
if (resp != null && resp.Success)
{
return (int)PayloadHandler.ExtractPayload<PixelResponse>(resp).RgbColor;
}
// TODO log Error
return -1;
}
private IEnumerable<InputAction> GetMouseMovingPath(ISequenceGenerator<Point> pGen, DelayGenerator dGen, ref int nActions)
{
List<InputAction> actions = new();
foreach (var p in pGen)
{
var input = new MouseMoveInput() { Position = new() { X = p.X, Y = p.Y } };
actions.Add(new InputAction()
{
Type = InputType.MouseMoveTo,
DelayMs = (uint)dGen.Next().Value,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(input))
});
++nActions;
}
return actions;
}
private IEnumerable<InputAction> GetMouseScrollSequence(ScrollAmountGenerator sGen, DelayGenerator dGen, ref int nActions)
{
List<InputAction> actions = new();
foreach (var s in sGen)
{
var input = new ScrollInput() { Offset = s };
actions.Add(new InputAction()
{
Type = InputType.MouseScroll,
DelayMs = (uint)dGen.Next().Value,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(input))
});
++nActions;
}
return actions;
}
private async Task<int> SendInputActions(int sessionId, IntPtr hwnd, IEnumerable<InputAction> actions, int nActions)
{
var resp = await SendRequest(sessionId, CommandType.InputAction, new InputRequest() { Hwnd = (int)hwnd, Actions = { actions } }).GetResponseAsync();
if (resp != null && resp.Success)
{
return nActions - PayloadHandler.ExtractPayload<InputResponse>(resp).Count;
}
// TODO log Error
return -nActions;
}
public Task<int> MouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, IntPtr hwnd = 0)
{
int nActions = 0;
return SendInputActions(sessionId, hwnd, GetMouseMovingPath(pGen, dGen, ref nActions), nActions);
}
public Task<int> HoldDownAndMouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
{
return HoldDownAndMouseMove(sessionId, pGen, dGen, new KeyShortcut(key), hwnd);
}
public Task<int> HoldDownAndMouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
{
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions(), holdUp = sc.GetHoldUpActions();
int nActions = holdDown.Count() + holdUp.Count();
return SendInputActions(sessionId, hwnd, holdDown.Concat(GetMouseMovingPath(pGen, dGen, ref nActions)).Concat(holdUp), nActions);
}
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
{
return MouseMoveAndPress(sessionId, pGen, dGen, new KeyShortcut(key), hwnd);
}
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
{
return MouseMoveAndPress(sessionId, pGen, dGen, new KeyShortcutSequence(sc), hwnd);
}
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcutSequence seq, IntPtr hwnd = 0)
{
IEnumerable<InputAction> press = seq.GetInputActions();
int nActions = press.Count();
return SendInputActions(sessionId, hwnd, GetMouseMovingPath(pGen, dGen, ref nActions).Concat(press), nActions);
}
public Task<int> MouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, IntPtr hwnd = 0)
{
int nActions = 0;
return SendInputActions(sessionId, hwnd, GetMouseScrollSequence(sGen, dGen, ref nActions), nActions);
}
public Task<int> HoldDownAndMouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
{
return HoldDownAndMouseScroll(sessionId, sGen, dGen, new KeyShortcut(key), hwnd);
}
public Task<int> HoldDownAndMouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
{
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions(), holdUp = sc.GetHoldUpActions();
int nActions = holdDown.Count() + holdUp.Count();
return SendInputActions(sessionId, hwnd, holdDown.Concat(GetMouseScrollSequence(sGen, dGen, ref nActions)).Concat(holdUp), nActions);
}
public Task<int> HoldDown(int sessionId, Key key, IntPtr hwnd = 0)
{
return HoldDown(sessionId, new KeyShortcut(key), hwnd);
}
public Task<int> HoldUp(int sessionId, Key key, IntPtr hwnd = 0)
{
return HoldUp(sessionId, new KeyShortcut(key), hwnd);
}
public Task<int> HoldDown(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
{
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions();
return SendInputActions(sessionId, hwnd, holdDown, holdDown.Count());
}
public Task<int> HoldUp(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
{
IEnumerable<InputAction> holdUp = sc.GetHoldUpActions();
return SendInputActions(sessionId, hwnd, holdUp, holdUp.Count());
}
public Task<int> Press(int sessionId, Key key, IntPtr hwnd = 0)
{
return Press(sessionId, new KeyShortcut(key), hwnd);
}
public Task<int> Press(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
{
return Press(sessionId, new KeyShortcutSequence(sc), hwnd);
}
public Task<int> Press(int sessionId, KeyShortcutSequence seq, IntPtr hwnd = 0)
{
IEnumerable<InputAction> press = seq.GetInputActions();
return SendInputActions(sessionId, hwnd, press, press.Count());
}
}
}

View File

@ -0,0 +1,6 @@
namespace WebmrAPI.Services
{
public class SessionService
{
}
}

94
Utils/BaseRandom.cs Normal file
View File

@ -0,0 +1,94 @@
using System.Numerics;
namespace WebmrAPI.Utils
{
public class BaseRandom
{
private readonly static Random _random = new Random();
private static readonly BaseRandom _self = new BaseRandom();
protected Random? _pRandom = null;
private Random Rand { get => _pRandom == null ? _random : _pRandom; }
public static byte[] NextGenerate(byte[] data)
{
return _self.Generate(data);
}
public static T NextGenerate<T>(T min, T max)
where T : struct, INumber<T>
{
return _self.Generate(min, max);
}
public static T NextSpread<T>(T value, T offset)
where T : struct, INumber<T>
{
return _self.Spread(value, offset);
}
public byte[] Generate(byte[] data)
{
Rand.NextBytes(data);
return data;
}
public double Generate(double min, double max)
{
if (min > max)
{
throw new ArgumentOutOfRangeException(nameof(min), "minValue cannot be greater than maxValue.");
}
return min + (max - min) * Rand.NextDouble();
}
public long Generate(long min, long max)
{
return Rand.NextInt64(min, max + 1);
}
public ulong Generate(ulong min, ulong max)
{
ulong res;
long lmin = (long)min;
long lmax = (long)max;
if (lmin > lmax) res = (ulong)Rand.NextInt64(lmax, lmin + 1);
else res = (ulong)Rand.NextInt64(lmin, lmax + 1);
return UInt64.Clamp(res, min, max);
}
public uint Generate(uint min, uint max)
{
return (uint)Generate((long)min, (long)max);
}
public T Generate<T>(T min, T max) where T : struct, INumber<T>
{
var min32 = Int32.CreateChecked(min);
var max32 = Int32.CreateChecked(max);
return T.CreateChecked(Rand.Next(min32, max32));
}
public T Spread<T>(T value, T offset)
where T : struct, INumber<T>
{
T min, max, cur;
cur = T.CreateChecked(Math.Abs(Double.CreateChecked(offset)));
min = value - cur;
max = value + cur;
if (min > max)
{
var tmp = min;
min = max;
max = tmp;
}
value = Generate(min, max);
return value;
}
public BaseRandom() { }
public BaseRandom(int seed)
{
_pRandom = new Random(seed);
}
}
}

171
Utils/Delay.cs Normal file
View File

@ -0,0 +1,171 @@
using System.Collections;
using WebmrAPI.Utils.Sequences;
namespace WebmrAPI.Utils
{
public class Delay : BaseRandom, IComparable<Delay>
{
private int _min = 0;
private int _max = 0;
private bool _mutable = true;
public readonly static Delay None = new Delay { _mutable = false };
public readonly static Delay Random = new Delay { _mutable = false, Min = 50, Max = 250 };
public int Value
{
set => _max = _min = value;
get => (_max > _min) ? Generate(_min, _max) : _min;
}
public int Min
{
get => _min > _max ? _max : _min;
set
{
if (!_mutable)
{
throw new InvalidOperationException("Object is not mutable");
}
_min = value;
}
}
public int Max
{
get => _max < _min ? _min : _max;
set
{
if (!_mutable)
{
throw new InvalidOperationException("Object is not mutable");
}
_max = value;
}
}
async public Task WaitAsync()
{
await Task.Delay(Value);
}
public void Wait()
{
Thread.Sleep(Value);
}
public static bool operator ==(Delay? left, Delay? right)
{
if (left == null && right == null) return true;
if (left == null || right == null) return false;
return left._min == right._min && left._max == right._max;
}
public static bool operator !=(Delay? left, Delay? right)
{
return !(left == right);
}
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
Delay other = (Delay)obj;
return this == other;
}
public override int GetHashCode()
{
return HashCode.Combine(_min, _max);
}
public static bool operator <(Delay left, Delay right)
{
if (left._min < right._min)
{
return true;
}
if (left._min == right._min && left._max < right._max)
{
return true;
}
return false;
}
public static bool operator >(Delay left, Delay right)
{
if (left._min > right._min)
{
return true;
}
if (left._min == right._min && left._max > right._max)
{
return true;
}
return false;
}
public static bool operator <=(Delay left, Delay right)
{
return left < right || left == right;
}
public static bool operator >=(Delay left, Delay right)
{
return left > right || left == right;
}
public int CompareTo(Delay? other)
{
if (other == null)
{
return 1;
}
int result = _min.CompareTo(other._min);
if (result != 0)
{
return result;
}
return _max.CompareTo(other._max);
}
}
public class DelayGenerator : ISequenceGenerator<Delay>
{
private ISequenceGenerator<int> _generator;
public Delay Min { get => new Delay { Value = _generator.Min }; }
public Delay Max { get => new Delay { Value = _generator.Max }; }
public double Progress { get => _generator.Progress; }
public double Step { get => _generator.Step; }
public bool HasValues { get => _generator.HasValues; }
public Delay Next()
{
if (!HasValues)
{
Reset();
}
return new Delay { Value = _generator.Next() };
}
public ISequenceGenerator<Delay> Reset()
{
_generator.Reset();
return this;
}
public DelayGenerator(ISequenceGenerator<int> generator)
{
_generator = generator;
}
public IEnumerator<Delay> GetEnumerator()
{
while (HasValues)
{
yield return Next();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

92
Utils/Perlin2D.cs Normal file
View File

@ -0,0 +1,92 @@
namespace WebmrAPI.Utils
{
public class Perlin2D : BaseRandom
{
byte[] _mTable = new byte[1024];
public Perlin2D(int seed = 0)
: base(seed)
{
Generate(_mTable);
}
private double[] GetPseudoRandomGradientVector(int x, int y)
{
int v = (int)(((x * 1836311903) ^ (y * 2971215073) + 4807526976) & 1023);
v = _mTable[v] & 3;
switch (v)
{
case 0: return new double[] { 1, 0 };
case 1: return new double[] { -1, 0 };
case 2: return new double[] { 0, 1 };
default: return new double[] { 0, -1 };
}
}
static double QunticCurve(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
static double Lerp(double a, double b, double t)
{
return a + (b - a) * t;
}
static double Dot(double[] a, double[] b)
{
return a[0] * b[0] + a[1] * b[1];
}
public double Noise(double fx, double fy)
{
int left = (int)System.Math.Floor(fx);
int top = (int)System.Math.Floor(fy);
double pointInQuadX = fx - left;
double pointInQuadY = fy - top;
double[] topLeftGradient = GetPseudoRandomGradientVector(left, top);
double[] topRightGradient = GetPseudoRandomGradientVector(left + 1, top);
double[] bottomLeftGradient = GetPseudoRandomGradientVector(left, top + 1);
double[] bottomRightGradient = GetPseudoRandomGradientVector(left + 1, top + 1);
double[] distanceToTopLeft = { pointInQuadX, pointInQuadY };
double[] distanceToTopRight = { pointInQuadX - 1, pointInQuadY };
double[] distanceToBottomLeft = { pointInQuadX, pointInQuadY - 1 };
double[] distanceToBottomRight = { pointInQuadX - 1, pointInQuadY - 1 };
double tx1 = Dot(distanceToTopLeft, topLeftGradient);
double tx2 = Dot(distanceToTopRight, topRightGradient);
double bx1 = Dot(distanceToBottomLeft, bottomLeftGradient);
double bx2 = Dot(distanceToBottomRight, bottomRightGradient);
pointInQuadX = QunticCurve(pointInQuadX);
pointInQuadY = QunticCurve(pointInQuadY);
double tx = Lerp(tx1, tx2, pointInQuadX);
double bx = Lerp(bx1, bx2, pointInQuadX);
double tb = Lerp(tx, bx, pointInQuadY);
return tb;
}
public double Noise(double fx, double fy, int octaves, double persistence = 0.5)
{
double amplitude = 1;
double max = 0;
double result = 0;
while (octaves-- > 0)
{
max += amplitude;
result += Noise(fx, fy) * amplitude;
amplitude *= persistence;
fx *= 2;
fy *= 2;
}
return result / max;
}
}
}

116
Utils/Scroll.cs Normal file
View File

@ -0,0 +1,116 @@
using WebmrAPI.Utils.Sequences;
namespace WebmrAPI.Utils
{
public sealed class ScrollAmountGenerator : BaseGenerator<int>
{
public enum Direction
{
Up,
Down
}
public class CorrectionData
{
public Direction Dir { get; set; }
public int Order { get; set; }
}
private double _fJitter = 0.12;
private double _fAccuracy = 0.83;
private int _nCorrections = 0;
private int _bAmount = 120;
public int TotalAmount { get; private set; }
public int CurrentAmount { get; private set; }
public int BaseAmount { get => _bAmount; private set => UpdateSpread(value, FJitter); }
public double FJitter { get => _fJitter; private set => UpdateSpread(BaseAmount, Math.Clamp(Math.Abs(value), 0.0, 1.0)); }
public int CorrectionLimit { get; private set; } = 1;
public override double Progress { get => (double)CurrentAmount / TotalAmount; protected set => base.Progress = value; }
public double FAccuracy { get => _fAccuracy; private set => _fAccuracy = Math.Clamp(Math.Abs(value), 0.0, 1.0); }
public Direction Dir { get; private set; }
public override double Step { get; protected set; }
public delegate void CorrectionHandler(object sender, CorrectionData data);
public event CorrectionHandler? Correction;
public ScrollAmountGenerator SetBaseAmount(int baseAmount) { BaseAmount = baseAmount; return this; }
public ScrollAmountGenerator SetJitterFactor(double jitterFactor) { FJitter = jitterFactor; return this; }
public ScrollAmountGenerator SetCorrectionLimit(int limit) { CorrectionLimit = limit; return this; }
public ScrollAmountGenerator SetAccuracyFactor(double accuracyFactor) { FAccuracy = accuracyFactor; return this; }
private void ToggleDirection()
{
Dir = Dir == Direction.Up ? Direction.Down : Direction.Up;
}
private void UpdateSpread(int baseAmount, double jitterFactor)
{
var offset = baseAmount * jitterFactor;
var amount = Math.Abs(Double.CreateChecked(baseAmount));
_bAmount = baseAmount;
_fJitter = jitterFactor;
Min = (int)(amount - offset);
Max = (int)(amount + offset);
}
protected override int Calc()
{
int value;
if (Generate(0, 100000) > (int)(FAccuracy * 100000))
value = Generate(Min, Max);
else value = BaseAmount;
if (Dir == Direction.Down)
{
value = -value;
}
CurrentAmount += value;
return value;
}
public override int Next()
{
if (Progress < 0)
{
Progress = 0;
return First();
}
if (!HasValues || IsZero(1.0 - Progress))
{
HasValues = false;
Progress = 1.0;
return Last();
}
else if (Progress > 1.0 && _nCorrections < CorrectionLimit)
{
ToggleDirection();
Correction?.Invoke(this, new CorrectionData { Dir = Dir, Order = ++_nCorrections });
}
return Calc();
}
public override ISequenceGenerator<int> Reset()
{
HasValues = true;
if ((_nCorrections & 1) != 0)
{
ToggleDirection();
}
_nCorrections = 0;
return this;
}
public ScrollAmountGenerator(int totalAmount, Direction dir)
: base(default, default)
{
TotalAmount = (dir == Direction.Up) ? totalAmount : -totalAmount;
Dir = dir;
UpdateSpread(BaseAmount, FJitter);
}
}
}

180
Utils/Sequences/Base.cs Normal file
View File

@ -0,0 +1,180 @@
using System.Collections;
using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
public interface ISequenceGenerator<T> : IEnumerable<T>
{
public static bool IsZero(double val, double epsilon = Double.Epsilon)
{
return Math.Abs(val) < epsilon;
}
public T Min { get; }
public T Max { get; }
public double Progress { get; }
public double Step { get; }
public bool HasValues { get; }
public T Next();
public ISequenceGenerator<T> Reset();
}
public interface INumberGenerator<T> : ISequenceGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get; }
public virtual double DMin { get => Double.CreateChecked(Min); }
public virtual double DMax { get => Double.CreateChecked(Max); }
public virtual double Mean { get => (DMin + DMax) / 2.0; }
}
public interface IPointGenerator : ISequenceGenerator<Point>
{
public static double CalcDistance(Point pt1, Point pt2)
{
return Math.Sqrt(Math.Pow(pt1.X - pt2.X, 2) + Math.Pow(pt1.Y - pt2.Y, 2));
}
public static double CalcAngle(Point pt1, Point pt2)
{
return Math.Atan2(pt2.Y - pt1.Y, pt2.X - pt1.X);
}
public static Point CalcPoint(double x, double y, double offset, double pAngle)
{
return new Point((int)Math.Round(x + offset * Math.Cos(pAngle)), (int)Math.Round(y + offset * Math.Sin(pAngle)));
}
public static Point CalcMiddle(Point start, Point end)
{
return new Point(
(int)Math.Round(start.X + (end.X - start.X) / 2.0),
(int)Math.Round(start.Y + (end.Y - start.Y) / 2.0)
);
}
public static (double distance, double angle) CalcDistanceAngle(Point pt1, Point pt2)
{
double dx = pt2.X - pt1.X;
double dy = pt2.Y - pt1.Y;
return (Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2)), Math.Atan2(dy, dx));
}
public static double DegreesToRadians(double degrees)
{
return degrees * Math.PI / 180.0;
}
public static double RadiansToDegrees(double radians)
{
return radians * (180.0 / Math.PI);
}
public static Point NewPointAngleDistance(Point refPt, double distance, double aRadians, double epsilon = Double.Epsilon)
{
int x, y;
if (IsZero(distance, epsilon)) return new Point(refPt.X, refPt.Y);
x = (int)Math.Round(refPt.X + distance * Math.Cos(aRadians));
y = (int)Math.Round(refPt.Y + distance * Math.Sin(aRadians));
return new Point(x, y);
}
public static Point NewPointDistance(Point refPt, Point targetPt, double distance, double epsilon = Double.Epsilon)
{
return NewPointAngleDistance(refPt, distance, CalcAngle(refPt, targetPt), epsilon);
}
public static Point NewPointAngle(Point refPt, Point targetPt, double aRadians, double epsilon = Double.Epsilon)
{
return NewPointAngleDistance(refPt, CalcDistance(refPt, targetPt), aRadians, epsilon);
}
public static Point NewPointDistanceFactor(Point refPt, Point targetPt, double factor, double epsilon = Double.Epsilon)
{
return NewPointAngleDistance(refPt, CalcDistance(refPt, targetPt) * factor, CalcAngle(refPt, targetPt), epsilon);
}
public static Point GetRandomPointInRect(Rectangle rect)
{
double x = BaseRandom.NextGenerate(rect.X, rect.Width);
double y = BaseRandom.NextGenerate(rect.Y, rect.Height);
return new Point((int)Math.Round(x), (int)Math.Round(y));
}
public IPointGenerator Self { get; }
public virtual Point Start { get => Min; }
public virtual Point End { get => Max; }
public virtual double LinearX { get => Start.X + (End.X - Start.X) * Progress; }
public virtual double LinearY { get => Start.Y + (End.Y - Start.Y) * Progress; }
}
public abstract class BaseGenerator<T> : BaseRandom, ISequenceGenerator<T>
{
protected virtual double Epsilon { get; set; } = 0.01;
protected virtual bool IsZero(double val)
{
return ISequenceGenerator<T>.IsZero(val, Epsilon);
}
private double _fShape = 1.0;
private double _fFade = 0.5;
public T Min { get; protected set; }
public T Max { get; protected set; }
public virtual double Progress { get; protected set; } = -1.0;
public double ProgressRemainder { get => 1.0 - Progress; }
public virtual bool HasValues { get; protected set; } = true;
public double FShape { get => _fShape; protected set => _fShape = Math.Clamp(value, 0.0, 1.0); }
public double FFade { get => _fFade; protected set => _fFade = Math.Clamp(value, Epsilon, 1.0); }
protected double Scale { get => Math.Pow(Progress * ProgressRemainder * 4.0, FFade) * FShape; }
public abstract double Step { get; protected set; }
public BaseGenerator(T min, T max)
{
Min = min;
Max = max;
}
protected static double CalcStepByCount(int count)
{
return 1.0 / count;
}
protected abstract T Calc();
protected virtual T First() => Calc();
protected virtual T Last() => Calc();
public virtual T Next()
{
if (Progress < 0)
{
Progress = 0;
return First();
}
if ((Progress += Step) >= 1.0)
{
HasValues = false;
Progress = 1.0;
return Last();
}
return Calc();
}
public virtual ISequenceGenerator<T> Reset()
{
HasValues = true;
Progress = -1.0;
return this;
}
public IEnumerator<T> GetEnumerator()
{
while (HasValues)
{
yield return Next();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

161
Utils/Sequences/Bezier.cs Normal file
View File

@ -0,0 +1,161 @@
using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
public abstract class BezierGenerator<T, TControl> : BaseGenerator<T> where TControl : struct
{
public bool IsCubic { get => Control2 != null; }
protected TControl Control1 { get; set; }
protected TControl? Control2 { get; set; }
public override double Step { get; protected set; }
protected double CalcBezier(double start, double end, double ctrl1, double? ctrl2)
{
double t = Progress;
double rt = ProgressRemainder;
if (IsCubic && ctrl2 != null)
{
double rt3 = rt * rt * rt;
double t3 = t * t * t;
double rt2_t = rt * rt * t;
double rt_t2 = rt * t * t;
return rt3 * start +
3 * rt2_t * ctrl1 +
3 * rt_t2 * ctrl2.Value +
t3 * end;
}
double rt2 = rt * rt;
double t2 = t * t;
double rt_t = rt * t;
return rt2 * start +
2 * rt_t * ctrl1 +
t2 * end;
}
public BezierGenerator(T min, T max, TControl ctrl1, TControl? ctrl2)
: base(min, max)
{
Control1 = ctrl1;
Control2 = ctrl2;
}
}
public class BezierNumberGenerator<T> : BezierGenerator<T, double>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
private double Bezier { get => CalcBezier(Self.DMin, Self.DMax, Control1, Control2); }
public BezierNumberGenerator<T> SetShapeFactor(double shape) { FShape = shape; return this; }
public BezierNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
protected override T Calc()
{
return T.CreateChecked(Math.Clamp(Self.Mean + (Bezier - Self.Mean) * Scale, Self.DMin, Self.DMax));
}
public BezierNumberGenerator(T start, T end, double ctrl1, double? ctrl2 = null, int count = 10)
: base(start, end, ctrl1, ctrl2) { Step = CalcStepByCount(count); }
}
public class ArcNumberGenerator<T> : BezierNumberGenerator<T> where T : struct, INumber<T>
{
public ArcNumberGenerator(T start, T end, int count = 10, bool bidirectional = true, bool reverse = false)
: base(start, end, Double.NaN, null, count)
{
var hMean = (Self.DMax - Self.Mean);
if (bidirectional)
{
Control1 = (reverse) ? Self.DMin : Self.DMax;
Control2 = (reverse) ? Self.DMax : Self.DMin;
}
else
{
Control1 = (reverse) ? Self.DMin : Self.DMax;
}
}
}
public class BezierPointGenerator : BezierGenerator<Point, Point>, IPointGenerator
{
protected readonly double _pAngle;
public IPointGenerator Self { get => this; }
public BezierPointGenerator SetShapeFactor(double shape) { FShape = shape; return this; }
public BezierPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
private double BezierX { get => CalcBezier(Min.X, Max.X, Control1.X, Control2?.X); }
private double BezierY { get => CalcBezier(Min.Y, Max.Y, Control1.Y, Control2?.Y); }
protected override Point Calc()
{
double dx = BezierX - Self.LinearX;
double dy = BezierY - Self.LinearY;
return IPointGenerator.CalcPoint(Self.LinearX + dx * Scale, Self.LinearY + dy * Scale, 0, 0);
}
public static BezierPointGenerator CreateByCount(Point start, Point end, Point ctrl1, Point? ctrl2 = null, int count = 10)
{
return new BezierPointGenerator(start, end, ctrl1, ctrl2)
{
Step = CalcStepByCount(count)
};
}
public static BezierPointGenerator CreateByStepSize(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5)
{
return new BezierPointGenerator(start, end, ctrl1, ctrl2, pxPerStep);
}
public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5)
: this(start, end, ctrl1, ctrl2)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null)
: base(start, end, ctrl1, ctrl2)
{
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
}
}
public sealed class ArcPointGenerator : BezierPointGenerator
{
public new ArcPointGenerator SetShapeFactor(double shape) { base.SetShapeFactor(shape); return this; }
public new ArcPointGenerator SetFadeFactor(double fade) { base.SetFadeFactor(fade); return this; }
public static ArcPointGenerator CreateByCount(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, int count = 10)
{
return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight)
{
Step = CalcStepByCount(count)
};
}
public static ArcPointGenerator CreateByStepSize(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5)
{
return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight, pxPerStep);
}
public ArcPointGenerator(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5)
: this(start, end, bidirectional, reverse, arcHeight)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
private ArcPointGenerator(Point start, Point end, bool bidirectional, bool reverse, double arcHeight)
: base(start, end, Point.Empty, null)
{
var height = (reverse) ? -arcHeight : arcHeight;
if (bidirectional)
{
Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 1.0 / 3.0), height, _pAngle);
Control2 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 2.0 / 3.0), -height, _pAngle);
}
else
{
Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.CalcMiddle(start, end), height, _pAngle);
}
}
}
}

View File

@ -0,0 +1,97 @@
using System.Collections;
using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
public abstract class ConsistentGenerator<T> : ISequenceGenerator<T>
{
private Queue<ISequenceGenerator<T>> _seq = new();
private Queue<ISequenceGenerator<T>> _mem = new();
private int _count = 0;
private int _index = 0;
public T Min { get; protected set; }
public T Max { get; protected set; }
public virtual double Progress { get => (double)_index / _count; }
public double Step { get => 1.0 / _count; }
public bool HasValues { get => _seq.Count > 0; }
protected void AddGenerator(ISequenceGenerator<T> generator)
{
Max = generator.Max;
_count += (int)(1.0 / generator.Step);
}
public T Next()
{
if (HasValues)
{
if (_seq.Peek().HasValues)
{
_index++;
return _seq.Peek().Next();
}
_mem.Enqueue(_seq.Dequeue().Reset());
return Next();
}
return Max;
}
public ISequenceGenerator<T> Reset()
{
while (_seq.Count > 0)
{
_mem.Enqueue(_seq.Dequeue().Reset());
}
_seq = _mem;
_mem = new();
_index = 0;
return this;
}
public IEnumerator<T> GetEnumerator()
{
while (HasValues)
{
yield return Next();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public ConsistentGenerator(ISequenceGenerator<T> generator)
{
Min = generator.Min;
Max = generator.Max;
_count = (int)(1.0 / generator.Step);
}
}
public sealed class ConsistentNumberGenerator<T> : ConsistentGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
public ConsistentNumberGenerator<T> Add(ISequenceGenerator<T> generator)
{
AddGenerator(generator);
return this;
}
public ConsistentNumberGenerator(ISequenceGenerator<T> generator)
: base(generator) { }
}
public sealed class ConsistentPointGenerator : ConsistentGenerator<Point>, IPointGenerator
{
public IPointGenerator Self { get => this; }
public ConsistentPointGenerator Add(ISequenceGenerator<Point> generator)
{
AddGenerator(generator);
return this;
}
public ConsistentPointGenerator(ISequenceGenerator<Point> generator)
: base(generator) { }
}
}

View File

@ -0,0 +1,206 @@
using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
public abstract class OscillationGenerator<T> : BaseGenerator<T>
{
private int _segments = 1;
private double _amplitude = 100.0;
public double Amplitude { get => _amplitude; protected set => _amplitude = Math.Abs(value); }
public int Segments { get => _segments; protected set => _segments = value < 1 ? 1 : value; }
public override double Step { get; protected set; }
protected abstract double FOffset { get; }
public OscillationGenerator(T min, T max)
: base(min, max) { }
}
public abstract class SawtoothGenerator<T> : OscillationGenerator<T>
{
protected override double FOffset { get { double s = (Progress * Segments) % 1.0; return (s < 0.5 ? s : (1.0 - s)) * 4.0 - 1.0; } }
public SawtoothGenerator(T min, T max)
: base(min, max) { }
}
public abstract class SineWaveGenerator<T> : OscillationGenerator<T>
{
protected override double FOffset { get => Math.Sin(Progress * (Segments * 2.0) * Math.PI); }
public SineWaveGenerator(T min, T max)
: base(min, max) { }
}
public abstract class SquareWaveGenerator<T> : OscillationGenerator<T>
{
protected override double FOffset { get => (Math.Floor(Progress * Segments) % 2 == 0) ? 1 : -1; }
public SquareWaveGenerator(T min, T max)
: base(min, max) { }
}
public sealed class SawtoothNumberGenerator<T> : SawtoothGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
public SawtoothNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SawtoothNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
public SawtoothNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
public SawtoothNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override T Calc()
{
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
}
public SawtoothNumberGenerator(T min, T max, int count = 10) : base(min, max)
{
Step = CalcStepByCount(count);
Amplitude = 1.0; // Set default
}
}
public sealed class SineWaveNumberGenerator<T> : SineWaveGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
public SineWaveNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SineWaveNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
public SineWaveNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
public SineWaveNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override T Calc()
{
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
}
public SineWaveNumberGenerator(T min, T max, int count = 10) : base(min, max)
{
Step = CalcStepByCount(count);
Amplitude = 1.0; // Set default
}
}
public sealed class SquareWaveNumberGenerator<T> : SquareWaveGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
public SquareWaveNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SquareWaveNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
public SquareWaveNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
public SquareWaveNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override T Calc()
{
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
}
public SquareWaveNumberGenerator(T min, T max, int count = 10) : base(min, max)
{
Step = CalcStepByCount(count);
Amplitude = 1.0; // Set default
}
}
public sealed class SawtoothPointGenerator : SawtoothGenerator<Point>, IPointGenerator
{
private readonly double _pAngle;
public IPointGenerator Self { get => this; }
public SawtoothPointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SawtoothPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
public SawtoothPointGenerator SetSegments(int count) { Segments = count; return this; }
public SawtoothPointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override Point Calc()
{
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
}
public static SawtoothPointGenerator CreateByCount(Point start, Point end, int count)
{
return new SawtoothPointGenerator(start, end) { Step = CalcStepByCount(count) };
}
public static SawtoothPointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
{
return new SawtoothPointGenerator(start, end, pxPerStep);
}
public SawtoothPointGenerator(Point start, Point end, double pxPerStep = 5)
: this(start, end)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
private SawtoothPointGenerator(Point start, Point end)
: base(start, end)
{
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
}
}
public sealed class SineWavePointGenerator : SineWaveGenerator<Point>, IPointGenerator
{
private readonly double _pAngle;
public IPointGenerator Self { get => this; }
public SineWavePointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SineWavePointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
public SineWavePointGenerator SetSegments(int count) { Segments = count; return this; }
public SineWavePointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override Point Calc()
{
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
}
public static SineWavePointGenerator CreateByCount(Point start, Point end, int count)
{
return new SineWavePointGenerator(start, end) { Step = CalcStepByCount(count) };
}
public static SineWavePointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
{
return new SineWavePointGenerator(start, end, pxPerStep);
}
public SineWavePointGenerator(Point start, Point end, double pxPerStep = 5)
: this(start, end)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
private SineWavePointGenerator(Point start, Point end)
: base(start, end)
{
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
}
}
public sealed class SquareWavePointGenerator : SquareWaveGenerator<Point>, IPointGenerator
{
private readonly double _pAngle;
public IPointGenerator Self { get => this; }
public SquareWavePointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
public SquareWavePointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
public SquareWavePointGenerator SetSegments(int count) { Segments = count; return this; }
public SquareWavePointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override Point Calc()
{
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
}
public static SquareWavePointGenerator CreateByCount(Point start, Point end, int count)
{
return new SquareWavePointGenerator(start, end) { Step = CalcStepByCount(count) };
}
public static SquareWavePointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
{
return new SquareWavePointGenerator(start, end, pxPerStep);
}
public SquareWavePointGenerator(Point start, Point end, double pxPerStep = 5)
: this(start, end)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
private SquareWavePointGenerator(Point start, Point end)
: base(start, end)
{
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
}
}
}

97
Utils/Sequences/Perlin.cs Normal file
View File

@ -0,0 +1,97 @@
using System.Drawing;
using System.Numerics;
namespace WebmrAPI.Utils.Sequences
{
public abstract class PerlinGenerator<T> : BaseGenerator<T>
{
private Perlin2D _perlin;
private double _amplitude = 100.0;
public double Amplitude { get => _amplitude; protected set => _amplitude = Math.Abs(value); }
public double Persistance { get; protected set; } = 0.5;
public double Frequency { get; protected set; } = 5.0;
public int Octaves { get; protected set; } = 4;
public double Seed { get; private set; }
public override double Step { get; protected set; }
protected double Noise { get => _perlin.Noise(Progress * Frequency, Seed, Octaves, Persistance); }
public PerlinGenerator(T min, T max, double seed)
: base(min, max)
{
Seed = seed;
_perlin = new Perlin2D((int)(Seed * 0x000fffff));
}
}
public sealed class PerlinNumberGenerator<T> : PerlinGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
{
public INumberGenerator<T> Self { get => this; }
public PerlinNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
public PerlinNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
public PerlinNumberGenerator<T> SetOctaves(int octaves) { Octaves = octaves; return this; }
public PerlinNumberGenerator<T> SetPersistence(double peristance) { Persistance = peristance; return this; }
public PerlinNumberGenerator<T> SetFrequency(double frequency) { Frequency = frequency; return this; }
public PerlinNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override T Calc()
{
var offs = Noise * Scale * ((Self.DMax - Self.DMin) / 2.0);
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
}
private static double GetSeed(T min, T max, double seed)
{
if (Double.IsNormal(seed)) return seed;
return (Double.CreateChecked(min) + Double.CreateChecked(max)) / 1000.0;
}
public PerlinNumberGenerator(T min, T max, int count = 10, double seed = Double.NaN) : base(min, max, GetSeed(min, max, seed))
{
Step = CalcStepByCount(count);
Amplitude = 1.0; // Set default
}
}
public sealed class PerlinPointGenerator : PerlinGenerator<Point>, IPointGenerator
{
private readonly double _pAngle;
public IPointGenerator Self { get => this; }
public PerlinPointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
public PerlinPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
public PerlinPointGenerator SetOctaves(int octaves) { Octaves = octaves; return this; }
public PerlinPointGenerator SetPersistence(double peristance) { Persistance = peristance; return this; }
public PerlinPointGenerator SetFrequency(double frequency) { Frequency = frequency; return this; }
public PerlinPointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
protected override Point Calc()
{
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Noise * Amplitude * Scale, _pAngle);
}
private static double GetSeed(Point start, Point end, double seed)
{
if (Double.IsNormal(seed)) return seed;
return (start.X + end.Y) / 1000.0;
}
public static PerlinPointGenerator CreateByCount(Point start, Point end, int count, double seed = Double.NaN)
{
return new PerlinPointGenerator(start, end, seed)
{
Step = CalcStepByCount(count)
};
}
public static PerlinPointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
{
return new PerlinPointGenerator(start, end, pxPerStep, seed);
}
public PerlinPointGenerator(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
: this(start, end, seed)
{
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
}
private PerlinPointGenerator(Point start, Point end, double seed)
: base(start, end, GetSeed(start, end, seed))
{
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
}
}
}

185
Utils/Shortcuts.cs Normal file
View File

@ -0,0 +1,185 @@
using Google.Protobuf;
using WebmrAPI.Utils.Sequences;
using WinIPC.Models;
using WinIPC.Utils;
namespace WebmrAPI.Utils
{
public class Key
{
public Button Code { get; private set; }
public Key(Button code) { Code = code; }
public Key(string key)
{
Button code;
switch (key.ToLowerInvariant())
{
case "-": case "_": Code = Button.Sub; break;
case "+": case "=": Code = Button.Add; break;
case "~": case "`": Code = Button.Tilde; break;
case "<": case ",": Code = Button.Comma; break;
case ">": case ".": Code = Button.Period; break;
case ":": case ";": Code = Button.Colon; break;
case "{": case "[": Code = Button.LBracket; break;
case "}": case "]": Code = Button.RBracket; break;
case "\\": case "|": Code = Button.BSol; break;
case "/": case "?": Code = Button.Sol; break;
case "'": case "\"": Code = Button.Quote; break;
case "num+": Code = Button.NumAdd; break;
case "num-": Code = Button.NumSub; break;
case "num*": Code = Button.NumMul; break;
case "num/": Code = Button.NumDiv; break;
case "num.": Code = Button.NumDec; break;
case "a": case "b": case "c": case "d": case "e": case "f":
case "g": case "h": case "i": case "j": case "k": case "l":
case "m": case "n": case "o": case "p": case "q": case "r":
case "s": case "t": case "u": case "v": case "w": case "x":
case "y": case "z": case "0": case "1": case "2": case "3":
case "4": case "5": case "6": case "7": case "8": case "9":
if (Enum.TryParse($"Key{key}", out code))
{
Code = code;
break;
}
else goto default;
case "shift": case "ctrl": case "alt": case "win":
if (Enum.TryParse($"L{key}", out code))
{
Code = code;
break;
}
else goto default;
default:
if (Enum.TryParse(key, out code))
{
Code = code;
break;
}
else throw new ArgumentException($"Unexpected key \"{key}\"");
}
}
}
public class KeyShortcutSequence
{
private Queue<KeyValuePair<KeyShortcut, uint>> _queue = new();
public KeyShortcutSequence(KeyShortcut key) { Next(key); }
public KeyShortcutSequence Next(KeyShortcut shortcut, int delay = 0)
{
_queue.Enqueue(new KeyValuePair<KeyShortcut, uint>(shortcut, (uint)delay));
return this;
}
public IEnumerable<InputAction> GetInputActions()
{
var actions = new InputAction[0];
do
{
var kvpair = _queue.Dequeue();
if (actions.Length > 0)
{
actions[actions.Length - 1].DelayMs += kvpair.Value;
}
actions = actions.Concat(kvpair.Key.GetInputActions()).ToArray();
} while (_queue.Count > 0);
return actions;
}
}
public class KeyShortcut
{
private readonly static DelayGenerator _dGen = new(new PerlinNumberGenerator<int>(75, 200, Int32.MaxValue));
private Queue<Key> _queue = new();
public KeyShortcut(Key key) { And(key); }
public KeyShortcut(string key) { And(key); }
public KeyShortcut And(Key key)
{
_queue.Enqueue(key);
return this;
}
public KeyShortcut And(string key)
{
return And(new Key(key));
}
public IEnumerable<InputAction> GetHoldDownActions()
{
Key key;
var actions = new InputAction[_queue.Count];
for (int i = 0, n = _queue.Count; i < n;)
{
_queue.Enqueue(key = _queue.Dequeue());
actions[i++] = new InputAction()
{
DelayMs = (uint)_dGen.Next().Value,
Type = InputType.KeyDown,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
};
}
return actions;
}
public IEnumerable<InputAction> GetHoldUpActions()
{
Key key;
var actions = new InputAction[_queue.Count];
for (int n = _queue.Count; n > 0;)
{
_queue.Enqueue(key = _queue.Dequeue());
actions[--n] = new InputAction()
{
DelayMs = (uint)_dGen.Next().Value,
Type = InputType.KeyUp,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
};
}
return actions;
}
public IEnumerable<InputAction> GetInputActions()
{
Key key;
int n = _queue.Count * 2;
var actions = new InputAction[n];
for (int i = 0; i < n;)
{
_queue.Enqueue(key = _queue.Dequeue());
actions[i++] = new InputAction()
{
DelayMs = (uint)_dGen.Next().Value,
Type = InputType.KeyDown,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
};
actions[--n] = new InputAction()
{
DelayMs = (uint)_dGen.Next().Value,
Type = InputType.KeyUp,
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
};
}
return actions;
}
}
}

View File

@ -27,5 +27,14 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
<PackageReference Include="Grpc.Tools" Version="2.63.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Lib\winipc-ua\Models\ipc.proto" GrpcServices="None" ProtoRoot="." />
</ItemGroup>
</Project>