Files
Artemis/Artemis/Artemis/Managers/LoopManager.cs
T
SpoinkyNL 00ff71d754 Updated CM SDK
Added Mastermouse Pro L and S for #325
2017-03-19 14:08:45 +01:00

210 lines
7.0 KiB
C#

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Models;
using Artemis.ViewModels;
using Ninject.Extensions.Logging;
namespace Artemis.Managers
{
/// <summary>
/// Manages the main programn loop
/// </summary>
public class LoopManager : IDisposable
{
private readonly DebugViewModel _debugViewModel;
private readonly DeviceManager _deviceManager;
private readonly ILogger _logger;
//private readonly Timer _loopTimer;
private readonly Task _loopTask;
private readonly ModuleManager _moduleManager;
public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
DebugViewModel debugViewModel)
{
_logger = logger;
_moduleManager = moduleManager;
_deviceManager = deviceManager;
_debugViewModel = debugViewModel;
// Setup timers
_loopTask = Task.Factory.StartNew(ProcessLoop);
_logger.Info("Intialized LoopManager");
}
public DebugViewModel DebugViewModel { get; set; }
/// <summary>
/// Gets whether the loop is running
/// </summary>
public bool Running { get; private set; }
public void Dispose()
{
_loopTask.Dispose();
}
private void ProcessLoop()
{
//TODO DarthAffe 14.01.2017: A stop-condition and a real cleanup instead of just aborting might be better
while (true)
{
try
{
long preUpdateTicks = DateTime.Now.Ticks;
Render();
int sleep = (int) (40f - (DateTime.Now.Ticks - preUpdateTicks) / 10000f);
if (sleep > 0)
Thread.Sleep(sleep);
}
catch (Exception e)
{
if (Debugger.IsAttached)
throw;
_logger.Warn(e, "Exception in render loop");
}
}
// ReSharper disable once FunctionNeverReturns
}
public Task StartAsync()
{
return Task.Run(() => Start());
}
private void Start()
{
if (Running)
return;
_logger.Debug("Starting LoopManager");
if (_deviceManager.ActiveKeyboard == null)
_deviceManager.EnableLastKeyboard();
while (_deviceManager.ChangingKeyboard)
Thread.Sleep(200);
// If still null, no last keyboard, so stop.
if (_deviceManager.ActiveKeyboard == null)
{
_logger.Debug("Cancel LoopManager start, no keyboard");
return;
}
if (_moduleManager.ActiveModule == null)
{
var lastModule = _moduleManager.GetLastModule();
if (lastModule == null || !lastModule.Settings.IsEnabled)
{
_logger.Debug("Cancel LoopManager start, no module");
return;
}
_moduleManager.ChangeActiveModule(lastModule);
}
Running = true;
}
public void Stop()
{
if (!Running)
return;
_logger.Debug("Stopping LoopManager");
Running = false;
_deviceManager.ReleaseActiveKeyboard();
}
private void Render()
{
if (!Running || _deviceManager.ChangingKeyboard)
return;
// Stop if no active module
if (_moduleManager.ActiveModule == null)
{
_logger.Debug("No active module, stopping");
Stop();
return;
}
var renderModule = _moduleManager.ActiveModule;
// Stop if no active keyboard
if (_deviceManager.ActiveKeyboard == null)
{
_logger.Debug("No active keyboard, stopping");
Stop();
return;
}
lock (_deviceManager.ActiveKeyboard)
{
// Skip frame if module is still initializing
if (renderModule.IsInitialized == false)
return;
// ApplyProperties the current module
if (renderModule.IsInitialized)
renderModule.Update();
// Get the devices that must be rendered to
var mice = _deviceManager.MiceProviders.Where(m => m.CanUse).ToList();
var headsets = _deviceManager.HeadsetProviders.Where(m => m.CanUse).ToList();
var generics = _deviceManager.GenericProviders.Where(m => m.CanUse).ToList();
var mousemats = _deviceManager.MousematProviders.Where(m => m.CanUse).ToList();
var keyboardOnly = !mice.Any() && !headsets.Any() && !generics.Any() && !mousemats.Any();
// Setup the frame for this tick
using (
var frame = new FrameModel(_deviceManager.ActiveKeyboard, mice.Any(), headsets.Any(), generics.Any(),
mousemats.Any()))
{
if (renderModule.IsInitialized)
renderModule.Render(frame, keyboardOnly);
// Draw enabled overlays on top of the renderModule
foreach (var overlayModel in _moduleManager.OverlayModules.Where(o => o.Settings.IsEnabled))
{
overlayModel.Update();
overlayModel.Render(frame, keyboardOnly);
}
// Render the frame's drawing context to bitmaps
frame.RenderBitmaps();
// Update the keyboard
_deviceManager.ActiveKeyboard?.DrawBitmap(frame.KeyboardBitmap);
// Update the other devices
foreach (var mouse in mice)
mouse.UpdateDevice(frame.MouseBitmap);
foreach (var headset in headsets)
headset.UpdateDevice(frame.HeadsetBitmap);
foreach (var generic in generics)
generic.UpdateDevice(frame.GenericBitmap);
foreach (var mousemat in mousemats)
mousemat.UpdateDevice(frame.MousematBitmap);
_debugViewModel.DrawFrame(frame);
OnRenderCompleted();
}
}
}
public event EventHandler RenderCompleted;
protected virtual void OnRenderCompleted()
{
RenderCompleted?.Invoke(this, EventArgs.Empty);
}
}
}