WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 9  →  ?path2? @ 10
/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);
}
}
}