Spring – Rev 1
?pathlinks?
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using Spring.Utilities;
namespace Spring.MouseKeyboard
{
public class KeyWatch : IDisposable
{
#region Public Events & Delegates
public event EventHandler<KeyUpEventArgs> KeyUp;
public event EventHandler<KeyDownEventArgs> KeyDown;
public event EventHandler<KeySpeedAdjustedEventArgs> SpeedAdjusted;
#endregion
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private ActionBlock<KeyAction[]> WatchSlowKeysActionBlock { get; set; }
private BatchBlock<KeyAction> WatchSlowKeysBatchBlock { get; set; }
private ActionBlock<KeyAction[]> WatchSpeedKeysActionBlock { get; set; }
private BatchBlock<KeyAction> WatchSpeedKeysBatchBlock { get; set; }
private ActionBlock<KeyAction[]> WatchKeysActionBlock { get; set; }
private BatchBlock<KeyAction> WatchKeysBatchBlock { get; set; }
private IKeyboardMouseEvents KeyboardMouseEvents { get; }
private Key Key { get; }
private BufferBlock<KeyAction> WatchKeysBufferBlock { get; }
private Stopwatch KeyPressStopwatch { get; }
private BroadcastBlock<KeyAction> WatchKeysBroadcastBlock { get; }
private bool _disposing;
private IDisposable _watchKeysBatchActionBlockLink;
private IDisposable _watchKeysBroadcastBatchBlockLink;
private IDisposable _watchSlowKeysBatchActionBlockLink;
private IDisposable _watchSlowKeysBroadcastBatchBlockLink;
private IDisposable _watchSpeedKeysBatchActionBlockLink;
private IDisposable _watchSpeedKeysBroadcastBatchBlockLink;
#endregion
#region Constructors, Destructors and Finalizers
public KeyWatch()
{
KeyPressStopwatch = new Stopwatch();
WatchKeysBufferBlock = new BufferBlock<KeyAction>();
WatchKeysBroadcastBlock = new BroadcastBlock<KeyAction>(key => key);
}
public KeyWatch(IKeyboardMouseEvents keyboardMouseEvents, Key springKey) :
this()
{
KeyboardMouseEvents = keyboardMouseEvents;
Key = springKey;
KeyboardMouseEvents.KeyUp += KeyboardMouseEvents_KeyUp;
KeyboardMouseEvents.KeyDown += KeyboardMouseEvents_KeyDown;
KeyboardMouseEvents.MouseWheel += KeyboardMouseEvents_MouseWheel;
Key.KeyComboConfigured += Key_SpringKeyComboConfigured;
Key.KeySlowConfigured += Key_SpringKeySlowConfigured;
Key.KeySpeedConfigured += Key_SpringKeySpeedConfigured;
}
public void Dispose()
{
if (_disposing)
{
return;
}
_disposing = true;
KeyboardMouseEvents.KeyUp -= KeyboardMouseEvents_KeyUp;
KeyboardMouseEvents.KeyDown -= KeyboardMouseEvents_KeyDown;
KeyboardMouseEvents.MouseWheel -= KeyboardMouseEvents_MouseWheel;
Key.KeyComboConfigured -= Key_SpringKeyComboConfigured;
Key.KeySlowConfigured -= Key_SpringKeySlowConfigured;
Key.KeySpeedConfigured -= Key_SpringKeySpeedConfigured;
_watchKeysBroadcastBatchBlockLink?.Dispose();
_watchKeysBatchActionBlockLink?.Dispose();
_watchSpeedKeysBroadcastBatchBlockLink?.Dispose();
_watchSpeedKeysBatchActionBlockLink?.Dispose();
_watchSlowKeysBroadcastBatchBlockLink?.Dispose();
_watchSlowKeysBatchActionBlockLink?.Dispose();
}
#endregion
#region Event Handlers
private void Key_SpringKeyComboConfigured(object sender, KeyComboConfiguredEventArgs e)
{
_watchKeysBroadcastBatchBlockLink?.Dispose();
_watchKeysBroadcastBatchBlockLink = null;
_watchKeysBatchActionBlockLink?.Dispose();
_watchKeysBatchActionBlockLink = null;
WatchKeysBatchBlock?.Complete();
WatchKeysActionBlock?.Complete();
WatchKeysBatchBlock = null;
WatchKeysActionBlock = null;
WatchKeysBatchBlock = new BatchBlock<KeyAction>(Key.KeyCombo.Count);
_watchKeysBroadcastBatchBlockLink = WatchKeysBroadcastBlock.LinkTo(WatchKeysBatchBlock,
new DataflowLinkOptions { PropagateCompletion = true });
WatchKeysActionBlock =
new ActionBlock<KeyAction[]>(OnComboKeyBatch,
new ExecutionDataflowBlockOptions { BoundedCapacity = 1, EnsureOrdered = true });
_watchKeysBatchActionBlockLink = WatchKeysBatchBlock.LinkTo(WatchKeysActionBlock,
new DataflowLinkOptions { PropagateCompletion = true });
}
private void Key_SpringKeySpeedConfigured(object sender, KeySpeedConfiguredEventArgs e)
{
_watchSpeedKeysBroadcastBatchBlockLink?.Dispose();
_watchSpeedKeysBroadcastBatchBlockLink = null;
_watchSpeedKeysBatchActionBlockLink?.Dispose();
_watchSpeedKeysBatchActionBlockLink = null;
WatchSpeedKeysBatchBlock?.Complete();
WatchSpeedKeysActionBlock?.Complete();
WatchSpeedKeysBatchBlock = null;
WatchSpeedKeysActionBlock = null;
WatchSpeedKeysBatchBlock = new BatchBlock<KeyAction>(Key.SpeedCombo.Count);
_watchSpeedKeysBroadcastBatchBlockLink = WatchKeysBroadcastBlock.LinkTo(WatchSpeedKeysBatchBlock,
new DataflowLinkOptions { PropagateCompletion = true });
WatchSpeedKeysActionBlock =
new ActionBlock<KeyAction[]>(OnSpeedKeyBatch,
new ExecutionDataflowBlockOptions { EnsureOrdered = true });
_watchSpeedKeysBatchActionBlockLink = WatchSpeedKeysBatchBlock.LinkTo(
WatchSpeedKeysActionBlock,
new DataflowLinkOptions { PropagateCompletion = true });
}
private void Key_SpringKeySlowConfigured(object sender, KeySlowConfiguredEventArgs e)
{
_watchSlowKeysBroadcastBatchBlockLink?.Dispose();
_watchSlowKeysBroadcastBatchBlockLink = null;
_watchSlowKeysBatchActionBlockLink?.Dispose();
_watchSlowKeysBatchActionBlockLink = null;
WatchSlowKeysBatchBlock?.Complete();
WatchSlowKeysActionBlock?.Complete();
WatchSlowKeysBatchBlock = null;
WatchSlowKeysActionBlock = null;
WatchSlowKeysBatchBlock = new BatchBlock<KeyAction>(Key.SlowCombo.Count);
_watchSlowKeysBroadcastBatchBlockLink = WatchKeysBroadcastBlock.LinkTo(WatchSlowKeysBatchBlock,
new DataflowLinkOptions { PropagateCompletion = true });
WatchSlowKeysActionBlock =
new ActionBlock<KeyAction[]>(OnSlowKeyBatch,
new ExecutionDataflowBlockOptions { EnsureOrdered = true });
_watchSlowKeysBatchActionBlockLink = WatchSlowKeysBatchBlock.LinkTo(WatchSlowKeysActionBlock,
new DataflowLinkOptions { PropagateCompletion = true });
}
private async void KeyboardMouseEvents_MouseWheel(object sender, MouseEventArgs e)
{
if (!Key.IsConfigured)
{
return;
}
if (!KeyPressStopwatch.IsRunning)
{
KeyPressStopwatch.Restart();
}
var wheel = e.Delta > 0 ? MouseScrollDirection.Up : MouseScrollDirection.Down;
await WatchKeysBufferBlock.SendAsync(new KeyActionMouseScrollDirection(wheel));
if (WatchKeysBroadcastBlock != null)
{
await WatchKeysBroadcastBlock.SendAsync(new KeyActionMouseScrollDirection(wheel));
}
if (!WatchKeysBufferBlock.TryReceiveAll(out _))
{
}
}
private async void KeyboardMouseEvents_KeyDown(object sender, KeyEventArgs e)
{
if (!Key.IsConfigured)
{
return;
}
if (!KeyPressStopwatch.IsRunning)
{
KeyPressStopwatch.Restart();
}
await WatchKeysBufferBlock.SendAsync(new KeyActionKeys(e.KeyCode));
if (WatchKeysBroadcastBlock != null)
{
await WatchKeysBroadcastBlock.SendAsync(new KeyActionKeys(e.KeyCode));
}
}
private void KeyboardMouseEvents_KeyUp(object sender, KeyEventArgs e)
{
if (!Key.IsConfigured)
{
return;
}
var keyPressTime = KeyPressStopwatch.Elapsed;
KeyPressStopwatch.Stop();
if (!WatchKeysBufferBlock.TryReceiveAll(out var keys))
{
return;
}
if (!Key.KeyCombo.SequenceContains(keys))
{
return;
}
KeyUp?.Invoke(this, new KeyUpEventArgs(keyPressTime));
}
#endregion
#region Private Methods
private void OnComboKeyBatch(KeyAction[] keys)
{
if (!Key.IsConfigured)
{
return;
}
if (!Key.KeyCombo.SequenceContains(keys))
{
KeyPressStopwatch.Reset();
return;
}
KeyDown?.Invoke(this, new KeyDownEventArgs(KeyPressStopwatch.Elapsed));
}
private void OnSlowKeyBatch(KeyAction[] keys)
{
if (!Key.IsSlowConfigured)
{
return;
}
if (!Key.SlowCombo.SequenceEqual(keys))
{
return;
}
SpeedAdjusted?.Invoke(this, new KeySpeedAdjustedEventArgs(-1));
}
private void OnSpeedKeyBatch(KeyAction[] keys)
{
if (!Key.IsSpeedConfigured)
{
return;
}
if (!Key.SpeedCombo.SequenceEqual(keys))
{
return;
}
SpeedAdjusted?.Invoke(this, new KeySpeedAdjustedEventArgs(1));
}
#endregion
}
}