WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 9  →  ?path2? @ 10
File deleted
/trunk/WingMan/Communication/TrackedClient.cs
File deleted
/trunk/WingMan/Communication/TrackedClients.cs
/trunk/WingMan/Communication/MqttAuthenticationFailureEventArgs.cs
@@ -0,0 +1,17 @@
using System;
using MQTTnet;
 
namespace WingMan.Communication
{
public class MqttAuthenticationFailureEventArgs : EventArgs
{
public MqttAuthenticationFailureEventArgs(MqttApplicationMessageReceivedEventArgs e, Exception ex)
{
Args = e;
Exception = ex;
}
 
public MqttApplicationMessageReceivedEventArgs Args { get; set; }
public Exception Exception { get; set; }
}
}
/trunk/WingMan/Communication/MqttCommunication.cs
@@ -0,0 +1,391 @@
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Protocol;
using MQTTnet.Server;
using WingMan.Utilities;
using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.MqttClientDisconnectedEventArgs;
 
namespace WingMan.Communication
{
public class MqttCommunication : IDisposable
{
public delegate void ClientAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e);
 
public delegate void ClientConnected(object sender, MqttClientConnectedEventArgs e);
 
public delegate void ClientConnectionFailed(object sender, MqttManagedProcessFailedEventArgs e);
 
public delegate void ClientDisconnected(object sender, MqttClientDisconnectedEventArgs e);
 
public delegate void ClientSubscribed(object sender, MqttClientSubscribedTopicEventArgs e);
 
public delegate void ClientUnsubscribed(object sender, MqttClientUnsubscribedTopicEventArgs e);
 
public delegate void MessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e);
 
public delegate void ServerAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e);
 
public delegate void ServerClientConnected(object sender, MQTTnet.Server.MqttClientConnectedEventArgs e);
 
public delegate void ServerClientDisconnected(object sender, MQTTnet.Server.MqttClientDisconnectedEventArgs e);
 
public delegate void ServerStarted(object sender, EventArgs e);
 
public delegate void ServerStopped(object sender, EventArgs e);
 
public MqttCommunication(TaskScheduler taskScheduler, CancellationToken cancellationToken)
{
TaskScheduler = taskScheduler;
CancellationToken = cancellationToken;
 
Client = new MqttFactory().CreateManagedMqttClient();
Server = new MqttFactory().CreateMqttServer();
}
 
private TaskScheduler TaskScheduler { get; }
 
private IManagedMqttClient Client { get; }
 
private IMqttServer Server { get; }
 
public bool Running { get; set; }
 
public string Nick { get; set; }
 
private IPAddress IpAddress { get; set; }
 
private int Port { get; set; }
 
private string Password { get; set; }
 
private CancellationToken CancellationToken { get; }
 
public MqttCommunicationType Type { get; set; }
 
public async void Dispose()
{
await Stop();
}
 
public event ClientAuthenticationFailed OnClientAuthenticationFailed;
 
public event ServerAuthenticationFailed OnServerAuthenticationFailed;
 
public event MessageReceived OnMessageReceived;
 
public event ClientConnected OnClientConnected;
 
public event ClientDisconnected OnClientDisconnected;
 
public event ClientConnectionFailed OnClientConnectionFailed;
 
public event ClientUnsubscribed OnClientUnsubscribed;
 
public event ClientSubscribed OnClientSubscribed;
 
public event ServerClientDisconnected OnServerClientDisconnected;
 
public event ServerClientConnected OnServerClientConnected;
 
public event ServerStarted OnServerStarted;
 
public event ServerStopped OnServerStopped;
 
public async Task<bool> Start(MqttCommunicationType type, IPAddress ipAddress, int port, string nick,
string password)
{
Type = type;
IpAddress = ipAddress;
Port = port;
Nick = nick;
Password = password;
 
switch (type)
{
case MqttCommunicationType.Client:
return await StartClient();
case MqttCommunicationType.Server:
return await StartServer();
}
 
return false;
}
 
private async Task<bool> StartClient()
{
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer(IpAddress.ToString(), Port);
 
// Setup and start a managed MQTT client.
var options = new ManagedMqttClientOptionsBuilder()
.WithClientOptions(clientOptions.Build())
.Build();
 
BindClientHandlers();
 
await Client.SubscribeAsync(
new TopicFilterBuilder()
.WithTopic("lobby")
.Build()
);
 
await Client.SubscribeAsync(
new TopicFilterBuilder()
.WithTopic("exchange")
.Build()
);
 
await Client.SubscribeAsync(
new TopicFilterBuilder()
.WithTopic("execute")
.Build()
);
 
await Client.StartAsync(options);
 
Running = true;
 
return Running;
}
 
private async Task StopClient()
{
UnbindClientHandlers();
 
await Client.StopAsync();
}
 
public void BindClientHandlers()
{
Client.Connected += ClientOnConnected;
Client.Disconnected += ClientOnDisconnected;
Client.ConnectingFailed += ClientOnConnectingFailed;
Client.ApplicationMessageReceived += ClientOnApplicationMessageReceived;
}
 
public void UnbindClientHandlers()
{
Client.Connected -= ClientOnConnected;
Client.Disconnected -= ClientOnDisconnected;
Client.ConnectingFailed -= ClientOnConnectingFailed;
Client.ApplicationMessageReceived -= ClientOnApplicationMessageReceived;
}
 
private async void ClientOnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
{
try
{
var load = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
 
e.ApplicationMessage.Payload = AES.Decrypt(e.ApplicationMessage.Payload, Password);
 
var load2 = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
 
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnMessageReceived?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
catch (Exception ex)
{
await Task.Delay(0, CancellationToken).ContinueWith(
_ => OnClientAuthenticationFailed?.Invoke(sender, new MqttAuthenticationFailureEventArgs(e, ex)),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
}
 
private async void ClientOnConnectingFailed(object sender, MqttManagedProcessFailedEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnClientConnectionFailed?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async void ClientOnDisconnected(object sender, MqttClientDisconnectedEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnClientDisconnected?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async void ClientOnConnected(object sender, MqttClientConnectedEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnClientConnected?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async Task<bool> StartServer()
{
var optionsBuilder = new MqttServerOptionsBuilder()
.WithDefaultEndpointBoundIPAddress(IpAddress)
.WithSubscriptionInterceptor(MqttSubscriptionIntercept)
.WithConnectionValidator(MqttConnectionValidator)
.WithDefaultEndpointPort(Port);
 
BindServerHandlers();
 
try
{
await Server.StartAsync(optionsBuilder.Build());
 
Running = true;
}
catch (Exception)
{
Running = false;
}
 
return Running;
}
 
private void MqttConnectionValidator(MqttConnectionValidatorContext context)
{
context.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
}
 
private async Task StopServer()
{
UnbindServerHandlers();
 
await Server.StopAsync();
}
 
private void MqttSubscriptionIntercept(MqttSubscriptionInterceptorContext context)
{
if (context.TopicFilter.Topic != "lobby" &&
context.TopicFilter.Topic != "exchange" &&
context.TopicFilter.Topic != "execute")
{
context.AcceptSubscription = false;
context.CloseConnection = true;
return;
}
 
context.AcceptSubscription = true;
context.CloseConnection = false;
}
 
private void BindServerHandlers()
{
Server.Started += ServerOnStarted;
Server.Stopped += ServerOnStopped;
Server.ClientConnected += ServerOnClientConnected;
Server.ClientDisconnected += ServerOnClientDisconnected;
Server.ClientSubscribedTopic += ServerOnClientSubscribedTopic;
Server.ClientUnsubscribedTopic += ServerOnClientUnsubscribedTopic;
Server.ApplicationMessageReceived += ServerOnApplicationMessageReceived;
}
 
private void UnbindServerHandlers()
{
Server.Started -= ServerOnStarted;
Server.Stopped -= ServerOnStopped;
Server.ClientConnected -= ServerOnClientConnected;
Server.ClientDisconnected -= ServerOnClientDisconnected;
Server.ClientSubscribedTopic -= ServerOnClientSubscribedTopic;
Server.ClientUnsubscribedTopic -= ServerOnClientUnsubscribedTopic;
Server.ApplicationMessageReceived -= ServerOnApplicationMessageReceived;
}
 
private async void ServerOnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
{
try
{
var load = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
 
e.ApplicationMessage.Payload = AES.Decrypt(e.ApplicationMessage.Payload, Password);
 
var load2 = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
 
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnMessageReceived?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
catch (Exception ex)
{
foreach (var clientSessionStatus in await Server.GetClientSessionsStatusAsync())
{
if (!string.Equals(clientSessionStatus.ClientId, e.ClientId, StringComparison.Ordinal))
continue;
 
await clientSessionStatus.DisconnectAsync();
}
 
await Task.Delay(0, CancellationToken).ContinueWith(
_ => OnServerAuthenticationFailed?.Invoke(sender, new MqttAuthenticationFailureEventArgs(e, ex)),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
}
 
private async void ServerOnClientUnsubscribedTopic(object sender, MqttClientUnsubscribedTopicEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnClientUnsubscribed?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async void ServerOnClientSubscribedTopic(object sender, MqttClientSubscribedTopicEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnClientSubscribed?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async void ServerOnClientDisconnected(object sender, MQTTnet.Server.MqttClientDisconnectedEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnServerClientDisconnected?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private async void ServerOnClientConnected(object sender, MQTTnet.Server.MqttClientConnectedEventArgs e)
{
await Task.Delay(0, CancellationToken).ContinueWith(_ => OnServerClientConnected?.Invoke(sender, e),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
}
 
private void ServerOnStopped(object sender, EventArgs e)
{
OnServerStopped?.Invoke(sender, e);
}
 
private void ServerOnStarted(object sender, EventArgs e)
{
OnServerStarted?.Invoke(sender, e);
}
 
public async Task Stop()
{
switch (Type)
{
case MqttCommunicationType.Server:
await StopServer();
break;
case MqttCommunicationType.Client:
await StopClient();
break;
}
 
Running = false;
}
 
public async Task Broadcast(string topic, byte[] payload)
{
var encryptedPayload = AES.Encrypt(payload, Password);
 
var load = Encoding.UTF8.GetString(encryptedPayload);
 
switch (Type)
{
case MqttCommunicationType.Client:
await Client.PublishAsync(new ManagedMqttApplicationMessage
{
ApplicationMessage = new MqttApplicationMessage {Topic = topic, Payload = encryptedPayload }
});
break;
case MqttCommunicationType.Server:
await Server.PublishAsync(new MqttApplicationMessage {Topic = topic, Payload = encryptedPayload });
break;
}
}
}
}
/trunk/WingMan/Communication/MqttCommunicationType.cs
@@ -0,0 +1,8 @@
namespace WingMan.Communication
{
public enum MqttCommunicationType
{
Server,
Client
}
}
/trunk/WingMan/Lobby/LobbyMessageSynchronizer.cs
@@ -63,7 +63,7 @@
 
memoryStream.Position = 0L;
 
await MqttCommunication.Broadcast("lobby", memoryStream.ToArray()).ConfigureAwait(false);
await MqttCommunication.Broadcast("lobby", memoryStream.ToArray());
}
}
}
File deleted
/trunk/WingMan/MouseKey/MouseKeyBindingsExchange.cs
File deleted
/trunk/WingMan/MouseKey/RemoteMouseKeyBindings.cs
File deleted
/trunk/WingMan/MouseKey/MouseKeyBindingExchange.cs
File deleted
/trunk/WingMan/MouseKey/MouseKeyBindings.cs
File deleted
/trunk/WingMan/MouseKey/MouseKeyBinding.cs
File deleted
/trunk/WingMan/MouseKey/RemoteMouseKeyBinding.cs
File deleted
/trunk/WingMan/MouseKey/MouseKeyBindingsSynchronizer.cs
File deleted
/trunk/WingMan/MouseKey/MouseKeyBindingsSynchronizedEventArgs.cs
/trunk/WingMan/MouseKey/ExecuteKeyBinding.cs
@@ -0,0 +1,23 @@
using System.Xml.Serialization;
 
namespace WingMan.MouseKey
{
public class ExecuteKeyBinding
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer =
new XmlSerializer(typeof(ExecuteKeyBinding));
 
public ExecuteKeyBinding()
{
}
 
public ExecuteKeyBinding(string nick, string name)
{
Nick = nick;
Name = name;
}
 
public string Nick { get; set; }
public string Name { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyBinding.cs
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace WingMan.MouseKey
{
public class KeyBinding : IEquatable<KeyBinding>
{
public KeyBinding()
{
}
 
public KeyBinding(string name, List<string> keys) : this()
{
Name = name;
Keys = keys;
}
 
public string DisplayName => $"{Name} ({string.Join(" + ", Keys.ToArray())})";
 
public string Name { get; set; } = string.Empty;
 
public List<string> Keys { get; set; } = new List<string>();
 
public bool Equals(KeyBinding other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name) && Keys.SequenceEqual(other.Keys);
}
 
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((KeyBinding) obj);
}
 
public override int GetHashCode()
{
unchecked
{
return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ (Keys != null ? Keys.GetHashCode() : 0);
}
}
}
}
/trunk/WingMan/MouseKey/KeyBindingExchange.cs
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Xml.Serialization;
 
namespace WingMan.MouseKey
{
public class KeyBindingExchange
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer =
new XmlSerializer(typeof(KeyBindingExchange));
 
public KeyBindingExchange()
{
}
 
public KeyBindingExchange(string nick, List<KeyBinding> keyBindings)
{
Nick = nick;
KeyBindings = keyBindings;
}
 
public string Nick { get; set; }
 
public List<KeyBinding> KeyBindings { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyBindingExecutingEventArgs.cs
@@ -0,0 +1,16 @@
using System;
 
namespace WingMan.MouseKey
{
public class KeyBindingExecutingEventArgs : EventArgs
{
public KeyBindingExecutingEventArgs(string nick, string name)
{
Nick = nick;
Name = name;
}
 
public string Nick { get; set; }
public string Name { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyBindingMatchedEventArgs.cs
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
 
namespace WingMan.MouseKey
{
public class KeyBindingMatchedEventArgs : EventArgs
{
public KeyBindingMatchedEventArgs(string nick, string name, List<string> keyCombo)
{
Nick = nick;
Name = name;
KeyCombo = keyCombo;
}
 
public string Nick { get; set; }
public string Name { get; set; }
public List<string> KeyCombo { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyBindingsExchange.cs
@@ -0,0 +1,9 @@
using System.Collections.Generic;
 
namespace WingMan.MouseKey
{
public class KeyBindingsExchange
{
public List<KeyBindingExchange> ExchangeBindings { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyBindingsSynchronizer.cs
@@ -0,0 +1,103 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using WingMan.Communication;
 
namespace WingMan.MouseKey
{
public class KeyBindingsSynchronizer : IDisposable
{
public delegate void MouseKeyBindingsSynchronized(object sender, KeyBindingsSynchronizerEventArgs e);
 
public KeyBindingsSynchronizer(LocalKeyBindings localKeyBindings, MqttCommunication mqttCommunication,
TaskScheduler taskScheduler, CancellationToken cancellationToken)
{
LocalKeyBindings = localKeyBindings;
MqttCommunication = mqttCommunication;
CancellationToken = cancellationToken;
TaskScheduler = taskScheduler;
 
SynchronizedMouseKeyBindings = new ConcurrentDictionary<string, List<KeyBinding>>();
 
MqttCommunication.OnMessageReceived += MqttCommunicationOnMessageReceived;
 
Task.Run(PeriodicSynchronize, CancellationToken);
}
 
private LocalKeyBindings LocalKeyBindings { get; }
 
private ConcurrentDictionary<string, List<KeyBinding>> SynchronizedMouseKeyBindings { get; }
 
private MqttCommunication MqttCommunication { get; }
 
private CancellationToken CancellationToken { get; }
private TaskScheduler TaskScheduler { get; }
 
public void Dispose()
{
MqttCommunication.OnMessageReceived -= MqttCommunicationOnMessageReceived;
}
 
public event MouseKeyBindingsSynchronized OnMouseKeyBindingsSynchronized;
 
private async void MqttCommunicationOnMessageReceived(object sender,
MqttApplicationMessageReceivedEventArgs e)
{
if (e.ApplicationMessage.Topic != "exchange")
return;
 
using (var memoryStream = new MemoryStream(e.ApplicationMessage.Payload))
{
memoryStream.Position = 0L;
 
var mouseKeyBindingsExchange =
(KeyBindingExchange) KeyBindingExchange.XmlSerializer.Deserialize(memoryStream);
 
// Do not add own bindings.
if (string.Equals(mouseKeyBindingsExchange.Nick, MqttCommunication.Nick))
return;
 
if (SynchronizedMouseKeyBindings.TryGetValue(mouseKeyBindingsExchange.Nick, out var mouseKeyBinding) &&
mouseKeyBinding.SequenceEqual(mouseKeyBindingsExchange.KeyBindings))
return;
 
await Task.Delay(0)
.ContinueWith(
_ => OnMouseKeyBindingsSynchronized?.Invoke(sender,
new KeyBindingsSynchronizerEventArgs(
mouseKeyBindingsExchange)),
CancellationToken, TaskContinuationOptions.None, TaskScheduler);
 
// Nick does not exist so the bindings will be added.
SynchronizedMouseKeyBindings.AddOrUpdate(mouseKeyBindingsExchange.Nick,
mouseKeyBindingsExchange.KeyBindings, (s, list) => mouseKeyBindingsExchange.KeyBindings);
}
}
 
private async Task PeriodicSynchronize()
{
do
{
await Task.Delay(1000, CancellationToken);
 
if (!MqttCommunication.Running)
continue;
 
using (var memoryStream = new MemoryStream())
{
KeyBindingExchange.XmlSerializer.Serialize(memoryStream,
new KeyBindingExchange(MqttCommunication.Nick, LocalKeyBindings.Bindings));
 
memoryStream.Position = 0L;
 
await MqttCommunication.Broadcast("exchange", memoryStream.ToArray());
}
} while (!CancellationToken.IsCancellationRequested);
}
}
}
/trunk/WingMan/MouseKey/KeyBindingsSynchronizerEventArgs.cs
@@ -0,0 +1,14 @@
using System;
 
namespace WingMan.MouseKey
{
public class KeyBindingsSynchronizerEventArgs : EventArgs
{
public KeyBindingsSynchronizerEventArgs(KeyBindingExchange keyExchangeBindings)
{
Bindings = keyExchangeBindings;
}
 
public KeyBindingExchange Bindings { get; set; }
}
}
/trunk/WingMan/MouseKey/KeyInterceptor.cs
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using WingMan.Communication;
 
namespace WingMan.MouseKey
{
public class KeyInterceptor : IDisposable
{
public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args);
 
public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication,
TaskScheduler taskScheduler, CancellationToken cancellationToken)
{
RemoteKeyBindings = remoteKeyBindings;
MqttCommunication = mqttCommunication;
TaskScheduler = taskScheduler;
CancellationToken = cancellationToken;
 
KeyCombo = new List<string>();
 
MouseKeyGloalHook = Hook.GlobalEvents();
MouseKeyGloalHook.KeyUp += MouseKeyGloalHookOnKeyUp;
MouseKeyGloalHook.KeyDown += MouseKeyGloalHookOnKeyDown;
}
 
private RemoteKeyBindings RemoteKeyBindings { get; }
private MqttCommunication MqttCommunication { get; }
private TaskScheduler TaskScheduler { get; }
private CancellationToken CancellationToken { get; }
 
private IKeyboardMouseEvents MouseKeyGloalHook { get; }
 
public List<string> KeyCombo { get; set; }
 
public void Dispose()
{
MouseKeyGloalHook.KeyUp -= MouseKeyGloalHookOnKeyUp;
MouseKeyGloalHook.KeyDown -= MouseKeyGloalHookOnKeyDown;
 
MouseKeyGloalHook.Dispose();
}
 
public event MouseKeyBindingMatched OnMouseKeyBindingMatched;
 
private void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = false;
 
if (KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key)) KeyCombo.Add(key);
}
 
private void MouseKeyGloalHookOnKeyUp(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = false;
 
var combo = new List<string>(KeyCombo);
 
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() => SimulateMouseKey(new List<string>(combo)), CancellationToken);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
 
KeyCombo.Clear();
}
 
private async Task SimulateMouseKey(List<string> mouseKeyCombo)
{
foreach (var binding in RemoteKeyBindings.Bindings)
{
if (!binding.Keys.SequenceEqual(mouseKeyCombo))
continue;
 
// Raise the match event.
await Task.Delay(0, CancellationToken)
.ContinueWith(
_ => OnMouseKeyBindingMatched?.Invoke(this,
new KeyBindingMatchedEventArgs(binding.Nick, binding.Name, binding.Keys)),
CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
 
using (var memoryStream = new MemoryStream())
{
ExecuteKeyBinding.XmlSerializer.Serialize(memoryStream,
new ExecuteKeyBinding(binding.Nick, binding.Name));
 
memoryStream.Position = 0L;
 
await MqttCommunication.Broadcast("execute", memoryStream.ToArray());
}
}
}
}
}
/trunk/WingMan/MouseKey/KeySimulator.cs
@@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using MQTTnet;
using SimWinInput;
using WingMan.Communication;
 
namespace WingMan.MouseKey
{
public class KeySimulator : IDisposable
{
public delegate void MouseKeyBindingExecuting(object sender, KeyBindingExecutingEventArgs args);
 
public KeySimulator(LocalKeyBindings localLocalKeyBindings, MqttCommunication mqttCommunication,
TaskScheduler formTaskScheduler, CancellationToken cancellationToken)
{
LocalLocalKeyBindings = localLocalKeyBindings;
MqttCommunication = mqttCommunication;
TaskScheduler = formTaskScheduler;
CancellationToken = cancellationToken;
 
MqttCommunication.OnMessageReceived += OnMqttMessageReceived;
}
 
private MqttCommunication MqttCommunication { get; }
private TaskScheduler TaskScheduler { get; }
private CancellationToken CancellationToken { get; }
private LocalKeyBindings LocalLocalKeyBindings { get; }
 
public void Dispose()
{
MqttCommunication.OnMessageReceived -= OnMqttMessageReceived;
}
 
public event MouseKeyBindingExecuting OnMouseKeyBindingExecuting;
 
private async void OnMqttMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
{
if (e.ApplicationMessage.Topic != "execute")
return;
 
using (var memoryStream = new MemoryStream(e.ApplicationMessage.Payload))
{
var executeMouseKeyBinding =
(ExecuteKeyBinding) ExecuteKeyBinding.XmlSerializer.Deserialize(memoryStream);
 
// Do not process own mouse key bindings.
if (!string.Equals(executeMouseKeyBinding.Nick, MqttCommunication.Nick, StringComparison.Ordinal))
return;
 
await Task.Delay(0, CancellationToken)
.ContinueWith(
_ => OnMouseKeyBindingExecuting?.Invoke(sender,
new KeyBindingExecutingEventArgs(executeMouseKeyBinding.Nick,
executeMouseKeyBinding.Name)),
CancellationToken,
TaskContinuationOptions.None, TaskScheduler);
 
Simulate(executeMouseKeyBinding);
}
}
 
private void Simulate(ExecuteKeyBinding executeBinding)
{
foreach (var localBinding in LocalLocalKeyBindings.Bindings)
{
if (!string.Equals(localBinding.Name, executeBinding.Name, StringComparison.Ordinal))
continue;
 
// Press
foreach (var key in localBinding.Keys)
{
if (KeyConversion.StringToKeys.TryGetValue(key, out var pressKey))
{
SimKeyboard.KeyDown(pressKey);
continue;
}
 
if (KeyConversion.StringToMouseButtons.TryGetValue(key, out var pressMouse))
switch (pressMouse)
{
case MouseButtons.Left:
SimMouse.Act(SimMouse.Action.LeftButtonDown, Cursor.Position.X, Cursor.Position.Y);
break;
case MouseButtons.Middle:
SimMouse.Act(SimMouse.Action.MiddleButtonDown, Cursor.Position.X, Cursor.Position.Y);
break;
case MouseButtons.Right:
SimMouse.Act(SimMouse.Action.RightButtonDown, Cursor.Position.X, Cursor.Position.Y);
break;
}
}
 
// Depress
foreach (var key in localBinding.Keys)
{
if (KeyConversion.StringToKeys.TryGetValue(key, out var pressKey))
{
SimKeyboard.KeyUp(pressKey);
continue;
}
 
if (KeyConversion.StringToMouseButtons.TryGetValue(key, out var pressMouse))
switch (pressMouse)
{
case MouseButtons.Left:
SimMouse.Act(SimMouse.Action.LeftButtonUp, Cursor.Position.X, Cursor.Position.Y);
break;
case MouseButtons.Middle:
SimMouse.Act(SimMouse.Action.MiddleButtonUp, Cursor.Position.X, Cursor.Position.Y);
break;
case MouseButtons.Right:
SimMouse.Act(SimMouse.Action.RightButtonUp, Cursor.Position.X, Cursor.Position.Y);
break;
}
}
}
}
}
}
/trunk/WingMan/MouseKey/LocalKeyBindings.cs
@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Xml.Serialization;
 
namespace WingMan.MouseKey
{
public class LocalKeyBindings
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer = new XmlSerializer(typeof(LocalKeyBindings));
 
public LocalKeyBindings()
{
}
 
public LocalKeyBindings(List<KeyBinding> bindings) : this()
{
Bindings = bindings;
}
 
public List<KeyBinding> Bindings { get; set; }
}
}
/trunk/WingMan/MouseKey/RemoteKeyBinding.cs
@@ -0,0 +1,24 @@
using System.Collections.Generic;
 
namespace WingMan.MouseKey
{
public class RemoteKeyBinding
{
public RemoteKeyBinding()
{
}
 
public RemoteKeyBinding(string nick, string name, List<string> mouseKeyCombo)
{
Nick = nick;
Name = name;
Keys = mouseKeyCombo;
}
 
public string Nick { get; set; }
 
public string Name { get; set; }
 
public List<string> Keys { get; set; }
}
}
/trunk/WingMan/MouseKey/RemoteKeyBindings.cs
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Xml.Serialization;
 
namespace WingMan.MouseKey
{
public class RemoteKeyBindings
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer =
new XmlSerializer(typeof(RemoteKeyBindings));
 
public RemoteKeyBindings()
{
}
 
public RemoteKeyBindings(List<RemoteKeyBinding> bindings)
{
Bindings = bindings;
}
 
public List<RemoteKeyBinding> Bindings { get; set; }
}
}
/trunk/WingMan/Properties/Strings.Designer.cs
@@ -133,6 +133,15 @@
}
/// <summary>
/// Looks up a localized string similar to Executing binding from remote client.
/// </summary>
internal static string Executing_binding_from_remote_client {
get {
return ResourceManager.GetString("Executing_binding_from_remote_client", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed loading local bindings.
/// </summary>
internal static string Failed_loading_local_bindings {
@@ -196,20 +205,20 @@
}
/// <summary>
/// Looks up a localized string similar to Matched helm key binding.
/// Looks up a localized string similar to Failed to authenticate with server.
/// </summary>
internal static string Matched_helm_key_binding {
internal static string Failed_to_authenticate_with_server {
get {
return ResourceManager.GetString("Matched_helm_key_binding", resourceCulture);
return ResourceManager.GetString("Failed_to_authenticate_with_server", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Server authentication failed.
/// Looks up a localized string similar to Matched remote key binding.
/// </summary>
internal static string Server_authentication_failed {
internal static string Matched_remote_key_binding {
get {
return ResourceManager.GetString("Server_authentication_failed", resourceCulture);
return ResourceManager.GetString("Matched_remote_key_binding", resourceCulture);
}
}
/trunk/WingMan/Properties/Strings.resx
@@ -141,6 +141,9 @@
<data name="Disconnect" xml:space="preserve">
<value>Disconnect</value>
</data>
<data name="Executing_binding_from_remote_client" xml:space="preserve">
<value>Executing binding from remote client</value>
</data>
<data name="Failed_loading_local_bindings" xml:space="preserve">
<value>Failed loading local bindings</value>
</data>
@@ -162,11 +165,11 @@
<data name="Failed_to_authenticate_client" xml:space="preserve">
<value>Failed to authenticate client</value>
</data>
<data name="Matched_helm_key_binding" xml:space="preserve">
<value>Matched helm key binding</value>
<data name="Failed_to_authenticate_with_server" xml:space="preserve">
<value>Failed to authenticate with server</value>
</data>
<data name="Server_authentication_failed" xml:space="preserve">
<value>Server authentication failed</value>
<data name="Matched_remote_key_binding" xml:space="preserve">
<value>Matched remote key binding</value>
</data>
<data name="Server_started" xml:space="preserve">
<value>Server started</value>
/trunk/WingMan/Utilities/AES.cs
@@ -2,7 +2,6 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace WingMan.Utilities
{
@@ -24,7 +23,7 @@
/// <param name="key">the encryption key</param>
/// <param name="separator">the separator to use between the cyphertext and the IV</param>
/// <returns>Base64 encoded encrypted data</returns>
public static async Task<byte[]> Encrypt(byte[] data, string key, string separator = ":")
public static byte[] Encrypt(byte[] data, string key, string separator = ":")
{
using (var rijdanelManaged = new RijndaelManaged())
{
@@ -36,29 +35,31 @@
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var derivedKey = new Rfc2898DeriveBytes(key, salt))
{
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
 
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream())
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
using (var memoryStream = new MemoryStream())
{
using (var inputStream = new MemoryStream(data))
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
await inputStream.CopyToAsync(cryptoStream).ConfigureAwait(false);
cryptoStream.FlushFinalBlock();
using (var inputStream = new MemoryStream(data))
{
inputStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
 
inputStream.Position = 0L;
inputStream.Position = 0L;
 
var payload = memoryStream.ToArray();
var payload = memoryStream.ToArray();
 
var base64Salt = Convert.ToBase64String(salt);
var base64Payload = Convert.ToBase64String(payload);
var base64Salt = Convert.ToBase64String(salt);
var base64Payload = Convert.ToBase64String(payload);
 
return Encoding.UTF8.GetBytes($"{base64Salt}{separator}{base64Payload}");
return Encoding.UTF8.GetBytes($"{base64Salt}{separator}{base64Payload}");
}
}
}
}
@@ -79,7 +80,7 @@
/// <param name="key">the encryption key</param>
/// <param name="separator">the separator to use between the cyphertext and the IV</param>
/// <returns>the decrypted data</returns>
public static async Task<byte[]> Decrypt(byte[] data, string key, string separator = ":")
public static byte[] Decrypt(byte[] data, string key, string separator = ":")
{
var input = Encoding.UTF8.GetString(data);
 
@@ -86,7 +87,7 @@
// retrieve the salt from the data.
var segments = input.Split(new[] {separator}, StringSplitOptions.None);
if (segments.Length != 2)
throw new ArgumentException("Invalid data.");
throw new ArgumentException("Invalid data: " + input);
 
using (var rijdanelManaged = new RijndaelManaged())
{
@@ -96,20 +97,21 @@
rijdanelManaged.Padding = AesPaddingMode;
 
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, Convert.FromBase64String(segments[0].Trim()));
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
using (var derivedKey = new Rfc2898DeriveBytes(key, Convert.FromBase64String(segments[0].Trim())))
{
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
 
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var memoryStream = new MemoryStream(Convert.FromBase64String(segments[1].Trim())))
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var memoryStream = new MemoryStream(Convert.FromBase64String(segments[1].Trim())))
{
using (var streamReader = new StreamReader(cryptoStream))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
return Encoding.UTF8.GetBytes(await streamReader.ReadToEndAsync()
.ConfigureAwait(false));
using (var streamReader = new StreamReader(cryptoStream))
{
return Encoding.UTF8.GetBytes(streamReader.ReadToEnd());
}
}
}
}
@@ -117,7 +119,7 @@
}
}
 
public static string LinearFeedbackShiftPassword(string password, int size = 32)
public static string ExpandKey(string password, int size = 32)
{
var sb = new StringBuilder(password);
do
/trunk/WingMan/Utilities/KeyConversion.cs
@@ -1,424 +1,376 @@
using System.Windows.Forms;
using System.Collections.Generic;
using System.Windows.Forms;
 
namespace WingMan
{
public static class KeyConversion
{
public static string ToDisplayName(this MouseButtons button)
{
var mouseButton = string.Empty;
switch (button)
public static readonly Dictionary<MouseButtons, string> MouseButtonsToString =
new Dictionary<MouseButtons, string>
{
case MouseButtons.Left:
mouseButton = "Left Mouse Button";
break;
case MouseButtons.Middle:
mouseButton = "Middle Mouse Button";
break;
case MouseButtons.Right:
mouseButton = "Right Mouse Button";
break;
}
{MouseButtons.Left, "Left Mouse Button"},
{MouseButtons.Middle, "Middle Mouse Button"},
{MouseButtons.Right, "Right Mouse Button"}
};
 
return mouseButton;
}
public static readonly Dictionary<string, MouseButtons> StringToMouseButtons =
new Dictionary<string, MouseButtons>
{
{"Left Mouse Button", MouseButtons.Left},
{"Middle Mouse Button", MouseButtons.Middle},
{"Right Mouse Button", MouseButtons.Right}
};
 
public static string ToDisplayName(this Keys keys)
public static readonly Dictionary<string, byte> StringToKeys = new Dictionary<string, byte>
{
var keyString = string.Empty;
switch ((int) keys)
{
case 0:
break;
case 1:
keyString = "Mouse Left";
break;
case 2:
keyString = "Mouse Right";
break;
case 3:
keyString = "Cancel";
break;
case 4:
keyString = "Mouse Middle";
break;
case 5:
keyString = "Special 1";
break;
case 6:
keyString = "Special 2";
break;
case 8:
keyString = "Back";
break;
case 9:
keyString = "TAB";
break;
case 12:
keyString = "Clear";
break;
case 13:
keyString = "Enter";
break;
case 16:
keyString = "Shift";
break;
case 17:
keyString = "Ctrl";
break;
case 18:
keyString = "Menu";
break;
case 19:
keyString = "Pause";
break;
case 20:
keyString = "Caps Lock";
break;
case 21:
keyString = "Kana/Hangul";
break;
case 23:
keyString = "Junja";
break;
case 24:
keyString = "Final";
break;
case 25:
keyString = "Hanja/Kanji";
break;
case 27:
keyString = "Esc";
break;
case 28:
keyString = "Convert";
break;
case 29:
keyString = "NonConvert";
break;
case 30:
keyString = "Accept";
break;
case 31:
keyString = "Mode";
break;
case 32:
keyString = "Space";
break;
case 33:
keyString = "Page Up";
break;
case 34:
keyString = "Page Down";
break;
case 35:
keyString = "End";
break;
case 36:
keyString = "Home";
break;
case 37:
keyString = "Left";
break;
case 38:
keyString = "Up";
break;
case 39:
keyString = "Right";
break;
case 40:
keyString = "Down";
break;
case 41:
keyString = "Select";
break;
case 42:
keyString = "Print";
break;
case 43:
keyString = "Execute";
break;
case 44:
keyString = "Snapshot";
break;
case 45:
keyString = "Insert";
break;
case 46:
keyString = "Delete";
break;
case 47:
keyString = "Help";
break;
case 48:
keyString = "Num 0";
break;
case 49:
keyString = "Num 1";
break;
case 50:
keyString = "Num 2";
break;
case 51:
keyString = "Num 3";
break;
case 52:
keyString = "Num 4";
break;
case 53:
keyString = "Num 5";
break;
case 54:
keyString = "Num 6";
break;
case 55:
keyString = "Num 7";
break;
case 56:
keyString = "Num 8";
break;
case 57:
keyString = "Num 9";
break;
case 65:
keyString = "A";
break;
case 66:
keyString = "B";
break;
case 67:
keyString = "C";
break;
case 68:
keyString = "D";
break;
case 69:
keyString = "E";
break;
case 70:
keyString = "F";
break;
case 71:
keyString = "G";
break;
case 72:
keyString = "H";
break;
case 73:
keyString = "I";
break;
case 74:
keyString = "J";
break;
case 75:
keyString = "K";
break;
case 76:
keyString = "L";
break;
case 77:
keyString = "M";
break;
case 78:
keyString = "N";
break;
case 79:
keyString = "O";
break;
case 80:
keyString = "P";
break;
case 81:
keyString = "Q";
break;
case 82:
keyString = "R";
break;
case 83:
keyString = "S";
break;
case 84:
keyString = "T";
break;
case 85:
keyString = "U";
break;
case 86:
keyString = "V";
break;
case 87:
keyString = "W";
break;
case 88:
keyString = "X";
break;
case 89:
keyString = "Y";
break;
case 90:
keyString = "Z";
break;
case 91:
keyString = "Windows Left";
break;
case 92:
keyString = "Windows Right";
break;
case 93:
keyString = "Application";
break;
case 95:
keyString = "Sleep";
break;
case 96:
keyString = "NumPad 0";
break;
case 97:
keyString = "NumPad 1";
break;
case 98:
keyString = "NumPad 2";
break;
case 99:
keyString = "NumPad 3";
break;
case 100:
keyString = "NumPad 4";
break;
case 101:
keyString = "NumPad 5";
break;
case 102:
keyString = "NumPad 6";
break;
case 103:
keyString = "NumPad 7";
break;
case 104:
keyString = "NumPad 8";
break;
case 105:
keyString = "NumPad 9";
break;
case 106:
keyString = "NumPad *";
break;
case 107:
keyString = "NumPad +";
break;
case 108:
keyString = "NumPad .";
break;
case 109:
keyString = "NumPad -";
break;
case 110:
keyString = "NumPad ,";
break;
case 111:
keyString = "NumPad /";
break;
case 112:
keyString = "F1";
break;
case 113:
keyString = "F2";
break;
case 114:
keyString = "F3";
break;
case 115:
keyString = "F4";
break;
case 116:
keyString = "F5";
break;
case 117:
keyString = "F6";
break;
case 118:
keyString = "F7";
break;
case 119:
keyString = "F8";
break;
case 120:
keyString = "F9";
break;
case 121:
keyString = "F10";
break;
case 122:
keyString = "F11";
break;
case 123:
keyString = "F12";
break;
case 124:
keyString = "F13";
break;
case 125:
keyString = "F14";
break;
case 126:
keyString = "F15";
break;
case 127:
keyString = "F16";
break;
case 128:
keyString = "F17";
break;
case 129:
keyString = "F18";
break;
case 130:
keyString = "F19";
break;
case 131:
keyString = "F20";
break;
case 132:
keyString = "F21";
break;
case 133:
keyString = "F22";
break;
case 134:
keyString = "F23";
break;
case 135:
keyString = "F24";
break;
case 144:
keyString = "Num lock";
break;
case 145:
keyString = "Scroll";
break;
case 160:
keyString = "Shift Left";
break;
case 161:
keyString = "Shift Right";
break;
case 162:
keyString = "Ctrl Left";
break;
case 163:
keyString = "Ctrl Right";
break;
case 164:
keyString = "Menu Left";
break;
case 165:
keyString = "Menu Right";
break;
default:
break;
}
{"None", 0},
{"LButton", 1},
{"RButton", 2},
{"Cancel", 3},
{"MButton", 4},
{"XButton1", 5},
{"XButton2", 6},
{"Back", 8},
{"Tab", 9},
{"LineFeed", 10},
{"Clear", 12},
{"Enter", 13},
{"ShiftKey", 16},
{"ControlKey", 17},
{"Menu", 18},
{"Pause", 19},
{"CapsLock", 20},
{"HangulMode", 21},
{"JunjaMode", 23},
{"FinalMode", 24},
{"KanjiMode", 25},
{"Escape", 27},
{"IMEConvert", 28},
{"IMENonconvert", 29},
{"IMEAccept", 30},
{"IMEModeChange", 31},
{"Space", 32},
{"PageUp", 33},
{"PageDown", 34},
{"End", 35},
{"Home", 36},
{"Left", 37},
{"Up", 38},
{"Right", 39},
{"Down", 40},
{"Select", 41},
{"Print", 42},
{"Execute", 43},
{"PrintScreen", 44},
{"Insert", 45},
{"Delete", 46},
{"Help", 47},
{"D0", 48},
{"D1", 49},
{"D2", 50},
{"D3", 51},
{"D4", 52},
{"D5", 53},
{"D6", 54},
{"D7", 55},
{"D8", 56},
{"D9", 57},
{"A", 65},
{"B", 66},
{"C", 67},
{"D", 68},
{"E", 69},
{"F", 70},
{"G", 71},
{"H", 72},
{"I", 73},
{"J", 74},
{"K", 75},
{"L", 76},
{"M", 77},
{"N", 78},
{"O", 79},
{"P", 80},
{"Q", 81},
{"R", 82},
{"S", 83},
{"T", 84},
{"U", 85},
{"V", 86},
{"W", 87},
{"X", 88},
{"Y", 89},
{"Z", 90},
{"LWin", 91},
{"RWin", 92},
{"Apps", 93},
{"Sleep", 95},
{"NumPad0", 96},
{"NumPad1", 97},
{"NumPad2", 98},
{"NumPad3", 99},
{"NumPad4", 100},
{"NumPad5", 101},
{"NumPad6", 102},
{"NumPad7", 103},
{"NumPad8", 104},
{"NumPad9", 105},
{"Multiply", 106},
{"Add", 107},
{"Separator", 108},
{"Subtract", 109},
{"Decimal", 110},
{"Divide", 111},
{"F1", 112},
{"F2", 113},
{"F3", 114},
{"F4", 115},
{"F5", 116},
{"F6", 117},
{"F7", 118},
{"F8", 119},
{"F9", 120},
{"F10", 121},
{"F11", 122},
{"F12", 123},
{"F13", 124},
{"F14", 125},
{"F15", 126},
{"F16", 127},
{"F17", 128},
{"F18", 129},
{"F19", 130},
{"F20", 131},
{"F21", 132},
{"F22", 133},
{"F23", 134},
{"F24", 135},
{"NumLock", 144},
{"Scroll", 145},
{"LShiftKey", 160},
{"RShiftKey", 161},
{"LControlKey", 162},
{"RControlKey", 163},
{"LMenu", 164},
{"RMenu", 165},
{"BrowserBack", 166},
{"BrowserForward", 167},
{"BrowserRefresh", 168},
{"BrowserStop", 169},
{"BrowserSearch", 170},
{"BrowserFavorites", 171},
{"BrowserHome", 172},
{"VolumeMute", 173},
{"VolumeDown", 174},
{"VolumeUp", 175},
{"MediaNextTrack", 176},
{"MediaPreviousTrack", 177},
{"MediaStop", 178},
{"MediaPlayPause", 179},
{"LaunchMail", 180},
{"SelectMedia", 181},
{"LaunchApplication1", 182},
{"LaunchApplication2", 183},
{"Oem1", 186},
{"Oemplus", 187},
{"Oemcomma", 188},
{"OemMinus", 189},
{"OemPeriod", 190},
{"Oem2", 191},
{"Oem3", 192},
{"Oem4", 219},
{"Oem5", 220},
{"Oem6", 221},
{"Oem7", 222},
{"Oem8", 223},
{"Oem102", 226},
{"ProcessKey", 229},
{"Packet", 231},
{"Attn", 246},
{"Crsel", 247},
{"Exsel", 248},
{"EraseEof", 249},
{"Play", 250},
{"Zoom", 251},
{"NoName", 252},
{"Pa1", 253},
{"OemClear", 254}
};
 
return keyString;
}
public static readonly Dictionary<byte, string> KeysToString = new Dictionary<byte, string>
{
{0, "None"},
{1, "LButton"},
{2, "RButton"},
{3, "Cancel"},
{4, "MButton"},
{5, "XButton1"},
{6, "XButton2"},
{8, "Back"},
{9, "Tab"},
{10, "LineFeed"},
{12, "Clear"},
{13, "Enter"},
{16, "ShiftKey"},
{17, "ControlKey"},
{18, "Menu"},
{19, "Pause"},
{20, "CapsLock"},
{21, "HanguelMode"},
{23, "JunjaMode"},
{24, "FinalMode"},
{25, "KanjiMode"},
{27, "Escape"},
{28, "IMEConvert"},
{29, "IMENonconvert"},
{30, "IMEAccept"},
{31, "IMEModeChange"},
{32, "Space"},
{33, "PageUp"},
{34, "PageDown"},
{35, "End"},
{36, "Home"},
{37, "Left"},
{38, "Up"},
{39, "Right"},
{40, "Down"},
{41, "Select"},
{42, "Print"},
{43, "Execute"},
{44, "PrintScreen"},
{45, "Insert"},
{46, "Delete"},
{47, "Help"},
{48, "D0"},
{49, "D1"},
{50, "D2"},
{51, "D3"},
{52, "D4"},
{53, "D5"},
{54, "D6"},
{55, "D7"},
{56, "D8"},
{57, "D9"},
{65, "A"},
{66, "B"},
{67, "C"},
{68, "D"},
{69, "E"},
{70, "F"},
{71, "G"},
{72, "H"},
{73, "I"},
{74, "J"},
{75, "K"},
{76, "L"},
{77, "M"},
{78, "N"},
{79, "O"},
{80, "P"},
{81, "Q"},
{82, "R"},
{83, "S"},
{84, "T"},
{85, "U"},
{86, "V"},
{87, "W"},
{88, "X"},
{89, "Y"},
{90, "Z"},
{91, "LWin"},
{92, "RWin"},
{93, "Apps"},
{95, "Sleep"},
{96, "NumPad0"},
{97, "NumPad1"},
{98, "NumPad2"},
{99, "NumPad3"},
{100, "NumPad4"},
{101, "NumPad5"},
{102, "NumPad6"},
{103, "NumPad7"},
{104, "NumPad8"},
{105, "NumPad9"},
{106, "Multiply"},
{107, "Add"},
{108, "Separator"},
{109, "Subtract"},
{110, "Decimal"},
{111, "Divide"},
{112, "F1"},
{113, "F2"},
{114, "F3"},
{115, "F4"},
{116, "F5"},
{117, "F6"},
{118, "F7"},
{119, "F8"},
{120, "F9"},
{121, "F10"},
{122, "F11"},
{123, "F12"},
{124, "F13"},
{125, "F14"},
{126, "F15"},
{127, "F16"},
{128, "F17"},
{129, "F18"},
{130, "F19"},
{131, "F20"},
{132, "F21"},
{133, "F22"},
{134, "F23"},
{135, "F24"},
{144, "NumLock"},
{145, "Scroll"},
{160, "LShiftKey"},
{161, "RShiftKey"},
{162, "LControlKey"},
{163, "RControlKey"},
{164, "LMenu"},
{165, "RMenu"},
{166, "BrowserBack"},
{167, "BrowserForward"},
{168, "BrowserRefresh"},
{169, "BrowserStop"},
{170, "BrowserSearch"},
{171, "BrowserFavorites"},
{172, "BrowserHome"},
{173, "VolumeMute"},
{174, "VolumeDown"},
{175, "VolumeUp"},
{176, "MediaNextTrack"},
{177, "MediaPreviousTrack"},
{178, "MediaStop"},
{179, "MediaPlayPause"},
{180, "LaunchMail"},
{181, "SelectMedia"},
{182, "LaunchApplication1"},
{183, "LaunchApplication2"},
{186, "Oem1"},
{187, "Oemplus"},
{188, "Oemcomma"},
{189, "OemMinus"},
{190, "OemPeriod"},
{191, "Oem2"},
{192, "Oem3"},
{219, "Oem4"},
{220, "Oem5"},
{221, "Oem6"},
{222, "Oem7"},
{223, "Oem8"},
{226, "Oem102"},
{229, "ProcessKey"},
{231, "Packet"},
{246, "Attn"},
{247, "Crsel"},
{248, "Exsel"},
{249, "EraseEof"},
{250, "Play"},
{251, "Zoom"},
{252, "NoName"},
{253, "Pa1"},
{254, "OemClear"}
};
}
}
/trunk/WingMan/WingMan.csproj
@@ -71,21 +71,25 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Communication\TrackedClient.cs" />
<Compile Include="Communication\TrackedClients.cs" />
<Compile Include="Communication\MqttAuthenticationFailureEventArgs.cs" />
<Compile Include="Lobby\LobbyMessageReceivedEventArgs.cs" />
<Compile Include="Lobby\LobbyMessageSynchronizer.cs" />
<Compile Include="MouseKey\MouseKeyBindingExchange.cs" />
<Compile Include="MouseKey\MouseKeyBindingsSynchronizedEventArgs.cs" />
<Compile Include="MouseKey\MouseKeyBindingsSynchronizer.cs" />
<Compile Include="MouseKey\ExecuteKeyBinding.cs" />
<Compile Include="MouseKey\KeyBindingExchange.cs" />
<Compile Include="MouseKey\KeyBindingExecutingEventArgs.cs" />
<Compile Include="MouseKey\KeyBindingMatchedEventArgs.cs" />
<Compile Include="MouseKey\KeyBindingsSynchronizerEventArgs.cs" />
<Compile Include="MouseKey\KeyBindingsSynchronizer.cs" />
<Compile Include="Communication\MqttCommunication.cs" />
<Compile Include="MouseKey\MouseKeyBinding.cs" />
<Compile Include="MouseKey\MouseKeyBindings.cs" />
<Compile Include="MouseKey\MouseKeyBindingsExchange.cs" />
<Compile Include="MouseKey\KeyBinding.cs" />
<Compile Include="MouseKey\LocalKeyBindings.cs" />
<Compile Include="MouseKey\KeyBindingsExchange.cs" />
<Compile Include="Lobby\LobbyMessage.cs" />
<Compile Include="Communication\MqttCommunicationType.cs" />
<Compile Include="MouseKey\RemoteMouseKeyBinding.cs" />
<Compile Include="MouseKey\RemoteMouseKeyBindings.cs" />
<Compile Include="MouseKey\KeyInterceptor.cs" />
<Compile Include="MouseKey\KeySimulator.cs" />
<Compile Include="MouseKey\RemoteKeyBinding.cs" />
<Compile Include="MouseKey\RemoteKeyBindings.cs" />
<Compile Include="Utilities\AES.cs" />
<Compile Include="Utilities\KeyConversion.cs" />
<Compile Include="WingManForm.cs">
/trunk/WingMan/WingManForm.Designer.cs
@@ -88,6 +88,7 @@
//
// RemoteBindingsComboBox
//
this.RemoteBindingsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.RemoteBindingsComboBox.FormattingEnabled = true;
this.RemoteBindingsComboBox.Location = new System.Drawing.Point(8, 24);
this.RemoteBindingsComboBox.Name = "RemoteBindingsComboBox";
@@ -104,6 +105,7 @@
this.button2.TabIndex = 4;
this.button2.Text = "Unbind";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.RemoteBindingsUnbindButtonClicked);
//
// button1
//
@@ -142,6 +144,7 @@
this.RemoteBindingsListBox.Name = "RemoteBindingsListBox";
this.RemoteBindingsListBox.Size = new System.Drawing.Size(232, 158);
this.RemoteBindingsListBox.TabIndex = 0;
this.RemoteBindingsListBox.SelectedValueChanged += new System.EventHandler(this.RemoteBindingsListBoxSelectedValueChanged);
//
// groupBox2
//
@@ -217,7 +220,7 @@
this.button3.TabIndex = 6;
this.button3.Text = "Remove";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.HelmRemoveButtonClick);
this.button3.Click += new System.EventHandler(this.LocalBindingsRemoveButtonClick);
//
// HelmAddButton
//
@@ -228,7 +231,7 @@
this.HelmAddButton.TabIndex = 5;
this.HelmAddButton.Text = "Add";
this.HelmAddButton.UseVisualStyleBackColor = true;
this.HelmAddButton.Click += new System.EventHandler(this.HelmAddButtonClick);
this.HelmAddButton.Click += new System.EventHandler(this.LocalAddBindingButtonClick);
//
// label2
//
@@ -246,7 +249,7 @@
this.LocalNameTextBox.Name = "LocalNameTextBox";
this.LocalNameTextBox.Size = new System.Drawing.Size(172, 20);
this.LocalNameTextBox.TabIndex = 3;
this.LocalNameTextBox.Click += new System.EventHandler(this.HelmNameTextBoxClick);
this.LocalNameTextBox.Click += new System.EventHandler(this.LocalNameTextBoxClick);
//
// groupBox4
//
/trunk/WingMan/WingManForm.cs
@@ -38,28 +38,28 @@
MqttCommunication.OnServerClientConnected += OnMqttServerClientConnected;
MqttCommunication.OnServerClientDisconnected += OnMqttServerClientDisconnected;
 
LocalMouseKeyBindings = new MouseKeyBindings(new List<MouseKeyBinding>());
RemoteMouseKeyBindings = new RemoteMouseKeyBindings(new List<RemoteMouseKeyBinding>());
LocalKeyBindings = new LocalKeyBindings(new List<KeyBinding>());
RemoteKeyBindings = new RemoteKeyBindings(new List<RemoteKeyBinding>());
 
LocalListBoxBindingSource = new BindingSource
{
DataSource = LocalMouseKeyBindings.Bindings
DataSource = LocalKeyBindings.Bindings
};
LocalBindingsListBox.DisplayMember = "DisplayName";
LocalBindingsListBox.ValueMember = "Keys";
LocalBindingsListBox.DataSource = LocalListBoxBindingSource;
 
MouseKeyBindingsExchange = new MouseKeyBindingsExchange
KeyBindingsExchange = new KeyBindingsExchange
{
ExchangeBindings = new List<MouseKeyBindingExchange>()
ExchangeBindings = new List<KeyBindingExchange>()
};
 
RemoteBindingsComboBoxSource = new BindingSource
{
DataSource = MouseKeyBindingsExchange.ExchangeBindings
DataSource = KeyBindingsExchange.ExchangeBindings
};
RemoteBindingsComboBox.DisplayMember = "Nick";
RemoteBindingsComboBox.ValueMember = "MouseKeyBindings";
RemoteBindingsComboBox.ValueMember = "KeyBindings";
RemoteBindingsComboBox.DataSource = RemoteBindingsComboBoxSource;
 
// Start lobby message synchronizer.
@@ -68,11 +68,19 @@
LobbyMessageSynchronizer.OnLobbyMessageReceived += OnLobbyMessageReceived;
 
// Start mouse key bindings synchronizer.
MouseKeyBindingsSynchronizer = new MouseKeyBindingsSynchronizer(LocalMouseKeyBindings, MqttCommunication,
KeyBindingsSynchronizer = new KeyBindingsSynchronizer(LocalKeyBindings, MqttCommunication,
FormTaskScheduler, FormCancellationTokenSource.Token);
MouseKeyBindingsSynchronizer.OnMouseKeyBindingsSynchronized += OnMouseKeyBindingsSynchronized;
KeyBindingsSynchronizer.OnMouseKeyBindingsSynchronized += OnMouseKeyBindingsSynchronized;
 
// Start key binding simulator.
// Start mouse key interceptor.
KeyInterceptor = new KeyInterceptor(RemoteKeyBindings, MqttCommunication, FormTaskScheduler,
FormCancellationTokenSource.Token);
KeyInterceptor.OnMouseKeyBindingMatched += OnMouseKeyBindingMatched;
 
// Start mouse key simulator.
KeySimulator = new KeySimulator(LocalKeyBindings, MqttCommunication, FormTaskScheduler,
FormCancellationTokenSource.Token);
KeySimulator.OnMouseKeyBindingExecuting += OnMouseKeyBindingExecuting;
}
 
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
@@ -83,45 +91,56 @@
 
private List<string> MouseKeyCombo { get; set; }
 
private MouseKeyBindings LocalMouseKeyBindings { get; }
private LocalKeyBindings LocalKeyBindings { get; }
 
private RemoteMouseKeyBindings RemoteMouseKeyBindings { get; }
private RemoteKeyBindings RemoteKeyBindings { get; }
 
private BindingSource LocalListBoxBindingSource { get; }
 
private BindingSource RemoteBindingsComboBoxSource { get; }
 
private MouseKeyBindingsExchange MouseKeyBindingsExchange { get; }
private KeyBindingsExchange KeyBindingsExchange { get; }
 
public MqttCommunication MqttCommunication { get; set; }
 
public LobbyMessageSynchronizer LobbyMessageSynchronizer { get; set; }
 
public MouseKeyBindingsSynchronizer MouseKeyBindingsSynchronizer { get; set; }
public KeyBindingsSynchronizer KeyBindingsSynchronizer { get; set; }
 
private async Task SaveLocalMouseKeyBindings()
public KeyInterceptor KeyInterceptor { get; set; }
 
public KeySimulator KeySimulator { get; set; }
 
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
try
{
using (var memoryStream = new MemoryStream())
{
MouseKeyBindings.XmlSerializer.Serialize(memoryStream, LocalMouseKeyBindings);
FormCancellationTokenSource?.Dispose();
FormCancellationTokenSource = null;
 
memoryStream.Position = 0L;
 
using (var fileStream = new FileStream("LocalMouseKeyBindings.xml", FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
catch (Exception)
if (disposing && components != null)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_saving_local_bindings}{Environment.NewLine}");
components.Dispose();
components = null;
}
 
base.Dispose(disposing);
}
 
private void OnMouseKeyBindingExecuting(object sender, KeyBindingExecutingEventArgs args)
{
ActivityTextBox.AppendText(
$"{Strings.Executing_binding_from_remote_client} : {args.Nick} : {args.Name}{Environment.NewLine}");
}
 
private void OnMouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args)
{
ActivityTextBox.AppendText(
$"{Strings.Matched_remote_key_binding} : {args.Nick} : {args.Name} : {string.Join(" + ", args.KeyCombo)}{Environment.NewLine}");
}
 
private void OnMqttServerClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)
{
ActivityTextBox.AppendText(
@@ -164,80 +183,75 @@
$"{Strings.Client_connection_failed}{Environment.NewLine}");
}
 
private void OnMqttServerAuthenticationFailed(object sender, EventArgs e)
private void OnMqttServerAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_to_authenticate_client}{Environment.NewLine}");
$"{Strings.Failed_to_authenticate_client} : {e.Exception}{Environment.NewLine}");
}
 
private void OnMqttClientAuthenticationFailed(object sender, EventArgs e)
private void OnMqttClientAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Server_authentication_failed}{Environment.NewLine}");
$"{Strings.Failed_to_authenticate_with_server} : {e.Exception}{Environment.NewLine}");
}
 
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
private void OnMouseKeyBindingsSynchronized(object sender, KeyBindingsSynchronizerEventArgs e)
{
if (disposing && components != null)
{
FormCancellationTokenSource.Dispose();
FormCancellationTokenSource = null;
 
components.Dispose();
}
 
base.Dispose(disposing);
}
 
private void OnMouseKeyBindingsSynchronized(object sender, MouseKeyBindingsSynchronizedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Synchronized_bindings_with_client} : {e.Bindings.Nick} : {e.Bindings.MouseKeyBindings.Count}{Environment.NewLine}");
$"{Strings.Synchronized_bindings_with_client} : {e.Bindings.Nick} : {e.Bindings.KeyBindings.Count}{Environment.NewLine}");
 
var exchangeBindings = MouseKeyBindingsExchange.ExchangeBindings.FirstOrDefault(exchangeBinding =>
var exchangeBindings = KeyBindingsExchange.ExchangeBindings.FirstOrDefault(exchangeBinding =>
string.Equals(exchangeBinding.Nick, e.Bindings.Nick, StringComparison.Ordinal));
 
// If the nick does not exist then add it.
if (exchangeBindings == null)
{
MouseKeyBindingsExchange.ExchangeBindings.Add(e.Bindings);
KeyBindingsExchange.ExchangeBindings.Add(e.Bindings);
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
UpdateRemoteItems();
return;
}
 
// If the bindings for the nick have not changed then do not update.
if (exchangeBindings.MouseKeyBindings.SequenceEqual(e.Bindings.MouseKeyBindings))
if (exchangeBindings.KeyBindings.SequenceEqual(e.Bindings.KeyBindings))
{
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
UpdateRemoteItems();
return;
}
 
// Update the bindings.
exchangeBindings.MouseKeyBindings = e.Bindings.MouseKeyBindings;
exchangeBindings.KeyBindings = e.Bindings.KeyBindings;
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
UpdateRemoteItems();
}
 
private void UpdateRemoteListBoxItems()
private void UpdateRemoteItems()
{
var exchangeBinding = (List<MouseKeyBinding>) RemoteBindingsComboBox.SelectedValue;
if (exchangeBinding == null)
var exchangeBindings = (List<KeyBinding>) RemoteBindingsComboBox.SelectedValue;
if (exchangeBindings == null)
return;
 
var replaceMouseBindings = new List<RemoteKeyBinding>();
foreach (var remoteBinding in RemoteKeyBindings.Bindings)
{
if (!exchangeBindings.Any(binding =>
string.Equals(binding.Name, remoteBinding.Name, StringComparison.Ordinal)))
continue;
 
replaceMouseBindings.Add(remoteBinding);
}
 
RemoteKeyBindings.Bindings = replaceMouseBindings;
 
RemoteBindingsListBox.Items.Clear();
RemoteBindingsListBox.DisplayMember = "Name";
RemoteBindingsListBox.ValueMember = "Name";
var i = exchangeBinding.Select(binding => (object) binding.Name).ToArray();
if (i.Length == 0)
var bindings = exchangeBindings.Select(binding => (object) binding.Name).ToArray();
if (bindings.Length == 0)
return;
 
RemoteBindingsListBox.Items.AddRange(i);
RemoteBindingsListBox.Items.AddRange(bindings);
}
 
private void OnLobbyMessageReceived(object sender, LobbyMessageReceivedEventArgs e)
@@ -260,7 +274,7 @@
// Stop the MQTT server if it is running.
if (MqttCommunication.Running)
{
await MqttCommunication.Stop().ConfigureAwait(false);
await MqttCommunication.Stop();
HostButton.BackColor = Color.Empty;
 
// Enable controls.
@@ -268,7 +282,7 @@
Address.Enabled = true;
Port.Enabled = true;
Nick.Enabled = true;
 
Password.Enabled = true;
return;
}
 
@@ -276,8 +290,8 @@
return;
 
// Start the MQTT server.
if (!await MqttCommunication.Start(MqttCommunicationType.Server, ipAddress, port, nick, password)
.ConfigureAwait(false))
if (!await MqttCommunication
.Start(MqttCommunicationType.Server, ipAddress, port, nick, password))
{
ActivityTextBox.AppendText(
$"{Strings.Failed_starting_server}{Environment.NewLine}");
@@ -291,6 +305,7 @@
Address.Enabled = false;
Port.Enabled = false;
Nick.Enabled = false;
Password.Enabled = false;
}
 
private bool ValidateAddressPort(out IPAddress address, out int port, out string nick, out string password)
@@ -340,7 +355,7 @@
return false;
}
 
password = AES.LinearFeedbackShiftPassword(Password.Text);
password = AES.ExpandKey(Password.Text);
 
Address.BackColor = Color.Empty;
Port.BackColor = Color.Empty;
@@ -354,7 +369,7 @@
{
if (MqttCommunication.Running)
{
await MqttCommunication.Stop().ConfigureAwait(false);
await MqttCommunication.Stop();
ConnectButton.Text = Strings.Connect;
ConnectButton.BackColor = Color.Empty;
 
@@ -361,6 +376,7 @@
Address.Enabled = true;
Port.Enabled = true;
Nick.Enabled = true;
Password.Enabled = true;
HostButton.Enabled = true;
return;
}
@@ -368,8 +384,8 @@
if (!ValidateAddressPort(out var ipAddress, out var port, out var nick, out var password))
return;
 
if (!await MqttCommunication.Start(MqttCommunicationType.Client, ipAddress, port, nick, password)
.ConfigureAwait(false))
if (!await MqttCommunication
.Start(MqttCommunicationType.Client, ipAddress, port, nick, password))
{
ActivityTextBox.AppendText(
$"{Strings.Failed_starting_client}{Environment.NewLine}");
@@ -383,6 +399,7 @@
Address.Enabled = false;
Port.Enabled = false;
Nick.Enabled = false;
Password.Enabled = false;
}
 
private async void LobbySayTextBoxKeyDown(object sender, KeyEventArgs e)
@@ -390,12 +407,12 @@
if (e.KeyCode != Keys.Enter)
return;
 
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text).ConfigureAwait(false);
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text);
 
LobbySayTextBox.Text = string.Empty;
}
 
private void HelmAddButtonClick(object sender, EventArgs e)
private void LocalAddBindingButtonClick(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(LocalNameTextBox.Text))
{
@@ -403,15 +420,25 @@
return;
}
 
// Only unique names allowed.
if (LocalKeyBindings.Bindings.Any(binding =>
string.Equals(binding.Name, LocalNameTextBox.Text, StringComparison.Ordinal)))
{
LocalNameTextBox.BackColor = Color.LightPink;
LocalBindingsListBox.BackColor = Color.LightPink;
return;
}
 
LocalNameTextBox.BackColor = Color.Empty;
LocalBindingsListBox.BackColor = Color.Empty;
 
ShowOverlayPanel();
 
MouseKeyCombo = new List<string>();
 
MouseKeyApplicationHook = Hook.AppEvents();
MouseKeyApplicationHook.MouseDown += LocalMouseKeyHookOnMouseDown;
MouseKeyApplicationHook = Hook.GlobalEvents();
MouseKeyApplicationHook.KeyUp += LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown += LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.MouseUp += LocalMouseKeyHookOnMouseUp;
}
 
private void ShowOverlayPanel()
@@ -421,16 +448,14 @@
OverlayPanel.Invalidate();
}
 
private async void LocalMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
private void LocalMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
{
LocalMouseKeyBindings.Bindings.Add(new MouseKeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
LocalKeyBindings.Bindings.Add(new KeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
 
LocalListBoxBindingSource.ResetBindings(false);
 
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
 
MouseKeyApplicationHook.Dispose();
 
@@ -437,7 +462,7 @@
LocalNameTextBox.Text = string.Empty;
HideOverlayPanel();
 
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
//await SaveLocalMouseKeyBindings();
}
 
private void HideOverlayPanel()
@@ -447,58 +472,35 @@
OverlayPanel.Invalidate();
}
 
private async void LocalMouseKeyHookOnMouseUp(object sender, MouseEventArgs e)
{
LocalMouseKeyBindings.Bindings.Add(new MouseKeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
 
LocalListBoxBindingSource.ResetBindings(false);
 
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
 
MouseKeyApplicationHook.Dispose();
 
LocalNameTextBox.Text = string.Empty;
HideOverlayPanel();
 
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
}
 
 
private void LocalMouseKeyHookOnMouseDown(object sender, MouseEventArgs e)
{
MouseKeyCombo.Add(e.Button.ToDisplayName());
}
 
private void LocalMouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
 
MouseKeyCombo.Add(e.KeyCode.ToDisplayName());
KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key);
 
MouseKeyCombo.Add(key);
}
 
private void HelmNameTextBoxClick(object sender, EventArgs e)
private void LocalNameTextBoxClick(object sender, EventArgs e)
{
LocalNameTextBox.BackColor = Color.Empty;
}
 
private async void HelmRemoveButtonClick(object sender, EventArgs e)
private void LocalBindingsRemoveButtonClick(object sender, EventArgs e)
{
var helmBinding = (MouseKeyBinding) LocalBindingsListBox.SelectedItem;
var helmBinding = (KeyBinding) LocalBindingsListBox.SelectedItem;
if (helmBinding == null)
return;
 
LocalMouseKeyBindings.Bindings.Remove(helmBinding);
LocalKeyBindings.Bindings.Remove(helmBinding);
LocalListBoxBindingSource.ResetBindings(false);
 
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
// await SaveLocalMouseKeyBindings();
}
 
private async void LobbySayButtonClick(object sender, EventArgs e)
{
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text).ConfigureAwait(false);
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text);
 
LobbySayTextBox.Text = string.Empty;
}
@@ -505,54 +507,54 @@
 
private void RemoteBindingsComboBoxSelectionChangeCompleted(object sender, EventArgs e)
{
UpdateRemoteListBoxItems();
UpdateRemoteItems();
}
 
private async void WingManFormOnLoad(object sender, EventArgs e)
private void WingManFormOnLoad(object sender, EventArgs e)
{
await LoadLocalMouseKeyBindings();
await LoadRemoteMouseKeyBindings();
// await LoadLocalMouseKeyBindings();
 
// await LoadRemoteMouseKeyBindings();
}
 
private async Task LoadLocalMouseKeyBindings()
private void RemoteBindingsBindButtonClicked(object sender, EventArgs e)
{
try
if (string.IsNullOrEmpty((string) RemoteBindingsListBox.SelectedItem))
{
using (var fileStream = new FileStream("LocalMouseKeyBindings.xml", FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
RemoteBindingsListBox.BackColor = Color.LightPink;
return;
}
 
memoryStream.Position = 0L;
RemoteBindingsListBox.BackColor = Color.Empty;
 
var loadedBindings =
(MouseKeyBindings) MouseKeyBindings.XmlSerializer.Deserialize(memoryStream);
ShowOverlayPanel();
 
foreach (var binding in loadedBindings.Bindings) LocalMouseKeyBindings.Bindings.Add(binding);
MouseKeyCombo = new List<string>();
 
LocalListBoxBindingSource.ResetBindings(false);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_loading_local_bindings}{Environment.NewLine}");
}
MouseKeyApplicationHook = Hook.GlobalEvents();
MouseKeyApplicationHook.KeyUp += RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown += RemoteMouseKeyHookOnKeyDown;
}
 
private void RemoteBindingsBindButtonClicked(object sender, EventArgs e)
private void RemoteBindingsUnbindButtonClicked(object sender, EventArgs e)
{
ShowOverlayPanel();
var item = (string) RemoteBindingsListBox.SelectedItem;
if (string.IsNullOrEmpty(item))
{
RemoteBindingsListBox.BackColor = Color.LightPink;
return;
}
 
MouseKeyCombo = new List<string>();
RemoteBindingsListBox.BackColor = Color.Empty;
 
MouseKeyApplicationHook = Hook.AppEvents();
MouseKeyApplicationHook.MouseDown += RemoteMouseKeyHookOnMouseDown;
MouseKeyApplicationHook.KeyUp += RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown += RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.MouseUp += RemoteMouseKeyHookOnMouseUp;
var remoteKeyBinding = RemoteKeyBindings.Bindings.FirstOrDefault(binding =>
string.Equals(binding.Name, item, StringComparison.Ordinal));
 
if (remoteKeyBinding == null)
return;
 
RemoteKeyBindings.Bindings.Remove(remoteKeyBinding);
RemoteBindingsBindToBox.Text = string.Empty;
}
 
private void RemoteMouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
@@ -559,23 +561,18 @@
{
e.SuppressKeyPress = true;
 
MouseKeyCombo.Add(e.KeyCode.ToDisplayName());
}
KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key);
 
private void RemoteMouseKeyHookOnMouseDown(object sender, MouseEventArgs e)
{
MouseKeyCombo.Add(e.Button.ToDisplayName());
MouseKeyCombo.Add(key);
}
 
private async void RemoteMouseKeyHookOnMouseUp(object sender, MouseEventArgs e)
private void RemoteMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
{
RemoteMouseKeyBindings.Bindings.Add(new RemoteMouseKeyBinding(RemoteBindingsComboBox.Text,
RemoteKeyBindings.Bindings.Add(new RemoteKeyBinding(RemoteBindingsComboBox.Text,
(string) RemoteBindingsListBox.SelectedItem, MouseKeyCombo));
 
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
 
MouseKeyApplicationHook.Dispose();
 
@@ -582,27 +579,80 @@
RemoteBindingsBindToBox.Text = string.Join(" + ", MouseKeyCombo);
HideOverlayPanel();
 
await SaveRemoteMouseKeyBindings().ConfigureAwait(false);
// await SaveRemoteMouseKeyBindings();
}
 
private async void RemoteMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
private void RemoteBindingsListBoxSelectedValueChanged(object sender, EventArgs e)
{
RemoteMouseKeyBindings.Bindings.Add(new RemoteMouseKeyBinding(RemoteBindingsComboBox.Text,
(string) RemoteBindingsListBox.SelectedItem, MouseKeyCombo));
RemoteBindingsBindToBox.Text = "";
 
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
var name = (string) RemoteBindingsListBox.SelectedItem;
if (string.IsNullOrEmpty(name))
return;
 
MouseKeyApplicationHook.Dispose();
foreach (var binding in RemoteKeyBindings.Bindings)
{
if (!string.Equals(binding.Name, name))
continue;
 
RemoteBindingsBindToBox.Text = string.Join(" + ", MouseKeyCombo);
HideOverlayPanel();
RemoteBindingsBindToBox.Text = string.Join(" + ", binding.Keys);
break;
}
}
 
await SaveRemoteMouseKeyBindings().ConfigureAwait(false);
#region Saving and loading
 
private async Task SaveLocalMouseKeyBindings()
{
try
{
using (var memoryStream = new MemoryStream())
{
LocalKeyBindings.XmlSerializer.Serialize(memoryStream, LocalKeyBindings);
 
memoryStream.Position = 0L;
 
using (var fileStream = new FileStream("LocalKeyBindings.xml", FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_saving_local_bindings}{Environment.NewLine}");
}
}
 
private async Task LoadLocalMouseKeyBindings()
{
try
{
using (var fileStream = new FileStream("LocalKeyBindings.xml", FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream);
 
memoryStream.Position = 0L;
 
var loadedBindings =
(LocalKeyBindings) LocalKeyBindings.XmlSerializer.Deserialize(memoryStream);
 
foreach (var binding in loadedBindings.Bindings) LocalKeyBindings.Bindings.Add(binding);
 
LocalListBoxBindingSource.ResetBindings(false);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_loading_local_bindings}{Environment.NewLine}");
}
}
 
private async Task SaveRemoteMouseKeyBindings()
{
try
@@ -609,13 +659,13 @@
{
using (var memoryStream = new MemoryStream())
{
RemoteMouseKeyBindings.XmlSerializer.Serialize(memoryStream, RemoteMouseKeyBindings);
RemoteKeyBindings.XmlSerializer.Serialize(memoryStream, RemoteKeyBindings);
 
memoryStream.Position = 0L;
 
using (var fileStream = new FileStream("RemoteMouseKeyBindings.xml", FileMode.Create))
using (var fileStream = new FileStream("RemoteKeyBindings.xml", FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
await memoryStream.CopyToAsync(fileStream);
}
}
}
@@ -630,18 +680,18 @@
{
try
{
using (var fileStream = new FileStream("RemoteMouseKeyBindings.xml", FileMode.Open))
using (var fileStream = new FileStream("RemoteKeyBindings.xml", FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
await fileStream.CopyToAsync(memoryStream);
 
memoryStream.Position = 0L;
 
var loadedBindings =
(RemoteMouseKeyBindings) RemoteMouseKeyBindings.XmlSerializer.Deserialize(memoryStream);
(RemoteKeyBindings) RemoteKeyBindings.XmlSerializer.Deserialize(memoryStream);
 
foreach (var binding in loadedBindings.Bindings) RemoteMouseKeyBindings.Bindings.Add(binding);
foreach (var binding in loadedBindings.Bindings) RemoteKeyBindings.Bindings.Add(binding);
}
}
}
@@ -651,5 +701,7 @@
$"{Strings.Failed_loading_remote_bindings}{Environment.NewLine}");
}
}
 
#endregion
}
}