WingMan – Diff between revs 21 and 24
?pathlinks?
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 | |