WingMan – Diff between revs 10 and 14

Subversion Repositories:
Rev:
Show entire fileIgnore whitespace
Rev 10 Rev 14
Line 1... Line 1...
1 using System; 1 using System;
2 using System.Collections.Generic; 2 using System.Collections.Specialized;
3 using System.IO; 3 using System.IO;
4 using System.Linq; 4 using System.Linq;
5 using System.Threading; 5 using System.Threading;
6 using System.Threading.Tasks; 6 using System.Threading.Tasks;
-   7 using System.Threading.Tasks.Dataflow;
7 using System.Windows.Forms; 8 using System.Windows.Forms;
8 using Gma.System.MouseKeyHook; 9 using Gma.System.MouseKeyHook;
9 using WingMan.Communication; 10 using WingMan.Communication;
-   11 using WingMan.Utilities;
Line 10... Line 12...
10   12  
11 namespace WingMan.MouseKey 13 namespace WingMan.Bindings
12 { 14 {
13 public class KeyInterceptor : IDisposable 15 public class KeyInterceptor : IDisposable
14 { 16 {
Line -... Line 17...
-   17 public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args);
-   18  
15 public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args); 19 private volatile bool ProcessPipe;
16   20  
17 public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication, 21 public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication,
-   22 TaskScheduler taskScheduler, CancellationToken cancellationToken)
-   23 {
18 TaskScheduler taskScheduler, CancellationToken cancellationToken) 24 DataFlowSemaphoreSlim = new SemaphoreSlim(1, 1);
-   25  
-   26 RemoteKeyBindings = remoteKeyBindings;
19 { 27 RemoteKeyBindings.Bindings.CollectionChanged += OnRemoteKeyBindingsChanged;
20 RemoteKeyBindings = remoteKeyBindings; 28  
21 MqttCommunication = mqttCommunication; 29 MqttCommunication = mqttCommunication;
Line 22... Line -...
22 TaskScheduler = taskScheduler; -  
23 CancellationToken = cancellationToken; -  
24   30 TaskScheduler = taskScheduler;
25 KeyCombo = new List<string>(); 31 CancellationToken = cancellationToken;
26   32  
27 MouseKeyGloalHook = Hook.GlobalEvents(); 33 MouseKeyGloalHook = Hook.GlobalEvents();
Line -... Line 34...
-   34 MouseKeyGloalHook.KeyUp += MouseKeyGloalHookOnKeyUp;
-   35 MouseKeyGloalHook.KeyDown += MouseKeyGloalHookOnKeyDown;
-   36 }
-   37  
-   38 private BatchBlock<string> KeyComboBatchBlock { get; set; }
-   39  
-   40 private ActionBlock<string[]> KeyComboActionBlock { get; set; }
-   41  
28 MouseKeyGloalHook.KeyUp += MouseKeyGloalHookOnKeyUp; 42 private IDisposable KeyComboDataFlowLink { get; set; }
29 MouseKeyGloalHook.KeyDown += MouseKeyGloalHookOnKeyDown; 43  
30 } 44 private SemaphoreSlim DataFlowSemaphoreSlim { get; }
31   45  
Line 32... Line 46...
32 private RemoteKeyBindings RemoteKeyBindings { get; } 46 private RemoteKeyBindings RemoteKeyBindings { get; }
33 private MqttCommunication MqttCommunication { get; } -  
34 private TaskScheduler TaskScheduler { get; } -  
Line 35... Line 47...
35 private CancellationToken CancellationToken { get; } 47 private MqttCommunication MqttCommunication { get; }
36   48 private TaskScheduler TaskScheduler { get; }
37 private IKeyboardMouseEvents MouseKeyGloalHook { get; } 49 private CancellationToken CancellationToken { get; }
38   50  
-   51 private IKeyboardMouseEvents MouseKeyGloalHook { get; set; }
Line 39... Line 52...
39 public List<string> KeyCombo { get; set; } 52  
40   53 public void Dispose()
Line 41... Line 54...
41 public void Dispose() 54 {
-   55 MouseKeyGloalHook.KeyUp -= MouseKeyGloalHookOnKeyUp;
-   56 MouseKeyGloalHook.KeyDown -= MouseKeyGloalHookOnKeyDown;
Line 42... Line 57...
42 { 57 RemoteKeyBindings.Bindings.CollectionChanged -= OnRemoteKeyBindingsChanged;
43 MouseKeyGloalHook.KeyUp -= MouseKeyGloalHookOnKeyUp; 58  
44 MouseKeyGloalHook.KeyDown -= MouseKeyGloalHookOnKeyDown; 59 KeyComboDataFlowLink?.Dispose();
Line -... Line 60...
-   60 KeyComboDataFlowLink = null;
-   61  
-   62 MouseKeyGloalHook?.Dispose();
-   63 MouseKeyGloalHook = null;
-   64 }
-   65  
-   66 private async void OnRemoteKeyBindingsChanged(object sender, NotifyCollectionChangedEventArgs e)
-   67 {
-   68 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken);
-   69  
-   70 try
-   71 {
-   72 // Break the link and dispose it.
45   73 KeyComboDataFlowLink?.Dispose();
-   74 KeyComboDataFlowLink = null;
-   75  
-   76 // Create a sliding window of size equal to the longest key combination.
-   77 var maxKeyComboLength = RemoteKeyBindings.Bindings.Max(binding => binding.Keys.Count);
-   78  
46 MouseKeyGloalHook.Dispose(); 79 KeyComboBatchBlock =
Line 47... Line 80...
47 } 80 new BatchBlock<string>(maxKeyComboLength);
48   81 KeyComboActionBlock = new ActionBlock<string[]>(ProcessKeyCombos,
49 public event MouseKeyBindingMatched OnMouseKeyBindingMatched; 82 new ExecutionDataflowBlockOptions {CancellationToken = CancellationToken});
Line -... Line 83...
-   83 KeyComboDataFlowLink = KeyComboBatchBlock.LinkTo(KeyComboActionBlock);
-   84 }
50   85 finally
-   86 {
Line 51... Line 87...
51 private void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e) 87 DataFlowSemaphoreSlim.Release();
-   88 }
52 { 89 }
53 e.SuppressKeyPress = false; 90  
Line -... Line 91...
-   91 private async Task ProcessKeyCombos(string[] keys)
-   92 {
54   93 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken);
-   94  
-   95 try
-   96 {
-   97 if (!ProcessPipe)
-   98 return;
-   99  
55 if (KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key)) KeyCombo.Add(key); 100 foreach (var binding in RemoteKeyBindings.Bindings)
-   101 {
-   102 if (!keys.SubsetEquals(binding.Keys))
Line -... Line 103...
-   103 continue;
-   104  
56 } 105 // Raise the match event.
-   106 await Task.Delay(0, CancellationToken)
-   107 .ContinueWith(
57   108 _ => OnMouseKeyBindingMatched?.Invoke(this,
58 private void MouseKeyGloalHookOnKeyUp(object sender, KeyEventArgs e) 109 new KeyBindingMatchedEventArgs(binding.Nick, binding.Name, binding.Keys)),
59 { 110 CancellationToken,
60 e.SuppressKeyPress = false; 111 TaskContinuationOptions.None, TaskScheduler);
61   112  
-   113 using (var memoryStream = new MemoryStream())
Line 62... Line -...
62 var combo = new List<string>(KeyCombo); -  
63   -  
64 #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed -  
65 Task.Run(() => SimulateMouseKey(new List<string>(combo)), CancellationToken); 114 {
66 #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed -  
67   -  
68 KeyCombo.Clear(); -  
Line 69... Line 115...
69 } 115 ExecuteKeyBinding.XmlSerializer.Serialize(memoryStream,
70   116 new ExecuteKeyBinding(binding.Nick, binding.Name));
71 private async Task SimulateMouseKey(List<string> mouseKeyCombo) -  
72 { 117  
Line -... Line 118...
-   118 memoryStream.Position = 0L;
73 foreach (var binding in RemoteKeyBindings.Bindings) 119  
Line 74... Line 120...
74 { 120 await MqttCommunication.Broadcast("execute", memoryStream.ToArray());
-   121 }
-   122 }
-   123 }
75 if (!binding.Keys.SequenceEqual(mouseKeyCombo)) 124 finally
-   125 {
-   126 DataFlowSemaphoreSlim.Release();
-   127 }
76 continue; 128 }
77   129  
-   130 public event MouseKeyBindingMatched OnMouseKeyBindingMatched;
-   131  
-   132 private async void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e)
-   133 {
-   134 ProcessPipe = true;
78 // Raise the match event. 135  
79 await Task.Delay(0, CancellationToken) 136 if (!KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key))