-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPointerProfile.cs
More file actions
141 lines (128 loc) · 5.99 KB
/
Copy pathPointerProfile.cs
File metadata and controls
141 lines (128 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using SwitchifyPc.Protocol;
using SwitchifyPc.Core.Settings;
namespace SwitchifyPc.Core.Input;
public sealed record Point(double X, double Y);
public sealed record Bounds(double X, double Y, double Width, double Height);
public sealed record DisplayInfo(Bounds Bounds, double ScaleFactor);
public sealed record PointerProfileInput(Point Cursor, DisplayInfo Display, int? MaxDelta = null);
public sealed record RecommendedDeltas(int Small, int Medium, int Large);
public sealed record MouseRepeatCapabilities(
bool Supported,
bool Enabled,
int IntervalMs,
int MoveIntervalMs,
int ScrollIntervalMs,
int MinIntervalMs,
int MaxIntervalMs);
public sealed record PointerSpeedCapabilities(
bool Supported,
bool SetSupported,
double ScalePercent,
double MinScalePercent,
double MaxScalePercent,
double StepPercent,
int BaseMoveDelta,
int EffectiveMoveDelta);
public sealed record PointerCapabilities(
bool NoAckMouseMove,
IReadOnlyList<string> NoAckCommands,
IReadOnlyList<string> SupportedCommands,
MouseRepeatCapabilities MouseRepeat,
PointerSpeedCapabilities PointerSpeed);
public sealed record PointerMovementProfile(
string DisplayId,
double ScaleFactor,
Bounds Bounds,
int MaxDelta,
RecommendedDeltas RecommendedDeltas,
PointerCapabilities Capabilities);
public static class PointerProfile
{
private const double ReferencePointerShortEdge = 1080;
private static readonly IReadOnlyDictionary<PointerMovementSizeKey, double> TargetReferenceNativeDeltas =
Enum.GetValues<PointerMovementSizeKey>().ToDictionary(
size => size,
size => ReferencePointerShortEdge * PointerMovementSettingsModel.FractionFor(PointerMovementSettingsModel.Default, size));
public static PointerMovementProfile Create(PointerProfileInput input)
{
Bounds bounds = NormalizeBounds(input.Display.Bounds);
double scaleFactor = double.IsFinite(input.Display.ScaleFactor) && input.Display.ScaleFactor > 0 ? input.Display.ScaleFactor : 1;
int maxDelta = input.MaxDelta ?? ProtocolConstants.MaxPointerDelta;
return new PointerMovementProfile(
DisplayId: $"{FormatNumber(bounds.X)}:{FormatNumber(bounds.Y)}:{FormatNumber(bounds.Width)}:{FormatNumber(bounds.Height)}:{FormatNumber(scaleFactor)}",
ScaleFactor: scaleFactor,
Bounds: bounds,
MaxDelta: maxDelta,
RecommendedDeltas: new RecommendedDeltas(
Small: ToLogicalDelta(TargetReferenceNativeDeltas[PointerMovementSizeKey.Small], scaleFactor, maxDelta),
Medium: ToLogicalDelta(TargetReferenceNativeDeltas[PointerMovementSizeKey.Medium], scaleFactor, maxDelta),
Large: ToLogicalDelta(TargetReferenceNativeDeltas[PointerMovementSizeKey.Large], scaleFactor, maxDelta)),
Capabilities: new PointerCapabilities(
NoAckMouseMove: true,
NoAckCommands: ProtocolConstants.NoAckControlCommandTypes.ToArray(),
SupportedCommands:
[
.. ProtocolConstants.NoAckControlCommandTypes,
"mouse.repeat.start",
"mouse.repeat.stop",
"keyboard.textStream.open",
"keyboard.textStream.chunk",
"keyboard.textStream.close",
"connection.ping",
"pointer.profile"
],
MouseRepeat: new MouseRepeatCapabilities(
Supported: true,
Enabled: MouseRepeatSettingsModel.Default.Enabled,
IntervalMs: MouseRepeatSettingsModel.Default.MoveIntervalMs,
MoveIntervalMs: MouseRepeatSettingsModel.Default.MoveIntervalMs,
ScrollIntervalMs: MouseRepeatSettingsModel.Default.ScrollIntervalMs,
MinIntervalMs: MouseRepeatSettingsModel.MinIntervalMs,
MaxIntervalMs: MouseRepeatSettingsModel.MaxIntervalMs),
PointerSpeed: PointerSpeedFor(PointerMovementSettingsModel.Default)));
}
private static Bounds NormalizeBounds(Bounds bounds)
{
return new Bounds(
X: FiniteOr(bounds.X, 0),
Y: FiniteOr(bounds.Y, 0),
Width: PositiveFiniteOr(bounds.Width, 1),
Height: PositiveFiniteOr(bounds.Height, 1));
}
private static double FiniteOr(double value, double fallback)
{
return double.IsFinite(value) ? value : fallback;
}
private static double PositiveFiniteOr(double value, double fallback)
{
return double.IsFinite(value) && value > 0 ? value : fallback;
}
private static int ToLogicalDelta(double nativePixels, double scaleFactor, int maxDelta)
{
return Clamp((int)Math.Round(nativePixels / scaleFactor, MidpointRounding.AwayFromZero), 1, maxDelta);
}
public static PointerSpeedCapabilities PointerSpeedFor(PointerMovementSettings settings)
{
double scalePercent = PointerMovementSettingsModel.ScalePercentFor(settings);
return new PointerSpeedCapabilities(
Supported: true,
SetSupported: true,
ScalePercent: scalePercent,
MinScalePercent: PointerMovementSettingsModel.PointerMovementScaleMin,
MaxScalePercent: PointerMovementSettingsModel.PointerMovementScaleMax,
StepPercent: PointerMovementSettingsModel.PointerMovementScaleStep,
BaseMoveDelta: PointerMovementSettingsModel.BaseMoveDelta,
EffectiveMoveDelta: Clamp(
(int)Math.Round(PointerMovementSettingsModel.BaseMoveDelta * (scalePercent / 100), MidpointRounding.AwayFromZero),
1,
ProtocolConstants.MaxPointerDelta));
}
private static int Clamp(int value, int min, int max)
{
return Math.Min(max, Math.Max(min, value));
}
private static string FormatNumber(double value)
{
return value.ToString("G15", System.Globalization.CultureInfo.InvariantCulture);
}
}