WingMan – Rev 18

Subversion Repositories:
Rev:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using SimWinInput;
using WingMan.Communication;
using WingMan.Utilities;

namespace WingMan.Bindings
{
    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 async 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))
                        continue;

                    SimKeyboard.KeyDown(pressKey);
                }

                await Task.Delay(250);

                // Depress
                foreach (var key in localBinding.Keys)
                {
                    if (!KeyConversion.StringToKeys.TryGetValue(key, out var pressKey))
                        continue;

                    SimKeyboard.KeyUp(pressKey);
                }
            }
        }
    }
}