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