WingMan – Rev 10

Subversion Repositories:
Rev:
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());
                }
            }
        }
    }
}