diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/Actions/DeviceVolumeAction.cs b/Windows/MassiveKnob.Plugin.CoreAudio/Actions/DeviceVolumeAction.cs new file mode 100644 index 0000000..93829f5 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/Actions/DeviceVolumeAction.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Controls; + +namespace MassiveKnob.Plugin.CoreAudio.Actions +{ + public class DeviceVolumeAction : IMassiveKnobAction + { + public Guid ActionId { get; } = new Guid("aabd329c-8be5-4d1e-90ab-5114143b21dd"); + public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.InputAnalog; + public string Name { get; } = "Set volume"; + public string Description { get; } = "Sets the volume for the selected device, regardless of the current default device."; + + + public IMassiveKnobActionInstance Create(IMassiveKnobActionContext context) + { + return new Instance(context); + } + + + private class Instance : IMassiveKnobAnalogAction + { + private readonly Settings settings; + + + public Instance(IMassiveKnobContext context) + { + settings = context.GetSettings(); + } + + + public void Dispose() + { + } + + + public UserControl CreateSettingsControl() + { + return null; + } + + + public ValueTask AnalogChanged(byte value) + { + // TODO set volume + return default; + } + } + + + private class Settings + { + + } + } +} diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnob.Plugin.CoreAudio.csproj b/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnob.Plugin.CoreAudio.csproj new file mode 100644 index 0000000..cb62ecd --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnob.Plugin.CoreAudio.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {5BD5E2F2-9923-4F74-AC69-ACDA0B847937} + Library + Properties + MassiveKnob.Plugin.CoreAudio + MassiveKnob.Plugin.CoreAudio + v4.7.2 + 512 + true + + + true + full + false + $(localappdata)\MassiveKnob\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + DeviceVolumeActionSettings.xaml + + + + + {a1298be4-1d23-416c-8c56-fc9264487a95} + MassiveKnob.Plugin + + + + + 4.5.4 + + + + + + Designer + MSBuild:Compile + + + + \ No newline at end of file diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnobCoreAudioPlugin.cs b/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnobCoreAudioPlugin.cs new file mode 100644 index 0000000..1e8cc5e --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/MassiveKnobCoreAudioPlugin.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using MassiveKnob.Plugin.CoreAudio.Actions; + +namespace MassiveKnob.Plugin.CoreAudio +{ + [MassiveKnobPlugin] + public class MassiveKnobCoreAudioPlugin : IMassiveKnobActionPlugin + { + public Guid PluginId { get; } = new Guid("eaa5d3f8-8f9b-4a4b-8e29-827228d23e95"); + public string Name { get; } = "Windows Core Audio"; + public string Description { get; } = "Included with Massive Knob by default. Provides volume control per device and default device switching."; + public string Author { get; } = "Mark van Renswoude "; + public string Url { get; } = "https://www.github.com/MvRens/MassiveKnob/"; + + public IEnumerable Actions { get; } = new IMassiveKnobAction[] + { + new DeviceVolumeAction() + }; + } +} diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/Properties/AssemblyInfo.cs b/Windows/MassiveKnob.Plugin.CoreAudio/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b37ec34 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MassiveKnob.Plugin.CoreAudio")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MassiveKnob.Plugin.CoreAudio")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5bd5e2f2-9923-4f74-ac69-acda0b847937")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml b/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml new file mode 100644 index 0000000..86aa8c4 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml.cs b/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml.cs new file mode 100644 index 0000000..6c1ed5b --- /dev/null +++ b/Windows/MassiveKnob.Plugin.CoreAudio/Settings/DeviceVolumeActionSettings.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace MassiveKnob.Plugin.CoreAudio.Settings +{ + /// + /// Interaction logic for DeviceVolumeActionSettings.xaml + /// + public partial class DeviceVolumeActionSettings : UserControl + { + public DeviceVolumeActionSettings() + { + InitializeComponent(); + } + } +} diff --git a/Windows/MassiveKnob.Plugin.MockDevice/Devices/MockDevice.cs b/Windows/MassiveKnob.Plugin.MockDevice/Devices/MockDevice.cs new file mode 100644 index 0000000..d402c4d --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/Devices/MockDevice.cs @@ -0,0 +1,45 @@ +using System; +using System.Windows.Controls; +using MassiveKnob.Plugin.MockDevice.Settings; + +namespace MassiveKnob.Plugin.MockDevice.Devices +{ + public class MockDevice : IMassiveKnobDevice + { + public Guid DeviceId { get; } = new Guid("e1a4977a-abf4-4c75-a17d-fd8d3a8451ff"); + public string Name { get; } = "Mock device"; + public string Description { get; } = "Emulates the actual device but does not communicate with anything."; + + public IMassiveKnobDeviceInstance Create(IMassiveKnobContext context) + { + return new Instance(context); + } + + + private class Instance : IMassiveKnobDeviceInstance + { + public Instance(IMassiveKnobContext context) + { + // TODO read settings + } + + + public void Dispose() + { + } + + + public UserControl CreateSettingsControl() + { + // TODO pass context + return new MockDeviceSettings(); + } + } + + + private class Settings + { + // TODO interval, etc. + } + } +} diff --git a/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnob.Plugin.MockDevice.csproj b/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnob.Plugin.MockDevice.csproj new file mode 100644 index 0000000..d9a3c45 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnob.Plugin.MockDevice.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {674DE974-B134-4DB5-BFAF-7BC3D05E16DE} + Library + Properties + MassiveKnob.Plugin.MockDevice + MassiveKnob.Plugin.MockDevice + v4.7.2 + 512 + true + + + true + full + false + $(localappdata)\MassiveKnob\Plugins\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + MockDeviceSettings.xaml + + + + + {A1298BE4-1D23-416C-8C56-FC9264487A95} + MassiveKnob.Plugin + + + + + + Designer + MSBuild:Compile + + + + \ No newline at end of file diff --git a/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnobMockDevicePlugin.cs b/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnobMockDevicePlugin.cs new file mode 100644 index 0000000..feb9eb5 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/MassiveKnobMockDevicePlugin.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace MassiveKnob.Plugin.MockDevice +{ + [MassiveKnobPlugin] + public class MassiveKnobMockDevicePlugin : IMassiveKnobDevicePlugin + { + public Guid PluginId { get; } = new Guid("85f04232-d70f-494c-94a2-41452591ffb3"); + public string Name { get; } = "Mock Device"; + public string Description { get; } = "Emulates the actual device but does not communicate with anything."; + public string Author { get; } = "Mark van Renswoude "; + public string Url { get; } = "https://www.github.com/MvRens/MassiveKnob/"; + + public IEnumerable Devices { get; } = new IMassiveKnobDevice[] + { + new Devices.MockDevice() + }; + } +} diff --git a/Windows/MassiveKnob.Plugin.MockDevice/Properties/AssemblyInfo.cs b/Windows/MassiveKnob.Plugin.MockDevice/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..63e0908 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MassiveKnob.Plugin.MockDevice")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MassiveKnob.Plugin.MockDevice")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("674de974-b134-4db5-bfaf-7bc3d05e16de")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml b/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml new file mode 100644 index 0000000..15b1798 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Number of knobs + + + Randomly change the volume + + diff --git a/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml.cs b/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml.cs new file mode 100644 index 0000000..843c196 --- /dev/null +++ b/Windows/MassiveKnob.Plugin.MockDevice/Settings/MockDeviceSettings.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace MassiveKnob.Plugin.MockDevice.Settings +{ + /// + /// Interaction logic for MockDeviceSettings.xaml + /// + public partial class MockDeviceSettings : UserControl + { + public MockDeviceSettings() + { + InitializeComponent(); + } + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobAction.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobAction.cs new file mode 100644 index 0000000..ba3f3fc --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobAction.cs @@ -0,0 +1,59 @@ +using System; + +namespace MassiveKnob.Plugin +{ + /// + /// Determines the type of control this action can be assigned to. + /// + [Flags] + public enum MassiveKnobActionType + { + /// + /// Can be assigned to a potentiometer. The action instance must implement IMassiveKnobAnalogAction. + /// + InputAnalog = 1 << 0, + + /// + /// Can be assigned to a button or switch. The action instance must implement IMassiveKnobDigitalAction. + /// + InputDigital = 1 << 1, + + /// + /// Can be assigned to an output, like an LED or relay. + /// + OutputSignal = 1 << 2 + } + + + /// + /// Provides information about an action which can be assigned to a knob or button. + /// + public interface IMassiveKnobAction + { + /// + /// Unique identifier for the action. + /// + Guid ActionId { get; } + + /// + /// Determines the type of control this action can be assigned to. + /// + MassiveKnobActionType ActionType { get; } + + /// + /// The name of the action as shown in the action list when assigning to a knob or button. + /// + string Name { get; } + + /// + /// A short description of the functionality provided by the action. + /// + string Description { get; } + + /// + /// Called when an action is bound to a knob or button to create an instance of the action. + /// + /// Provides an interface to the Massive Knob settings and device. Can be stored until the action instance is disposed. + IMassiveKnobActionInstance Create(IMassiveKnobActionContext context); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobActionContext.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobActionContext.cs new file mode 100644 index 0000000..9f08a67 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobActionContext.cs @@ -0,0 +1,12 @@ +namespace MassiveKnob.Plugin +{ + /// + public interface IMassiveKnobActionContext : IMassiveKnobContext + { + /// + /// Sets the state of the signal. Only valid for OutputSignal action types, will raise an exception otherwise. + /// + /// Whether the signal is on or off. + void SetSignal(bool on); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobActionInstance.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobActionInstance.cs new file mode 100644 index 0000000..3973f60 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobActionInstance.cs @@ -0,0 +1,18 @@ +using System; +using System.Windows.Controls; + +namespace MassiveKnob.Plugin +{ + /// + /// Base interface for actions assigned to a knob or button. Implement one of the descendant + /// interfaces to provide input or output. + /// + public interface IMassiveKnobActionInstance : IDisposable + { + /// + /// Called when an action should display it's settings. Assume the width is variable, height is + /// determined by the UserControl. Return null to indicate there are no settings for this action. + /// + UserControl CreateSettingsControl(); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobActionPlugin.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobActionPlugin.cs new file mode 100644 index 0000000..255defc --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobActionPlugin.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MassiveKnob.Plugin +{ + /// + /// Implemented by plugins supporting actions which can be assigned to knobs or buttons. + /// + public interface IMassiveKnobActionPlugin : IMassiveKnobPlugin + { + /// + /// A list of actions supported by the plugin which can be assigned to knobs or buttons. + /// + IEnumerable Actions { get; } + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobAnalogAction.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobAnalogAction.cs new file mode 100644 index 0000000..15cfae1 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobAnalogAction.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace MassiveKnob.Plugin +{ + /// + /// Required to be implemented for Action type InputAnalog. Receives an update when a knob's position changes. + /// + public interface IMassiveKnobAnalogAction : IMassiveKnobActionInstance + { + /// + /// Called when a knob's position changes. + /// + /// The new value. Range is 0 to 100. + ValueTask AnalogChanged(byte value); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobContext.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobContext.cs new file mode 100644 index 0000000..fdecb2a --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobContext.cs @@ -0,0 +1,22 @@ +namespace MassiveKnob.Plugin +{ + /// + /// Provides an interface to the Massive Knob settings and device. + /// + public interface IMassiveKnobContext + { + /// + /// Reads the settings for the current action instance. + /// + /// The class type to be deserialized using Newtonsoft.Json. + /// The deserialized class if settings were previously stored, or a new instance of the class otherwise. + T GetSettings() where T : class, new(); + + /// + /// Stores the settings for the current action instance. + /// + /// The class type to be serialized using Newtonsoft.Json. + /// The object to be serialized using Newtonsoft.Json. + void SetSettings(T settings) where T : class, new(); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobDevice.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobDevice.cs new file mode 100644 index 0000000..f949076 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobDevice.cs @@ -0,0 +1,31 @@ +using System; + +namespace MassiveKnob.Plugin +{ + /// + /// Contains information about the device supported by this plugin. + /// + public interface IMassiveKnobDevice + { + /// + /// Unique identifier for the device. + /// + Guid DeviceId { get; } + + /// + /// The name of the action as shown in the action list when assigning to a knob or button. + /// + string Name { get; } + + /// + /// A short description of the functionality provided by the action. + /// + string Description { get; } + + /// + /// Called when the device is selected. + /// + /// Provides an interface to the Massive Knob settings and device. Can be stored until the device instance is disposed. + IMassiveKnobDeviceInstance Create(IMassiveKnobContext context); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobDeviceInstance.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobDeviceInstance.cs new file mode 100644 index 0000000..d8307df --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobDeviceInstance.cs @@ -0,0 +1,17 @@ +using System; +using System.Windows.Controls; + +namespace MassiveKnob.Plugin +{ + /// + /// Represents a connection to a Massive Knob device. + /// + public interface IMassiveKnobDeviceInstance : IDisposable + { + /// + /// Called when a device should display it's settings. Assume the width is variable, height is + /// determined by the UserControl. Return null to indicate there are no settings for this device. + /// + UserControl CreateSettingsControl(); + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobDigitalAction.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobDigitalAction.cs new file mode 100644 index 0000000..fb3992f --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobDigitalAction.cs @@ -0,0 +1,9 @@ +namespace MassiveKnob.Plugin +{ + /// + /// Required to be implemented for Action type InputDigital. Receives an update when a knob's position changes. + /// + public interface IMassiveKnobDigitalAction : IMassiveKnobActionInstance + { + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobHardwarePlugin.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobHardwarePlugin.cs new file mode 100644 index 0000000..b20c651 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobHardwarePlugin.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MassiveKnob.Plugin +{ + /// + /// Implemented by plugins which interface with Massive Knob device. + /// + public interface IMassiveKnobDevicePlugin : IMassiveKnobPlugin + { + /// + /// A list of devices supported by the plugin. + /// + IEnumerable Devices { get; } + } +} diff --git a/Windows/MassiveKnob.Plugin/IMassiveKnobPlugin.cs b/Windows/MassiveKnob.Plugin/IMassiveKnobPlugin.cs new file mode 100644 index 0000000..d7b15a0 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/IMassiveKnobPlugin.cs @@ -0,0 +1,36 @@ +using System; + +namespace MassiveKnob.Plugin +{ + /// + /// Minimum required interface to implement for writing a Massive Knob plugin. + /// Implement one of the various descendant interfaces to provide more functionality. + /// + public interface IMassiveKnobPlugin + { + /// + /// Unique identifier for the plugin. + /// + Guid PluginId { get; } + + /// + /// The name of the plugin as shown in the plugin list. + /// + string Name { get; } + + /// + /// A short description of the functionality provided by the plugin. + /// + string Description { get; } + + /// + /// The name of the author(s) of the plugin. + /// + string Author { get; } + + /// + /// URL to the plugin's website. + /// + string Url { get; } + } +} diff --git a/Windows/MassiveKnob.Plugin/MassiveKnob.Plugin.csproj b/Windows/MassiveKnob.Plugin/MassiveKnob.Plugin.csproj new file mode 100644 index 0000000..8cdc5b2 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/MassiveKnob.Plugin.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {A1298BE4-1D23-416C-8C56-FC9264487A95} + Library + Properties + MassiveKnob.Plugin + MassiveKnob.Plugin + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\MassiveKnob.Plugin.xml + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\MassiveKnob.Plugin.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4.5.4 + + + + \ No newline at end of file diff --git a/Windows/MassiveKnob.Plugin/MassiveKnobPluginAttribute.cs b/Windows/MassiveKnob.Plugin/MassiveKnobPluginAttribute.cs new file mode 100644 index 0000000..17c16b3 --- /dev/null +++ b/Windows/MassiveKnob.Plugin/MassiveKnobPluginAttribute.cs @@ -0,0 +1,17 @@ +using System; +using JetBrains.Annotations; + +// ReSharper disable once UnusedMember.Global - public API + +namespace MassiveKnob.Plugin +{ + /// + /// Attach this attribute to a class to register it as a Massive Knob plugin. + /// The class must implement IMassiveKnobPlugin. + /// + [AttributeUsage(AttributeTargets.Class)] + [MeansImplicitUse] + public class MassiveKnobPluginAttribute : Attribute + { + } +} diff --git a/Windows/MassiveKnob.Plugin/Properties/AssemblyInfo.cs b/Windows/MassiveKnob.Plugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dddcccd --- /dev/null +++ b/Windows/MassiveKnob.Plugin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MassiveKnob.PluginHost")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MassiveKnob.PluginHost")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a1298be4-1d23-416c-8c56-fc9264487a95")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Windows/MassiveKnob.Plugin/ReSharper/JetBrains.Annotations.cs b/Windows/MassiveKnob.Plugin/ReSharper/JetBrains.Annotations.cs new file mode 100644 index 0000000..2790b2e --- /dev/null +++ b/Windows/MassiveKnob.Plugin/ReSharper/JetBrains.Annotations.cs @@ -0,0 +1,180 @@ +/* + * Stripped version of the ReSharper Annotations source. Enables hinting without referencing the + * ReSharper.Annotations NuGet package. + * + * If you need more annotations, this code was generated using + * ReSharper - Options - Code Annotations - Copy C# implementation to clipboard + */ + + +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; +// ReSharper disable InheritdocConsiderUsage + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +// ReSharper disable once CheckNamespace +namespace JetBrains.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + internal sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + internal sealed class NotNullAttribute : Attribute { } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + internal sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; } + + public ImplicitUseTargetFlags TargetFlags { get; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + internal sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + internal enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8 + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + internal enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + internal sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; } + } +} \ No newline at end of file diff --git a/Windows/MassiveKnob.sln b/Windows/MassiveKnob.sln index 64ec8c6..85a3fb7 100644 --- a/Windows/MassiveKnob.sln +++ b/Windows/MassiveKnob.sln @@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31005.135 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob", "MassiveKnob.csproj", "{73130EC7-49B3-40AD-8367-1095C0F41905}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob", "MassiveKnob\MassiveKnob.csproj", "{73130EC7-49B3-40AD-8367-1095C0F41905}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin", "MassiveKnob.Plugin\MassiveKnob.Plugin.csproj", "{A1298BE4-1D23-416C-8C56-FC9264487A95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.CoreAudio", "MassiveKnob.Plugin.CoreAudio\MassiveKnob.Plugin.CoreAudio.csproj", "{5BD5E2F2-9923-4F74-AC69-ACDA0B847937}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassiveKnob.Plugin.MockDevice", "MassiveKnob.Plugin.MockDevice\MassiveKnob.Plugin.MockDevice.csproj", "{674DE974-B134-4DB5-BFAF-7BC3D05E16DE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +21,18 @@ Global {73130EC7-49B3-40AD-8367-1095C0F41905}.Debug|Any CPU.Build.0 = Debug|Any CPU {73130EC7-49B3-40AD-8367-1095C0F41905}.Release|Any CPU.ActiveCfg = Release|Any CPU {73130EC7-49B3-40AD-8367-1095C0F41905}.Release|Any CPU.Build.0 = Release|Any CPU + {A1298BE4-1D23-416C-8C56-FC9264487A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1298BE4-1D23-416C-8C56-FC9264487A95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1298BE4-1D23-416C-8C56-FC9264487A95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1298BE4-1D23-416C-8C56-FC9264487A95}.Release|Any CPU.Build.0 = Release|Any CPU + {5BD5E2F2-9923-4F74-AC69-ACDA0B847937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BD5E2F2-9923-4F74-AC69-ACDA0B847937}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BD5E2F2-9923-4F74-AC69-ACDA0B847937}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BD5E2F2-9923-4F74-AC69-ACDA0B847937}.Release|Any CPU.Build.0 = Release|Any CPU + {674DE974-B134-4DB5-BFAF-7BC3D05E16DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {674DE974-B134-4DB5-BFAF-7BC3D05E16DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {674DE974-B134-4DB5-BFAF-7BC3D05E16DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {674DE974-B134-4DB5-BFAF-7BC3D05E16DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Windows/App.config b/Windows/MassiveKnob/App.config similarity index 100% rename from Windows/App.config rename to Windows/MassiveKnob/App.config diff --git a/Windows/MassiveKnob/App.xaml b/Windows/MassiveKnob/App.xaml new file mode 100644 index 0000000..5a65864 --- /dev/null +++ b/Windows/MassiveKnob/App.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Windows/MassiveKnob/App.xaml.cs b/Windows/MassiveKnob/App.xaml.cs new file mode 100644 index 0000000..5192f72 --- /dev/null +++ b/Windows/MassiveKnob/App.xaml.cs @@ -0,0 +1,75 @@ +using System.Diagnostics; +using System.Windows; +using Hardcodet.Wpf.TaskbarNotification; +using MassiveKnob.View; +using SimpleInjector; + +namespace MassiveKnob +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App + { + private readonly Container container; + private TaskbarIcon notifyIcon; + + private SettingsWindow settingsWindow; + + + public App(Container container) + { + this.container = container; + InitializeComponent(); + } + + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + notifyIcon = (TaskbarIcon)FindResource("NotifyIcon"); + Debug.Assert(notifyIcon != null, nameof(notifyIcon) + " != null"); + } + + + protected override void OnExit(ExitEventArgs e) + { + notifyIcon?.Dispose(); + + base.OnExit(e); + } + + + + private void ShowSettings() + { + if (settingsWindow == null) + { + settingsWindow = container.GetInstance(); + settingsWindow.Closed += (sender, args) => { settingsWindow = null; }; + settingsWindow.Show(); + } + else + settingsWindow.Activate(); + } + + + private void NotifyIconMenuSettingsClick(object sender, RoutedEventArgs e) + { + ShowSettings(); + } + + + private void NotifyIconTrayMouseDoubleClick(object sender, RoutedEventArgs e) + { + ShowSettings(); + } + + + private void NotifyIconMenuQuitClick(object sender, RoutedEventArgs e) + { + Shutdown(); + } + } +} \ No newline at end of file diff --git a/Windows/Forms/SettingsForm.Designer.cs b/Windows/MassiveKnob/Forms/SettingsForm.Designer.cs similarity index 100% rename from Windows/Forms/SettingsForm.Designer.cs rename to Windows/MassiveKnob/Forms/SettingsForm.Designer.cs diff --git a/Windows/Forms/SettingsForm.cs b/Windows/MassiveKnob/Forms/SettingsForm.cs similarity index 100% rename from Windows/Forms/SettingsForm.cs rename to Windows/MassiveKnob/Forms/SettingsForm.cs diff --git a/Windows/Forms/SettingsForm.resx b/Windows/MassiveKnob/Forms/SettingsForm.resx similarity index 100% rename from Windows/Forms/SettingsForm.resx rename to Windows/MassiveKnob/Forms/SettingsForm.resx diff --git a/Windows/Hardware/AbstractMassiveKnobHardware.cs b/Windows/MassiveKnob/Hardware/AbstractMassiveKnobHardware.cs similarity index 100% rename from Windows/Hardware/AbstractMassiveKnobHardware.cs rename to Windows/MassiveKnob/Hardware/AbstractMassiveKnobHardware.cs diff --git a/Windows/Hardware/CoreAudioDeviceManager.cs b/Windows/MassiveKnob/Hardware/CoreAudioDeviceManager.cs similarity index 100% rename from Windows/Hardware/CoreAudioDeviceManager.cs rename to Windows/MassiveKnob/Hardware/CoreAudioDeviceManager.cs diff --git a/Windows/Hardware/IAudioDeviceManager.cs b/Windows/MassiveKnob/Hardware/IAudioDeviceManager.cs similarity index 100% rename from Windows/Hardware/IAudioDeviceManager.cs rename to Windows/MassiveKnob/Hardware/IAudioDeviceManager.cs diff --git a/Windows/Hardware/IMassiveKnobHardware.cs b/Windows/MassiveKnob/Hardware/IMassiveKnobHardware.cs similarity index 100% rename from Windows/Hardware/IMassiveKnobHardware.cs rename to Windows/MassiveKnob/Hardware/IMassiveKnobHardware.cs diff --git a/Windows/Hardware/MockMassiveKnobHardware.cs b/Windows/MassiveKnob/Hardware/MockMassiveKnobHardware.cs similarity index 100% rename from Windows/Hardware/MockMassiveKnobHardware.cs rename to Windows/MassiveKnob/Hardware/MockMassiveKnobHardware.cs diff --git a/Windows/Hardware/SerialMassiveKnobHardware.cs b/Windows/MassiveKnob/Hardware/SerialMassiveKnobHardware.cs similarity index 100% rename from Windows/Hardware/SerialMassiveKnobHardware.cs rename to Windows/MassiveKnob/Hardware/SerialMassiveKnobHardware.cs diff --git a/Windows/MassiveKnob/Helpers/ComboBoxTemplateSelector.cs b/Windows/MassiveKnob/Helpers/ComboBoxTemplateSelector.cs new file mode 100644 index 0000000..adda8da --- /dev/null +++ b/Windows/MassiveKnob/Helpers/ComboBoxTemplateSelector.cs @@ -0,0 +1,54 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; +using System.Windows.Media; + +namespace MassiveKnob.Helpers +{ + // Source: https://stackoverflow.com/questions/4672867/can-i-use-a-different-template-for-the-selected-item-in-a-wpf-combobox-than-for + public class ComboBoxTemplateSelector : DataTemplateSelector + { + public DataTemplate SelectedItemTemplate { get; set; } + public DataTemplateSelector SelectedItemTemplateSelector { get; set; } + public DataTemplate DropdownItemsTemplate { get; set; } + public DataTemplateSelector DropdownItemsTemplateSelector { get; set; } + + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + var itemToCheck = container; + + // Search up the visual tree, stopping at either a ComboBox or + // a ComboBoxItem (or null). This will determine which template to use + while (itemToCheck != null && !(itemToCheck is ComboBoxItem) && !(itemToCheck is ComboBox)) + itemToCheck = VisualTreeHelper.GetParent(itemToCheck); + + // If you stopped at a ComboBoxItem, you're in the dropdown + var inDropDown = (itemToCheck is ComboBoxItem); + + return inDropDown + ? DropdownItemsTemplate ?? DropdownItemsTemplateSelector?.SelectTemplate(item, container) + : SelectedItemTemplate ?? SelectedItemTemplateSelector?.SelectTemplate(item, container); + } + } + + + public class ComboBoxTemplateSelectorExtension : MarkupExtension + { + public DataTemplate SelectedItemTemplate { get; set; } + public DataTemplateSelector SelectedItemTemplateSelector { get; set; } + public DataTemplate DropdownItemsTemplate { get; set; } + public DataTemplateSelector DropdownItemsTemplateSelector { get; set; } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return new ComboBoxTemplateSelector() + { + SelectedItemTemplate = SelectedItemTemplate, + SelectedItemTemplateSelector = SelectedItemTemplateSelector, + DropdownItemsTemplate = DropdownItemsTemplate, + DropdownItemsTemplateSelector = DropdownItemsTemplateSelector + }; + } + } +} diff --git a/Windows/MassiveKnob/Helpers/DelegateCommand.cs b/Windows/MassiveKnob/Helpers/DelegateCommand.cs new file mode 100644 index 0000000..e895714 --- /dev/null +++ b/Windows/MassiveKnob/Helpers/DelegateCommand.cs @@ -0,0 +1,80 @@ +using System; +using System.Windows.Input; + +namespace MassiveKnob.Helpers +{ + public class DelegateCommand : ICommand + { + private readonly Action execute; + private readonly Func canExecute; + + + public DelegateCommand(Action execute) : this(execute, null) + { + } + + + public DelegateCommand(Action execute, Func canExecute) + { + this.execute = execute ?? throw new ArgumentNullException(nameof(execute)); + this.canExecute = canExecute; + } + + + public bool CanExecute(object parameter) + { + return canExecute?.Invoke() ?? true; + } + + + public void Execute(object parameter) + { + execute(); + } + + + public event EventHandler CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + } + + + public class DelegateCommand : ICommand + { + private readonly Action execute; + private readonly Predicate canExecute; + + + public DelegateCommand(Action execute) : this(execute, null) + { + } + + + public DelegateCommand(Action execute, Predicate canExecute) + { + this.execute = execute ?? throw new ArgumentNullException(nameof(execute)); + this.canExecute = canExecute; + } + + + public bool CanExecute(object parameter) + { + return canExecute?.Invoke((T)parameter) ?? true; + } + + + public void Execute(object parameter) + { + execute((T)parameter); + } + + + public event EventHandler CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + } +} diff --git a/Windows/MainIcon.ico b/Windows/MassiveKnob/MainIcon.ico similarity index 100% rename from Windows/MainIcon.ico rename to Windows/MassiveKnob/MainIcon.ico diff --git a/Windows/MassiveKnob.csproj b/Windows/MassiveKnob/MassiveKnob.csproj similarity index 72% rename from Windows/MassiveKnob.csproj rename to Windows/MassiveKnob/MassiveKnob.csproj index 7655aa1..fb0171e 100644 --- a/Windows/MassiveKnob.csproj +++ b/Windows/MassiveKnob/MassiveKnob.csproj @@ -10,6 +10,7 @@ MassiveKnob v4.7.2 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true true @@ -41,8 +42,11 @@ MassiveKnob.Program + + + @@ -50,22 +54,26 @@ - + - - Form - - - SettingsForm.cs - + + + + + + + + + SettingsWindow.xaml + @@ -75,37 +83,10 @@ True Strings.resx - - UserControl - - - KnobDeviceControl.cs - - - SettingsForm.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - ResXFileCodeGenerator Strings.Designer.cs - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - @@ -123,6 +104,9 @@ 0.11.24 + + 1.0.8 + 12.0.3 @@ -135,6 +119,33 @@ + + + + + Designer + MSBuild:Compile + + + App.xaml + Code + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + {A1298BE4-1D23-416C-8C56-FC9264487A95} + MassiveKnob.Plugin + diff --git a/Windows/MassiveKnob/Model/IMassiveKnobOrchestrator.cs b/Windows/MassiveKnob/Model/IMassiveKnobOrchestrator.cs new file mode 100644 index 0000000..a818b26 --- /dev/null +++ b/Windows/MassiveKnob/Model/IMassiveKnobOrchestrator.cs @@ -0,0 +1,11 @@ +using MassiveKnob.Plugin; + +namespace MassiveKnob.Model +{ + public interface IMassiveKnobOrchestrator + { + IMassiveKnobDeviceInstance ActiveDeviceInstance { get; } + + IMassiveKnobDeviceInstance SetActiveDevice(IMassiveKnobDevice device); + } +} diff --git a/Windows/MassiveKnob/Model/IPluginManager.cs b/Windows/MassiveKnob/Model/IPluginManager.cs new file mode 100644 index 0000000..87e185d --- /dev/null +++ b/Windows/MassiveKnob/Model/IPluginManager.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using MassiveKnob.Plugin; + +namespace MassiveKnob.Model +{ + public interface IPluginManager + { + IEnumerable Plugins { get; } + + IEnumerable GetDevicePlugins(); + } +} diff --git a/Windows/MassiveKnob/Model/MassiveKnobOrchestrator.cs b/Windows/MassiveKnob/Model/MassiveKnobOrchestrator.cs new file mode 100644 index 0000000..87e2de8 --- /dev/null +++ b/Windows/MassiveKnob/Model/MassiveKnobOrchestrator.cs @@ -0,0 +1,52 @@ +using MassiveKnob.Plugin; + +namespace MassiveKnob.Model +{ + public class MassiveKnobOrchestrator : IMassiveKnobOrchestrator + { + private readonly Settings.Settings settings; + + + public IMassiveKnobDeviceInstance ActiveDeviceInstance { get; private set; } + + + public MassiveKnobOrchestrator(Settings.Settings settings) + { + this.settings = settings; + } + + + public IMassiveKnobDeviceInstance SetActiveDevice(IMassiveKnobDevice device) + { + ActiveDeviceInstance?.Dispose(); + ActiveDeviceInstance = device?.Create(new Context(settings)); + + return ActiveDeviceInstance; + } + + + + public class Context : IMassiveKnobContext + { + private readonly Settings.Settings settings; + + + public Context(Settings.Settings settings) + { + this.settings = settings; + } + + public T GetSettings() where T : class, new() + { + // TODO + return default; + } + + + public void SetSettings(T settings) where T : class, new() + { + // TODO + } + } + } +} diff --git a/Windows/MassiveKnob/Model/PluginManager.cs b/Windows/MassiveKnob/Model/PluginManager.cs new file mode 100644 index 0000000..48fec68 --- /dev/null +++ b/Windows/MassiveKnob/Model/PluginManager.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using MassiveKnob.Plugin; + +namespace MassiveKnob.Model +{ + public class PluginManager : IPluginManager + { + private readonly List plugins = new List(); + + + public IEnumerable Plugins => plugins; + + public IEnumerable GetDevicePlugins() + { + return plugins.Where(p => p is IMassiveKnobDevicePlugin).Cast(); + } + + + public void Load() + { + var codeBase = Assembly.GetEntryAssembly()?.CodeBase; + if (!string.IsNullOrEmpty(codeBase)) + { + var localPath = Path.GetDirectoryName(new Uri(codeBase).LocalPath); + if (!string.IsNullOrEmpty(localPath)) + { + var applicationPluginPath = Path.Combine(localPath, @"Plugins"); + LoadPlugins(applicationPluginPath); + } + } + + + var localPluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"MassiveKnob", @"Plugins"); + LoadPlugins(localPluginPath); + } + + + private void LoadPlugins(string path) + { + if (!Directory.Exists(path)) + return; + + var filenames = Directory.GetFiles(path, "*.dll"); + + foreach (var filename in filenames) + { + try + { + var pluginAssembly = Assembly.LoadFrom(filename); + RegisterPlugins(pluginAssembly); + } + catch (Exception e) + { + // TODO report error +// Console.WriteLine(e); + throw; + } + } + } + + + private void RegisterPlugins(Assembly assembly) + { + var pluginTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute() != null); + foreach (var pluginType in pluginTypes) + { + var pluginInstance = Activator.CreateInstance(pluginType); + if (!(pluginInstance is IMassiveKnobPlugin)) + throw new InvalidCastException($"Type {pluginType.FullName} claims to be a MassiveKnobPlugin but does not implement IMassiveKnobPlugin"); + + plugins.Add((IMassiveKnobPlugin)pluginInstance); + } + } + } +} diff --git a/Windows/MassiveKnob/Program.cs b/Windows/MassiveKnob/Program.cs new file mode 100644 index 0000000..f28ec47 --- /dev/null +++ b/Windows/MassiveKnob/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using MassiveKnob.Model; +using MassiveKnob.Settings; +using MassiveKnob.View; +using MassiveKnob.ViewModel; +using SimpleInjector; + +namespace MassiveKnob +{ + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main() + { + MainAsync().GetAwaiter().GetResult(); + } + + + private static async Task MainAsync() + { + var container = new Container(); + container.Options.EnableAutoVerification = false; + + container.RegisterSingleton(); + + container.Register(); + container.Register(); + container.Register(); + + var settings = await SettingsJsonSerializer.Deserialize(); + container.RegisterInstance(settings); + + var pluginManager = new PluginManager(); + pluginManager.Load(); + container.RegisterInstance(pluginManager); + + + var app = container.GetInstance(); + app.Run(); + } + } +} diff --git a/Windows/Properties/AssemblyInfo.cs b/Windows/MassiveKnob/Properties/AssemblyInfo.cs similarity index 100% rename from Windows/Properties/AssemblyInfo.cs rename to Windows/MassiveKnob/Properties/AssemblyInfo.cs diff --git a/Windows/Resources/Icon.ai b/Windows/MassiveKnob/Resources/Icon.ai similarity index 100% rename from Windows/Resources/Icon.ai rename to Windows/MassiveKnob/Resources/Icon.ai diff --git a/Windows/Resources/MainIcon.ico b/Windows/MassiveKnob/Resources/MainIcon.ico similarity index 100% rename from Windows/Resources/MainIcon.ico rename to Windows/MassiveKnob/Resources/MainIcon.ico diff --git a/Windows/Resources/NotifyIcon.ico b/Windows/MassiveKnob/Resources/NotifyIcon.ico similarity index 100% rename from Windows/Resources/NotifyIcon.ico rename to Windows/MassiveKnob/Resources/NotifyIcon.ico diff --git a/Windows/MassiveKnob/Settings/Settings.cs b/Windows/MassiveKnob/Settings/Settings.cs new file mode 100644 index 0000000..2ac71c3 --- /dev/null +++ b/Windows/MassiveKnob/Settings/Settings.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace MassiveKnob.Settings +{ + public class Settings + { + public DeviceSettings Device { get; set; } + public List Actions { get; set; } + + + public static Settings Default() + { + return new Settings + { + Device = null, + Actions = new List() + }; + } + + + public class DeviceSettings + { + public Guid? PluginId { get; set; } + public Guid? DeviceId { get; set; } + public JObject Settings { get; set; } + } + + + public class ActionSettings + { + public Guid PluginId { get; set; } + public Guid ActionId { get; set; } + public JObject Settings { get; set; } + } + } +} diff --git a/Windows/Settings/SettingsJsonSerializer.cs b/Windows/MassiveKnob/Settings/SettingsJsonSerializer.cs similarity index 53% rename from Windows/Settings/SettingsJsonSerializer.cs rename to Windows/MassiveKnob/Settings/SettingsJsonSerializer.cs index d696165..dddc346 100644 --- a/Windows/Settings/SettingsJsonSerializer.cs +++ b/Windows/MassiveKnob/Settings/SettingsJsonSerializer.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; @@ -25,8 +24,7 @@ namespace MassiveKnob.Settings public static async Task Serialize(Settings settings, string filename) { - var serializedSettings = SerializedSettings.FromSettings(settings); - var json = JsonConvert.SerializeObject(serializedSettings); + var json = JsonConvert.SerializeObject(settings); using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read, 4096, true)) using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) @@ -58,49 +56,7 @@ namespace MassiveKnob.Settings if (string.IsNullOrEmpty(json)) return Settings.Default(); - var serializedSettings = JsonConvert.DeserializeObject(json); - return serializedSettings.ToSettings(); - } - - - private class SerializedSettings - { - // ReSharper disable MemberCanBePrivate.Local - used for JSON serialization - public string SerialPort; - public SerializedKnobSettings[] Knobs; - // ReSharper restore MemberCanBePrivate.Local - - - public static SerializedSettings FromSettings(Settings settings) - { - return new SerializedSettings - { - SerialPort = settings.SerialPort, - Knobs = settings.Knobs.Select(knob => new SerializedKnobSettings - { - DeviceId = knob.DeviceId - }).ToArray() - }; - } - - - public Settings ToSettings() - { - return new Settings - { - SerialPort = SerialPort, - Knobs = Knobs.Select(knob => new Settings.KnobSettings - { - DeviceId = knob.DeviceId - }).ToList() - }; - } - - - public class SerializedKnobSettings - { - public Guid? DeviceId; - } + return JsonConvert.DeserializeObject(json); } } } diff --git a/Windows/Strings.Designer.cs b/Windows/MassiveKnob/Strings.Designer.cs similarity index 100% rename from Windows/Strings.Designer.cs rename to Windows/MassiveKnob/Strings.Designer.cs diff --git a/Windows/Strings.resx b/Windows/MassiveKnob/Strings.resx similarity index 100% rename from Windows/Strings.resx rename to Windows/MassiveKnob/Strings.resx diff --git a/Windows/MassiveKnob/Style.xaml b/Windows/MassiveKnob/Style.xaml new file mode 100644 index 0000000..443fe67 --- /dev/null +++ b/Windows/MassiveKnob/Style.xaml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/Windows/UserControls/KnobDeviceControl.Designer.cs b/Windows/MassiveKnob/UserControls/KnobDeviceControl.Designer.cs similarity index 100% rename from Windows/UserControls/KnobDeviceControl.Designer.cs rename to Windows/MassiveKnob/UserControls/KnobDeviceControl.Designer.cs diff --git a/Windows/UserControls/KnobDeviceControl.cs b/Windows/MassiveKnob/UserControls/KnobDeviceControl.cs similarity index 100% rename from Windows/UserControls/KnobDeviceControl.cs rename to Windows/MassiveKnob/UserControls/KnobDeviceControl.cs diff --git a/Windows/MassiveKnob/View/SettingsWindow.xaml b/Windows/MassiveKnob/View/SettingsWindow.xaml new file mode 100644 index 0000000..601f5e4 --- /dev/null +++ b/Windows/MassiveKnob/View/SettingsWindow.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + Device + + + + + + + + + Controls + + + diff --git a/Windows/MassiveKnob/View/SettingsWindow.xaml.cs b/Windows/MassiveKnob/View/SettingsWindow.xaml.cs new file mode 100644 index 0000000..8d9ec31 --- /dev/null +++ b/Windows/MassiveKnob/View/SettingsWindow.xaml.cs @@ -0,0 +1,16 @@ +using MassiveKnob.ViewModel; + +namespace MassiveKnob.View +{ + /// + /// Interaction logic for SettingsWindow.xaml + /// + public partial class SettingsWindow + { + public SettingsWindow(SettingsViewModel settingsViewModel) + { + DataContext = settingsViewModel; + InitializeComponent(); + } + } +} diff --git a/Windows/MassiveKnob/ViewModel/SettingsViewModel.cs b/Windows/MassiveKnob/ViewModel/SettingsViewModel.cs new file mode 100644 index 0000000..4764a9b --- /dev/null +++ b/Windows/MassiveKnob/ViewModel/SettingsViewModel.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Windows.Controls; +using MassiveKnob.Model; +using MassiveKnob.Plugin; + +namespace MassiveKnob.ViewModel +{ + public class SettingsViewModel : INotifyPropertyChanged + { + private readonly Settings.Settings settings; + private readonly IMassiveKnobOrchestrator orchestrator; + private DeviceViewModel selectedDevice; + private UserControl settingsControl; + + + public IEnumerable Devices { get; } + public DeviceViewModel SelectedDevice + { + get => selectedDevice; + + set + { + if (value == selectedDevice) + return; + + selectedDevice = value; + var deviceInstance = orchestrator.SetActiveDevice(value?.Device); + + if (value == null) + settings.Device = null; + else + { + settings.Device = new Settings.Settings.DeviceSettings + { + PluginId = value.Plugin.PluginId, + DeviceId = value.Device.DeviceId, + Settings = null + }; + } + + OnPropertyChanged(); + + SettingsControl = deviceInstance?.CreateSettingsControl(); + } + } + + public UserControl SettingsControl + { + get => settingsControl; + + set + { + if (value == settingsControl) + return; + + settingsControl = value; + OnPropertyChanged(); + } + } + + + + + public SettingsViewModel(IPluginManager pluginManager, Settings.Settings settings, IMassiveKnobOrchestrator orchestrator) + { + this.settings = settings; + this.orchestrator = orchestrator; + + Devices = pluginManager.GetDevicePlugins().SelectMany(dp => dp.Devices.Select(d => new DeviceViewModel(dp, d))); + + if (settings.Device != null) + SelectedDevice = Devices.FirstOrDefault(d => + d.Plugin.PluginId == settings.Device.PluginId && + d.Device.DeviceId == settings.Device.DeviceId); + } + + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + + + public class DeviceViewModel + { + // ReSharper disable UnusedMember.Global - used by WPF Binding + public string Name => Device.Name; + public string Description => Device.Description; + // ReSharper restore UnusedMember.Global + + public IMassiveKnobDevicePlugin Plugin { get; } + public IMassiveKnobDevice Device { get; } + + + public DeviceViewModel(IMassiveKnobDevicePlugin plugin, IMassiveKnobDevice device) + { + Plugin = plugin; + Device = device; + } + } + } +} diff --git a/Windows/Program.cs b/Windows/Program.cs deleted file mode 100644 index 8f2f1f3..0000000 --- a/Windows/Program.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Windows.Forms; -using MassiveKnob.Forms; -using MassiveKnob.Hardware; -using SimpleInjector; -using SimpleInjector.Diagnostics; - -namespace MassiveKnob -{ - public static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - public static void Main() - { - var container = BuildContainer(); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(container.GetInstance()); - } - - - private static Container BuildContainer() - { - var container = new Container(); - container.Options.EnableAutoVerification = false; - - container.Register(); - container.GetRegistration(typeof(SettingsForm))?.Registration - .SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, "Windows Form implements IDisposable"); - - container.Register(); - - // For testing without the hardware: - container.Register(); - //container.Register(() => new MockMassiveKnobHardwareFactory(3, TimeSpan.FromSeconds(1), 25)); - - return container; - } - } -} diff --git a/Windows/Properties/Resources.Designer.cs b/Windows/Properties/Resources.Designer.cs deleted file mode 100644 index a1a7364..0000000 --- a/Windows/Properties/Resources.Designer.cs +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -namespace MassiveKnob.Properties -{ - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MassiveKnob.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/Windows/Properties/Resources.resx b/Windows/Properties/Resources.resx deleted file mode 100644 index af7dbeb..0000000 --- a/Windows/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Windows/Properties/Settings.Designer.cs b/Windows/Properties/Settings.Designer.cs deleted file mode 100644 index 28fa051..0000000 --- a/Windows/Properties/Settings.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -namespace MassiveKnob.Properties -{ - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/Windows/Properties/Settings.settings b/Windows/Properties/Settings.settings deleted file mode 100644 index 3964565..0000000 --- a/Windows/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Windows/Settings/Settings.cs b/Windows/Settings/Settings.cs deleted file mode 100644 index ddfc593..0000000 --- a/Windows/Settings/Settings.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MassiveKnob.Settings -{ - public class Settings - { - public string SerialPort { get; set; } - public List Knobs { get; set; } - - - public static Settings Default() - { - return new Settings - { - Knobs = new List() - }; - } - - - public class KnobSettings - { - public Guid? DeviceId { get; set; } - } - } -}