WingMan – Diff between revs 21 and 24

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 21 Rev 24
1 using System; 1 using System;
2 using System.Collections.Specialized; 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.Threading.Tasks.Dataflow;
8 using System.Windows.Forms; 8 using System.Windows.Forms;
9 using Gma.System.MouseKeyHook; 9 using Gma.System.MouseKeyHook;
10 using WingMan.Communication; 10 using WingMan.Communication;
11 using WingMan.Utilities; 11 using WingMan.Utilities;
12   12  
13 namespace WingMan.Bindings 13 namespace WingMan.Bindings
14 { 14 {
15 public class KeyInterceptor : IDisposable 15 public class KeyInterceptor : IDisposable
16 { 16 {
17 public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args); 17 public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args);
18   18  
19 private volatile bool ProcessPipe; 19 private volatile bool ProcessPipe;
20   20  
21 public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication, 21 public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication,
22 TaskScheduler taskScheduler, CancellationToken cancellationToken) 22 TaskScheduler taskScheduler, CancellationToken cancellationToken)
23 { 23 {
24 DataFlowSemaphoreSlim = new SemaphoreSlim(1, 1); 24 DataFlowSemaphoreSlim = new SemaphoreSlim(1, 1);
25   25  
26 RemoteKeyBindings = remoteKeyBindings; 26 RemoteKeyBindings = remoteKeyBindings;
27 RemoteKeyBindings.Bindings.CollectionChanged += OnRemoteKeyBindingsChanged; 27 RemoteKeyBindings.Bindings.CollectionChanged += OnRemoteKeyBindingsChanged;
28   28  
29 MqttCommunication = mqttCommunication; 29 MqttCommunication = mqttCommunication;
30 TaskScheduler = taskScheduler; 30 TaskScheduler = taskScheduler;
31 CancellationToken = cancellationToken; 31 CancellationToken = cancellationToken;
32   32  
33 MouseKeyGloalHook = Hook.GlobalEvents(); 33 MouseKeyGloalHook = Hook.GlobalEvents();
34 MouseKeyGloalHook.KeyUp += MouseKeyGloalHookOnKeyUp; 34 MouseKeyGloalHook.KeyUp += MouseKeyGloalHookOnKeyUp;
35 MouseKeyGloalHook.KeyDown += MouseKeyGloalHookOnKeyDown; 35 MouseKeyGloalHook.KeyDown += MouseKeyGloalHookOnKeyDown;
36 } 36 }
37   37  
38 private BatchBlock<string> KeyComboBatchBlock { get; set; } 38 private BatchBlock<string> KeyComboBatchBlock { get; set; }
39 private ActionBlock<string[]> KeyComboActionBlock { get; set; } 39 private ActionBlock<string[]> KeyComboActionBlock { get; set; }
40 private IDisposable KeyComboDataFlowLink { get; set; } 40 private IDisposable KeyComboDataFlowLink { get; set; }
41 private SemaphoreSlim DataFlowSemaphoreSlim { get; } 41 private SemaphoreSlim DataFlowSemaphoreSlim { get; }
42 private RemoteKeyBindings RemoteKeyBindings { get; } 42 private RemoteKeyBindings RemoteKeyBindings { get; }
43 private MqttCommunication MqttCommunication { get; } 43 private MqttCommunication MqttCommunication { get; }
44 private TaskScheduler TaskScheduler { get; } 44 private TaskScheduler TaskScheduler { get; }
45 private CancellationToken CancellationToken { get; } 45 private CancellationToken CancellationToken { get; }
46 private IKeyboardMouseEvents MouseKeyGloalHook { get; set; } 46 private IKeyboardMouseEvents MouseKeyGloalHook { get; set; }
47   47  
48 public void Dispose() 48 public void Dispose()
49 { 49 {
50 MouseKeyGloalHook.KeyUp -= MouseKeyGloalHookOnKeyUp; 50 MouseKeyGloalHook.KeyUp -= MouseKeyGloalHookOnKeyUp;
51 MouseKeyGloalHook.KeyDown -= MouseKeyGloalHookOnKeyDown; 51 MouseKeyGloalHook.KeyDown -= MouseKeyGloalHookOnKeyDown;
52 RemoteKeyBindings.Bindings.CollectionChanged -= OnRemoteKeyBindingsChanged; 52 RemoteKeyBindings.Bindings.CollectionChanged -= OnRemoteKeyBindingsChanged;
53   53  
54 KeyComboDataFlowLink?.Dispose(); 54 KeyComboDataFlowLink?.Dispose();
55 KeyComboDataFlowLink = null; 55 KeyComboDataFlowLink = null;
56   56  
57 MouseKeyGloalHook?.Dispose(); 57 MouseKeyGloalHook?.Dispose();
58 MouseKeyGloalHook = null; 58 MouseKeyGloalHook = null;
59 } 59 }
60   60  
61 private async void OnRemoteKeyBindingsChanged(object sender, NotifyCollectionChangedEventArgs e) 61 private async void OnRemoteKeyBindingsChanged(object sender, NotifyCollectionChangedEventArgs e)
62 { 62 {
63 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken); 63 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken);
64   64  
65 try 65 try
66 { 66 {
67 // Break the link and dispose it. 67 // Break the link and dispose it.
68 KeyComboDataFlowLink?.Dispose(); 68 KeyComboDataFlowLink?.Dispose();
69 KeyComboDataFlowLink = null; 69 KeyComboDataFlowLink = null;
70   70  
71 // Create a sliding window of size equal to the longest key combination. 71 // Create a sliding window of size equal to the longest key combination.
-   72 if (!RemoteKeyBindings.Bindings.Any())
-   73 return;
-   74  
-   75 var remoteKeyBindings = RemoteKeyBindings.Bindings.Where(binding => binding.Keys.Any()).ToList();
-   76 if (!remoteKeyBindings.Any())
-   77 return;
-   78  
72 var maxKeyComboLength = RemoteKeyBindings.Bindings.Max(binding => binding.Keys.Count); 79 var maxKeyComboLength = remoteKeyBindings.Max(binding => binding.Keys.Count);
73 if (maxKeyComboLength <= 0) 80 if (maxKeyComboLength <= 0)
74 return; 81 return;
75   82  
76 KeyComboBatchBlock = 83 KeyComboBatchBlock =
77 new BatchBlock<string>(maxKeyComboLength); 84 new BatchBlock<string>(maxKeyComboLength);
78 KeyComboActionBlock = new ActionBlock<string[]>(ProcessKeyCombos, 85 KeyComboActionBlock = new ActionBlock<string[]>(ProcessKeyCombos,
79 new ExecutionDataflowBlockOptions {CancellationToken = CancellationToken}); 86 new ExecutionDataflowBlockOptions {CancellationToken = CancellationToken});
80 KeyComboDataFlowLink = KeyComboBatchBlock.LinkTo(KeyComboActionBlock); 87 KeyComboDataFlowLink = KeyComboBatchBlock.LinkTo(KeyComboActionBlock);
81 } 88 }
82 finally 89 finally
83 { 90 {
84 DataFlowSemaphoreSlim.Release(); 91 DataFlowSemaphoreSlim.Release();
85 } 92 }
86 } 93 }
87   94  
88 private async Task ProcessKeyCombos(string[] keys) 95 private async Task ProcessKeyCombos(string[] keys)
89 { 96 {
90 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken); 97 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken);
91   98  
92 try 99 try
93 { 100 {
94 if (!ProcessPipe) 101 if (!ProcessPipe)
95 return; 102 return;
96   103  
97 foreach (var binding in RemoteKeyBindings.Bindings) 104 foreach (var binding in RemoteKeyBindings.Bindings)
98 { 105 {
99 if (!keys.SubsetEquals(binding.Keys)) 106 if (!keys.SubsetEquals(binding.Keys))
100 continue; 107 continue;
101   108  
102 // Raise the match event. 109 // Raise the match event.
103 await Task.Delay(0, CancellationToken) 110 await Task.Delay(0, CancellationToken)
104 .ContinueWith( 111 .ContinueWith(
105 _ => OnMouseKeyBindingMatched?.Invoke(this, 112 _ => OnMouseKeyBindingMatched?.Invoke(this,
106 new KeyBindingMatchedEventArgs(binding.Nick, binding.Name, binding.Keys)), 113 new KeyBindingMatchedEventArgs(binding.Nick, binding.Name, binding.Keys)),
107 CancellationToken, 114 CancellationToken,
108 TaskContinuationOptions.None, TaskScheduler); 115 TaskContinuationOptions.None, TaskScheduler);
109   116  
110 using (var memoryStream = new MemoryStream()) 117 using (var memoryStream = new MemoryStream())
111 { 118 {
112 ExecuteKeyBinding.XmlSerializer.Serialize(memoryStream, 119 ExecuteKeyBinding.XmlSerializer.Serialize(memoryStream,
113 new ExecuteKeyBinding(binding.Nick, binding.Name)); 120 new ExecuteKeyBinding(binding.Nick, binding.Name));
114   121  
115 memoryStream.Position = 0L; 122 memoryStream.Position = 0L;
116   123  
117 await MqttCommunication.Broadcast("execute", memoryStream.ToArray()); 124 await MqttCommunication.Broadcast("execute", memoryStream.ToArray());
118 } 125 }
119 } 126 }
120 } 127 }
121 finally 128 finally
122 { 129 {
123 DataFlowSemaphoreSlim.Release(); 130 DataFlowSemaphoreSlim.Release();
124 } 131 }
125 } 132 }
126   133  
127 public event MouseKeyBindingMatched OnMouseKeyBindingMatched; 134 public event MouseKeyBindingMatched OnMouseKeyBindingMatched;
128   135  
129 private async void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e) 136 private async void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e)
130 { 137 {
131 ProcessPipe = true; 138 ProcessPipe = true;
132   139  
133 if (!KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key)) 140 if (!KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key))
134 return; 141 return;
135   142  
136 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken); 143 await DataFlowSemaphoreSlim.WaitAsync(CancellationToken);
137 try 144 try
138 { 145 {
139 if (KeyComboBatchBlock != null) 146 if (KeyComboBatchBlock != null)
140 await KeyComboBatchBlock.SendAsync(key, CancellationToken); 147 await KeyComboBatchBlock.SendAsync(key, CancellationToken);
141 } 148 }
142 finally 149 finally
143 { 150 {
144 DataFlowSemaphoreSlim.Release(); 151 DataFlowSemaphoreSlim.Release();
145 } 152 }
146 } 153 }
147   154  
148 private void MouseKeyGloalHookOnKeyUp(object sender, KeyEventArgs e) 155 private void MouseKeyGloalHookOnKeyUp(object sender, KeyEventArgs e)
149 { 156 {
150 ProcessPipe = false; 157 ProcessPipe = false;
151 } 158 }
152 } 159 }
153 } 160 }
154   161