WingMan – Rev 23

Subversion Repositories:
Rev:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using WindowsInput;
using WindowsInput.Native;
using MQTTnet;
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;

            InputSimulator = new InputSimulator();
        }

        private InputSimulator InputSimulator { get; }

        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);

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                Task.Run(() => Simulate(executeMouseKeyBinding), CancellationToken);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            }
        }

        private void Simulate(ExecuteKeyBinding executeBinding)
        {
            foreach (var localBinding in LocalLocalKeyBindings.Bindings)
            {
                if (!string.Equals(localBinding.Name, executeBinding.Name, StringComparison.Ordinal))
                    continue;

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

                    InputSimulator.Keyboard.KeyDown((VirtualKeyCode) press);
                }

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

                    InputSimulator.Keyboard.KeyUp((VirtualKeyCode) press);
                }
            }
        }
    }
}