Skip to content

Commit

Permalink
v2.9.0 - Updated new Stream Deck APIs + New features
Browse files Browse the repository at this point in the history
- Updated plugin creation logic to support issue where Stream Deck sends multiple WillAppear messages in Multi-Actions
- Updated API to support new State parameter in SetImageAsync/SetTitleAsync
- Added FIPS-compliant support to SHA512 in Tools module. MD5 functions are now obsolete
- New GraphicTools.WrapStringToFitImage() function will automatically wrap the text so it wil fit the key when using SetTitleAsync()
  • Loading branch information
BarRaider committed Jun 25, 2020
1 parent 690b023 commit f3f8890
Show file tree
Hide file tree
Showing 13 changed files with 409 additions and 106 deletions.
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

11 changes: 5 additions & 6 deletions NUGET.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
**Author's website and contact information:** [https://barraider.com](https://barraider.com)
** Samples of plugins using this framework: [Samples][1]

### Version 2.8 is out!
- Introduced `GraphicsUtils` class with a bunch of helper functions to manipulate the SD images
- Added new `Tools.FormatNumber()` function converts 54265 to 54.27k
- New ExtensionMethods for `Graphics` object: `DrawAndMeasureString` / `GetTextCenter`
- Updated dependency packages to latest versions
- Bug fix where SDConnection was not properly disposed.
### Version 2.9 is out!
- Updated plugin creation logic to support issue where Stream Deck sends multiple WillAppear messages in Multi-Actions
- Updated API to support new State parameter in SetImageAsync/SetTitleAsync
- Added FIPS-compliant support to SHA512 in Tools module. MD5 functions are now obsolete
- New GraphicTools.WrapStringToFitImage() function will automatically wrap the text so it wil fit the key when using SetTitleAsync()

## Features
- Sample plugin now included in this project on Github
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
* [Install.bat](https://github.com/BarRaider/streamdeck-tools/blob/master/utils/install.bat) - Script that quickly uninstalls and reinstalls your plugin on the streamdeck (view batch file for more details)
* [StreamDeck-Tools Template](https://github.com/BarRaider/streamdeck-tools/raw/master/utils/StreamDeck-Tools%20Template.vsix) for Visual Studio - Automatically creates a project with all the files needed to compile a plugin

### Version 2.8 is out!
### Version 2.9 is out!
- Updated plugin creation logic to support issue where Stream Deck sends multiple WillAppear messages in Multi-Actions
- Updated API to support new State parameter in SetImageAsync/SetTitleAsync
- Added FIPS-compliant support to SHA512 in Tools module. MD5 functions are now obsolete
- New GraphicTools.WrapStringToFitImage() function will automatically wrap the text so it wil fit the key when using SetTitleAsync()

- Introduced `GraphicsUtils` class with a bunch of helper functions to manipulate the SD images
- Added new `Tools.FormatNumber()` function converts 54265 to 54.27k
- New ExtensionMethods for `Graphics` object: `DrawAndMeasureString` / `GetTextCenter`
Expand Down
26 changes: 24 additions & 2 deletions SamplePlugin/PluginAction.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using BarRaider.SdTools;
using BarRaider.SdTools.Wrappers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -102,12 +104,32 @@ public override void Dispose()
Logger.Instance.LogMessage(TracingLevel.INFO, $"Destructor called");
}

public override void KeyPressed(KeyPayload payload)
public async override void KeyPressed(KeyPayload payload)
{
Logger.Instance.LogMessage(TracingLevel.INFO, "Key Pressed");
TitleParameters tp = new TitleParameters(new FontFamily("Arial"), FontStyle.Bold, 20, Color.White, true, TitleVerticalAlignment.Middle);
using (Image image = Tools.GenerateGenericKeyImage(out Graphics graphics))
{
graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, image.Width, image.Height);
graphics.AddTextPath(tp, image.Height, image.Width, "Test");
graphics.Dispose();

await Connection.SetImageAsync(image);
}
}

public override void KeyReleased(KeyPayload payload) { }
public async override void KeyReleased(KeyPayload payload)
{
TitleParameters tp = new TitleParameters(new FontFamily("Arial"), FontStyle.Bold, 20, Color.White, true, TitleVerticalAlignment.Middle);
using (Image image = Tools.GenerateGenericKeyImage(out Graphics graphics))
{
graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, image.Width, image.Height);
graphics.AddTextPath(tp, image.Height, image.Width, "Test", Color.Black, 7);
graphics.Dispose();

await Connection.SetImageAsync(image);
}
}

public override void OnTick() { }

Expand Down
9 changes: 3 additions & 6 deletions SamplePlugin/SamplePlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="CommandLine, Version=2.7.82.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.2.7.82\lib\net461\CommandLine.dll</HintPath>
<Reference Include="CommandLine, Version=2.8.0.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.6.8\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="streamdeck-client-csharp, Version=4.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\streamdeck-client-csharp.4.1.1\lib\netstandard2.0\streamdeck-client-csharp.dll</HintPath>
<HintPath>..\packages\NLog.4.7.2\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
Expand Down
5 changes: 2 additions & 3 deletions SamplePlugin/packages.config
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommandLineParser" version="2.7.82" targetFramework="net472" />
<package id="CommandLineParser" version="2.8.0" targetFramework="net472" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
<package id="NLog" version="4.6.8" targetFramework="net472" />
<package id="streamdeck-client-csharp" version="4.1.1" targetFramework="net472" />
<package id="NLog" version="4.7.2" targetFramework="net472" />
<package id="System.Drawing.Common" version="4.7.0" targetFramework="net472" />
</packages>
9 changes: 7 additions & 2 deletions barraider-sdtools/Backend/PluginContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private async void RunTick()
}
}

// Stopwatch instance created
// Action is loaded in the Stream Deck
private async void Connection_OnWillAppear(object sender, StreamDeckEventReceivedEventArgs<WillAppearEvent> e)
{
SDConnection conn = new SDConnection(connection, pluginUUID, deviceInfo, e.Event.Action, e.Event.Context, e.Event.Device);
Expand All @@ -137,13 +137,18 @@ private async void Connection_OnWillAppear(object sender, StreamDeckEventReceive
{
try
{
if (instances.ContainsKey(e.Event.Context) && instances[e.Event.Context] != null)
{
Logger.Instance.LogMessage(TracingLevel.INFO, $"WillAppear called for already existing context {e.Event.Context} (might be inside a multi-action)");
return;
}
InitialPayload payload = new InitialPayload(GenerateKeyCoordinates(e.Event.Payload.Coordinates),
e.Event.Payload.Settings, e.Event.Payload.State, e.Event.Payload.IsInMultiAction, deviceInfo);
instances[e.Event.Context] = (PluginBase)Activator.CreateInstance(supportedActions[e.Event.Action], conn, payload);
}
catch (Exception ex)
{
Logger.Instance.LogMessage(TracingLevel.FATAL, $"Could not create instance of {supportedActions[e.Event.Action]} - Maybe class does not inherit PluginBase with the same constructor? {ex}");
Logger.Instance.LogMessage(TracingLevel.FATAL, $"Could not create instance of {supportedActions[e.Event.Action]} with context {e.Event.Context} - This may be due to an Exception raised in the constructor, or the class does not inherit PluginBase with the same constructor {ex}");
}
}
else
Expand Down
19 changes: 11 additions & 8 deletions barraider-sdtools/Backend/SDConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,31 +121,33 @@ public async Task GetGlobalSettingsAsync()
/// Sets an image on the StreamDeck key.
/// </summary>
/// <param name="base64Image">Base64 encoded image</param>
/// <param name="state">A 0-based integer value representing the state of an action with multiple states. This is an optional parameter. If not specified, the title is set to all states.</param>
/// <param name="forceSendToStreamdeck">Should image be sent even if it is identical to the one sent previously. Default is false</param>
/// <returns></returns>
public async Task SetImageAsync(string base64Image, bool forceSendToStreamdeck = false)
public async Task SetImageAsync(string base64Image, int? state = null, bool forceSendToStreamdeck = false)
{
string hash = Tools.StringToMD5(base64Image);
string hash = Tools.StringToSHA512(base64Image);
if (forceSendToStreamdeck || hash != previousImageHash)
{
previousImageHash = hash;
await StreamDeckConnection.SetImageAsync(base64Image, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware);
await StreamDeckConnection.SetImageAsync(base64Image, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware, state);
}
}

/// <summary>
/// Sets an image on the StreamDeck key
/// </summary>
/// <param name="image">Image object</param>
/// <param name="state">A 0-based integer value representing the state of an action with multiple states. This is an optional parameter. If not specified, the title is set to all states.</param>
/// <param name="forceSendToStreamdeck">Should image be sent even if it is identical to the one sent previously. Default is false</param>
/// <returns></returns>
public async Task SetImageAsync(Image image, bool forceSendToStreamdeck = false)
public async Task SetImageAsync(Image image, int? state = null, bool forceSendToStreamdeck = false)
{
string hash = Tools.ImageToMD5(image);
string hash = Tools.ImageToSHA512(image);
if (forceSendToStreamdeck || hash != previousImageHash)
{
previousImageHash = hash;
await StreamDeckConnection.SetImageAsync(image, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware);
await StreamDeckConnection.SetImageAsync(image, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware, state);
}
}

Expand All @@ -162,10 +164,11 @@ public async Task SetDefaultImageAsync()
/// Sets a title on the StreamDeck key
/// </summary>
/// <param name="title"></param>
/// <param name="state">A 0-based integer value representing the state of an action with multiple states. This is an optional parameter. If not specified, the title is set to all states.</param>
/// <returns></returns>
public async Task SetTitleAsync(string title)
public async Task SetTitleAsync(string title, int? state = null)
{
await StreamDeckConnection.SetTitleAsync(title, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware);
await StreamDeckConnection.SetTitleAsync(title, ContextId, streamdeck_client_csharp.SDKTarget.HardwareAndSoftware, state);
}

/// <summary>
Expand Down
80 changes: 78 additions & 2 deletions barraider-sdtools/Tools/ExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using streamdeck_client_csharp.Events;
using BarRaider.SdTools.Wrappers;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -55,7 +56,7 @@ public static KeyCoordinates ToKeyCoordinates(this streamdeck_client_csharp.Even
/// <param name="deviceInfo"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
public static StreamDeckDeviceInfo ToStreamDeckDeviceInfo(this DeviceInfo deviceInfo, string deviceId)
public static StreamDeckDeviceInfo ToStreamDeckDeviceInfo(this streamdeck_client_csharp.Events.DeviceInfo deviceInfo, string deviceId)
{
if (deviceInfo == null)
{
Expand Down Expand Up @@ -163,6 +164,81 @@ public static float GetTextCenter(this Graphics graphics, string text, int image
return stringWidth;
}

/// <summary>
/// Adds a text path to an existing Graphics object. Uses TitleParameters to emulate the Text settings in the Property Inspector
/// </summary>
/// <param name="graphics"></param>
/// <param name="titleParameters"></param>
/// <param name="imageHeight"></param>
/// <param name="imageWidth"></param>
/// <param name="text"></param>
/// <param name="pixelsAlignment"></param>
public static void AddTextPath(this Graphics graphics, TitleParameters titleParameters, int imageHeight, int imageWidth, string text, int pixelsAlignment = 15)
{
AddTextPath(graphics, titleParameters, imageHeight, imageWidth, text, Color.Black, 1, pixelsAlignment);
}

/// <summary>
/// Adds a text path to an existing Graphics object. Uses TitleParameters to emulate the Text settings in the Property Inspector
/// </summary>
/// <param name="graphics"></param>
/// <param name="titleParameters"></param>
/// <param name="imageHeight"></param>
/// <param name="imageWidth"></param>
/// <param name="text"></param>
/// <param name="strokeColor"></param>
/// <param name="strokeThickness"></param>
/// <param name="pixelsAlignment"></param>
public static void AddTextPath(this Graphics graphics, TitleParameters titleParameters, int imageHeight, int imageWidth, string text, Color strokeColor, float strokeThickness, int pixelsAlignment = 15)
{
try
{
if (titleParameters == null)
{
Logger.Instance.LogMessage(TracingLevel.ERROR, $"AddTextPath: titleParameters is null");
return;
}

Font font = new Font(titleParameters.FontFamily, (float)titleParameters.FontSizeInPixelsScaledToDefaultImage, titleParameters.FontStyle, GraphicsUnit.Pixel);
Color color = titleParameters.TitleColor;
graphics.PageUnit = GraphicsUnit.Pixel;
float ratio = graphics.DpiY / imageWidth;
SizeF stringSize = graphics.MeasureString(text, font);
float textWidth = stringSize.Width * (1 - ratio);
float textHeight = stringSize.Height * (1 - ratio);
int stringWidth = 0;
if (textWidth < imageWidth)
{
stringWidth = (int)(Math.Abs((imageWidth - textWidth)) / 2) - pixelsAlignment;
}

int stringHeight = pixelsAlignment; // Top
if (titleParameters.VerticalAlignment == TitleVerticalAlignment.Middle)
{
stringHeight = (imageHeight / 2) - pixelsAlignment;
}
else if (titleParameters.VerticalAlignment == TitleVerticalAlignment.Bottom)
{
stringHeight = (int)(Math.Abs((imageHeight - textHeight)) - pixelsAlignment);
}

Pen stroke = new Pen(strokeColor, strokeThickness);
GraphicsPath gpath = new GraphicsPath();
gpath.AddString(text,
font.FontFamily,
(int)font.Style,
graphics.DpiY * font.SizeInPoints / imageWidth,
new Point(stringWidth, stringHeight),
new StringFormat());
graphics.DrawPath(stroke, gpath);
graphics.FillPath(new SolidBrush(color), gpath);
}
catch (Exception ex)
{
Logger.Instance.LogMessage(TracingLevel.ERROR, $"AddTextPath Exception {ex}");
}
}

#endregion
}
}
Loading

0 comments on commit f3f8890

Please sign in to comment.