From ec3357e3bb6db2396f20da992f83cbe1bd3cde03 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:07:42 +0200 Subject: [PATCH 1/8] Dedicated interfaces for Dials vs Keys --- barraider-sdtools/Backend/IEncoderPlugin.cs | 28 +++++++++++++++++++++ barraider-sdtools/Backend/IKeypadPlugin.cs | 19 ++++++++++++++ barraider-sdtools/Backend/ISDConnection.cs | 14 +++++++++++ 3 files changed, 61 insertions(+) create mode 100644 barraider-sdtools/Backend/IEncoderPlugin.cs create mode 100644 barraider-sdtools/Backend/IKeypadPlugin.cs diff --git a/barraider-sdtools/Backend/IEncoderPlugin.cs b/barraider-sdtools/Backend/IEncoderPlugin.cs new file mode 100644 index 0000000..592471a --- /dev/null +++ b/barraider-sdtools/Backend/IEncoderPlugin.cs @@ -0,0 +1,28 @@ +using BarRaider.SdTools.Payloads; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools +{ + /// + /// Interface used to capture dial/encoder events + /// + public interface IEncoderPlugin : ICommonPluginFunctions + { + /// + /// Called when the dial is rotated + /// + void DialRotate(DialRotatePayload payload); + + /// + /// Called when the Dial is pressed or released + /// + void DialPress(DialPressPayload payload); + + /// + /// Called when the touchpad (above the dials) is pressed + /// + void TouchPress(TouchpadPressPayload payload); + } +} diff --git a/barraider-sdtools/Backend/IKeypadPlugin.cs b/barraider-sdtools/Backend/IKeypadPlugin.cs new file mode 100644 index 0000000..66d468b --- /dev/null +++ b/barraider-sdtools/Backend/IKeypadPlugin.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools +{ + /// + /// Interface used to capture key events + /// + public interface IKeypadPlugin : ICommonPluginFunctions + { + void KeyPressed(KeyPayload payload); + + /// + /// Called when a Stream Deck key is released + /// + void KeyReleased(KeyPayload payload); + } +} diff --git a/barraider-sdtools/Backend/ISDConnection.cs b/barraider-sdtools/Backend/ISDConnection.cs index c29f68d..f10e18e 100644 --- a/barraider-sdtools/Backend/ISDConnection.cs +++ b/barraider-sdtools/Backend/ISDConnection.cs @@ -185,6 +185,20 @@ public interface ISDConnection : IDisposable /// Task SetStateAsync(uint state); + /// + /// Sets the values of touchpad layouts items + /// + /// Dictionary holding the layout item keys and values you want to change + /// + Task SetFeedbackAsync(Dictionary dictKeyValue); + + /// + /// Sets the value of a single touchpad layout item + /// + /// + /// + Task SetFeedbackAsync(string layoutItemKey, string value); + #endregion /// From f864d3d726e4e9be71bbf4ef976e21ec3d6d9927 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:08:16 +0200 Subject: [PATCH 2/8] Deprecate PluginBase --- barraider-sdtools/Backend/PluginBase.cs | 92 +++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 barraider-sdtools/Backend/PluginBase.cs diff --git a/barraider-sdtools/Backend/PluginBase.cs b/barraider-sdtools/Backend/PluginBase.cs new file mode 100644 index 0000000..eca4dfa --- /dev/null +++ b/barraider-sdtools/Backend/PluginBase.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json.Linq; + +namespace BarRaider.SdTools +{ + + /// + /// Obsolete! Use `KeypadBase` moving forward, or choose one of the other options: `EncoderBase`, `KeyAndEncoderBase` + /// + [Obsolete("PluginBase will be removed in next version. Use either 'KeypadBase' (if you don't support dials), 'EncoderBase' (for only dials), 'KeyAndEncoderBase' (for both keys and dials) instead")] + public abstract class PluginBase : IKeypadPlugin + { + /// + /// Called when a Stream Deck key is pressed + /// + public abstract void KeyPressed(KeyPayload payload); + + /// + /// Called when a Stream Deck key is released + /// + public abstract void KeyReleased(KeyPayload payload); + + /// + /// Called when the PropertyInspector has new settings + /// + /// + public abstract void ReceivedSettings(ReceivedSettingsPayload payload); + + /// + /// Called when GetGlobalSettings is called. + /// + /// + public abstract void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); + + /// + /// Called every second + /// Logic for displaying title/image can go here + /// + public abstract void OnTick(); + + /// + /// Abstract method Called when the plugin is disposed + /// + public abstract void Dispose(); + + /// + /// Main iDisposable Dispose function + /// + public void Destroy() + { + Dispose(); + if (Connection != null) + { + Connection.Dispose(); + } + } + + /// + /// Connection object which handles your communication with the Stream Deck app + /// + protected ISDConnection Connection { get; private set; } + + /// + /// Constructor for PluginBase. Receives the communication and plugin settings + /// Note that the settings object is not used by the base and should be consumed by the deriving class. + /// Usually, a private class inside the deriving class is created which stores the settings + /// Example for settings usage: + /// * if (payload.Settings == null || payload.Settings.Count == 0) + /// * { + /// * // Create default settings + /// * } + /// * else + /// * { + /// this.settings = payload.Settings.ToObject(); + /// * } + /// + /// + /// Communication module with Stream Deck + /// Plugin settings - NOTE: Not used in base class, should be consumed by deriving class +#pragma warning disable IDE0060 // Remove unused parameter + public PluginBase(ISDConnection connection, InitialPayload payload) +#pragma warning restore IDE0060 // Remove unused parameter + { + Connection = connection; + } + } +} \ No newline at end of file From cf733fbd045cd17e5f54e35897864327b43d2b14 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:08:35 +0200 Subject: [PATCH 3/8] Add support for new Dial events --- barraider-sdtools/Backend/PluginContainer.cs | 117 +++++++++++++++++-- 1 file changed, 109 insertions(+), 8 deletions(-) diff --git a/barraider-sdtools/Backend/PluginContainer.cs b/barraider-sdtools/Backend/PluginContainer.cs index 104eb29..deed49c 100644 --- a/barraider-sdtools/Backend/PluginContainer.cs +++ b/barraider-sdtools/Backend/PluginContainer.cs @@ -23,7 +23,7 @@ class PluginContainer private static readonly Dictionary supportedActions = new Dictionary(); // Holds all instances of plugin - private static readonly Dictionary instances = new Dictionary(); + private static readonly Dictionary instances = new Dictionary(); public PluginContainer(PluginActionId[] supportedActionIds) { @@ -46,6 +46,9 @@ public void Run(StreamDeckOptions options) connection.OnKeyUp += Connection_OnKeyUp; connection.OnWillAppear += Connection_OnWillAppear; connection.OnWillDisappear += Connection_OnWillDisappear; + connection.OnDialRotate += Connection_OnDialRotate; + connection.OnDialPress += Connection_OnDialPress; + connection.OnTouchpadPress += Connection_OnTouchpadPress; // Settings changed connection.OnDidReceiveSettings += Connection_OnDidReceiveSettings; @@ -88,7 +91,14 @@ private async void Connection_OnKeyDown(object sender, SDEventReceivedEventArgs< { KeyPayload payload = new KeyPayload(e.Event.Payload.Coordinates, e.Event.Payload.Settings, e.Event.Payload.State, e.Event.Payload.UserDesiredState, e.Event.Payload.IsInMultiAction); - instances[e.Event.Context].KeyPressed(payload); + if (instances[e.Event.Context] is IKeypadPlugin plugin) + { + plugin.KeyPressed(payload); + } + else + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Keydown General Error: Could not convert {e.Event.Context} to IKeypadPlugin"); + } } } finally @@ -111,7 +121,14 @@ private async void Connection_OnKeyUp(object sender, SDEventReceivedEventArgs kvp in instances.ToArray()) + foreach (KeyValuePair kvp in instances.ToArray()) { kvp.Value.OnTick(); } @@ -160,7 +176,7 @@ private async void Connection_OnWillAppear(object sender, SDEventReceivedEventAr } InitialPayload payload = new InitialPayload(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); + instances[e.Event.Context] = (ICommonPluginFunctions)Activator.CreateInstance(supportedActions[e.Event.Action], conn, payload); } catch (Exception ex) { @@ -189,8 +205,8 @@ private async void Connection_OnWillDisappear(object sender, SDEventReceivedEven if (instances.ContainsKey(e.Event.Context)) { - instances[e.Event.Context].Destroy(); - instances.Remove(e.Event.Context); + instances[e.Event.Context].Destroy(); + instances.Remove(e.Event.Context); } } finally @@ -242,6 +258,91 @@ private async void Connection_OnDidReceiveGlobalSettings(object sender, SDEventR } } + private async void Connection_OnTouchpadPress(object sender, SDEventReceivedEventArgs e) + { + await instancesLock.WaitAsync(); + try + { +#if DEBUG + Logger.Instance.LogMessage(TracingLevel.DEBUG, $"TouchpadPress: Context: {e.Event.Context} Action: {e.Event.Action} Payload: {e.Event.Payload?.ToStringEx()}"); +#endif + + if (instances.ContainsKey(e.Event.Context)) + { + TouchpadPressPayload payload = new TouchpadPressPayload(e.Event.Payload.Coordinates,e.Event.Payload.Settings, e.Event.Payload.Controller, e.Event.Payload.IsLongPress, e.Event.Payload.TapPosition); + if (instances[e.Event.Context] is IEncoderPlugin plugin) + { + plugin.TouchPress(payload); + } + else + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"TouchpadPress General Error: Could not convert {e.Event.Context} to IEncoderPlugin"); + } + } + } + finally + { + instancesLock.Release(); + } + } + + private async void Connection_OnDialPress(object sender, SDEventReceivedEventArgs e) + { + await instancesLock.WaitAsync(); + try + { +#if DEBUG + Logger.Instance.LogMessage(TracingLevel.DEBUG, $"DialPress: Context: {e.Event.Context} Action: {e.Event.Action} Payload: {e.Event.Payload?.ToStringEx()}"); +#endif + + if (instances.ContainsKey(e.Event.Context)) + { + DialPressPayload payload = new DialPressPayload(e.Event.Payload.Coordinates, e.Event.Payload.Settings, e.Event.Payload.Controller, e.Event.Payload.IsDialPressed); + if (instances[e.Event.Context] is IEncoderPlugin plugin) + { + plugin.DialPress(payload); + } + else + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"DialPress General Error: Could not convert {e.Event.Context} to IEncoderPlugin"); + } + } + } + finally + { + instancesLock.Release(); + } + } + + private async void Connection_OnDialRotate(object sender, SDEventReceivedEventArgs e) + { + await instancesLock.WaitAsync(); + try + { +#if DEBUG + Logger.Instance.LogMessage(TracingLevel.DEBUG, $"DialRotate: Context: {e.Event.Context} Action: {e.Event.Action} Payload: {e.Event.Payload?.ToStringEx()}"); +#endif + + if (instances.ContainsKey(e.Event.Context)) + { + DialRotatePayload payload = new DialRotatePayload(e.Event.Payload.Coordinates, e.Event.Payload.Settings, e.Event.Payload.Controller, e.Event.Payload.Ticks, e.Event.Payload.IsDialPressed); + if (instances[e.Event.Context] is IEncoderPlugin plugin) + { + plugin.DialRotate(payload); + } + else + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"DialRotate General Error: Could not convert {e.Event.Context} to IEncoderPlugin"); + } + } + } + finally + { + instancesLock.Release(); + } + } + + private void Connection_OnConnected(object sender, EventArgs e) { From 214159b193e7d457c2ab79f398f715258922d67a Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:08:55 +0200 Subject: [PATCH 4/8] Dedicated interface for common functions across dials and keys --- .../Backend/ICommonPluginFunctions.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 barraider-sdtools/Backend/ICommonPluginFunctions.cs diff --git a/barraider-sdtools/Backend/ICommonPluginFunctions.cs b/barraider-sdtools/Backend/ICommonPluginFunctions.cs new file mode 100644 index 0000000..5d7fe06 --- /dev/null +++ b/barraider-sdtools/Backend/ICommonPluginFunctions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools +{ + public interface ICommonPluginFunctions : IDisposable + { + /// + /// Called when the PropertyInspector has new settings + /// + /// + void ReceivedSettings(ReceivedSettingsPayload payload); + + /// + /// Called when GetGlobalSettings is called. + /// + /// + void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); + + /// + /// Called every second + /// Logic for displaying title/image can go here + /// + void OnTick(); + + /// + /// Internal function used by StreamDeckTools to prevent memory leaks + /// + void Destroy(); + } +} From b877379a9d7b40677d0acc6dcfaf8bfba0540846 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:09:34 +0200 Subject: [PATCH 5/8] Dedicated abstract class for keypad plugin --- barraider-sdtools/Backend/KeypadBase.cs | 94 +++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 barraider-sdtools/Backend/KeypadBase.cs diff --git a/barraider-sdtools/Backend/KeypadBase.cs b/barraider-sdtools/Backend/KeypadBase.cs new file mode 100644 index 0000000..36f72ba --- /dev/null +++ b/barraider-sdtools/Backend/KeypadBase.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json.Linq; + +namespace BarRaider.SdTools +{ + + /// + /// Main abstract class your plugin should derive from for keys (not dials) + /// For dials use the EncoderBase or KeyAndEncoderBase + /// Holds implementation for all the basic functions + /// If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + /// + public abstract class KeypadBase : IKeypadPlugin + { + /// + /// Called when a Stream Deck key is pressed + /// + public abstract void KeyPressed(KeyPayload payload); + + /// + /// Called when a Stream Deck key is released + /// + public abstract void KeyReleased(KeyPayload payload); + + /// + /// Called when the PropertyInspector has new settings + /// + /// + public abstract void ReceivedSettings(ReceivedSettingsPayload payload); + + /// + /// Called when GetGlobalSettings is called. + /// + /// + public abstract void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); + + /// + /// Called every second + /// Logic for displaying title/image can go here + /// + public abstract void OnTick(); + + /// + /// Abstract method Called when the plugin is disposed + /// + public abstract void Dispose(); + + /// + /// Main iDisposable Dispose function + /// + public void Destroy() + { + Dispose(); + if (Connection != null) + { + Connection.Dispose(); + } + } + + /// + /// Connection object which handles your communication with the Stream Deck app + /// + protected ISDConnection Connection { get; private set; } + + /// + /// Constructor for PluginBase. Receives the communication and plugin settings + /// Note that the settings object is not used by the base and should be consumed by the deriving class. + /// Usually, a private class inside the deriving class is created which stores the settings + /// Example for settings usage: + /// * if (payload.Settings == null || payload.Settings.Count == 0) + /// * { + /// * // Create default settings + /// * } + /// * else + /// * { + /// this.settings = payload.Settings.ToObject(); + /// * } + /// + /// + /// Communication module with Stream Deck + /// Plugin settings - NOTE: Not used in base class, should be consumed by deriving class +#pragma warning disable IDE0060 // Remove unused parameter + public KeypadBase(ISDConnection connection, InitialPayload payload) +#pragma warning restore IDE0060 // Remove unused parameter + { + Connection = connection; + } + } +} \ No newline at end of file From 6f5e76f1037042f1b12dbdf7e6b405fd4f98d875 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:09:41 +0200 Subject: [PATCH 6/8] Dedicated abstract class for dial plugin --- barraider-sdtools/Backend/EncoderBase.cs | 95 ++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 barraider-sdtools/Backend/EncoderBase.cs diff --git a/barraider-sdtools/Backend/EncoderBase.cs b/barraider-sdtools/Backend/EncoderBase.cs new file mode 100644 index 0000000..02a065e --- /dev/null +++ b/barraider-sdtools/Backend/EncoderBase.cs @@ -0,0 +1,95 @@ +using BarRaider.SdTools.Payloads; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools +{ + /// + /// Main abstract class your plugin should derive from for dials (not keys) + /// For keys use the KeyBase or KeyAndEncoderBase + /// Holds implementation for all the basic functions + /// If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + /// + public abstract class EncoderBase : IEncoderPlugin + { + /// + /// Called when the dial is rotated + /// + public abstract void DialRotate(DialRotatePayload payload); + + /// + /// Called when the Dial is pressed or released + /// + public abstract void DialPress(DialPressPayload payload); + + /// + /// Called when the touchpad (above the dials) is pressed + /// + public abstract void TouchPress(TouchpadPressPayload payload); + + /// + /// Called when the PropertyInspector has new settings + /// + /// + public abstract void ReceivedSettings(ReceivedSettingsPayload payload); + + /// + /// Called when GetGlobalSettings is called. + /// + /// + public abstract void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); + + /// + /// Called every second + /// Logic for displaying title/image can go here + /// + public abstract void OnTick(); + + /// + /// Abstract method Called when the plugin is disposed + /// + public abstract void Dispose(); + + /// + /// Main iDisposable Dispose function + /// + public void Destroy() + { + Dispose(); + if (Connection != null) + { + Connection.Dispose(); + } + } + + /// + /// Connection object which handles your communication with the Stream Deck app + /// + protected ISDConnection Connection { get; private set; } + + /// + /// Constructor for PluginBase. Receives the communication and plugin settings + /// Note that the settings object is not used by the base and should be consumed by the deriving class. + /// Usually, a private class inside the deriving class is created which stores the settings + /// Example for settings usage: + /// * if (payload.Settings == null || payload.Settings.Count == 0) + /// * { + /// * // Create default settings + /// * } + /// * else + /// * { + /// this.settings = payload.Settings.ToObject(); + /// * } + /// + /// + /// Communication module with Stream Deck + /// Plugin settings - NOTE: Not used in base class, should be consumed by deriving class +#pragma warning disable IDE0060 // Remove unused parameter + public EncoderBase(ISDConnection connection, InitialPayload payload) +#pragma warning restore IDE0060 // Remove unused parameter + { + Connection = connection; + } + } +} From f579d49fb7faffeab940b77ed6e028112b888f01 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:10:05 +0200 Subject: [PATCH 7/8] Dedicated abstract class for keypad + dial plugin --- .../Backend/KeyAndEncoderBase.cs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 barraider-sdtools/Backend/KeyAndEncoderBase.cs diff --git a/barraider-sdtools/Backend/KeyAndEncoderBase.cs b/barraider-sdtools/Backend/KeyAndEncoderBase.cs new file mode 100644 index 0000000..d701ea8 --- /dev/null +++ b/barraider-sdtools/Backend/KeyAndEncoderBase.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json.Linq; + +namespace BarRaider.SdTools +{ + + /// + /// Main abstract class your plugin should derive from for keys (not dials) + /// For dials use the EncoderBase or KeyAndEncoderBase + /// Holds implementation for all the basic functions + /// If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + /// + public abstract class KeyAndEncoderBase : IKeypadPlugin, IEncoderPlugin + { + /// + /// Called when the dial is rotated + /// + public abstract void DialRotate(DialRotatePayload payload); + + /// + /// Called when the Dial is pressed or released + /// + public abstract void DialPress(DialPressPayload payload); + + /// + /// Called when the touchpad (above the dials) is pressed + /// + public abstract void TouchPress(TouchpadPressPayload payload); + + /// + /// Called when a Stream Deck key is pressed + /// + public abstract void KeyPressed(KeyPayload payload); + + /// + /// Called when a Stream Deck key is released + /// + public abstract void KeyReleased(KeyPayload payload); + + /// + /// Called when the PropertyInspector has new settings + /// + /// + public abstract void ReceivedSettings(ReceivedSettingsPayload payload); + + /// + /// Called when GetGlobalSettings is called. + /// + /// + public abstract void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); + + /// + /// Called every second + /// Logic for displaying title/image can go here + /// + public abstract void OnTick(); + + /// + /// Abstract method Called when the plugin is disposed + /// + public abstract void Dispose(); + + /// + /// Main iDisposable Dispose function + /// + public void Destroy() + { + Dispose(); + if (Connection != null) + { + Connection.Dispose(); + } + } + + /// + /// Connection object which handles your communication with the Stream Deck app + /// + protected ISDConnection Connection { get; private set; } + + /// + /// Constructor for PluginBase. Receives the communication and plugin settings + /// Note that the settings object is not used by the base and should be consumed by the deriving class. + /// Usually, a private class inside the deriving class is created which stores the settings + /// Example for settings usage: + /// * if (payload.Settings == null || payload.Settings.Count == 0) + /// * { + /// * // Create default settings + /// * } + /// * else + /// * { + /// this.settings = payload.Settings.ToObject(); + /// * } + /// + /// + /// Communication module with Stream Deck + /// Plugin settings - NOTE: Not used in base class, should be consumed by deriving class +#pragma warning disable IDE0060 // Remove unused parameter + public KeyAndEncoderBase(ISDConnection connection, InitialPayload payload) +#pragma warning restore IDE0060 // Remove unused parameter + { + Connection = connection; + } + } +} \ No newline at end of file From 0622f5f6f65fbc59c8da3d09b360acbf7506e163 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:10:50 +0200 Subject: [PATCH 8/8] SD+ Support --- barraider-sdtools/Backend/SDConnection.cs | 173 ++-- .../Messages/SetFeedbackMessage.cs | 23 + .../Communication/SDEvents/BaseEvent.cs | 7 + .../Communication/SDEvents/DialPressEvent.cs | 38 + .../Communication/SDEvents/DialRotateEvent.cs | 38 + .../Communication/SDEvents/TouchTapEvent.cs | 38 + .../Communication/SDEvents/WillAppearEvent.cs | 3 +- .../SDEvents/WillDisappearEvent.cs | 3 +- .../Communication/StreamDeckConnection.cs | 90 +- .../AppearancePayload.cs | 8 +- .../Payloads/DialPressPayload.cs | 58 ++ .../Payloads/DialRotatePayload.cs | 65 ++ barraider-sdtools/Payloads/KeyPayload.cs | 6 +- .../Payloads/TouchpadPressPayload.cs | 66 ++ .../StreamDeckInfo/DeviceType.cs | 9 + .../Tools/PayloadExtensionMethods.cs | 30 +- barraider-sdtools/Tools/PluginBase.cs | 93 --- barraider-sdtools/Wrappers/PluginActionId.cs | 4 +- barraider-sdtools/streamdeck-tools.xml | 789 +++++++++++++++--- 19 files changed, 1207 insertions(+), 334 deletions(-) create mode 100644 barraider-sdtools/Communication/Messages/SetFeedbackMessage.cs create mode 100644 barraider-sdtools/Communication/SDEvents/DialPressEvent.cs create mode 100644 barraider-sdtools/Communication/SDEvents/DialRotateEvent.cs create mode 100644 barraider-sdtools/Communication/SDEvents/TouchTapEvent.cs rename barraider-sdtools/{Communication/SDEvents => Payloads}/AppearancePayload.cs (79%) create mode 100644 barraider-sdtools/Payloads/DialPressPayload.cs create mode 100644 barraider-sdtools/Payloads/DialRotatePayload.cs create mode 100644 barraider-sdtools/Payloads/TouchpadPressPayload.cs delete mode 100644 barraider-sdtools/Tools/PluginBase.cs diff --git a/barraider-sdtools/Backend/SDConnection.cs b/barraider-sdtools/Backend/SDConnection.cs index 5357145..82fb3da 100644 --- a/barraider-sdtools/Backend/SDConnection.cs +++ b/barraider-sdtools/Backend/SDConnection.cs @@ -9,6 +9,7 @@ using BarRaider.SdTools.Payloads; using BarRaider.SdTools.Communication; using BarRaider.SdTools.Communication.SDEvents; +using System.Collections.Generic; namespace BarRaider.SdTools { @@ -79,7 +80,95 @@ public class SDConnection : ISDConnection #endregion - #region Public Implementations + #region Public Properties + + + /// + /// An opaque value identifying the plugin. This value is received during the Registration procedure + /// + [JsonIgnore] + public String ContextId { get; private set; } + + /// + /// An opaque value identifying the device the plugin is launched on. + /// + [JsonIgnore] + public String DeviceId { get; private set; } + + /// + /// StreamDeckConnection object, initialized based on the args received when launching the program + /// + [JsonIgnore] + public StreamDeckConnection StreamDeckConnection { get; private set; } + + #endregion + + /// + /// Public constructor, a StreamDeckConnection object is required along with the current action and context IDs + /// These will be used to correctly communicate with the StreamDeck App + /// + /// + /// + /// + /// + /// + /// /// + public SDConnection(StreamDeckConnection connection, string pluginUUID, StreamDeckInfo deviceInfo, string actionId, string contextId, string deviceId) + { + StreamDeckConnection = connection; + this.pluginUUID = pluginUUID; + this.deviceInfo = deviceInfo; + this.actionId = actionId; + this.ContextId = contextId; + this.DeviceId = deviceId; + + StreamDeckConnection.OnSendToPlugin += Connection_OnSendToPlugin; + StreamDeckConnection.OnTitleParametersDidChange += Connection_OnTitleParametersDidChange; + StreamDeckConnection.OnApplicationDidTerminate += Connection_OnApplicationDidTerminate; + StreamDeckConnection.OnApplicationDidLaunch += Connection_OnApplicationDidLaunch; + StreamDeckConnection.OnDeviceDidDisconnect += Connection_OnDeviceDidDisconnect; + StreamDeckConnection.OnDeviceDidConnect += Connection_OnDeviceDidConnect; + StreamDeckConnection.OnPropertyInspectorDidAppear += Connection_OnPropertyInspectorDidAppear; + StreamDeckConnection.OnPropertyInspectorDidDisappear += Connection_OnPropertyInspectorDidDisappear; + StreamDeckConnection.OnSystemDidWakeUp += StreamDeckConnection_OnSystemDidWakeUp; + } + + #region Public Methods + + /// + /// Dispose (Destructor) function + /// + public void Dispose() + { + StreamDeckConnection.OnSendToPlugin -= Connection_OnSendToPlugin; + StreamDeckConnection.OnTitleParametersDidChange -= Connection_OnTitleParametersDidChange; + StreamDeckConnection.OnApplicationDidTerminate -= Connection_OnApplicationDidTerminate; + StreamDeckConnection.OnApplicationDidLaunch -= Connection_OnApplicationDidLaunch; + StreamDeckConnection.OnDeviceDidDisconnect -= Connection_OnDeviceDidDisconnect; + StreamDeckConnection.OnDeviceDidConnect -= Connection_OnDeviceDidConnect; + StreamDeckConnection.OnPropertyInspectorDidAppear -= Connection_OnPropertyInspectorDidAppear; + StreamDeckConnection.OnPropertyInspectorDidDisappear -= Connection_OnPropertyInspectorDidDisappear; + StreamDeckConnection.OnSystemDidWakeUp -= StreamDeckConnection_OnSystemDidWakeUp; + } + + /// + /// Gets the Stream Deck device's info + /// + /// + public StreamDeckDeviceInfo DeviceInfo() + { + if (deviceInfo == null || string.IsNullOrEmpty(DeviceId)) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Could not get DeviceInfo for DeviceId: {DeviceId} Devices: {deviceInfo?.Devices?.Length}"); + return null; + } + + return deviceInfo.Devices.Where(d => d.Id == DeviceId).FirstOrDefault(); + } + + #endregion + + #region Public Requests /// /// Send settings to the PropertyInspector @@ -241,21 +330,6 @@ public async Task LogSDMessage(string message) await StreamDeckConnection.LogMessageAsync(message); } - /// - /// Gets the Stream Deck device's info - /// - /// - public StreamDeckDeviceInfo DeviceInfo() - { - if (deviceInfo == null || string.IsNullOrEmpty(DeviceId)) - { - Logger.Instance.LogMessage(TracingLevel.ERROR, $"Could not get DeviceInfo for DeviceId: {DeviceId} Devices: {deviceInfo?.Devices?.Length}"); - return null; - } - - return deviceInfo.Devices.Where(d => d.Id == DeviceId).FirstOrDefault(); - } - /// /// Tells Stream Deck to return the current plugin settings via the ReceivedSettings function /// @@ -295,72 +369,27 @@ public async Task SetStateAsync(uint state) await StreamDeckConnection.SetStateAsync(state, ContextId); } - #endregion - - /// - /// An opaque value identifying the plugin. This value is received during the Registration procedure - /// - [JsonIgnore] - public String ContextId { get; private set; } - - /// - /// An opaque value identifying the device the plugin is launched on. - /// - [JsonIgnore] - public String DeviceId { get; private set; } - - /// - /// StreamDeckConnection object, initialized based on the args received when launching the program - /// - [JsonIgnore] - public StreamDeckConnection StreamDeckConnection { get; private set; } - /// - /// Public constructor, a StreamDeckConnection object is required along with the current action and context IDs - /// These will be used to correctly communicate with the StreamDeck App + /// Sets the values of touchpad layouts items /// - /// - /// - /// - /// - /// - /// /// - public SDConnection(StreamDeckConnection connection, string pluginUUID, StreamDeckInfo deviceInfo, string actionId, string contextId, string deviceId) + /// + /// + public async Task SetFeedbackAsync(Dictionary dictKeyValues) { - StreamDeckConnection = connection; - this.pluginUUID = pluginUUID; - this.deviceInfo = deviceInfo; - this.actionId = actionId; - this.ContextId = contextId; - this.DeviceId = deviceId; - - StreamDeckConnection.OnSendToPlugin += Connection_OnSendToPlugin; - StreamDeckConnection.OnTitleParametersDidChange += Connection_OnTitleParametersDidChange; - StreamDeckConnection.OnApplicationDidTerminate += Connection_OnApplicationDidTerminate; - StreamDeckConnection.OnApplicationDidLaunch += Connection_OnApplicationDidLaunch; - StreamDeckConnection.OnDeviceDidDisconnect += Connection_OnDeviceDidDisconnect; - StreamDeckConnection.OnDeviceDidConnect += Connection_OnDeviceDidConnect; - StreamDeckConnection.OnPropertyInspectorDidAppear += Connection_OnPropertyInspectorDidAppear; - StreamDeckConnection.OnPropertyInspectorDidDisappear += Connection_OnPropertyInspectorDidDisappear; - StreamDeckConnection.OnSystemDidWakeUp += StreamDeckConnection_OnSystemDidWakeUp; + await StreamDeckConnection.SetFeedbackAsync(dictKeyValues, ContextId); } /// - /// Dispose (Destructor) function + /// Sets the value of a single touchpad layout item /// - public void Dispose() + /// + /// + public async Task SetFeedbackAsync(string layoutItemKey, string value) { - StreamDeckConnection.OnSendToPlugin -= Connection_OnSendToPlugin; - StreamDeckConnection.OnTitleParametersDidChange -= Connection_OnTitleParametersDidChange; - StreamDeckConnection.OnApplicationDidTerminate -= Connection_OnApplicationDidTerminate; - StreamDeckConnection.OnApplicationDidLaunch -= Connection_OnApplicationDidLaunch; - StreamDeckConnection.OnDeviceDidDisconnect -= Connection_OnDeviceDidDisconnect; - StreamDeckConnection.OnDeviceDidConnect -= Connection_OnDeviceDidConnect; - StreamDeckConnection.OnPropertyInspectorDidAppear -= Connection_OnPropertyInspectorDidAppear; - StreamDeckConnection.OnPropertyInspectorDidDisappear -= Connection_OnPropertyInspectorDidDisappear; - StreamDeckConnection.OnSystemDidWakeUp -= StreamDeckConnection_OnSystemDidWakeUp; + await StreamDeckConnection.SetFeedbackAsync(new Dictionary() { { layoutItemKey, value } }, ContextId); } + #endregion #region Event Wrappers diff --git a/barraider-sdtools/Communication/Messages/SetFeedbackMessage.cs b/barraider-sdtools/Communication/Messages/SetFeedbackMessage.cs new file mode 100644 index 0000000..ada73a9 --- /dev/null +++ b/barraider-sdtools/Communication/Messages/SetFeedbackMessage.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace BarRaider.SdTools.Communication.Messages +{ + internal class SetFeedbackMessage : IMessage + { + [JsonProperty("event")] + public string Event { get { return "setFeedback"; } } + + [JsonProperty("context")] + public string Context { get; private set; } + + [JsonProperty("payload")] + public Dictionary DictKeyValues { get; private set; } + + public SetFeedbackMessage(Dictionary dictKeyValues, string pluginUUID) + { + this.Context = pluginUUID; + DictKeyValues = dictKeyValues; + } + } +} diff --git a/barraider-sdtools/Communication/SDEvents/BaseEvent.cs b/barraider-sdtools/Communication/SDEvents/BaseEvent.cs index 47b5195..2ddfc5d 100644 --- a/barraider-sdtools/Communication/SDEvents/BaseEvent.cs +++ b/barraider-sdtools/Communication/SDEvents/BaseEvent.cs @@ -26,6 +26,9 @@ internal static class EventTypes public const string PropertyInspectorDidAppear = "propertyInspectorDidAppear"; public const string PropertyInspectorDidDisappear = "propertyInspectorDidDisappear"; public const string SendToPlugin = "sendToPlugin"; + public const string DialRotate = "dialRotate"; + public const string DialPress = "dialPress"; + public const string TouchpadPress = "touchTap"; } /// @@ -58,6 +61,10 @@ public abstract class BaseEvent { EventTypes.PropertyInspectorDidDisappear, typeof(PropertyInspectorDidDisappearEvent) }, { EventTypes.SendToPlugin, typeof(SendToPluginEvent) }, + + { EventTypes.DialRotate, typeof(DialRotateEvent) }, + { EventTypes.DialPress, typeof(DialPressEvent) }, + { EventTypes.TouchpadPress, typeof(TouchpadPress) }, }; /// diff --git a/barraider-sdtools/Communication/SDEvents/DialPressEvent.cs b/barraider-sdtools/Communication/SDEvents/DialPressEvent.cs new file mode 100644 index 0000000..669f305 --- /dev/null +++ b/barraider-sdtools/Communication/SDEvents/DialPressEvent.cs @@ -0,0 +1,38 @@ +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Communication.SDEvents +{ + /// + /// Payload for Dial press/unpress event + /// + public class DialPressEvent : BaseEvent + { + /// + /// Action Name + /// + [JsonProperty("action")] + public string Action { get; private set; } + + /// + /// Unique Action UUID + /// + [JsonProperty("context")] + public string Context { get; private set; } + + /// + /// Device UUID key was pressed on + /// + [JsonProperty("device")] + public string Device { get; private set; } + + /// + /// Information on dial rotation + /// + [JsonProperty("payload")] + public DialPressPayload Payload { get; private set; } + } +} diff --git a/barraider-sdtools/Communication/SDEvents/DialRotateEvent.cs b/barraider-sdtools/Communication/SDEvents/DialRotateEvent.cs new file mode 100644 index 0000000..cb0bbec --- /dev/null +++ b/barraider-sdtools/Communication/SDEvents/DialRotateEvent.cs @@ -0,0 +1,38 @@ +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Communication.SDEvents +{ + /// + /// Payload for dial rotation event + /// + public class DialRotateEvent : BaseEvent + { + /// + /// Action Name + /// + [JsonProperty("action")] + public string Action { get; private set; } + + /// + /// Unique Action UUID + /// + [JsonProperty("context")] + public string Context { get; private set; } + + /// + /// Device UUID key was pressed on + /// + [JsonProperty("device")] + public string Device { get; private set; } + + /// + /// Information on dial rotation + /// + [JsonProperty("payload")] + public DialRotatePayload Payload { get; private set; } + } +} diff --git a/barraider-sdtools/Communication/SDEvents/TouchTapEvent.cs b/barraider-sdtools/Communication/SDEvents/TouchTapEvent.cs new file mode 100644 index 0000000..a6f70bb --- /dev/null +++ b/barraider-sdtools/Communication/SDEvents/TouchTapEvent.cs @@ -0,0 +1,38 @@ +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Communication.SDEvents +{ + /// + /// Payload for touchpad press + /// + public class TouchpadPress : BaseEvent + { + /// + /// Action Name + /// + [JsonProperty("action")] + public string Action { get; private set; } + + /// + /// Unique Action UUID + /// + [JsonProperty("context")] + public string Context { get; private set; } + + /// + /// Device UUID key was pressed on + /// + [JsonProperty("device")] + public string Device { get; private set; } + + /// + /// Information on touchpad press + /// + [JsonProperty("payload")] + public TouchpadPressPayload Payload { get; private set; } + } +} diff --git a/barraider-sdtools/Communication/SDEvents/WillAppearEvent.cs b/barraider-sdtools/Communication/SDEvents/WillAppearEvent.cs index 1409462..99d9a6b 100644 --- a/barraider-sdtools/Communication/SDEvents/WillAppearEvent.cs +++ b/barraider-sdtools/Communication/SDEvents/WillAppearEvent.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; namespace BarRaider.SdTools.Communication.SDEvents { diff --git a/barraider-sdtools/Communication/SDEvents/WillDisappearEvent.cs b/barraider-sdtools/Communication/SDEvents/WillDisappearEvent.cs index c80c7bd..061a035 100644 --- a/barraider-sdtools/Communication/SDEvents/WillDisappearEvent.cs +++ b/barraider-sdtools/Communication/SDEvents/WillDisappearEvent.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; namespace BarRaider.SdTools.Communication.SDEvents { diff --git a/barraider-sdtools/Communication/StreamDeckConnection.cs b/barraider-sdtools/Communication/StreamDeckConnection.cs index 15c179c..8e871cf 100644 --- a/barraider-sdtools/Communication/StreamDeckConnection.cs +++ b/barraider-sdtools/Communication/StreamDeckConnection.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -36,6 +37,8 @@ public class StreamDeckConnection /// public string UUID { get; private set; } + #region Public Events + /// /// Raised when plugin is connected to stream deck app /// @@ -121,6 +124,23 @@ public class StreamDeckConnection /// public event EventHandler> OnSendToPlugin; + /// + /// Raised when a dial is rotated + /// + public event EventHandler> OnDialRotate; + + /// + /// Raised when a dial is pressed or unpressed + /// + public event EventHandler> OnDialPress; + + /// + /// Raised when the tochpad is pressed + /// + public event EventHandler> OnTouchpadPress; + + #endregion + internal StreamDeckConnection(int port, string uuid, string registerEvent) { this.Port = port; @@ -142,6 +162,13 @@ internal void Stop() cancelTokenSource.Cancel(); } + internal Task SendAsync(IMessage message) + { + return SendAsync(JsonConvert.SerializeObject(message)); + } + + #region Requests + internal Task SetTitleAsync(string title, string context, SDKTarget target, int? state) { return SendAsync(new SetTitleMessage(title, context, target, state)); @@ -224,11 +251,13 @@ internal Task OpenUrlAsync(Uri uri) return SendAsync(new OpenUrlMessage(uri)); } - internal Task SendAsync(IMessage message) + internal Task SetFeedbackAsync(Dictionary dictKeyValues, string context) { - return SendAsync(JsonConvert.SerializeObject(message)); + return SendAsync(new SetFeedbackMessage(dictKeyValues, context)); } + #endregion + #region Private Methods private async Task SendAsync(string text) @@ -319,37 +348,38 @@ private async Task ReceiveAsync() continue; } - _ = Task.Run(() => + try { - try - { - switch (evt.Event) - { - case EventTypes.KeyDown: OnKeyDown?.Invoke(this, new SDEventReceivedEventArgs(evt as KeyDownEvent)); break; - case EventTypes.KeyUp: OnKeyUp?.Invoke(this, new SDEventReceivedEventArgs(evt as KeyUpEvent)); break; - case EventTypes.WillAppear: OnWillAppear?.Invoke(this, new SDEventReceivedEventArgs(evt as WillAppearEvent)); break; - case EventTypes.WillDisappear: OnWillDisappear?.Invoke(this, new SDEventReceivedEventArgs(evt as WillDisappearEvent)); break; - case EventTypes.TitleParametersDidChange: OnTitleParametersDidChange?.Invoke(this, new SDEventReceivedEventArgs(evt as TitleParametersDidChangeEvent)); break; - case EventTypes.DeviceDidConnect: OnDeviceDidConnect?.Invoke(this, new SDEventReceivedEventArgs(evt as DeviceDidConnectEvent)); break; - case EventTypes.DeviceDidDisconnect: OnDeviceDidDisconnect?.Invoke(this, new SDEventReceivedEventArgs(evt as DeviceDidDisconnectEvent)); break; - case EventTypes.ApplicationDidLaunch: OnApplicationDidLaunch?.Invoke(this, new SDEventReceivedEventArgs(evt as ApplicationDidLaunchEvent)); break; - case EventTypes.ApplicationDidTerminate: OnApplicationDidTerminate?.Invoke(this, new SDEventReceivedEventArgs(evt as ApplicationDidTerminateEvent)); break; - case EventTypes.SystemDidWakeUp: OnSystemDidWakeUp?.Invoke(this, new SDEventReceivedEventArgs(evt as SystemDidWakeUpEvent)); break; - case EventTypes.DidReceiveSettings: OnDidReceiveSettings?.Invoke(this, new SDEventReceivedEventArgs(evt as DidReceiveSettingsEvent)); break; - case EventTypes.DidReceiveGlobalSettings: OnDidReceiveGlobalSettings?.Invoke(this, new SDEventReceivedEventArgs(evt as DidReceiveGlobalSettingsEvent)); break; - case EventTypes.PropertyInspectorDidAppear: OnPropertyInspectorDidAppear?.Invoke(this, new SDEventReceivedEventArgs(evt as PropertyInspectorDidAppearEvent)); break; - case EventTypes.PropertyInspectorDidDisappear: OnPropertyInspectorDidDisappear?.Invoke(this, new SDEventReceivedEventArgs(evt as PropertyInspectorDidDisappearEvent)); break; - case EventTypes.SendToPlugin: OnSendToPlugin?.Invoke(this, new SDEventReceivedEventArgs(evt as SendToPluginEvent)); break; - default: - Logger.Instance.LogMessage(TracingLevel.WARN, $"{this.GetType()} Unsupported Stream Deck event: {strBuffer}"); - break; - } - } - catch (Exception ex) + switch (evt.Event) { - Logger.Instance.LogMessage(TracingLevel.ERROR, $"{this.GetType()} Unhandled 3rd party exception when triggering {evt.Event} event. Exception: {ex}"); + case EventTypes.KeyDown: OnKeyDown?.Invoke(this, new SDEventReceivedEventArgs(evt as KeyDownEvent)); break; + case EventTypes.KeyUp: OnKeyUp?.Invoke(this, new SDEventReceivedEventArgs(evt as KeyUpEvent)); break; + case EventTypes.WillAppear: OnWillAppear?.Invoke(this, new SDEventReceivedEventArgs(evt as WillAppearEvent)); break; + case EventTypes.WillDisappear: OnWillDisappear?.Invoke(this, new SDEventReceivedEventArgs(evt as WillDisappearEvent)); break; + case EventTypes.TitleParametersDidChange: OnTitleParametersDidChange?.Invoke(this, new SDEventReceivedEventArgs(evt as TitleParametersDidChangeEvent)); break; + case EventTypes.DeviceDidConnect: OnDeviceDidConnect?.Invoke(this, new SDEventReceivedEventArgs(evt as DeviceDidConnectEvent)); break; + case EventTypes.DeviceDidDisconnect: OnDeviceDidDisconnect?.Invoke(this, new SDEventReceivedEventArgs(evt as DeviceDidDisconnectEvent)); break; + case EventTypes.ApplicationDidLaunch: OnApplicationDidLaunch?.Invoke(this, new SDEventReceivedEventArgs(evt as ApplicationDidLaunchEvent)); break; + case EventTypes.ApplicationDidTerminate: OnApplicationDidTerminate?.Invoke(this, new SDEventReceivedEventArgs(evt as ApplicationDidTerminateEvent)); break; + case EventTypes.SystemDidWakeUp: OnSystemDidWakeUp?.Invoke(this, new SDEventReceivedEventArgs(evt as SystemDidWakeUpEvent)); break; + case EventTypes.DidReceiveSettings: OnDidReceiveSettings?.Invoke(this, new SDEventReceivedEventArgs(evt as DidReceiveSettingsEvent)); break; + case EventTypes.DidReceiveGlobalSettings: OnDidReceiveGlobalSettings?.Invoke(this, new SDEventReceivedEventArgs(evt as DidReceiveGlobalSettingsEvent)); break; + case EventTypes.PropertyInspectorDidAppear: OnPropertyInspectorDidAppear?.Invoke(this, new SDEventReceivedEventArgs(evt as PropertyInspectorDidAppearEvent)); break; + case EventTypes.PropertyInspectorDidDisappear: OnPropertyInspectorDidDisappear?.Invoke(this, new SDEventReceivedEventArgs(evt as PropertyInspectorDidDisappearEvent)); break; + case EventTypes.SendToPlugin: OnSendToPlugin?.Invoke(this, new SDEventReceivedEventArgs(evt as SendToPluginEvent)); break; + case EventTypes.DialRotate: OnDialRotate?.Invoke(this, new SDEventReceivedEventArgs(evt as DialRotateEvent)); break; + case EventTypes.DialPress: OnDialPress?.Invoke(this, new SDEventReceivedEventArgs(evt as DialPressEvent)); break; + case EventTypes.TouchpadPress: OnTouchpadPress?.Invoke(this, new SDEventReceivedEventArgs(evt as TouchpadPress)); break; + default: + Logger.Instance.LogMessage(TracingLevel.WARN, $"{this.GetType()} Unsupported Stream Deck event: {strBuffer}"); + break; } - }); + } + catch (Exception ex) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"{this.GetType()} Unhandled 3rd party exception when triggering {evt.Event} event. Exception: {ex}"); + } + } } } diff --git a/barraider-sdtools/Communication/SDEvents/AppearancePayload.cs b/barraider-sdtools/Payloads/AppearancePayload.cs similarity index 79% rename from barraider-sdtools/Communication/SDEvents/AppearancePayload.cs rename to barraider-sdtools/Payloads/AppearancePayload.cs index 1ead753..507b869 100644 --- a/barraider-sdtools/Communication/SDEvents/AppearancePayload.cs +++ b/barraider-sdtools/Payloads/AppearancePayload.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace BarRaider.SdTools.Communication.SDEvents +namespace BarRaider.SdTools.Payloads { /// /// Payload for Apperance settings @@ -31,5 +31,11 @@ public class AppearancePayload /// [JsonProperty("isInMultiAction")] public bool IsInMultiAction { get; private set; } + + /// + /// Controller which issued the event + /// + [JsonProperty("controller")] + public string Controller { get; private set; } } } diff --git a/barraider-sdtools/Payloads/DialPressPayload.cs b/barraider-sdtools/Payloads/DialPressPayload.cs new file mode 100644 index 0000000..1b5ee97 --- /dev/null +++ b/barraider-sdtools/Payloads/DialPressPayload.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Payloads +{ + /// + /// Payload received when a dial is pressed or unpressed + /// + public class DialPressPayload + { + /// + /// Controller which issued the event + /// + [JsonProperty("controller")] + public string Controller { get; private set; } + + /// + /// Current event settings + /// + [JsonProperty("settings")] + public JObject Settings { get; private set; } + + /// + /// Coordinates of key on the stream deck + /// + [JsonProperty("coordinates")] + public KeyCoordinates Coordinates { get; private set; } + + /// + /// Boolean whether the dial is currently pressed or not + /// + [JsonProperty("pressed")] + public bool IsDialPressed { get; private set; } + + /// + /// Constructor + /// + /// + /// + /// + /// + public DialPressPayload(KeyCoordinates coordinates, JObject settings, string controller, bool isDialPressed) + { + Coordinates = coordinates; + Settings = settings; + Controller = controller; + IsDialPressed = isDialPressed; + } + + /// + /// Default constructor for serialization + /// + public DialPressPayload() { } + } +} diff --git a/barraider-sdtools/Payloads/DialRotatePayload.cs b/barraider-sdtools/Payloads/DialRotatePayload.cs new file mode 100644 index 0000000..d19dc3c --- /dev/null +++ b/barraider-sdtools/Payloads/DialRotatePayload.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Payloads +{ + /// + /// Payload received when a dial is rotated + /// + public class DialRotatePayload + { + /// + /// Controller which issued the event + /// + [JsonProperty("controller")] + public string Controller { get; private set; } + + /// + /// Current event settings + /// + [JsonProperty("settings")] + public JObject Settings { get; private set; } + + /// + /// Coordinates of key on the stream deck + /// + [JsonProperty("coordinates")] + public KeyCoordinates Coordinates { get; private set; } + + /// + /// Number of ticks rotated. Positive is to the right, negative to the left + /// + [JsonProperty("ticks")] + public int Ticks { get; private set; } + + /// + /// Boolean whether the dial is currently pressed or not + /// + [JsonProperty("pressed")] + public bool IsDialPressed { get; private set; } + + /// + /// Constructor + /// + /// + /// + /// + /// + public DialRotatePayload(KeyCoordinates coordinates, JObject settings, string controller, int ticks, bool isDialPressed) + { + Coordinates = coordinates; + Settings = settings; + Controller = controller; + Ticks = ticks; + IsDialPressed = isDialPressed; + } + + /// + /// Default constructor for serialization + /// + public DialRotatePayload() { } + } +} diff --git a/barraider-sdtools/Payloads/KeyPayload.cs b/barraider-sdtools/Payloads/KeyPayload.cs index e9259f4..a97a058 100644 --- a/barraider-sdtools/Payloads/KeyPayload.cs +++ b/barraider-sdtools/Payloads/KeyPayload.cs @@ -9,19 +9,19 @@ namespace BarRaider.SdTools public class KeyPayload { /// - /// Current plugin settings + /// Current event settings /// [JsonProperty("settings")] public JObject Settings { get; private set; } /// - /// Location of plugin on the stream deck + /// Coordinates of key on the stream deck /// [JsonProperty("coordinates")] public KeyCoordinates Coordinates { get; private set; } /// - /// Current plugin state + /// Current key state /// [JsonProperty("state")] public uint State { get; private set; } diff --git a/barraider-sdtools/Payloads/TouchpadPressPayload.cs b/barraider-sdtools/Payloads/TouchpadPressPayload.cs new file mode 100644 index 0000000..64aa4b7 --- /dev/null +++ b/barraider-sdtools/Payloads/TouchpadPressPayload.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BarRaider.SdTools.Payloads +{ + /// + /// Payload received when the touchpad (above the dials) is pressed + /// + public class TouchpadPressPayload + { + /// + /// Controller which issued the event + /// + [JsonProperty("controller")] + public string Controller { get; private set; } + + /// + /// Current event settings + /// + [JsonProperty("settings")] + public JObject Settings { get; private set; } + + /// + /// Coordinates of key on the stream deck + /// + [JsonProperty("coordinates")] + public KeyCoordinates Coordinates { get; private set; } + + /// + /// Boolean whether it was a long press or not + /// + [JsonProperty("hold")] + public bool IsLongPress { get; private set; } + + /// + /// Position on touchpad which was pressed + /// + [JsonProperty("tapPos")] + public int[] TapPosition { get; private set; } + + + /// + /// Constructor + /// + /// + /// + /// + /// + public TouchpadPressPayload(KeyCoordinates coordinates, JObject settings, string controller, bool isLongPress, int[] tapPosition) + { + Coordinates = coordinates; + Settings = settings; + Controller = controller; + IsLongPress = isLongPress; + TapPosition = tapPosition; + } + + /// + /// Default constructor for serialization + /// + public TouchpadPressPayload() { } + } +} diff --git a/barraider-sdtools/StreamDeckInfo/DeviceType.cs b/barraider-sdtools/StreamDeckInfo/DeviceType.cs index 61cb861..af05a84 100644 --- a/barraider-sdtools/StreamDeckInfo/DeviceType.cs +++ b/barraider-sdtools/StreamDeckInfo/DeviceType.cs @@ -39,5 +39,14 @@ public enum DeviceType /// StreamDeckPedal = 5, + /// + /// Corsair CUE SDK (?) + /// + CorsairCueSDK = 6, + + /// + /// Stream Deck+ + /// + StreamDeckPlus = 7, } } diff --git a/barraider-sdtools/Tools/PayloadExtensionMethods.cs b/barraider-sdtools/Tools/PayloadExtensionMethods.cs index ff690df..d76068a 100644 --- a/barraider-sdtools/Tools/PayloadExtensionMethods.cs +++ b/barraider-sdtools/Tools/PayloadExtensionMethods.cs @@ -1,5 +1,4 @@ -using BarRaider.SdTools.Communication.SDEvents; -using BarRaider.SdTools.Payloads; +using BarRaider.SdTools.Payloads; using System; using System.Collections.Generic; using System.Text; @@ -43,5 +42,32 @@ internal static string ToStringEx(this ReceivedGlobalSettingsPayload gsp) } return $"Settings: {gsp.Settings}"; } + + internal static string ToStringEx(this DialRotatePayload drp) + { + if (drp == null) + { + return "DialRotatePayload is null!"; + } + return $"Controller: {drp.Controller} Ticks: {drp.Ticks} Coordinates: ({drp.Coordinates?.Row},{drp.Coordinates?.Column}) Settings: {drp.Settings}"; + } + + internal static string ToStringEx(this DialPressPayload dpp) + { + if (dpp == null) + { + return "DialPressPayload is null!"; + } + return $"Controller: {dpp.Controller} IsDialPressed: {dpp.IsDialPressed} Coordinates: ({dpp.Coordinates?.Row},{dpp.Coordinates?.Column}) Settings: {dpp.Settings}"; + } + + internal static string ToStringEx(this TouchpadPressPayload tpp) + { + if (tpp == null) + { + return "KeyPayload is null!"; + } + return $"Controller: {tpp.Controller} LongPress: {tpp.IsLongPress} Position: {(tpp.TapPosition?.Length == 2 ? tpp.TapPosition[0].ToString() + "," + tpp.TapPosition[1] : "Invalid")} Coordinates: ({tpp.Coordinates?.Row},{tpp.Coordinates?.Column}) Settings: {tpp.Settings}"; + } } } diff --git a/barraider-sdtools/Tools/PluginBase.cs b/barraider-sdtools/Tools/PluginBase.cs deleted file mode 100644 index 1d6999f..0000000 --- a/barraider-sdtools/Tools/PluginBase.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using BarRaider.SdTools.Payloads; -using Newtonsoft.Json.Linq; - -namespace BarRaider.SdTools -{ - - /// - /// Main abstract class your plugin should derive from - /// Holds implementation for all the basic functions - /// If you're missing an event, you can register to it from the Connection.StreamDeckConnection object - /// - public abstract class PluginBase : IDisposable - { - /// - /// Called when a Stream Deck key is pressed - /// - public abstract void KeyPressed(KeyPayload payload); - - /// - /// Called when a Stream Deck key is released - /// - public abstract void KeyReleased(KeyPayload payload); - - /// - /// Called when the PropertyInspector has new settings - /// - /// - public abstract void ReceivedSettings(ReceivedSettingsPayload payload); - - /// - /// Called when GetGlobalSettings is called. - /// - /// - public abstract void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload); - - /// - /// Called every second - /// Logic for displaying title/image can go here - /// - public abstract void OnTick(); - - /// - /// Abstract method Called when the plugin is disposed - /// - public abstract void Dispose(); - - /// - /// Main iDisposable Dispose function - /// - public void Destroy() - { - Dispose(); - if (Connection != null) - { - Connection.Dispose(); - } - } - - /// - /// Connection object which handles your communication with the Stream Deck app - /// - protected ISDConnection Connection { get; private set; } - - /// - /// Constructor for PluginBase. Receives the communication and plugin settings - /// Note that the settings object is not used by the base and should be consumed by the deriving class. - /// Usually, a private class inside the deriving class is created which stores the settings - /// Example for settings usage: - /// * if (payload.Settings == null || payload.Settings.Count == 0) - /// * { - /// * // Create default settings - /// * } - /// * else - /// * { - /// this.settings = payload.Settings.ToObject(); - /// * } - /// - /// - /// Communication module with Stream Deck - /// Plugin settings - NOTE: Not used in base class, should be consumed by deriving class -#pragma warning disable IDE0060 // Remove unused parameter - public PluginBase(ISDConnection connection, InitialPayload payload) -#pragma warning restore IDE0060 // Remove unused parameter - { - Connection = connection; - } - } -} \ No newline at end of file diff --git a/barraider-sdtools/Wrappers/PluginActionId.cs b/barraider-sdtools/Wrappers/PluginActionId.cs index a9d24a3..58c4d7f 100644 --- a/barraider-sdtools/Wrappers/PluginActionId.cs +++ b/barraider-sdtools/Wrappers/PluginActionId.cs @@ -31,9 +31,9 @@ public Type PluginBaseType } private set { - if (value == null || !value.IsSubclassOf(typeof(PluginBase))) + if (value == null || (!typeof(IKeypadPlugin).IsAssignableFrom(value) && !typeof(IEncoderPlugin).IsAssignableFrom(value))) { - throw new NotSupportedException("Class type set to PluginBaseType does not inherit PluginBase"); + throw new NotSupportedException("Class type set to PluginBaseType does not inherit IKeypadPlugin or IEncoderPlugin"); } pluginBaseType = value; } diff --git a/barraider-sdtools/streamdeck-tools.xml b/barraider-sdtools/streamdeck-tools.xml index b61028e..542e000 100644 --- a/barraider-sdtools/streamdeck-tools.xml +++ b/barraider-sdtools/streamdeck-tools.xml @@ -27,6 +27,134 @@ + + + Main abstract class your plugin should derive from for dials (not keys) + For keys use the KeyBase or KeyAndEncoderBase + Holds implementation for all the basic functions + If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + + + + + Called when the dial is rotated + + + + + Called when the Dial is pressed or released + + + + + Called when the touchpad (above the dials) is pressed + + + + + Called when the PropertyInspector has new settings + + + + + + Called when GetGlobalSettings is called. + + + + + + Called every second + Logic for displaying title/image can go here + + + + + Abstract method Called when the plugin is disposed + + + + + Main iDisposable Dispose function + + + + + Connection object which handles your communication with the Stream Deck app + + + + + Constructor for PluginBase. Receives the communication and plugin settings + Note that the settings object is not used by the base and should be consumed by the deriving class. + Usually, a private class inside the deriving class is created which stores the settings + Example for settings usage: + * if (payload.Settings == null || payload.Settings.Count == 0) + * { + * // Create default settings + * } + * else + * { + this.settings = payload.Settings.ToObject(); + * } + + + Communication module with Stream Deck + Plugin settings - NOTE: Not used in base class, should be consumed by deriving class + + + + Called when the PropertyInspector has new settings + + + + + + Called when GetGlobalSettings is called. + + + + + + Called every second + Logic for displaying title/image can go here + + + + + Internal function used by StreamDeckTools to prevent memory leaks + + + + + Interface used to capture dial/encoder events + + + + + Called when the dial is rotated + + + + + Called when the Dial is pressed or released + + + + + Called when the touchpad (above the dials) is pressed + + + + + Interface used to capture key events + + + + + Called when a Stream Deck key is released + + Interface for a Stream Deck connection @@ -204,6 +332,20 @@ + + + Sets the values of touchpad layouts items + + Dictionary holding the layout item keys and values you want to change + + + + + Sets the value of a single touchpad layout item + + + + An opaque value identifying the plugin. This value is received during the Registration procedure @@ -219,6 +361,228 @@ StreamDeckConnection object, initialized based on the args received when launching the program + + + Main abstract class your plugin should derive from for keys (not dials) + For dials use the EncoderBase or KeyAndEncoderBase + Holds implementation for all the basic functions + If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + + + + + Called when the dial is rotated + + + + + Called when the Dial is pressed or released + + + + + Called when the touchpad (above the dials) is pressed + + + + + Called when a Stream Deck key is pressed + + + + + Called when a Stream Deck key is released + + + + + Called when the PropertyInspector has new settings + + + + + + Called when GetGlobalSettings is called. + + + + + + Called every second + Logic for displaying title/image can go here + + + + + Abstract method Called when the plugin is disposed + + + + + Main iDisposable Dispose function + + + + + Connection object which handles your communication with the Stream Deck app + + + + + Constructor for PluginBase. Receives the communication and plugin settings + Note that the settings object is not used by the base and should be consumed by the deriving class. + Usually, a private class inside the deriving class is created which stores the settings + Example for settings usage: + * if (payload.Settings == null || payload.Settings.Count == 0) + * { + * // Create default settings + * } + * else + * { + this.settings = payload.Settings.ToObject(); + * } + + + Communication module with Stream Deck + Plugin settings - NOTE: Not used in base class, should be consumed by deriving class + + + + Main abstract class your plugin should derive from for keys (not dials) + For dials use the EncoderBase or KeyAndEncoderBase + Holds implementation for all the basic functions + If you're missing an event, you can register to it from the Connection.StreamDeckConnection object + + + + + Called when a Stream Deck key is pressed + + + + + Called when a Stream Deck key is released + + + + + Called when the PropertyInspector has new settings + + + + + + Called when GetGlobalSettings is called. + + + + + + Called every second + Logic for displaying title/image can go here + + + + + Abstract method Called when the plugin is disposed + + + + + Main iDisposable Dispose function + + + + + Connection object which handles your communication with the Stream Deck app + + + + + Constructor for PluginBase. Receives the communication and plugin settings + Note that the settings object is not used by the base and should be consumed by the deriving class. + Usually, a private class inside the deriving class is created which stores the settings + Example for settings usage: + * if (payload.Settings == null || payload.Settings.Count == 0) + * { + * // Create default settings + * } + * else + * { + this.settings = payload.Settings.ToObject(); + * } + + + Communication module with Stream Deck + Plugin settings - NOTE: Not used in base class, should be consumed by deriving class + + + + Obsolete! Use `KeypadBase` moving forward, or choose one of the other options: `EncoderBase`, `KeyAndEncoderBase` + + + + + Called when a Stream Deck key is pressed + + + + + Called when a Stream Deck key is released + + + + + Called when the PropertyInspector has new settings + + + + + + Called when GetGlobalSettings is called. + + + + + + Called every second + Logic for displaying title/image can go here + + + + + Abstract method Called when the plugin is disposed + + + + + Main iDisposable Dispose function + + + + + Connection object which handles your communication with the Stream Deck app + + + + + Constructor for PluginBase. Receives the communication and plugin settings + Note that the settings object is not used by the base and should be consumed by the deriving class. + Usually, a private class inside the deriving class is created which stores the settings + Example for settings usage: + * if (payload.Settings == null || payload.Settings.Count == 0) + * { + * // Create default settings + * } + * else + * { + this.settings = payload.Settings.ToObject(); + * } + + + Communication module with Stream Deck + Plugin settings - NOTE: Not used in base class, should be consumed by deriving class + Connection object which handles your communication with the Stream Deck app @@ -279,6 +643,44 @@ Event received when the computer wakes up + + + An opaque value identifying the plugin. This value is received during the Registration procedure + + + + + An opaque value identifying the device the plugin is launched on. + + + + + StreamDeckConnection object, initialized based on the args received when launching the program + + + + + Public constructor, a StreamDeckConnection object is required along with the current action and context IDs + These will be used to correctly communicate with the StreamDeck App + + + + + + + /// + + + + Dispose (Destructor) function + + + + + Gets the Stream Deck device's info + + + Send settings to the PropertyInspector @@ -373,12 +775,6 @@ - - - Gets the Stream Deck device's info - - - Tells Stream Deck to return the current plugin settings via the ReceivedSettings function @@ -406,37 +802,19 @@ - + - An opaque value identifying the plugin. This value is received during the Registration procedure + Sets the values of touchpad layouts items + + - - - An opaque value identifying the device the plugin is launched on. - - - - - StreamDeckConnection object, initialized based on the args received when launching the program - - - - - Public constructor, a StreamDeckConnection object is required along with the current action and context IDs - These will be used to correctly communicate with the StreamDeck App - - - - - - - /// - - + - Dispose (Destructor) function + Sets the value of a single touchpad layout item + + @@ -475,31 +853,6 @@ Empty payload in event - - - Payload for Apperance settings - - - - - Additional settings - - - - - Coordinates of key pressed - - - - - State of key - - - - - Is action in MultiAction - - Payload for ApplicationDidLaunch event @@ -560,6 +913,56 @@ UUID of device that was disconnected + + + Payload for Dial press/unpress event + + + + + Action Name + + + + + Unique Action UUID + + + + + Device UUID key was pressed on + + + + + Information on dial rotation + + + + + Payload for dial rotation event + + + + + Action Name + + + + + Unique Action UUID + + + + + Device UUID key was pressed on + + + + + Information on dial rotation + + Payload for DidReceiveGlobalSettings Event @@ -735,6 +1138,31 @@ Title settings + + + Payload for touchpad press + + + + + Action Name + + + + + Unique Action UUID + + + + + Device UUID key was pressed on + + + + + Information on touchpad press + + Payload for WillAppearEvent event @@ -905,6 +1333,21 @@ Raised when a payload is sent to the plugin from the PI + + + Raised when a dial is rotated + + + + + Raised when a dial is pressed or unpressed + + + + + Raised when the tochpad is pressed + + Payload for ApplicationDidLaunch event @@ -1102,6 +1545,36 @@ + + + Payload for Apperance settings + + + + + Additional settings + + + + + Coordinates of key pressed + + + + + State of key + + + + + Is action in MultiAction + + + + + Controller which issued the event + + ApplicationPayload @@ -1118,6 +1591,89 @@ + + + Payload received when a dial is pressed or unpressed + + + + + Controller which issued the event + + + + + Current event settings + + + + + Coordinates of key on the stream deck + + + + + Boolean whether the dial is currently pressed or not + + + + + Constructor + + + + + + + + + Default constructor for serialization + + + + + Payload received when a dial is rotated + + + + + Controller which issued the event + + + + + Current event settings + + + + + Coordinates of key on the stream deck + + + + + Number of ticks rotated. Positive is to the right, negative to the left + + + + + Boolean whether the dial is currently pressed or not + + + + + Constructor + + + + + + + + + Default constructor for serialization + + Class holding all the information passed to the plugin when the program was launched @@ -1238,6 +1794,50 @@ Color of title + + + Payload received when the touchpad (above the dials) is pressed + + + + + Controller which issued the event + + + + + Current event settings + + + + + Coordinates of key on the stream deck + + + + + Boolean whether it was a long press or not + + + + + Position on touchpad which was pressed + + + + + Constructor + + + + + + + + + Default constructor for serialization + + Payload received during the plugin's constructor @@ -1285,17 +1885,17 @@ - Current plugin settings + Current event settings - Location of plugin on the stream deck + Coordinates of key on the stream deck - Current plugin state + Current key state @@ -1809,75 +2409,6 @@ - - - Main abstract class your plugin should derive from - Holds implementation for all the basic functions - If you're missing an event, you can register to it from the Connection.StreamDeckConnection object - - - - - Called when a Stream Deck key is pressed - - - - - Called when a Stream Deck key is released - - - - - Called when the PropertyInspector has new settings - - - - - - Called when GetGlobalSettings is called. - - - - - - Called every second - Logic for displaying title/image can go here - - - - - Abstract method Called when the plugin is disposed - - - - - Main iDisposable Dispose function - - - - - Connection object which handles your communication with the Stream Deck app - - - - - Constructor for PluginBase. Receives the communication and plugin settings - Note that the settings object is not used by the base and should be consumed by the deriving class. - Usually, a private class inside the deriving class is created which stores the settings - Example for settings usage: - * if (payload.Settings == null || payload.Settings.Count == 0) - * { - * // Create default settings - * } - * else - * { - this.settings = payload.Settings.ToObject(); - * } - - - Communication module with Stream Deck - Plugin settings - NOTE: Not used in base class, should be consumed by deriving class - Helper class for generating random numbers