Implement exporting and importing platform settings to a file.
This commit is contained in:
parent
2d8d7c0be1
commit
9ed01c29ba
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using SMXJSON;
|
||||||
|
|
||||||
namespace smx_config
|
namespace smx_config
|
||||||
{
|
{
|
||||||
@ -37,6 +38,62 @@ namespace smx_config
|
|||||||
return Color.FromRgb(R, G, B);
|
return Color.FromRgb(R, G, B);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a Color as an HTML color code.
|
||||||
|
public static string ColorToString(Color color)
|
||||||
|
{
|
||||||
|
// WPF's Color.ToString() returns #AARRGGBB, which is just wrong. Alpha is always
|
||||||
|
// last in HTML color codes. We don't need alpha, so just strip it off.
|
||||||
|
return "#" + color.ToString().Substring(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse #RRGGBB and return a Color, or white if the string isn't in the correct format.
|
||||||
|
public static Color ParseColorString(string s)
|
||||||
|
{
|
||||||
|
// We only expect "#RRGGBB".
|
||||||
|
if(s.Length != 7 || !s.StartsWith("#"))
|
||||||
|
return Color.FromRgb(255,255,255);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Color) ColorConverter.ConvertFromString(s);
|
||||||
|
}
|
||||||
|
catch(System.FormatException)
|
||||||
|
{
|
||||||
|
return Color.FromRgb(255,255,255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Light values are actually in the range 0-170 and not 0-255, since higher values aren't
|
||||||
|
// any brighter and just draw more power. The auto-lighting colors that we're configuring
|
||||||
|
// need to be scaled to this range too, but show full range colors in the UI.
|
||||||
|
readonly static double LightsScaleFactor = 0.666666f;
|
||||||
|
static public Byte ScaleColor(Byte c)
|
||||||
|
{
|
||||||
|
return (Byte) Math.Round(c * LightsScaleFactor);
|
||||||
|
}
|
||||||
|
static public Byte UnscaleColor(Byte c)
|
||||||
|
{
|
||||||
|
Byte result = (Byte) Math.Round(Math.Min(255, c / LightsScaleFactor));
|
||||||
|
|
||||||
|
// The color values we output are quantized, since we're scaling an 8-bit value.
|
||||||
|
// This doesn't have any real effect, but it causes #FFFFFF in the settings export
|
||||||
|
// file to be written out as #FDFDFD (which has the same value in hardware). Just
|
||||||
|
// so the common value of white is clean, snap these values to 0xFF. The end result
|
||||||
|
// will be the same.
|
||||||
|
if(result >= 0xFD)
|
||||||
|
return 0xFF;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Color ScaleColor(Color c)
|
||||||
|
{
|
||||||
|
return Color.FromRgb(ScaleColor(c.R), ScaleColor(c.G), ScaleColor(c.B));
|
||||||
|
}
|
||||||
|
|
||||||
|
static public Color UnscaleColor(Color c)
|
||||||
|
{
|
||||||
|
return Color.FromRgb(UnscaleColor(c.R), UnscaleColor(c.G), UnscaleColor(c.B));
|
||||||
|
}
|
||||||
|
|
||||||
public static Color FromHSV(double H, double S, double V)
|
public static Color FromHSV(double H, double S, double V)
|
||||||
{
|
{
|
||||||
H = H % 360;
|
H = H % 360;
|
||||||
@ -96,6 +153,18 @@ namespace smx_config
|
|||||||
s = C / M;
|
s = C / M;
|
||||||
v = M;
|
v = M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read path. If an error is encountered, return "".
|
||||||
|
public static string ReadFile(string path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return System.IO.File.ReadAllText(path);
|
||||||
|
}
|
||||||
|
catch(System.IO.IOException)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class just makes it easier to assemble binary command packets.
|
// This class just makes it easier to assemble binary command packets.
|
||||||
@ -201,4 +270,119 @@ namespace smx_config
|
|||||||
SMX.SMX.SetLights(cmd.Get());
|
SMX.SMX.SetLights(cmd.Get());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static class SMXHelpers
|
||||||
|
{
|
||||||
|
// Export configurable values in SMXConfig to a JSON string.
|
||||||
|
public static string ExportSettingsToJSON(SMX.SMXConfig config)
|
||||||
|
{
|
||||||
|
Dictionary<string, Object> dict = new Dictionary<string, Object>();
|
||||||
|
List<int> panelLowThresholds = new List<int>();
|
||||||
|
panelLowThresholds.Add(config.panelThreshold0Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold1Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold2Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold3Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold4Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold5Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold6Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold7Low);
|
||||||
|
panelLowThresholds.Add(config.panelThreshold8Low);
|
||||||
|
dict.Add("panelLowThresholds", panelLowThresholds);
|
||||||
|
|
||||||
|
List<int> panelHighThresholds = new List<int>();
|
||||||
|
panelHighThresholds.Add(config.panelThreshold0High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold1High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold2High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold3High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold4High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold5High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold6High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold7High);
|
||||||
|
panelHighThresholds.Add(config.panelThreshold8High);
|
||||||
|
dict.Add("panelHighThresholds", panelHighThresholds);
|
||||||
|
|
||||||
|
// Store the enabled panel mask as a simple list of which panels are selected.
|
||||||
|
bool[] enabledPanels = config.GetEnabledPanels();
|
||||||
|
List<int> enabledPanelList = new List<int>();
|
||||||
|
for(int panel = 0; panel < 9; ++panel)
|
||||||
|
{
|
||||||
|
if(enabledPanels[panel])
|
||||||
|
enabledPanelList.Add(panel);
|
||||||
|
}
|
||||||
|
dict.Add("enabledPanels", enabledPanelList);
|
||||||
|
|
||||||
|
// Store panel colors.
|
||||||
|
List<string> panelColors = new List<string>();
|
||||||
|
for(int PanelIndex = 0; PanelIndex < 9; ++PanelIndex)
|
||||||
|
{
|
||||||
|
// Scale colors from the hardware value back to the 0-255 value we use in the UI.
|
||||||
|
Color color = Color.FromRgb(config.stepColor[PanelIndex*3+0], config.stepColor[PanelIndex*3+1], config.stepColor[PanelIndex*3+2]);
|
||||||
|
color = Helpers.UnscaleColor(color);
|
||||||
|
panelColors.Add(Helpers.ColorToString(color));
|
||||||
|
}
|
||||||
|
dict.Add("panelColors", panelColors);
|
||||||
|
|
||||||
|
return SMXJSON.SerializeJSON.Serialize(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import a saved JSON configuration to an SMXConfig.
|
||||||
|
public static void ImportSettingsFromJSON(string json, ref SMX.SMXConfig config)
|
||||||
|
{
|
||||||
|
Dictionary<string, Object> dict = SMXJSON.ParseJSON.Parse<Dictionary<string, Object>>(json);
|
||||||
|
|
||||||
|
// Read the thresholds. If any values are missing, we'll leave the value in config alone.
|
||||||
|
List<Object> newPanelLowThresholds = dict.Get("panelLowThresholds", new List<Object>());
|
||||||
|
config.panelThreshold0Low = newPanelLowThresholds.Get(0, config.panelThreshold0Low);
|
||||||
|
config.panelThreshold1Low = newPanelLowThresholds.Get(1, config.panelThreshold1Low);
|
||||||
|
config.panelThreshold2Low = newPanelLowThresholds.Get(2, config.panelThreshold2Low);
|
||||||
|
config.panelThreshold3Low = newPanelLowThresholds.Get(3, config.panelThreshold3Low);
|
||||||
|
config.panelThreshold4Low = newPanelLowThresholds.Get(4, config.panelThreshold4Low);
|
||||||
|
config.panelThreshold5Low = newPanelLowThresholds.Get(5, config.panelThreshold5Low);
|
||||||
|
config.panelThreshold6Low = newPanelLowThresholds.Get(6, config.panelThreshold6Low);
|
||||||
|
config.panelThreshold7Low = newPanelLowThresholds.Get(7, config.panelThreshold7Low);
|
||||||
|
config.panelThreshold8Low = newPanelLowThresholds.Get(8, config.panelThreshold8Low);
|
||||||
|
|
||||||
|
List<Object> newPanelHighThresholds = dict.Get("panelHighThresholds", new List<Object>());
|
||||||
|
config.panelThreshold0High = newPanelHighThresholds.Get(0, config.panelThreshold0High);
|
||||||
|
config.panelThreshold1High = newPanelHighThresholds.Get(1, config.panelThreshold1High);
|
||||||
|
config.panelThreshold2High = newPanelHighThresholds.Get(2, config.panelThreshold2High);
|
||||||
|
config.panelThreshold3High = newPanelHighThresholds.Get(3, config.panelThreshold3High);
|
||||||
|
config.panelThreshold4High = newPanelHighThresholds.Get(4, config.panelThreshold4High);
|
||||||
|
config.panelThreshold5High = newPanelHighThresholds.Get(5, config.panelThreshold5High);
|
||||||
|
config.panelThreshold6High = newPanelHighThresholds.Get(6, config.panelThreshold6High);
|
||||||
|
config.panelThreshold7High = newPanelHighThresholds.Get(7, config.panelThreshold7High);
|
||||||
|
config.panelThreshold8High = newPanelHighThresholds.Get(8, config.panelThreshold8High);
|
||||||
|
|
||||||
|
List<Object> enabledPanelList = dict.Get<List<Object>>("enabledPanels", null);
|
||||||
|
if(enabledPanelList != null)
|
||||||
|
{
|
||||||
|
bool[] enabledPanels = new bool[9];
|
||||||
|
for(int i = 0; i < enabledPanelList.Count; ++i)
|
||||||
|
{
|
||||||
|
int panel = enabledPanelList.Get(i, 0);
|
||||||
|
|
||||||
|
// Sanity check:
|
||||||
|
if(panel < 0 || panel >= 9)
|
||||||
|
continue;
|
||||||
|
enabledPanels[panel] = true;
|
||||||
|
}
|
||||||
|
config.SetEnabledPanels(enabledPanels);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> panelColors = dict.Get<List<Object>>("panelColors", null);
|
||||||
|
if(panelColors != null)
|
||||||
|
{
|
||||||
|
for(int PanelIndex = 0; PanelIndex < 9 && PanelIndex < panelColors.Count; ++PanelIndex)
|
||||||
|
{
|
||||||
|
string colorString = panelColors.Get(PanelIndex, "#FFFFFF");
|
||||||
|
Color color = Helpers.ParseColorString(colorString);
|
||||||
|
color = Helpers.ScaleColor(color);
|
||||||
|
|
||||||
|
config.stepColor[PanelIndex*3+0] = color.R;
|
||||||
|
config.stepColor[PanelIndex*3+1] = color.G;
|
||||||
|
config.stepColor[PanelIndex*3+2] = color.B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -781,6 +781,16 @@ Input will be disabled from deselected panels.</TextBlock>
|
|||||||
<controls:PanelSelector Focusable="false" />
|
<controls:PanelSelector Focusable="false" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<Separator Margin="0,10,0,10" />
|
||||||
|
<TextBlock HorizontalAlignment="Center"
|
||||||
|
xml:space="preserve" FontSize="16">Import/export settings</TextBlock>
|
||||||
|
<TextBlock xml:space="preserve" HorizontalAlignment="Center" Margin="0,5,0,0" TextAlignment="Center"
|
||||||
|
>Export current settings to a file.</TextBlock>
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
|
<Button Content="Export" Width="50" Padding="5,2" Margin="5,10" Click="ExportSettings" />
|
||||||
|
<Button Content="Import" Width="50" Padding="5,2" Margin="5,10" Click="ImportSettings" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Separator Margin="0,10,0,10" />
|
<Separator Margin="0,10,0,10" />
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center"
|
||||||
xml:space="preserve" FontSize="16">Reset all settings</TextBlock>
|
xml:space="preserve" FontSize="16">Reset all settings</TextBlock>
|
||||||
|
@ -82,5 +82,56 @@ namespace smx_config
|
|||||||
SMX.SMX.FactoryReset(pad);
|
SMX.SMX.FactoryReset(pad);
|
||||||
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExportSettings(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Save the current thresholds on the first available pad as a preset.
|
||||||
|
for(int pad = 0; pad < 2; ++pad)
|
||||||
|
{
|
||||||
|
SMX.SMXConfig config;
|
||||||
|
if(!SMX.SMX.GetConfig(pad, out config))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string json = SMXHelpers.ExportSettingsToJSON(config);
|
||||||
|
|
||||||
|
Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
|
||||||
|
dialog.FileName = "StepManiaX settings";
|
||||||
|
dialog.DefaultExt = ".smxcfg";
|
||||||
|
dialog.Filter = "StepManiaX settings (.smxcfg)|*.smxcfg";
|
||||||
|
bool? result = dialog.ShowDialog();
|
||||||
|
if(result == null || !(bool) result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
System.IO.File.WriteAllText(dialog.FileName, json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImportSettings(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Prompt for a file to read.
|
||||||
|
Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
|
||||||
|
dialog.FileName = "StepManiaX settings";
|
||||||
|
dialog.DefaultExt = ".smxcfg";
|
||||||
|
dialog.Filter = "StepManiaX settings (.smxcfg)|*.smxcfg";
|
||||||
|
bool? result = dialog.ShowDialog();
|
||||||
|
if(result == null || !(bool) result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string json = Helpers.ReadFile(dialog.FileName);
|
||||||
|
|
||||||
|
// Apply settings from the file to all connected pads.
|
||||||
|
for(int pad = 0; pad < 2; ++pad)
|
||||||
|
{
|
||||||
|
SMX.SMXConfig config;
|
||||||
|
if(!SMX.SMX.GetConfig(pad, out config))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SMXHelpers.ImportSettingsFromJSON(json, ref config);
|
||||||
|
SMX.SMX.SetConfig(pad, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,23 @@ namespace SMX
|
|||||||
(enabledSensors[4] & 0xF0) != 0,
|
(enabledSensors[4] & 0xF0) != 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set enabledSensors from an array returned from GetEnabledPanels.
|
||||||
|
public void SetEnabledPanels(bool[] panels)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 5; ++i)
|
||||||
|
enabledSensors[i] = 0;
|
||||||
|
|
||||||
|
if(panels[0]) enabledSensors[0] |= 0xF0;
|
||||||
|
if(panels[1]) enabledSensors[0] |= 0x0F;
|
||||||
|
if(panels[2]) enabledSensors[1] |= 0xF0;
|
||||||
|
if(panels[3]) enabledSensors[1] |= 0x0F;
|
||||||
|
if(panels[4]) enabledSensors[2] |= 0xF0;
|
||||||
|
if(panels[5]) enabledSensors[2] |= 0x0F;
|
||||||
|
if(panels[6]) enabledSensors[3] |= 0xF0;
|
||||||
|
if(panels[7]) enabledSensors[3] |= 0x0F;
|
||||||
|
if(panels[8]) enabledSensors[4] |= 0xF0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public struct SMXSensorTestModeData
|
public struct SMXSensorTestModeData
|
||||||
|
@ -103,6 +103,7 @@
|
|||||||
<Compile Include="DiagnosticsWidgets.cs" />
|
<Compile Include="DiagnosticsWidgets.cs" />
|
||||||
<Compile Include="DoubleSlider.cs" />
|
<Compile Include="DoubleSlider.cs" />
|
||||||
<Compile Include="Helpers.cs" />
|
<Compile Include="Helpers.cs" />
|
||||||
|
<Compile Include="SMXJSON.cs" />
|
||||||
<Compile Include="SMX.cs" />
|
<Compile Include="SMX.cs" />
|
||||||
<Compile Include="Widgets.cs" />
|
<Compile Include="Widgets.cs" />
|
||||||
<Page Include="MainWindow.xaml">
|
<Page Include="MainWindow.xaml">
|
||||||
|
531
smx-config/SMXJSON.cs
Normal file
531
smx-config/SMXJSON.cs
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
// All C# JSON implementations are either pretty awful or incredibly bloated, so we
|
||||||
|
// just use our own.
|
||||||
|
namespace SMXJSON
|
||||||
|
{
|
||||||
|
public class JSONError: Exception
|
||||||
|
{
|
||||||
|
public JSONError(string error)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public class ParseError: JSONError
|
||||||
|
{
|
||||||
|
public ParseError(StringReader reader, string error):
|
||||||
|
base(error)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static class ObjectListExtensions
|
||||||
|
{
|
||||||
|
public static T Get<T>(this List<object> array, int idx, T defaultValue)
|
||||||
|
{
|
||||||
|
if(idx < 0 || idx >= array.Count)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
object value = array[idx];
|
||||||
|
if(!typeof(T).IsAssignableFrom(value.GetType()))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our numbers are always doubles. Add some other basic data types for convenience.
|
||||||
|
public static int Get(this List<object> array, int idx, int defaultValue)
|
||||||
|
{
|
||||||
|
return (int) array.Get(idx, (double) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Byte Get(this List<object> array, int idx, Byte defaultValue)
|
||||||
|
{
|
||||||
|
return (Byte) array.Get(idx, (double) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Get(this List<object> array, int idx, float defaultValue)
|
||||||
|
{
|
||||||
|
return (float) array.Get(idx, (double) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the value of key. If it doesn't exist, or doesn't have the expected
|
||||||
|
// type, return defaultValue.
|
||||||
|
public static T Get<T>(this Dictionary<string, Object> dict, string key, T defaultValue)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
if(!dict.TryGetValue(key, out value))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if(!typeof(T).IsAssignableFrom(value.GetType()))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set result to the value of key if it exists and has the correct type, and return
|
||||||
|
// true. Otherwise, leave result unchanged and return false.
|
||||||
|
public static bool GetValue<T>(this Dictionary<string, Object> dict, string key, ref T result)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
if(!dict.TryGetValue(key, out value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!typeof(T).IsAssignableFrom(result.GetType()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
result = (T) value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our numbers are always doubles. Add some other basic data types for convenience.
|
||||||
|
public static int Get(this Dictionary<string, Object> dict, string key, int defaultValue)
|
||||||
|
{
|
||||||
|
return (int) dict.Get(key, (double) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Byte Get(this Dictionary<string, Object> dict, string key, Byte defaultValue)
|
||||||
|
{
|
||||||
|
return (Byte) dict.Get(key, (double) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Get(this Dictionary<string, Object> dict, string key, float defaultValue)
|
||||||
|
{
|
||||||
|
return (float) dict.Get(key, (double) defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SerializeJSON
|
||||||
|
{
|
||||||
|
static public string Serialize(object obj)
|
||||||
|
{
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
Serialize(obj, output, 0);
|
||||||
|
output.Append('\n');
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add start-of-line indentation.
|
||||||
|
static private void AddIndent(StringBuilder output, int indent)
|
||||||
|
{
|
||||||
|
output.Append(' ', indent*4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize a boolean.
|
||||||
|
static private void SerializeObject(bool value, StringBuilder output)
|
||||||
|
{
|
||||||
|
if(value)
|
||||||
|
output.Append("true");
|
||||||
|
else
|
||||||
|
output.Append("false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize a string.
|
||||||
|
static private void SerializeObject(String str, StringBuilder output)
|
||||||
|
{
|
||||||
|
output.Append('"');
|
||||||
|
|
||||||
|
foreach(char c in str)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '"': output.Append("\\\""); break;
|
||||||
|
case '\\': output.Append("\\\\"); break;
|
||||||
|
case '\b': output.Append("\\b"); break;
|
||||||
|
case '\n': output.Append("\\n"); break;
|
||||||
|
case '\r': output.Append("\\r"); break;
|
||||||
|
case '\t': output.Append("\\t"); break;
|
||||||
|
default:
|
||||||
|
// We don't escape Unicode. Every sane JSON parser accepts UTF-8.
|
||||||
|
output.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Append('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize an array.
|
||||||
|
static private void SerializeObject<T>(List<T> array, StringBuilder output, int indent)
|
||||||
|
{
|
||||||
|
output.Append("[\n");
|
||||||
|
bool first = true;
|
||||||
|
indent += 1;
|
||||||
|
foreach(T element in array)
|
||||||
|
{
|
||||||
|
if(first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
output.Append(",\n");
|
||||||
|
|
||||||
|
AddIndent(output, indent);
|
||||||
|
Serialize(element, output, indent);
|
||||||
|
}
|
||||||
|
output.Append('\n');
|
||||||
|
|
||||||
|
indent -= 1;
|
||||||
|
AddIndent(output, indent);
|
||||||
|
output.Append(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize a dictionary.
|
||||||
|
static private void SerializeObject<T>(Dictionary<string, T> dict, StringBuilder output, int indent)
|
||||||
|
{
|
||||||
|
output.Append("{\n");
|
||||||
|
|
||||||
|
indent += 1;
|
||||||
|
bool first = true;
|
||||||
|
foreach(KeyValuePair<string,T> element in dict)
|
||||||
|
{
|
||||||
|
if(first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
output.Append(",\n");
|
||||||
|
|
||||||
|
AddIndent(output, indent);
|
||||||
|
SerializeObject(element.Key, output);
|
||||||
|
output.Append(':');
|
||||||
|
output.Append(' ');
|
||||||
|
Serialize(element.Value, output, indent);
|
||||||
|
}
|
||||||
|
output.Append('\n');
|
||||||
|
|
||||||
|
indent -= 1;
|
||||||
|
AddIndent(output, indent);
|
||||||
|
output.Append('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize an object based on its type.
|
||||||
|
static public void Serialize(object obj, StringBuilder output, int indent)
|
||||||
|
{
|
||||||
|
if(obj == null) { output.Append("null"); return; }
|
||||||
|
|
||||||
|
if(typeof(Int32).IsInstanceOfType(obj)) { output.Append(obj.ToString()); return; }
|
||||||
|
if(typeof(float).IsInstanceOfType(obj)) { output.Append(obj.ToString()); return; }
|
||||||
|
if(typeof(Double).IsInstanceOfType(obj)) { output.Append(obj.ToString()); return; }
|
||||||
|
|
||||||
|
if(typeof(Boolean).IsInstanceOfType(obj)) { SerializeObject((Boolean) obj, output); return; }
|
||||||
|
if(typeof(string).IsInstanceOfType(obj)) { SerializeObject((string) obj, output); return; }
|
||||||
|
|
||||||
|
// C# generics aren't very well designed, so this is clunky. We should be able to cast
|
||||||
|
// a List<string> to List<object>, but some overzealous language designers thought that
|
||||||
|
// since that has some unsafe uses, we shouldn't be allowed to use it for perfectly safe
|
||||||
|
// uses either (eg. read-only access).
|
||||||
|
if(obj.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>)))
|
||||||
|
{
|
||||||
|
Type valueType = obj.GetType().GetGenericArguments()[0];
|
||||||
|
if(valueType == typeof(object)) { SerializeObject((List<object>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Int32)) { SerializeObject((List<Int32>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(float)) { SerializeObject((List<float>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Double)) { SerializeObject((List<Double>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Boolean)) { SerializeObject((List<Boolean>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(string)) { SerializeObject((List<string>)obj, output, indent); return; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(obj.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)))
|
||||||
|
{
|
||||||
|
Type keyType = obj.GetType().GetGenericArguments()[0];
|
||||||
|
if(typeof(string).IsAssignableFrom(keyType))
|
||||||
|
{
|
||||||
|
Type valueType = obj.GetType().GetGenericArguments()[1];
|
||||||
|
if(valueType == typeof(object)) { SerializeObject((Dictionary<string, object>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Int32)) { SerializeObject((Dictionary<string, Int32>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(float)) { SerializeObject((Dictionary<string, float>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Double)) { SerializeObject((Dictionary<string, Double>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(Boolean)) { SerializeObject((Dictionary<string, Boolean>)obj, output, indent); return; }
|
||||||
|
if(valueType == typeof(string)) { SerializeObject((Dictionary<string, string>)obj, output, indent); return; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JSONError("Unsupported type: " + obj.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParseJSON
|
||||||
|
{
|
||||||
|
static private void SkipWhitespace(StringReader reader)
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int c = reader.Peek();
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\n':
|
||||||
|
case '\t':
|
||||||
|
reader.Read();
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON. On error, return null.
|
||||||
|
public static Object Parse(string json)
|
||||||
|
{
|
||||||
|
return ParseWithExceptions(new StringReader(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON, expecting a specific outer type. On parse error, return a default value.
|
||||||
|
public static T Parse<T>(string json) where T: new()
|
||||||
|
{
|
||||||
|
Object result = Parse(json);
|
||||||
|
if(!typeof(T).IsAssignableFrom(result.GetType()))
|
||||||
|
return new T();
|
||||||
|
return (T) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON. On error, raise JSONError.
|
||||||
|
//
|
||||||
|
// Most of the time, errors aren't expected and putting exception handling around every
|
||||||
|
// place JSON is parsed can be brittle. Parse() can be used instead to just return
|
||||||
|
// a default value.
|
||||||
|
public static Object ParseWithExceptions(StringReader reader)
|
||||||
|
{
|
||||||
|
Object result = ParseJSONValue(reader);
|
||||||
|
|
||||||
|
SkipWhitespace(reader);
|
||||||
|
|
||||||
|
// Other than whitespace, we should be at the end of the file.
|
||||||
|
if(reader.Read() != -1)
|
||||||
|
throw new ParseError(reader, "Unexpected data at the end of the string");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object ParseJSONValue(StringReader reader)
|
||||||
|
{
|
||||||
|
SkipWhitespace(reader);
|
||||||
|
int nextCharacter = reader.Peek();
|
||||||
|
switch(nextCharacter)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
return ReadJSONString(reader);
|
||||||
|
case '{':
|
||||||
|
return ReadJSONDictionary(reader);
|
||||||
|
case '[':
|
||||||
|
return ReadJSONArray(reader);
|
||||||
|
}
|
||||||
|
if(nextCharacter == '-' || (nextCharacter >= '0' && nextCharacter <= '9'))
|
||||||
|
return ReadJSONNumber(reader);
|
||||||
|
|
||||||
|
if(reader.Peek() == 'n')
|
||||||
|
{
|
||||||
|
// The only valid value this can be is "null".
|
||||||
|
Expect(reader, 'n');
|
||||||
|
Expect(reader, 'u');
|
||||||
|
Expect(reader, 'l');
|
||||||
|
Expect(reader, 'l');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reader.Peek() == 't')
|
||||||
|
{
|
||||||
|
Expect(reader, 't');
|
||||||
|
Expect(reader, 'r');
|
||||||
|
Expect(reader, 'u');
|
||||||
|
Expect(reader, 'e');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reader.Peek() == 'f')
|
||||||
|
{
|
||||||
|
Expect(reader, 'f');
|
||||||
|
Expect(reader, 'a');
|
||||||
|
Expect(reader, 'l');
|
||||||
|
Expect(reader, 's');
|
||||||
|
Expect(reader, 'e');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseError(reader, "Unexpected token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip whitespace, then read one character, which we expect to have a specific value.
|
||||||
|
static private void Expect(StringReader reader, char character)
|
||||||
|
{
|
||||||
|
SkipWhitespace(reader);
|
||||||
|
|
||||||
|
if(reader.Read() != character)
|
||||||
|
throw new ParseError(reader, "Expected " + character);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private List<Object> ReadJSONArray(StringReader reader)
|
||||||
|
{
|
||||||
|
Expect(reader, '[');
|
||||||
|
List<Object> result = new List<Object>();
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
Object value = ParseJSONValue(reader);
|
||||||
|
result.Add(value);
|
||||||
|
|
||||||
|
SkipWhitespace(reader);
|
||||||
|
int nextCharacter = reader.Read();
|
||||||
|
switch(nextCharacter)
|
||||||
|
{
|
||||||
|
case ']':
|
||||||
|
return result;
|
||||||
|
case ',':
|
||||||
|
continue;
|
||||||
|
case -1:
|
||||||
|
throw new ParseError(reader, "Unexpected EOF reading array");
|
||||||
|
default:
|
||||||
|
throw new ParseError(reader, "Unexpected token " + nextCharacter + " reading array");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static private Dictionary<string, Object> ReadJSONDictionary(StringReader reader)
|
||||||
|
{
|
||||||
|
Expect(reader, '{');
|
||||||
|
|
||||||
|
Dictionary<string, Object> result = new Dictionary<string, Object>();
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
string key = ReadJSONString(reader);
|
||||||
|
Expect(reader, ':');
|
||||||
|
Object value = ParseJSONValue(reader);
|
||||||
|
result.Add(key, value);
|
||||||
|
|
||||||
|
SkipWhitespace(reader);
|
||||||
|
switch(reader.Read())
|
||||||
|
{
|
||||||
|
case '}':
|
||||||
|
return result;
|
||||||
|
case ',':
|
||||||
|
continue;
|
||||||
|
case -1:
|
||||||
|
throw new ParseError(reader, "Unexpected EOF reading dictionary");
|
||||||
|
default:
|
||||||
|
throw new ParseError(reader, "Unexpected token reading dictionary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private string ReadJSONString(StringReader reader)
|
||||||
|
{
|
||||||
|
Expect(reader, '"');
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int c = reader.Read();
|
||||||
|
if(c == -1)
|
||||||
|
throw new ParseError(reader, "Unexpected EOF reading string");
|
||||||
|
if(c == '"')
|
||||||
|
break;
|
||||||
|
|
||||||
|
// XXX: untested
|
||||||
|
if(c == '\\')
|
||||||
|
{
|
||||||
|
c = reader.Read();
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
case '/':
|
||||||
|
result.Append((char) c);
|
||||||
|
break;
|
||||||
|
case 'b': result.Append('\b'); break;
|
||||||
|
case 'n': result.Append('\n'); break;
|
||||||
|
case 'r': result.Append('\r'); break;
|
||||||
|
case 't': result.Append('\t'); break;
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
// Parse a \u1234 escape.
|
||||||
|
int codePoint = 0;
|
||||||
|
for(int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
codePoint *= 10;
|
||||||
|
c = reader.Read();
|
||||||
|
if(c == -1)
|
||||||
|
throw new ParseError(reader, "Unexpected EOF reading string");
|
||||||
|
if(c < '0' || c > '9')
|
||||||
|
throw new ParseError(reader, "Unexpected token " + c + " reading Unicode escape");
|
||||||
|
codePoint += c - (int) '0';
|
||||||
|
}
|
||||||
|
result.Append((char) codePoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ParseError(reader, "Unrecognized escape sequence in string: \\" + (char) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append((char) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private double ReadJSONNumber(StringReader reader)
|
||||||
|
{
|
||||||
|
StringBuilder number = new StringBuilder();
|
||||||
|
bool negative = false;
|
||||||
|
if(reader.Peek() == '-')
|
||||||
|
{
|
||||||
|
negative = true;
|
||||||
|
reader.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
int nextCharacter = reader.Read();
|
||||||
|
if(nextCharacter == '0')
|
||||||
|
number.Append((char) nextCharacter);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(nextCharacter < '1' || nextCharacter > '9')
|
||||||
|
throw new ParseError(reader, "Unexpected token reading number");
|
||||||
|
number.Append((char) nextCharacter);
|
||||||
|
|
||||||
|
while(reader.Peek() >= '0' && reader.Peek() <= '9')
|
||||||
|
number.Append((char) reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reader.Peek() == '.')
|
||||||
|
{
|
||||||
|
number.Append(reader.Read());
|
||||||
|
|
||||||
|
if(reader.Peek() < '0' || reader.Peek() > '9')
|
||||||
|
throw new ParseError(reader, "Unexpected token reading number");
|
||||||
|
|
||||||
|
while(reader.Peek() >= '0' && reader.Peek() <= '9')
|
||||||
|
number.Append((char) reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reader.Peek() == 'e' || reader.Peek() == 'E')
|
||||||
|
{
|
||||||
|
number.Append((char) reader.Read());
|
||||||
|
if(reader.Peek() == '+' || reader.Peek() == '-')
|
||||||
|
number.Append((char) reader.Read());
|
||||||
|
|
||||||
|
if(reader.Peek() < '0' || reader.Peek() > '9')
|
||||||
|
throw new ParseError(reader, "Unexpected token reading number");
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
nextCharacter = reader.Read();
|
||||||
|
if(nextCharacter < '0' || nextCharacter > '9')
|
||||||
|
break;
|
||||||
|
number.Append((char) nextCharacter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double result;
|
||||||
|
if(!Double.TryParse(number.ToString(), out result))
|
||||||
|
throw new ParseError(reader, "Unexpected error parsing number \"" + number.ToString() + "\"");
|
||||||
|
|
||||||
|
if(negative)
|
||||||
|
result = -result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -456,37 +456,13 @@ namespace smx_config
|
|||||||
bool[] enabledPanels = config.GetEnabledPanels();
|
bool[] enabledPanels = config.GetEnabledPanels();
|
||||||
Visibility = enabledPanels[PanelIndex]? Visibility.Visible:Visibility.Hidden;
|
Visibility = enabledPanels[PanelIndex]? Visibility.Visible:Visibility.Hidden;
|
||||||
|
|
||||||
Color rgb = ColorPicker.UnscaleColor(Color.FromRgb(
|
Color rgb = Helpers.UnscaleColor(Color.FromRgb(
|
||||||
config.stepColor[PanelIndex*3+0],
|
config.stepColor[PanelIndex*3+0],
|
||||||
config.stepColor[PanelIndex*3+1],
|
config.stepColor[PanelIndex*3+1],
|
||||||
config.stepColor[PanelIndex*3+2]));
|
config.stepColor[PanelIndex*3+2]));
|
||||||
PanelColor = new SolidColorBrush(rgb);
|
PanelColor = new SolidColorBrush(rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return #RRGGBB for the color set on this panel.
|
|
||||||
private string GetColorString()
|
|
||||||
{
|
|
||||||
// WPF's Color.ToString() returns #AARRGGBB, which is just wrong. Alpha is always
|
|
||||||
// last in HTML color codes. We don't need alpha, so just strip it off.
|
|
||||||
return "#" + PanelColor.Color.ToString().Substring(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse #RRGGBB and return a Color, or white if the string isn't in the correct format.
|
|
||||||
private static Color ParseColorString(string s)
|
|
||||||
{
|
|
||||||
// We only expect "#RRGGBB".
|
|
||||||
if(s.Length != 7 || !s.StartsWith("#"))
|
|
||||||
return Color.FromRgb(255,255,255);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return (Color) ColorConverter.ConvertFromString(s);
|
|
||||||
}
|
|
||||||
catch(System.FormatException)
|
|
||||||
{
|
|
||||||
return Color.FromRgb(255,255,255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Point MouseDownPosition;
|
Point MouseDownPosition;
|
||||||
|
|
||||||
protected override void OnMouseDown(MouseButtonEventArgs e)
|
protected override void OnMouseDown(MouseButtonEventArgs e)
|
||||||
@ -507,7 +483,7 @@ namespace smx_config
|
|||||||
if (Math.Abs(position.X - MouseDownPosition.X) >= SystemParameters.MinimumHorizontalDragDistance ||
|
if (Math.Abs(position.X - MouseDownPosition.X) >= SystemParameters.MinimumHorizontalDragDistance ||
|
||||||
Math.Abs(position.Y - MouseDownPosition.Y) >= SystemParameters.MinimumVerticalDragDistance)
|
Math.Abs(position.Y - MouseDownPosition.Y) >= SystemParameters.MinimumVerticalDragDistance)
|
||||||
{
|
{
|
||||||
DragDrop.DoDragDrop(this, GetColorString(), DragDropEffects.Copy);
|
DragDrop.DoDragDrop(this, Helpers.ColorToString(PanelColor.Color), DragDropEffects.Copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +503,7 @@ namespace smx_config
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Parse the color being dragged onto us.
|
// Parse the color being dragged onto us.
|
||||||
Color color = ParseColorString(data.GetData(typeof(string)) as string);
|
Color color = Helpers.ParseColorString(data.GetData(typeof(string)) as string);
|
||||||
|
|
||||||
// Update the panel color.
|
// Update the panel color.
|
||||||
int PanelIndex = Panel % 9;
|
int PanelIndex = Panel % 9;
|
||||||
@ -538,9 +514,10 @@ namespace smx_config
|
|||||||
|
|
||||||
// Light colors are 8-bit values, but we only use values between 0-170. Higher values
|
// Light colors are 8-bit values, but we only use values between 0-170. Higher values
|
||||||
// don't make the panel noticeably brighter, and just draw more power.
|
// don't make the panel noticeably brighter, and just draw more power.
|
||||||
config.stepColor[PanelIndex*3+0] = ColorPicker.ScaleColor(color.R);
|
color = Helpers.ScaleColor(color);
|
||||||
config.stepColor[PanelIndex*3+1] = ColorPicker.ScaleColor(color.G);
|
config.stepColor[PanelIndex*3+0] = color.R;
|
||||||
config.stepColor[PanelIndex*3+2] = ColorPicker.ScaleColor(color.B);
|
config.stepColor[PanelIndex*3+1] = color.G;
|
||||||
|
config.stepColor[PanelIndex*3+2] = color.B;
|
||||||
|
|
||||||
SMX.SMX.SetConfig(Pad, config);
|
SMX.SMX.SetConfig(Pad, config);
|
||||||
CurrentSMXDevice.singleton.FireConfigurationChanged(this);
|
CurrentSMXDevice.singleton.FireConfigurationChanged(this);
|
||||||
@ -664,29 +641,6 @@ namespace smx_config
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Light values are actually in the range 0-170 and not 0-255, since higher values aren't
|
|
||||||
// any brighter and just draw more power. The auto-lighting colors that we're configuring
|
|
||||||
// need to be scaled to this range too, but show full range colors in the UI.
|
|
||||||
readonly static double LightsScaleFactor = 0.666666f;
|
|
||||||
static public Byte ScaleColor(Byte c) { return (Byte) (c * LightsScaleFactor); }
|
|
||||||
static public Byte UnscaleColor(Byte c) { return (Byte) Math.Min(255, c / LightsScaleFactor); }
|
|
||||||
|
|
||||||
static public Color ScaleColor(Color c)
|
|
||||||
{
|
|
||||||
return Color.FromRgb(
|
|
||||||
ColorPicker.ScaleColor(c.R),
|
|
||||||
ColorPicker.ScaleColor(c.G),
|
|
||||||
ColorPicker.ScaleColor(c.B));
|
|
||||||
}
|
|
||||||
|
|
||||||
static public Color UnscaleColor(Color c)
|
|
||||||
{
|
|
||||||
return Color.FromRgb(
|
|
||||||
ColorPicker.UnscaleColor(c.R),
|
|
||||||
ColorPicker.UnscaleColor(c.G),
|
|
||||||
ColorPicker.UnscaleColor(c.B));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveToConfig()
|
private void SaveToConfig()
|
||||||
{
|
{
|
||||||
if(UpdatingUI)
|
if(UpdatingUI)
|
||||||
@ -707,9 +661,9 @@ namespace smx_config
|
|||||||
// Light colors are 8-bit values, but we only use values between 0-170. Higher values
|
// Light colors are 8-bit values, but we only use values between 0-170. Higher values
|
||||||
// don't make the panel noticeably brighter, and just draw more power.
|
// don't make the panel noticeably brighter, and just draw more power.
|
||||||
int PanelIndex = SelectedPanel % 9;
|
int PanelIndex = SelectedPanel % 9;
|
||||||
config.stepColor[PanelIndex*3+0] = ScaleColor(color.R);
|
config.stepColor[PanelIndex*3+0] = Helpers.ScaleColor(color.R);
|
||||||
config.stepColor[PanelIndex*3+1] = ScaleColor(color.G);
|
config.stepColor[PanelIndex*3+1] = Helpers.ScaleColor(color.G);
|
||||||
config.stepColor[PanelIndex*3+2] = ScaleColor(color.B);
|
config.stepColor[PanelIndex*3+2] = Helpers.ScaleColor(color.B);
|
||||||
|
|
||||||
SMX.SMX.SetConfig(pad, config);
|
SMX.SMX.SetConfig(pad, config);
|
||||||
CurrentSMXDevice.singleton.FireConfigurationChanged(this);
|
CurrentSMXDevice.singleton.FireConfigurationChanged(this);
|
||||||
@ -723,10 +677,10 @@ namespace smx_config
|
|||||||
|
|
||||||
// Reverse the scaling we applied in SaveToConfig.
|
// Reverse the scaling we applied in SaveToConfig.
|
||||||
int PanelIndex = SelectedPanel % 9;
|
int PanelIndex = SelectedPanel % 9;
|
||||||
Color rgb = Color.FromRgb(
|
Color rgb = Helpers.UnscaleColor(Color.FromRgb(
|
||||||
UnscaleColor(config.stepColor[PanelIndex*3+0]),
|
config.stepColor[PanelIndex*3+0],
|
||||||
UnscaleColor(config.stepColor[PanelIndex*3+1]),
|
config.stepColor[PanelIndex*3+1],
|
||||||
UnscaleColor(config.stepColor[PanelIndex*3+2]));
|
config.stepColor[PanelIndex*3+2]));
|
||||||
double h, s, v;
|
double h, s, v;
|
||||||
Helpers.ToHSV(rgb, out h, out s, out v);
|
Helpers.ToHSV(rgb, out h, out s, out v);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user