Implemented basic plugin architecture

Moving interface to WPF for easier integration
This commit is contained in:
Mark van Renswoude 2021-02-21 14:04:24 +01:00
parent 19484d8115
commit dc0e761244
69 changed files with 1730 additions and 374 deletions

View File

@ -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<Settings>();
}
public void Dispose()
{
}
public UserControl CreateSettingsControl()
{
return null;
}
public ValueTask AnalogChanged(byte value)
{
// TODO set volume
return default;
}
}
private class Settings
{
}
}
}

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5BD5E2F2-9923-4F74-AC69-ACDA0B847937}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MassiveKnob.Plugin.CoreAudio</RootNamespace>
<AssemblyName>MassiveKnob.Plugin.CoreAudio</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(localappdata)\MassiveKnob\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Actions\DeviceVolumeAction.cs" />
<Compile Include="MassiveKnobCoreAudioPlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\DeviceVolumeActionSettings.xaml.cs">
<DependentUpon>DeviceVolumeActionSettings.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MassiveKnob.Plugin\MassiveKnob.Plugin.csproj">
<Project>{a1298be4-1d23-416c-8c56-fc9264487a95}</Project>
<Name>MassiveKnob.Plugin</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions">
<Version>4.5.4</Version>
</PackageReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Page Include="Settings\DeviceVolumeActionSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -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 <mark@x2software.net>";
public string Url { get; } = "https://www.github.com/MvRens/MassiveKnob/";
public IEnumerable<IMassiveKnobAction> Actions { get; } = new IMassiveKnobAction[]
{
new DeviceVolumeAction()
};
}
}

View File

@ -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")]

View File

@ -0,0 +1,12 @@
<UserControl x:Class="MassiveKnob.Plugin.CoreAudio.Settings.DeviceVolumeActionSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MassiveKnob.Plugin.CoreAudio.Settings"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</UserControl>

View File

@ -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
{
/// <summary>
/// Interaction logic for DeviceVolumeActionSettings.xaml
/// </summary>
public partial class DeviceVolumeActionSettings : UserControl
{
public DeviceVolumeActionSettings()
{
InitializeComponent();
}
}
}

View File

@ -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.
}
}
}

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{674DE974-B134-4DB5-BFAF-7BC3D05E16DE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MassiveKnob.Plugin.MockDevice</RootNamespace>
<AssemblyName>MassiveKnob.Plugin.MockDevice</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(localappdata)\MassiveKnob\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Devices\MockDevice.cs" />
<Compile Include="MassiveKnobMockDevicePlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\MockDeviceSettings.xaml.cs">
<DependentUpon>MockDeviceSettings.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MassiveKnob.Plugin\MassiveKnob.Plugin.csproj">
<Project>{A1298BE4-1D23-416C-8C56-FC9264487A95}</Project>
<Name>MassiveKnob.Plugin</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Page Include="Settings\MockDeviceSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -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 <mark@x2software.net>";
public string Url { get; } = "https://www.github.com/MvRens/MassiveKnob/";
public IEnumerable<IMassiveKnobDevice> Devices { get; } = new IMassiveKnobDevice[]
{
new Devices.MockDevice()
};
}
}

View File

@ -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")]

View File

@ -0,0 +1,24 @@
<UserControl x:Class="MassiveKnob.Plugin.MockDevice.Settings.MockDeviceSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MassiveKnob.Plugin.MockDevice.Settings"
mc:Ignorable="d" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="4">Number of knobs</TextBlock>
<TextBox Grid.Row="0" Grid.Column="1" Margin="4" Width="150" HorizontalAlignment="Left"></TextBox>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="4">Randomly change the volume</CheckBox>
</Grid>
</UserControl>

View File

@ -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
{
/// <summary>
/// Interaction logic for MockDeviceSettings.xaml
/// </summary>
public partial class MockDeviceSettings : UserControl
{
public MockDeviceSettings()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,59 @@
using System;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Determines the type of control this action can be assigned to.
/// </summary>
[Flags]
public enum MassiveKnobActionType
{
/// <summary>
/// Can be assigned to a potentiometer. The action instance must implement IMassiveKnobAnalogAction.
/// </summary>
InputAnalog = 1 << 0,
/// <summary>
/// Can be assigned to a button or switch. The action instance must implement IMassiveKnobDigitalAction.
/// </summary>
InputDigital = 1 << 1,
/// <summary>
/// Can be assigned to an output, like an LED or relay.
/// </summary>
OutputSignal = 1 << 2
}
/// <summary>
/// Provides information about an action which can be assigned to a knob or button.
/// </summary>
public interface IMassiveKnobAction
{
/// <summary>
/// Unique identifier for the action.
/// </summary>
Guid ActionId { get; }
/// <summary>
/// Determines the type of control this action can be assigned to.
/// </summary>
MassiveKnobActionType ActionType { get; }
/// <summary>
/// The name of the action as shown in the action list when assigning to a knob or button.
/// </summary>
string Name { get; }
/// <summary>
/// A short description of the functionality provided by the action.
/// </summary>
string Description { get; }
/// <summary>
/// Called when an action is bound to a knob or button to create an instance of the action.
/// </summary>
/// <param name="context">Provides an interface to the Massive Knob settings and device. Can be stored until the action instance is disposed.</param>
IMassiveKnobActionInstance Create(IMassiveKnobActionContext context);
}
}

View File

@ -0,0 +1,12 @@
namespace MassiveKnob.Plugin
{
/// <inheritdoc />
public interface IMassiveKnobActionContext : IMassiveKnobContext
{
/// <summary>
/// Sets the state of the signal. Only valid for OutputSignal action types, will raise an exception otherwise.
/// </summary>
/// <param name="on">Whether the signal is on or off.</param>
void SetSignal(bool on);
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Windows.Controls;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Base interface for actions assigned to a knob or button. Implement one of the descendant
/// interfaces to provide input or output.
/// </summary>
public interface IMassiveKnobActionInstance : IDisposable
{
/// <summary>
/// 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.
/// </summary>
UserControl CreateSettingsControl();
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Implemented by plugins supporting actions which can be assigned to knobs or buttons.
/// </summary>
public interface IMassiveKnobActionPlugin : IMassiveKnobPlugin
{
/// <summary>
/// A list of actions supported by the plugin which can be assigned to knobs or buttons.
/// </summary>
IEnumerable<IMassiveKnobAction> Actions { get; }
}
}

View File

@ -0,0 +1,16 @@
using System.Threading.Tasks;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Required to be implemented for Action type InputAnalog. Receives an update when a knob's position changes.
/// </summary>
public interface IMassiveKnobAnalogAction : IMassiveKnobActionInstance
{
/// <summary>
/// Called when a knob's position changes.
/// </summary>
/// <param name="value">The new value. Range is 0 to 100.</param>
ValueTask AnalogChanged(byte value);
}
}

View File

@ -0,0 +1,22 @@
namespace MassiveKnob.Plugin
{
/// <summary>
/// Provides an interface to the Massive Knob settings and device.
/// </summary>
public interface IMassiveKnobContext
{
/// <summary>
/// Reads the settings for the current action instance.
/// </summary>
/// <typeparam name="T">The class type to be deserialized using Newtonsoft.Json.</typeparam>
/// <returns>The deserialized class if settings were previously stored, or a new instance of the class otherwise.</returns>
T GetSettings<T>() where T : class, new();
/// <summary>
/// Stores the settings for the current action instance.
/// </summary>
/// <typeparam name="T">The class type to be serialized using Newtonsoft.Json.</typeparam>
/// <param name="settings">The object to be serialized using Newtonsoft.Json.</param>
void SetSettings<T>(T settings) where T : class, new();
}
}

View File

@ -0,0 +1,31 @@
using System;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Contains information about the device supported by this plugin.
/// </summary>
public interface IMassiveKnobDevice
{
/// <summary>
/// Unique identifier for the device.
/// </summary>
Guid DeviceId { get; }
/// <summary>
/// The name of the action as shown in the action list when assigning to a knob or button.
/// </summary>
string Name { get; }
/// <summary>
/// A short description of the functionality provided by the action.
/// </summary>
string Description { get; }
/// <summary>
/// Called when the device is selected.
/// </summary>
/// <param name="context">Provides an interface to the Massive Knob settings and device. Can be stored until the device instance is disposed.</param>
IMassiveKnobDeviceInstance Create(IMassiveKnobContext context);
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Windows.Controls;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Represents a connection to a Massive Knob device.
/// </summary>
public interface IMassiveKnobDeviceInstance : IDisposable
{
/// <summary>
/// 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.
/// </summary>
UserControl CreateSettingsControl();
}
}

View File

@ -0,0 +1,9 @@
namespace MassiveKnob.Plugin
{
/// <summary>
/// Required to be implemented for Action type InputDigital. Receives an update when a knob's position changes.
/// </summary>
public interface IMassiveKnobDigitalAction : IMassiveKnobActionInstance
{
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Implemented by plugins which interface with Massive Knob device.
/// </summary>
public interface IMassiveKnobDevicePlugin : IMassiveKnobPlugin
{
/// <summary>
/// A list of devices supported by the plugin.
/// </summary>
IEnumerable<IMassiveKnobDevice> Devices { get; }
}
}

View File

@ -0,0 +1,36 @@
using System;
namespace MassiveKnob.Plugin
{
/// <summary>
/// Minimum required interface to implement for writing a Massive Knob plugin.
/// Implement one of the various descendant interfaces to provide more functionality.
/// </summary>
public interface IMassiveKnobPlugin
{
/// <summary>
/// Unique identifier for the plugin.
/// </summary>
Guid PluginId { get; }
/// <summary>
/// The name of the plugin as shown in the plugin list.
/// </summary>
string Name { get; }
/// <summary>
/// A short description of the functionality provided by the plugin.
/// </summary>
string Description { get; }
/// <summary>
/// The name of the author(s) of the plugin.
/// </summary>
string Author { get; }
/// <summary>
/// URL to the plugin's website.
/// </summary>
string Url { get; }
}
}

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A1298BE4-1D23-416C-8C56-FC9264487A95}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MassiveKnob.Plugin</RootNamespace>
<AssemblyName>MassiveKnob.Plugin</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\MassiveKnob.Plugin.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\MassiveKnob.Plugin.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="IMassiveKnobActionContext.cs" />
<Compile Include="IMassiveKnobActionInstance.cs" />
<Compile Include="IMassiveKnobActionPlugin.cs" />
<Compile Include="IMassiveKnobDeviceInstance.cs" />
<Compile Include="IMassiveKnobDigitalAction.cs" />
<Compile Include="IMassiveKnobAnalogAction.cs" />
<Compile Include="IMassiveKnobContext.cs" />
<Compile Include="IMassiveKnobDevice.cs" />
<Compile Include="IMassiveKnobHardwarePlugin.cs" />
<Compile Include="MassiveKnobPluginAttribute.cs" />
<Compile Include="IMassiveKnobPlugin.cs" />
<Compile Include="IMassiveKnobAction.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReSharper\JetBrains.Annotations.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions">
<Version>4.5.4</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,17 @@
using System;
using JetBrains.Annotations;
// ReSharper disable once UnusedMember.Global - public API
namespace MassiveKnob.Plugin
{
/// <summary>
/// Attach this attribute to a class to register it as a Massive Knob plugin.
/// The class must implement IMassiveKnobPlugin.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
[MeansImplicitUse]
public class MassiveKnobPluginAttribute : Attribute
{
}
}

View File

@ -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")]

View File

@ -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
{
/// <summary>
/// Indicates that the value of the marked element could be <c>null</c> sometimes,
/// so the check for <c>null</c> is necessary before its usage.
/// </summary>
/// <example><code>
/// [CanBeNull] object Test() => null;
///
/// void UseTest() {
/// var p = Test();
/// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event |
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)]
internal sealed class CanBeNullAttribute : Attribute { }
/// <summary>
/// Indicates that the value of the marked element could never be <c>null</c>.
/// </summary>
/// <example><code>
/// [NotNull] object Foo() {
/// return null; // Warning: Possible 'null' assignment
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event |
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)]
internal sealed class NotNullAttribute : Attribute { }
/// <summary>
/// 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).
/// </summary>
[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; }
}
/// <summary>
/// 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)
/// </summary>
[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,
/// <summary>Only entity marked with attribute considered used.</summary>
Access = 1,
/// <summary>Indicates implicit assignment to a member.</summary>
Assign = 2,
/// <summary>
/// Indicates implicit instantiation of a type with fixed constructor signature.
/// That means any unused constructor parameters won't be reported as such.
/// </summary>
InstantiatedWithFixedConstructorSignature = 4,
/// <summary>Indicates implicit instantiation of a type.</summary>
InstantiatedNoFixedConstructorSignature = 8
}
/// <summary>
/// Specify what is considered used implicitly when marked
/// with <see cref="MeansImplicitUseAttribute"/> or <see cref="UsedImplicitlyAttribute"/>.
/// </summary>
[Flags]
internal enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
/// <summary>Members of entity marked with attribute are considered used.</summary>
Members = 2,
/// <summary>Entity marked with attribute and all its members considered used.</summary>
WithMembers = Itself | Members
}
/// <summary>
/// This attribute is intended to mark publicly available API
/// which should not be removed and so is treated as used.
/// </summary>
[MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]
internal sealed class PublicAPIAttribute : Attribute
{
public PublicAPIAttribute() { }
public PublicAPIAttribute([NotNull] string comment)
{
Comment = comment;
}
[CanBeNull] public string Comment { get; }
}
}

View File

@ -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

View File

@ -0,0 +1,26 @@
<Application x:Class="MassiveKnob.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Style.xaml" />
</ResourceDictionary.MergedDictionaries>
<ContextMenu x:Shared="false" x:Key="NotifyIconMenu">
<MenuItem Header="_Settings" Click="NotifyIconMenuSettingsClick" Style="{StaticResource DefaultMenuItem}" />
<MenuItem Header="_Quit" Click="NotifyIconMenuQuitClick" />
</ContextMenu>
<tb:TaskbarIcon
x:Key="NotifyIcon"
Visibility="Visible"
ToolTipText="Massive Knob"
IconSource="/Resources/NotifyIcon.ico"
ContextMenu="{StaticResource NotifyIconMenu}"
TrayMouseDoubleClick="NotifyIconTrayMouseDoubleClick"/>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -0,0 +1,75 @@
using System.Diagnostics;
using System.Windows;
using Hardcodet.Wpf.TaskbarNotification;
using MassiveKnob.View;
using SimpleInjector;
namespace MassiveKnob
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
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>();
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();
}
}
}

View File

@ -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
};
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Windows.Input;
namespace MassiveKnob.Helpers
{
public class DelegateCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public DelegateCommand(Action execute) : this(execute, null)
{
}
public DelegateCommand(Action execute, Func<bool> 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<T> : ICommand
{
private readonly Action<T> execute;
private readonly Predicate<T> canExecute;
public DelegateCommand(Action<T> execute) : this(execute, null)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> 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;
}
}
}

View File

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 169 KiB

View File

@ -10,6 +10,7 @@
<AssemblyName>MassiveKnob</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
@ -41,8 +42,11 @@
<StartupObject>MassiveKnob.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -50,22 +54,26 @@
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Forms\SettingsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\SettingsForm.Designer.cs">
<DependentUpon>SettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="Hardware\AbstractMassiveKnobHardware.cs" />
<Compile Include="Hardware\CoreAudioDeviceManager.cs" />
<Compile Include="Hardware\IAudioDeviceManager.cs" />
<Compile Include="Hardware\IMassiveKnobHardware.cs" />
<Compile Include="Hardware\MockMassiveKnobHardware.cs" />
<Compile Include="Hardware\SerialMassiveKnobHardware.cs" />
<Compile Include="Helpers\ComboBoxTemplateSelector.cs" />
<Compile Include="Helpers\DelegateCommand.cs" />
<Compile Include="Model\IMassiveKnobOrchestrator.cs" />
<Compile Include="Model\IPluginManager.cs" />
<Compile Include="Model\MassiveKnobOrchestrator.cs" />
<Compile Include="Model\PluginManager.cs" />
<Compile Include="ViewModel\SettingsViewModel.cs" />
<Compile Include="View\SettingsWindow.xaml.cs">
<DependentUpon>SettingsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\Settings.cs" />
@ -75,37 +83,10 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
<Compile Include="UserControls\KnobDeviceControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="UserControls\KnobDeviceControl.Designer.cs">
<DependentUpon>KnobDeviceControl.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="Forms\SettingsForm.resx">
<DependentUpon>SettingsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Include="Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@ -123,6 +104,9 @@
<PackageReference Include="Dapplo.Windows.Messages">
<Version>0.11.24</Version>
</PackageReference>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf">
<Version>1.0.8</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
@ -135,6 +119,33 @@
</ItemGroup>
<ItemGroup>
<Content Include="MainIcon.ico" />
<Resource Include="Resources\NotifyIcon.ico" />
</ItemGroup>
<ItemGroup>
<Page Include="App.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Include="Style.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\SettingsWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MassiveKnob.Plugin\MassiveKnob.Plugin.csproj">
<Project>{A1298BE4-1D23-416C-8C56-FC9264487A95}</Project>
<Name>MassiveKnob.Plugin</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -0,0 +1,11 @@
using MassiveKnob.Plugin;
namespace MassiveKnob.Model
{
public interface IMassiveKnobOrchestrator
{
IMassiveKnobDeviceInstance ActiveDeviceInstance { get; }
IMassiveKnobDeviceInstance SetActiveDevice(IMassiveKnobDevice device);
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using MassiveKnob.Plugin;
namespace MassiveKnob.Model
{
public interface IPluginManager
{
IEnumerable<IMassiveKnobPlugin> Plugins { get; }
IEnumerable<IMassiveKnobDevicePlugin> GetDevicePlugins();
}
}

View File

@ -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<T>() where T : class, new()
{
// TODO
return default;
}
public void SetSettings<T>(T settings) where T : class, new()
{
// TODO
}
}
}
}

View File

@ -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<IMassiveKnobPlugin> plugins = new List<IMassiveKnobPlugin>();
public IEnumerable<IMassiveKnobPlugin> Plugins => plugins;
public IEnumerable<IMassiveKnobDevicePlugin> GetDevicePlugins()
{
return plugins.Where(p => p is IMassiveKnobDevicePlugin).Cast<IMassiveKnobDevicePlugin>();
}
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<MassiveKnobPluginAttribute>() != 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);
}
}
}
}

View File

@ -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
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
var container = new Container();
container.Options.EnableAutoVerification = false;
container.RegisterSingleton<IMassiveKnobOrchestrator, MassiveKnobOrchestrator>();
container.Register<App>();
container.Register<SettingsWindow>();
container.Register<SettingsViewModel>();
var settings = await SettingsJsonSerializer.Deserialize();
container.RegisterInstance(settings);
var pluginManager = new PluginManager();
pluginManager.Load();
container.RegisterInstance<IPluginManager>(pluginManager);
var app = container.GetInstance<App>();
app.Run();
}
}
}

View File

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 169 KiB

View File

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

View File

@ -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<ActionSettings> Actions { get; set; }
public static Settings Default()
{
return new Settings
{
Device = null,
Actions = new List<ActionSettings>()
};
}
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; }
}
}
}

View File

@ -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<SerializedSettings>(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<Settings>(json);
}
}
}

View File

@ -0,0 +1,22 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="MenuItem" x:Key="DefaultMenuItem">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style TargetType="TextBlock" x:Key="Header">
<Setter Property="Background" Value="SlateGray" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="5" />
</Style>
<Style TargetType="StackPanel" x:Key="Content">
<Setter Property="Margin" Value="8" />
</Style>
<Style TargetType="TextBlock" x:Key="ComboBoxDescription">
<Setter Property="Foreground" Value="{x:Static SystemColors.GrayTextBrush}" />
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,45 @@
<Window x:Class="MassiveKnob.View.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="clr-namespace:MassiveKnob.Helpers"
mc:Ignorable="d"
Title="Massive Knob - Settings" Height="555" Width="704.231"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Style.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="DeviceDropdownItem">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" Style="{StaticResource ComboBoxDescription}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DeviceSelectedItem">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<ScrollViewer>
<StackPanel Orientation="Vertical">
<TextBlock Style="{StaticResource Header}">Device</TextBlock>
<StackPanel Orientation="Vertical" Style="{StaticResource Content}">
<ComboBox SelectedItem="{Binding SelectedDevice}" ItemsSource="{Binding Devices}" ItemTemplateSelector="{helpers:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource DeviceSelectedItem},
DropdownItemsTemplate={StaticResource DeviceDropdownItem}}" />
<ContentControl Focusable="False" Content="{Binding SettingsControl}" />
</StackPanel>
<TextBlock Style="{StaticResource Header}">Controls</TextBlock>
</StackPanel>
</ScrollViewer>
</Window>

View File

@ -0,0 +1,16 @@
using MassiveKnob.ViewModel;
namespace MassiveKnob.View
{
/// <summary>
/// Interaction logic for SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow
{
public SettingsWindow(SettingsViewModel settingsViewModel)
{
DataContext = settingsViewModel;
InitializeComponent();
}
}
}

View File

@ -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<DeviceViewModel> 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;
}
}
}
}

View File

@ -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
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main()
{
var container = BuildContainer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(container.GetInstance<SettingsForm>());
}
private static Container BuildContainer()
{
var container = new Container();
container.Options.EnableAutoVerification = false;
container.Register<SettingsForm>();
container.GetRegistration(typeof(SettingsForm))?.Registration
.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, "Windows Form implements IDisposable");
container.Register<IAudioDeviceManagerFactory, CoreAudioDeviceManagerFactory>();
// For testing without the hardware:
container.Register<IMassiveKnobHardwareFactory, SerialMassiveKnobHardwareFactory>();
//container.Register<IMassiveKnobHardwareFactory>(() => new MockMassiveKnobHardwareFactory(3, TimeSpan.FromSeconds(1), 25));
return container;
}
}
}

View File

@ -1,70 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MassiveKnob.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,29 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View File

@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
namespace MassiveKnob.Settings
{
public class Settings
{
public string SerialPort { get; set; }
public List<KnobSettings> Knobs { get; set; }
public static Settings Default()
{
return new Settings
{
Knobs = new List<KnobSettings>()
};
}
public class KnobSettings
{
public Guid? DeviceId { get; set; }
}
}
}