Spring – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.ComponentModel;
3 using System.Diagnostics;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
7 using System.Threading.Tasks.Dataflow;
8 using System.Windows.Forms;
9 using Gma.System.MouseKeyHook;
10 using Spring.MouseKeyboard;
11 using Spring.Utilities;
12 using Spring.Utilities.Collections.RangeTree;
13 using SpringCombos;
14 using Debug = System.Diagnostics.Debug;
15  
16 namespace Spring.Charging
17 {
18 public class Charge : IDisposable
19 {
20 #region Public Events & Delegates
21  
22 public event EventHandler<ChargeLoadedEventArgs> ChargeLoaded;
23  
24 public event EventHandler<ChargeChangedEventArgs> ChargeChanged;
25  
26 public event EventHandler<ChargeStartEventArgs> ChargeStart;
27  
28 #endregion
29  
30 #region Public Enums, Properties and Fields
31  
32 public Combos Combos { get; private set; }
33  
34 public int Fuzz { get; set; }
35  
36 public static Random Random { get; set; }
37  
38 #endregion
39  
40 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
41  
42 private KeyWatch WatchKey { get; }
43  
44 private Key Key { get; }
45  
46 private IKeyboardMouseEvents KeyboardMouseEvents { get; }
47  
48 private Configuration.Configuration Configuration { get; }
49  
50 private BufferBlock<Combo> RecordKeyComboInputBlock { get; }
51  
52 private BufferBlock<Combo> RecordKeyComboOutputBlock { get; }
53  
54 private TransformBlock<Combo, Combo> RecordKeyTransformBlock { get; }
55  
56 private Stopwatch Stopwatch { get; }
57  
58 private int ChargeRepeat { get; set; }
59  
60 private RangeTreeFunc<TimeSpan, int> RangeTreeFunc { get; set; }
61  
62 private long _comboCount;
63  
64 private bool _disposing;
65  
66 private volatile bool _isCharging;
67  
68 #endregion
69  
70 #region Constructors, Destructors and Finalizers
71  
72 public Charge() => Stopwatch = new Stopwatch();
73  
74 public Charge(Combos springCombos,
75 Configuration.Configuration springConfiguration,
76 IKeyboardMouseEvents keyboardMouseEvents,
77 Key springKey,
78 KeyWatch watchKey) : this()
79 {
80 Combos = springCombos;
81 Configuration = springConfiguration;
82 KeyboardMouseEvents = keyboardMouseEvents;
83 Key = springKey;
84 WatchKey = watchKey;
85  
86 Random = new Random();
87  
88 RangeTreeFunc = new RangeTreeFunc<TimeSpan, int>
89 {
90 {
91 TimeSpan.FromSeconds(0),
92 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
93 FuncNoCharge
94 },
95 {
96 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
97 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
98 FuncNoCharge
99 },
100 {
101 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
102 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
103 Configuration.Spring.Charge.ChargeSeconds),
104 FuncCharge
105 },
106 {
107 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
108 Configuration.Spring.Charge.ChargeSeconds),
109 TimeSpan.MaxValue, FuncNoCharge
110 }
111 };
112  
113 RecordKeyComboInputBlock = new BufferBlock<Combo>();
114  
115 RecordKeyTransformBlock =
116 new TransformBlock<Combo, Combo>(TransformSpringCombos,
117 new ExecutionDataflowBlockOptions { EnsureOrdered = true });
118  
119 RecordKeyComboInputBlock.LinkTo(RecordKeyTransformBlock);
120  
121 RecordKeyComboOutputBlock = new BufferBlock<Combo>();
122 RecordKeyTransformBlock.LinkTo(RecordKeyComboOutputBlock, combo => combo != null);
123 RecordKeyTransformBlock.LinkTo(DataflowBlock.NullTarget<Combo>());
124  
125 Configuration.Spring.Charge.PropertyChanged += SpringLoading_PropertyChanged;
126 Configuration.Spring.PropertyChanged += Spring_PropertyChanged;
127  
128 WatchKey.KeyUp += WatchKey_SpringKeyUp;
129 WatchKey.KeyDown += WatchKey_KeyDown;
130 }
131  
132 public void Dispose()
133 {
134 if (_disposing)
135 {
136 return;
137 }
138  
139 _disposing = true;
140  
141 Configuration.Spring.Charge.PropertyChanged -= SpringLoading_PropertyChanged;
142 Configuration.Spring.PropertyChanged -= Spring_PropertyChanged;
143  
144 WatchKey.KeyUp -= WatchKey_SpringKeyUp;
145 WatchKey.KeyDown -= WatchKey_KeyDown;
146 }
147  
148 #endregion
149  
150 #region Event Handlers
151  
152 private void Spring_PropertyChanged(object sender, PropertyChangedEventArgs e)
153 {
154 Fuzz = ((Configuration.Spring) sender).Fuzz;
155 }
156  
157 private void WatchKey_KeyDown(object sender, KeyDownEventArgs e)
158 {
159 if (_isCharging)
160 {
161 return;
162 }
163  
164 ChargeRepeat = RangeTreeFunc.Query(e.Elapsed)
165 .SingleOrDefault();
166  
167 $"Loaded repeat: {ChargeRepeat}".ToDebugConsole();
168 }
169  
170 private void SpringLoading_PropertyChanged(object sender, PropertyChangedEventArgs e)
171 {
172 Configuration.Spring.Charge = (Configuration.Charge) sender;
173  
174 RangeTreeFunc = new RangeTreeFunc<TimeSpan, int>
175 {
176 {
177 TimeSpan.FromSeconds(0),
178 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
179 FuncNoCharge
180 },
181 {
182 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
183 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
184 FuncNoCharge
185 },
186 {
187 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
188 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
189 Configuration.Spring.Charge.ChargeSeconds),
190 FuncCharge
191 },
192 {
193 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
194 Configuration.Spring.Charge.ChargeSeconds),
195 TimeSpan.MaxValue, FuncNoCharge
196 }
197 };
198 }
199  
200 private void WatchKey_SpringKeyUp(object sender, KeyUpEventArgs e)
201 {
202 if (!Key.IsConfigured)
203 {
204 return;
205 }
206  
207 StopCharging();
208  
209 // Do not record if press time is smaller than two seconds.
210 if (e.TimePressed < TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds))
211 {
212 return;
213 }
214  
215 StartCharging();
216 }
217  
218 private async void IKeyboardMouseEvents_MouseMove(object sender, MouseEventArgs e)
219 {
220 if (!Configuration.Spring.Charge.MouseMove)
221 {
222 return;
223 }
224  
225 await StartCharge(new MouseCombo(e, ComboAction.Move));
226 }
227  
228 private async void IKeyboardMouseEvents_MouseUp(object sender, MouseEventArgs e)
229 {
230 if (!Configuration.Spring.Charge.MouseClick)
231 {
232 return;
233 }
234  
235 await StartCharge(new MouseCombo(e, ComboAction.Up));
236 }
237  
238 private async void IKeyboardMouseEvents_MouseDown(object sender, MouseEventArgs e)
239 {
240 if (!Configuration.Spring.Charge.MouseClick)
241 {
242 return;
243 }
244  
245 await StartCharge(new MouseCombo(e, ComboAction.Down));
246 }
247  
248 private async void IKeyboardMouseEvents_KeyDown(object sender, KeyEventArgs e)
249 {
250 if (!Configuration.Spring.Charge.Keyboard)
251 {
252 return;
253 }
254  
255 await StartCharge(new KeyboardCombo(e, ComboAction.Down));
256 }
257  
258 private async void IKeyboardMouseEvents_KeyUp(object sender, KeyEventArgs e)
259 {
260 if (!Configuration.Spring.Charge.Keyboard)
261 {
262 return;
263 }
264  
265 await StartCharge(new KeyboardCombo(e, ComboAction.Up));
266 }
267  
268 #endregion
269  
270 #region Public Methods
271  
272 public void StartCharge(Combos combos)
273 {
274 Combos = combos;
275  
276 ChargeChanged?.Invoke(this, new ChargeChangedEventArgs(Combos));
277 }
278  
279 public void Load(Combos combos)
280 {
281 Combos = combos;
282  
283 ChargeLoaded?.Invoke(this, new ChargeLoadedEventArgs(Combos));
284 }
285  
286 #endregion
287  
288 #region Private Methods
289  
290 private int FuncCharge(TimeSpan x)
291 {
292 var value = x.Seconds - Configuration.Spring.Charge.ChargeSeconds;
293  
294 return value == 0 ? 0 : value;
295 }
296  
297 private static int FuncNoCharge(TimeSpan _) => 0;
298  
299 private async Task StartCharge<T>(T eventArgs) where T : Combo
300 {
301 var fuzz = Random.Next(0, Configuration.Spring.Fuzz);
302  
303 var pauseCombo = new PauseCombo(Stopwatch.Elapsed) { Fuzz = fuzz };
304  
305 await RecordKeyComboInputBlock.SendAsync(pauseCombo);
306  
307 Stopwatch.Restart();
308  
309 await RecordKeyComboInputBlock.SendAsync(eventArgs);
310 }
311  
312 private void StopCharging()
313 {
314 Stopwatch.Stop();
315  
316 KeyboardMouseEvents.KeyUp -= IKeyboardMouseEvents_KeyUp;
317 KeyboardMouseEvents.KeyDown -= IKeyboardMouseEvents_KeyDown;
318 KeyboardMouseEvents.MouseDown -= IKeyboardMouseEvents_MouseDown;
319 KeyboardMouseEvents.MouseUp -= IKeyboardMouseEvents_MouseUp;
320 KeyboardMouseEvents.MouseMove -= IKeyboardMouseEvents_MouseMove;
321  
322 if (!RecordKeyComboOutputBlock.TryReceiveAll(out var springCombos))
323 {
324 return;
325 }
326  
327 if (!_isCharging)
328 {
329 return;
330 }
331  
332 var cleanCombos = springCombos.SequenceSubtract(Key.KeyCombo, CompareSpringKeys);
333  
334 Combos = new Combos(cleanCombos, ChargeRepeat);
335  
336 ChargeChanged?.Invoke(this, new ChargeChangedEventArgs(Combos));
337  
338 _isCharging = false;
339 }
340  
341 private void StartCharging()
342 {
343 _isCharging = true;
344  
345 Stopwatch.Restart();
346  
347 KeyboardMouseEvents.KeyUp += IKeyboardMouseEvents_KeyUp;
348 KeyboardMouseEvents.KeyDown += IKeyboardMouseEvents_KeyDown;
349 KeyboardMouseEvents.MouseDown += IKeyboardMouseEvents_MouseDown;
350 KeyboardMouseEvents.MouseUp += IKeyboardMouseEvents_MouseUp;
351 KeyboardMouseEvents.MouseMove += IKeyboardMouseEvents_MouseMove;
352  
353 Interlocked.Exchange(ref _comboCount, 0);
354  
355 ChargeStart?.Invoke(this, new ChargeStartEventArgs());
356 }
357  
358 private Combo TransformSpringCombos(Combo arg)
359 {
360 arg.Index = (int) Interlocked.Read(ref _comboCount);
361  
362 switch (arg)
363 {
364 case PauseCombo _:
365 Interlocked.Increment(ref _comboCount);
366  
367 return arg;
368  
369 case KeyboardCombo _:
370 if (Configuration.Spring.Charge.Keyboard)
371 {
372 Interlocked.Increment(ref _comboCount);
373  
374 return arg;
375 }
376  
377 break;
378  
379 case MouseCombo mouseCombo:
380  
381 switch (mouseCombo.ComboAction)
382 {
383 case ComboAction.Move:
384 if (Configuration.Spring.Charge.MouseMove)
385 {
386 Interlocked.Increment(ref _comboCount);
387  
388 return arg;
389 }
390  
391 break;
392 case ComboAction.Up:
393 case ComboAction.Down:
394 if (Configuration.Spring.Charge.MouseClick)
395 {
396 Interlocked.Increment(ref _comboCount);
397  
398 return arg;
399 }
400  
401 break;
402 }
403  
404 break;
405  
406 default:
407 Debug.Assert(true, "Unknown combo found in combo record charge queue.");
408  
409 break;
410 }
411  
412 return null;
413 }
414  
415 private static bool CompareSpringKeys(Combo combo, KeyAction keyAction)
416 {
417 if (!(combo is KeyboardCombo springKeyEventArgs) || !(keyAction is KeyActionKeys keyActionKeys))
418 {
419 return false;
420 }
421  
422 return springKeyEventArgs.Keys == keyActionKeys.Keys;
423 }
424  
425 #endregion
426 }
427 }