From b91ad6fcdda085d100957c51c798cb2b28247aa5 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Tue, 27 Dec 2016 12:58:02 +0100 Subject: [PATCH] Added LUA timer module --- Artemis/Artemis/Artemis.csproj | 2 + .../Corsair/CorsairKeyboard.cs | 314 +++++++++--------- Artemis/Artemis/Managers/LuaManager.cs | 66 +++- .../Profiles/Lua/Modules/LuaTimerModule.cs | 43 +++ .../Profiles/Lua/Modules/Timer/LuaTimer.cs | 59 ++++ Artemis/Artemis/Resources/lua-placeholder.lua | 4 +- 6 files changed, 321 insertions(+), 167 deletions(-) create mode 100644 Artemis/Artemis/Profiles/Lua/Modules/LuaTimerModule.cs create mode 100644 Artemis/Artemis/Profiles/Lua/Modules/Timer/LuaTimer.cs diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index 057b9d6a7..6ee49f2d7 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -518,6 +518,8 @@ + + diff --git a/Artemis/Artemis/DeviceProviders/Corsair/CorsairKeyboard.cs b/Artemis/Artemis/DeviceProviders/Corsair/CorsairKeyboard.cs index 331f02531..0dd0f2f32 100644 --- a/Artemis/Artemis/DeviceProviders/Corsair/CorsairKeyboard.cs +++ b/Artemis/Artemis/DeviceProviders/Corsair/CorsairKeyboard.cs @@ -1,160 +1,160 @@ -using System; -using System.Drawing; -using System.Linq; -using System.Windows; -using System.Windows.Forms; -using Artemis.DeviceProviders.Corsair.Utilities; -using Artemis.Properties; -using Artemis.Utilities; -using CUE.NET; -using CUE.NET.Brushes; -using CUE.NET.Devices.Generic; -using CUE.NET.Devices.Generic.Enums; -using CUE.NET.Exceptions; -using CUE.NET.Helper; -using Ninject.Extensions.Logging; -using Point = System.Drawing.Point; - -namespace Artemis.DeviceProviders.Corsair -{ - public class CorsairKeyboard : KeyboardProvider - { - private CUE.NET.Devices.Keyboard.CorsairKeyboard _keyboard; - private ImageBrush _keyboardBrush; - - public CorsairKeyboard(ILogger logger) - { - Logger = logger; - Name = "Corsair RGB Keyboard"; - CantEnableText = "Couldn't connect to your Corsair keyboard.\n" + - "Please check your cables and/or drivers (could be outdated) and that Corsair Utility Engine is running.\n" + - "In CUE, make sure \"Enable SDK\" is checked under Global Settings.\n\n" + - "If needed, you can select a different keyboard in Artemis under settings."; - } - - public ILogger Logger { get; set; } - - public override bool CanEnable() - { - return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); - } - - /// - /// Enables the SDK and sets updatemode to manual as well as the color of the background to black. - /// - public override void Enable() - { - if (!CueSDK.IsInitialized) - CueSDK.Initialize(); - - CueSDK.UpdateMode = UpdateMode.Manual; - _keyboard = CueSDK.KeyboardSDK; - switch (_keyboard.DeviceInfo.Model) - { - case "K95 RGB": - Height = 7; - Width = 25; - Slug = "corsair-k95-rgb"; - PreviewSettings = new PreviewSettings(676, 190, new Thickness(0, -15, 0, 0), Resources.k95); - break; - case "K70 RGB": - case "K70 RGB RAPIDFIRE": - case "K70 LUX RGB": - Height = 7; - Width = 21; - Slug = "corsair-k70-rgb"; - PreviewSettings = new PreviewSettings(676, 210, new Thickness(0, -25, 0, 0), Resources.k70); - break; - case "K65 RGB": - case "CGK65 RGB": - case "K65 LUX RGB": - case "K65 RGB RAPIDFIRE": - Height = 7; - Width = 18; - Slug = "corsair-k65-rgb"; - PreviewSettings = new PreviewSettings(610, 240, new Thickness(0, -30, 0, 0), Resources.k65); - break; - case "STRAFE RGB": - Height = 7; - Width = 22; - Slug = "corsair-strafe-rgb"; - PreviewSettings = new PreviewSettings(665, 215, new Thickness(0, -5, 0, 0), Resources.strafe); - break; - } - - Logger.Debug("Corsair SDK reported device as: {0}", _keyboard.DeviceInfo.Model); - _keyboard.Brush = _keyboardBrush ?? (_keyboardBrush = new ImageBrush()); - } - - public override void Disable() - { - try +using System; +using System.Drawing; +using System.Linq; +using System.Windows; +using System.Windows.Forms; +using Artemis.DeviceProviders.Corsair.Utilities; +using Artemis.Properties; +using Artemis.Utilities; +using CUE.NET; +using CUE.NET.Brushes; +using CUE.NET.Devices.Generic; +using CUE.NET.Devices.Generic.Enums; +using CUE.NET.Exceptions; +using CUE.NET.Helper; +using Ninject.Extensions.Logging; +using Point = System.Drawing.Point; + +namespace Artemis.DeviceProviders.Corsair +{ + public class CorsairKeyboard : KeyboardProvider + { + private CUE.NET.Devices.Keyboard.CorsairKeyboard _keyboard; + private ImageBrush _keyboardBrush; + + public CorsairKeyboard(ILogger logger) + { + Logger = logger; + Name = "Corsair RGB Keyboard"; + CantEnableText = "Couldn't connect to your Corsair keyboard.\n" + + "Please check your cables and/or drivers (could be outdated) and that Corsair Utility Engine is running.\n" + + "In CUE, make sure \"Enable SDK\" is checked under Global Settings.\n\n" + + "If needed, you can select a different keyboard in Artemis under settings."; + } + + public ILogger Logger { get; set; } + + public override bool CanEnable() + { + return CueSDK.IsSDKAvailable(CorsairDeviceType.Keyboard); + } + + /// + /// Enables the SDK and sets updatemode to manual as well as the color of the background to black. + /// + public override void Enable() + { + if (!CueSDK.IsInitialized) + CueSDK.Initialize(); + + CueSDK.UpdateMode = UpdateMode.Manual; + _keyboard = CueSDK.KeyboardSDK; + switch (_keyboard.DeviceInfo.Model) + { + case "K95 RGB": + Height = 7; + Width = 25; + Slug = "corsair-k95-rgb"; + PreviewSettings = new PreviewSettings(676, 190, new Thickness(0, -15, 0, 0), Resources.k95); + break; + case "K70 RGB": + case "K70 RGB RAPIDFIRE": + case "K70 LUX RGB": + Height = 7; + Width = 21; + Slug = "corsair-k70-rgb"; + PreviewSettings = new PreviewSettings(676, 210, new Thickness(0, -25, 0, 0), Resources.k70); + break; + case "K65 RGB": + case "CGK65 RGB": + case "K65 LUX RGB": + case "K65 RGB RAPIDFIRE": + Height = 7; + Width = 18; + Slug = "corsair-k65-rgb"; + PreviewSettings = new PreviewSettings(610, 240, new Thickness(0, -30, 0, 0), Resources.k65); + break; + case "STRAFE RGB": + Height = 7; + Width = 22; + Slug = "corsair-strafe-rgb"; + PreviewSettings = new PreviewSettings(665, 215, new Thickness(0, -5, 0, 0), Resources.strafe); + break; + } + + Logger.Debug("Corsair SDK reported device as: {0}", _keyboard.DeviceInfo.Model); + _keyboard.Brush = _keyboardBrush ?? (_keyboardBrush = new ImageBrush()); + } + + public override void Disable() + { + try { if (CueSDK.IsInitialized) - CueSDK.Reinitialize(); - } - catch (WrapperException e) - { - // This occurs when releasing the SDK after sleep, ignore it - if (e.Message != "The previously loaded Keyboard got disconnected.") - throw; - } - } - - /// - /// Properly resizes any size bitmap to the keyboard by creating a rectangle whose size is dependent on the bitmap - /// size. - /// - /// - public override void DrawBitmap(Bitmap bitmap) - { - using (var image = ImageUtilities.ResizeImage(bitmap, Width, Height)) - { - // For STRAFE, stretch the image on row 2. - if (_keyboard.DeviceInfo.Model == "STRAFE RGB") - { - using (var strafeBitmap = new Bitmap(22, 8)) - { - using (var g = Graphics.FromImage(strafeBitmap)) - { - g.DrawImage(image, new Point(0, 0)); - g.DrawImage(image, new Rectangle(0, 3, 22, 7), new Rectangle(0, 2, 22, 7), - GraphicsUnit.Pixel); - - _keyboardBrush.Image = strafeBitmap; - _keyboard.Update(); - } - } - } - else - { - _keyboardBrush.Image = image; - _keyboard.Update(); - } - } - } - - public override KeyMatch? GetKeyPosition(Keys keyCode) - { - var widthMultiplier = Width/_keyboard.Brush.RenderedRectangle.Width; - var heightMultiplier = Height/_keyboard.Brush.RenderedRectangle.Height; - - CorsairLed cueLed = null; - try - { - cueLed = _keyboard.Leds.FirstOrDefault(k => k.Id.ToString() == keyCode.ToString()) ?? - _keyboard.Leds.FirstOrDefault(k => k.Id == KeyMap.FormsKeys[keyCode]); - } - catch (Exception) - { - // ignored - } - - if (cueLed == null) - return null; - - var center = cueLed.LedRectangle.GetCenter(); - return new KeyMatch(keyCode, (int) (center.X*widthMultiplier), (int) (center.Y*heightMultiplier)); - } - } + CueSDK.Reinitialize(); + } + catch (WrapperException e) + { + // This occurs when releasing the SDK after sleep, ignore it + if (e.Message != "The previously loaded Keyboard got disconnected.") + throw; + } + } + + /// + /// Properly resizes any size bitmap to the keyboard by creating a rectangle whose size is dependent on the bitmap + /// size. + /// + /// + public override void DrawBitmap(Bitmap bitmap) + { + using (var image = ImageUtilities.ResizeImage(bitmap, Width, Height)) + { + // For STRAFE, stretch the image on row 2. + if (_keyboard.DeviceInfo.Model == "STRAFE RGB") + { + using (var strafeBitmap = new Bitmap(22, 8)) + { + using (var g = Graphics.FromImage(strafeBitmap)) + { + g.DrawImage(image, new Point(0, 0)); + g.DrawImage(image, new Rectangle(0, 3, 22, 7), new Rectangle(0, 2, 22, 7), + GraphicsUnit.Pixel); + + _keyboardBrush.Image = strafeBitmap; + _keyboard.Update(); + } + } + } + else + { + _keyboardBrush.Image = image; + _keyboard.Update(); + } + } + } + + public override KeyMatch? GetKeyPosition(Keys keyCode) + { + var widthMultiplier = Width/_keyboard.Brush.RenderedRectangle.Width; + var heightMultiplier = Height/_keyboard.Brush.RenderedRectangle.Height; + + CorsairLed cueLed = null; + try + { + cueLed = _keyboard.Leds.FirstOrDefault(k => k.Id.ToString() == keyCode.ToString()) ?? + _keyboard.Leds.FirstOrDefault(k => k.Id == KeyMap.FormsKeys[keyCode]); + } + catch (Exception) + { + // ignored + } + + if (cueLed == null) + return null; + + var center = cueLed.LedRectangle.GetCenter(); + return new KeyMatch(keyCode, (int) (center.X*widthMultiplier), (int) (center.Y*heightMultiplier)); + } + } } \ No newline at end of file diff --git a/Artemis/Artemis/Managers/LuaManager.cs b/Artemis/Artemis/Managers/LuaManager.cs index 6e8fc166c..3cf15a722 100644 --- a/Artemis/Artemis/Managers/LuaManager.cs +++ b/Artemis/Artemis/Managers/LuaManager.cs @@ -21,8 +21,8 @@ namespace Artemis.Managers private readonly DeviceManager _deviceManager; private readonly IKernel _kernel; private readonly ILogger _logger; - private List _luaModules; private readonly Script _luaScript; + private List _luaModules; private FileSystemWatcher _watcher; public LuaManager(IKernel kernel, ILogger logger, DeviceManager deviceManager) @@ -52,17 +52,15 @@ namespace Artemis.Managers // Get new instances of all modules _luaModules = _kernel.Get>(); - ProfileModule = (LuaProfileModule)_luaModules.First(m => m.ModuleName == "Profile"); - EventsModule = (LuaEventsModule)_luaModules.First(m => m.ModuleName == "Events"); + ProfileModule = (LuaProfileModule) _luaModules.First(m => m.ModuleName == "Profile"); + EventsModule = (LuaEventsModule) _luaModules.First(m => m.ModuleName == "Events"); // Setup new state _luaScript.Options.DebugPrint = LuaPrint; // Insert each module into the script's globals foreach (var luaModule in _luaModules) - { _luaScript.Globals[luaModule.ModuleName] = luaModule; - } // If there is no LUA script, don't bother executing the string if (ProfileModel.LuaScript.IsNullOrEmpty()) @@ -74,6 +72,7 @@ namespace Artemis.Managers { lock (_luaScript) { + UpdateLuaSource(ProfileModel); _luaScript.DoString(ProfileModel.LuaScript); } } @@ -116,7 +115,6 @@ namespace Artemis.Managers } if (EventsModule != null) - { lock (EventsModule.InvokeLock) { lock (_luaScript) @@ -124,13 +122,51 @@ namespace Artemis.Managers _luaScript.DoString(""); } } - } else - { lock (_luaScript) { _luaScript.DoString(""); } + } + + /// + /// Safely call a function on the active script + /// + /// + /// + public void Call(DynValue function, DynValue[] args = null) + { + if (EventsModule == null) + return; + + try + { + lock (EventsModule.InvokeLock) + { + lock (_luaScript) + { + if (args != null) + _luaScript.Call(function, args); + else + _luaScript.Call(function); + } + } + } + catch (ArgumentException e) + { + _logger.Error("[{0}-LUA]: Error: {1}", ProfileModel.Name, e.Message); + } + catch (InternalErrorException e) + { + _logger.Error("[{0}-LUA]: Error: {1}", ProfileModel.Name, e.DecoratedMessage); + } + catch (SyntaxErrorException e) + { + _logger.Error("[{0}-LUA]: Error: {1}", ProfileModel.Name, e.DecoratedMessage); + } + catch (ScriptRuntimeException e) + { + _logger.Error("[{0}-LUA]: Error: {1}", ProfileModel.Name, e.DecoratedMessage); } } @@ -143,6 +179,20 @@ namespace Artemis.Managers #endregion + /// + /// Updates a profile's LUA script to be compatible with the latest version of Artemis, if needed. + /// This function obviously won't fix completely custom profiles but it'll fix copied LUA. + /// + /// + private static void UpdateLuaSource(ProfileModel profileModel) + { + // 1.7.1.0 - Events cleanup + profileModel.LuaScript = profileModel.LuaScript.Replace("function updateHandler(profile, eventArgs)", + "function updateHandler(eventArgs)"); + profileModel.LuaScript = profileModel.LuaScript.Replace("function drawHandler(profile, eventArgs)", + "function drawHandler(eventArgs)"); + } + #region Editor public void OpenEditor() diff --git a/Artemis/Artemis/Profiles/Lua/Modules/LuaTimerModule.cs b/Artemis/Artemis/Profiles/Lua/Modules/LuaTimerModule.cs new file mode 100644 index 000000000..1045d74fc --- /dev/null +++ b/Artemis/Artemis/Profiles/Lua/Modules/LuaTimerModule.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Artemis.Managers; +using Artemis.Profiles.Lua.Modules.Timer; +using MoonSharp.Interpreter; + +namespace Artemis.Profiles.Lua.Modules +{ + [MoonSharpUserData] + public class LuaTimerModule : LuaModule + { + private readonly List _timers; + + public LuaTimerModule(LuaManager luaManager) : base(luaManager) + { + _timers = new List(); + } + + public override string ModuleName => "Timer"; + + public override void Dispose() + { + foreach (var luaTimer in _timers) + luaTimer.Dispose(); + + _timers.Clear(); + } + + public LuaTimer SetTimer(DynValue function, int interval, int timesToExecute, params DynValue[] args) + { + var luaTimer = new LuaTimer(this, function, interval, timesToExecute, args); + _timers.Add(luaTimer); + return luaTimer; + } + + public void RemoveTimer(LuaTimer luaTimer) + { + if (_timers.Contains(luaTimer)) + _timers.Remove(luaTimer); + + luaTimer.Dispose(); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Profiles/Lua/Modules/Timer/LuaTimer.cs b/Artemis/Artemis/Profiles/Lua/Modules/Timer/LuaTimer.cs new file mode 100644 index 000000000..fa4fded1f --- /dev/null +++ b/Artemis/Artemis/Profiles/Lua/Modules/Timer/LuaTimer.cs @@ -0,0 +1,59 @@ +using System; +using System.Timers; +using MoonSharp.Interpreter; + +namespace Artemis.Profiles.Lua.Modules.Timer +{ + [MoonSharpUserData] + public class LuaTimer : IDisposable + { + private readonly DynValue[] _args; + private readonly DynValue _function; + private readonly System.Timers.Timer _timer; + private readonly LuaTimerModule _timerModule; + private readonly int _timesToExecute; + private int _timesExecuted; + + public LuaTimer(LuaTimerModule timerModule, DynValue function, int interval, int timesToExecute, + params DynValue[] args) + { + _timerModule = timerModule; + _function = function; + _timesToExecute = timesToExecute; + _args = args; + _timesExecuted = 0; + + // Setup timer + _timer = new System.Timers.Timer(interval); + _timer.Elapsed += TimerOnElapsed; + _timer.Start(); + } + + public void Dispose() + { + _timer?.Stop(); + _timer?.Dispose(); + } + + public void Stop() + { + _timerModule.RemoveTimer(this); + } + + private void TimerOnElapsed(object sender, ElapsedEventArgs e) + { + if (_args != null) + _timerModule.LuaManager.Call(_function, _args); + else + _timerModule.LuaManager.Call(_function); + + // Don't keep track of execution if times is set to 0 (infinite) + if (_timesToExecute <= 0) + return; + + _timesExecuted++; + if (_timesExecuted >= _timesToExecute) + _timerModule.RemoveTimer(this); + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/Resources/lua-placeholder.lua b/Artemis/Artemis/Resources/lua-placeholder.lua index 87b7a962a..68a963307 100644 --- a/Artemis/Artemis/Resources/lua-placeholder.lua +++ b/Artemis/Artemis/Resources/lua-placeholder.lua @@ -14,7 +14,7 @@ -- changes are applied to the profile and the script is restarted. -- This event is raised after every profile update, before drawing. -function updateHandler(profile, eventArgs) +function updateHandler(eventArgs) -- In this example we only want to update once per frame when the keyboard is -- updated. If you don't do this the updateHandler will trigger on every -- device's update. @@ -26,7 +26,7 @@ function updateHandler(profile, eventArgs) end -- This event is raised after every profile draw, after updating. -function drawHandler(profile, eventArgs) +function drawHandler(eventArgs) -- In this example we only want to draw to the keyboard. Each device has it's -- own drawing event if eventArgs.DeviceType != "keyboard" then