Animations should be enabled only if the user selects "GIF animations". They were being enabled when in the default panel color mode, which was confusing and not intended.
669 lines
29 KiB
C#
669 lines
29 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Media;
|
|
using System.Windows.Interop;
|
|
|
|
namespace smx_config
|
|
{
|
|
public partial class MainWindow: Window
|
|
{
|
|
OnConfigChange onConfigChange;
|
|
ShowAutoLightsColor showAutoLightsColor = new ShowAutoLightsColor();
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
|
|
onConfigChange = new OnConfigChange(this, delegate (LoadFromConfigDelegateArgs args)
|
|
{
|
|
LoadUIFromConfig(args);
|
|
});
|
|
|
|
// If we're controlling GIF animations and the firmware doesn't support
|
|
// doing animations internally, confirm exiting, since you can minimize
|
|
// to tray to keep playing animations. If we're not controlling animations,
|
|
// or the firmware supports doing them automatically, don't bug the user
|
|
// with a prompt.
|
|
Closing += delegate (object sender, System.ComponentModel.CancelEventArgs e)
|
|
{
|
|
LoadFromConfigDelegateArgs args = CurrentSMXDevice.singleton.GetState();
|
|
|
|
// Don't use ActivePads here. Even if P1 is selected for configuration,
|
|
// we can still be controlling animations for P2, so check both connected
|
|
// pads.
|
|
bool shouldConfirmExit = false;
|
|
for(int pad = 0; pad < 2; ++pad)
|
|
{
|
|
SMX.SMXConfig config;
|
|
if(!SMX.SMX.GetConfig(pad, out config))
|
|
continue;
|
|
|
|
// If the firmware is version 4 or higher, it supports animations directly.
|
|
// The user can upload GIF animations and doesn't need to leave us running
|
|
// for them to work. You can still use this tool to drive animations, but
|
|
// don't confirm exiting.
|
|
if(config.masterVersion >= 4)
|
|
continue;
|
|
|
|
// If AutoLightingUsePressedAnimations isn't set, the panel is using step
|
|
// coloring instead of pressed animations. All firmwares support this.
|
|
// Don't confirm exiting for this mode.
|
|
if((config.configFlags & SMX.SMXConfigFlags.AutoLightingUsePressedAnimations) == 0)
|
|
continue;
|
|
|
|
shouldConfirmExit = true;
|
|
}
|
|
|
|
if(!shouldConfirmExit)
|
|
return;
|
|
|
|
MessageBoxResult result = MessageBox.Show(
|
|
"Close StepManiaX configuration?\n\n" +
|
|
"GIF animations will keep playing if the application is minimized.",
|
|
"StepManiaX", System.Windows.MessageBoxButton.YesNo);
|
|
if(result == MessageBoxResult.No)
|
|
e.Cancel = true;
|
|
};
|
|
}
|
|
|
|
bool IsThresholdSliderShown(string type)
|
|
{
|
|
bool AdvancedModeEnabled = Properties.Settings.Default.AdvancedMode;
|
|
SMX.SMXConfig config = ActivePad.GetFirstActivePadConfig();
|
|
bool[] enabledPanels = config.GetEnabledPanels();
|
|
|
|
// Check the list of sensors this slider controls. If the list is empty, don't show it.
|
|
// For example, if the user adds all four sensors on the up panel to custom-sensors, the
|
|
// up button has nothing left to control, so we'll hide it.
|
|
//
|
|
// Don't do this for custom, inner-sensors or outer-sensors. Those are always shown in
|
|
// advanced mode.
|
|
List<ThresholdSettings.PanelAndSensor> panelAndSensors = ThresholdSettings.GetControlledSensorsForSliderType(type, AdvancedModeEnabled, false);
|
|
if(type == "custom-sensors" || type == "inner-sensors" || type == "outer-sensors")
|
|
{
|
|
if(!AdvancedModeEnabled || !config.fsr())
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if(panelAndSensors.Count == 0)
|
|
return false;
|
|
}
|
|
|
|
// Hide thresholds that only affect panels that are disabled, so we don't show
|
|
// corner panel sliders in advanced mode if the corner panels are disabled. We
|
|
// don't handle this in GetControlledSensorsForSliderType, since we do want cardinal
|
|
// and corner to write thresholds to disabled panels, so they're in sync if they're
|
|
// turned back on.
|
|
switch(type)
|
|
{
|
|
case "up-left": return enabledPanels[0];
|
|
case "up": return enabledPanels[1];
|
|
case "up-right": return enabledPanels[2];
|
|
case "left": return enabledPanels[3];
|
|
case "center": return enabledPanels[4];
|
|
case "right": return enabledPanels[5];
|
|
case "down-left": return enabledPanels[6];
|
|
case "down": return enabledPanels[7];
|
|
case "down-right": return enabledPanels[8];
|
|
|
|
// Show cardinal and corner if at least one panel they affect is enabled.
|
|
case "cardinal": return enabledPanels[3] || enabledPanels[5] || enabledPanels[8];
|
|
case "corner": return enabledPanels[0] || enabledPanels[2] || enabledPanels[6] || enabledPanels[8];
|
|
default: return true;
|
|
}
|
|
}
|
|
|
|
ThresholdSlider CreateThresholdSlider(string type)
|
|
{
|
|
ThresholdSlider slider = new ThresholdSlider();
|
|
slider.Type = type;
|
|
return slider;
|
|
}
|
|
|
|
void CreateThresholdSliders()
|
|
{
|
|
// Remove and recreate threshold sliders.
|
|
ThresholdSliderContainer.Children.Clear();
|
|
foreach(string sliderName in ThresholdSettings.thresholdSliderNames)
|
|
{
|
|
if(!IsThresholdSliderShown(sliderName))
|
|
continue;
|
|
|
|
ThresholdSlider slider = CreateThresholdSlider(sliderName);
|
|
DockPanel.SetDock(slider, Dock.Top);
|
|
slider.Margin = new Thickness(0, 8, 0, 0);
|
|
ThresholdSliderContainer.Children.Add(slider);
|
|
}
|
|
|
|
ThresholdSettings.SyncSliderThresholds();
|
|
}
|
|
|
|
public override void OnApplyTemplate()
|
|
{
|
|
base.OnApplyTemplate();
|
|
|
|
// Add our WndProc hook.
|
|
HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
|
|
source.AddHook(new HwndSourceHook(WndProcHook));
|
|
|
|
Version1.Content = "SMXConfig version " + SMX.SMX.Version();
|
|
Version2.Content = "SMXConfig version " + SMX.SMX.Version();
|
|
|
|
AutoLightsColor.StartedDragging += delegate () { showAutoLightsColor.Start(); };
|
|
AutoLightsColor.StoppedDragging += delegate () { showAutoLightsColor.Stop(); };
|
|
AutoLightsColor.StoppedDragging += delegate () { showAutoLightsColor.Stop(); };
|
|
|
|
CreateThresholdSliders();
|
|
|
|
// This doesn't happen at the same time AutoLightsColor is used, since they're on different tabs.
|
|
Diagnostics.SetShowAllLights += delegate (bool on)
|
|
{
|
|
if(on)
|
|
showAutoLightsColor.Start();
|
|
else
|
|
showAutoLightsColor.Stop();
|
|
};
|
|
|
|
SetAllPanelsToCurrentColor.Click += delegate (object sender, RoutedEventArgs e)
|
|
{
|
|
// Get the color of the selected color button, and apply it to all other buttons.
|
|
Color color = selectedButton.getColor();
|
|
|
|
ColorButton[] colorButtons = getColorPickerButtons();
|
|
foreach(ColorButton button in colorButtons)
|
|
{
|
|
// Only apply this to panel colors, not the floor color.
|
|
if((button as PanelColorButton) == null)
|
|
continue;
|
|
|
|
button.setColor(color);
|
|
}
|
|
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
};
|
|
|
|
// Listen to clicks on the panel color buttons.
|
|
ColorButton[] buttons = getColorPickerButtons();
|
|
foreach(ColorButton button in buttons)
|
|
{
|
|
button.Click += delegate (object sender, RoutedEventArgs e)
|
|
{
|
|
ColorButton clickedButton = sender as ColorButton;
|
|
selectedButton = clickedButton;
|
|
|
|
RefreshSelectedColorPicker();
|
|
};
|
|
}
|
|
}
|
|
|
|
// We have two main modes: color mode and GIF animation mode. These behave differently
|
|
// on gen1-3 pads and gen4 pads.
|
|
//
|
|
// On gen4 pads, this determines whether we show animations on press, or show a solid color.
|
|
// This makes it easy to pick a solid color if that's all you want to change, instead of having
|
|
// to make a GIF with the color. We always show animations on release in both modes.
|
|
//
|
|
// On gen1-3 pads, panels are black in color mode, so they behave the same as they did originally.
|
|
// Animations are only shown in GIF animation mode. These animations are enabled with
|
|
// LightsAnimation_SetAuto.
|
|
//
|
|
// The gen1-3 firmware ignores the AutoLightingUsePressedAnimations flag, but we still use it to
|
|
// store which mode the user has selected.
|
|
private void PressedColorModeButton(object sender, RoutedEventArgs e)
|
|
{
|
|
// The user pressed either the "panel colors" or "GIF animations" button.
|
|
bool pressedPanelColors = sender == PanelColorsButton;
|
|
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
|
|
// If we're in panel colors mode, clear the AutoLightingUsePressedAnimations flag.
|
|
// Otherwise, set it.
|
|
if(pressedPanelColors)
|
|
config.configFlags &= ~SMX.SMXConfigFlags.AutoLightingUsePressedAnimations;
|
|
else
|
|
config.configFlags |= SMX.SMXConfigFlags.AutoLightingUsePressedAnimations;
|
|
SMX.SMX.SetConfig(activePad.Item1, config);
|
|
}
|
|
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
private void LoadUIFromConfig(LoadFromConfigDelegateArgs args)
|
|
{
|
|
// Refresh whether LightsAnimation_SetAuto should be enabled.
|
|
SMX.SMXConfig firstConfig = ActivePad.GetFirstActivePadConfig();
|
|
bool usePressedAnimationsEnabled = (firstConfig.configFlags & SMX.SMXConfigFlags.AutoLightingUsePressedAnimations) != 0;
|
|
SMX.SMX.LightsAnimation_SetAuto(usePressedAnimationsEnabled);
|
|
|
|
bool EitherControllerConnected = args.controller[0].info.connected || args.controller[1].info.connected;
|
|
Main.Visibility = EitherControllerConnected ? Visibility.Visible : Visibility.Hidden;
|
|
Searching.Visibility = EitherControllerConnected ? Visibility.Hidden : Visibility.Visible;
|
|
ConnectedPads.Visibility = EitherControllerConnected ? Visibility.Visible : Visibility.Hidden;
|
|
PanelColorP1.Visibility = args.controller[0].info.connected ? Visibility.Visible : Visibility.Collapsed;
|
|
PanelColorP2.Visibility = args.controller[1].info.connected ? Visibility.Visible : Visibility.Collapsed;
|
|
EnableCenterTopSensorCheckbox.Visibility =
|
|
P1_Floor.Visibility =
|
|
P2_Floor.Visibility =
|
|
args.firmwareVersion() >= 5 ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
// Show the color slider or GIF UI depending on which one is set in flags.
|
|
// If both pads are turned on, just use the first one.
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
|
|
// If SMXConfigFlags_AutoLightingUsePressedAnimations is set, show the GIF UI.
|
|
// If it's not set, show the color slider UI.
|
|
SMX.SMXConfigFlags flags = config.configFlags;
|
|
bool usePressedAnimations = (flags & SMX.SMXConfigFlags.AutoLightingUsePressedAnimations) != 0;
|
|
ColorPickerGroup.Visibility = usePressedAnimations ? Visibility.Collapsed : Visibility.Visible;
|
|
GIFGroup.Visibility = usePressedAnimations ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
// Tell the color mode buttons which one is selected, to set the button highlight.
|
|
PanelColorsButton.Selected = !usePressedAnimations;
|
|
GIFAnimationsButton.Selected = usePressedAnimations;
|
|
|
|
break;
|
|
}
|
|
|
|
RefreshConnectedPadList(args);
|
|
RefreshUploadPadText(args);
|
|
RefreshSelectedColorPicker();
|
|
|
|
// If a device has connected or disconnected, refresh the displayed threshold
|
|
// sliders. Don't do this otherwise, or we'll do this when the sliders are
|
|
// dragged.
|
|
if(args.ConnectionsChanged)
|
|
CreateThresholdSliders();
|
|
|
|
// Show the threshold warning explanation if any panels are showing the threshold warning icon.
|
|
bool ShowThresholdWarningText = false;
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
for(int panelIdx = 0; panelIdx < 9; ++panelIdx)
|
|
{
|
|
for(int sensor = 0; sensor < 4; ++sensor)
|
|
{
|
|
if(config.ShowThresholdWarning(panelIdx, sensor))
|
|
ShowThresholdWarningText = true;
|
|
}
|
|
}
|
|
}
|
|
ThresholdWarningText.Visibility = ShowThresholdWarningText ? Visibility.Visible : Visibility.Hidden;
|
|
|
|
// If a second controller has connected and we're on Both, see if we need to prompt
|
|
// to sync configs. We only actually need to do this if a controller just connected.
|
|
if(args.ConfigurationChanged)
|
|
CheckConfiguringBothPads(args);
|
|
}
|
|
|
|
ColorButton selectedButton;
|
|
|
|
// Return all color picker buttons.
|
|
ColorButton[] getColorPickerButtons()
|
|
{
|
|
return new ColorButton[] {
|
|
P1_0, P1_1, P1_2,
|
|
P1_3, P1_4, P1_5,
|
|
P1_6, P1_7, P1_8,
|
|
P1_Floor,
|
|
|
|
P2_0, P2_1, P2_2,
|
|
P2_3, P2_4, P2_5,
|
|
P2_6, P2_7, P2_8,
|
|
P2_Floor,
|
|
};
|
|
}
|
|
|
|
// Update the selected color picker based on the value of selectedButton.
|
|
private void RefreshSelectedColorPicker()
|
|
{
|
|
LoadFromConfigDelegateArgs args = CurrentSMXDevice.singleton.GetState();
|
|
|
|
ColorButton[] buttons = getColorPickerButtons();
|
|
|
|
// If our selected button isn't enabled (or no button is selected), try to select a
|
|
// different one.
|
|
if(selectedButton == null || !selectedButton.isEnabled(args))
|
|
{
|
|
foreach(ColorButton button in buttons)
|
|
{
|
|
if(button.isEnabled(args))
|
|
{
|
|
selectedButton = button;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tell the buttons which one is selected.
|
|
foreach(ColorButton button in buttons)
|
|
button.IsSelected = button == selectedButton;
|
|
|
|
// Tell the color slider which button is selected.
|
|
AutoLightsColor.colorButton = selectedButton;
|
|
}
|
|
|
|
// Update which of the "Leave this application running", etc. blocks to display.
|
|
private void RefreshUploadPadText(LoadFromConfigDelegateArgs args)
|
|
{
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
|
|
bool uploadsSupported = config.masterVersion >= 4;
|
|
LeaveRunning.Visibility = uploadsSupported ? Visibility.Collapsed : Visibility.Visible;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ConnectedPadList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
ComboBoxItem selection = ConnectedPadList.SelectedItem as ComboBoxItem;
|
|
ActivePad.SelectedPad newSelection;
|
|
if(selection == ConnectedPadList_P1)
|
|
newSelection = ActivePad.SelectedPad.P1;
|
|
else if(selection == ConnectedPadList_P2)
|
|
newSelection = ActivePad.SelectedPad.P2;
|
|
else
|
|
newSelection = ActivePad.SelectedPad.Both;
|
|
if(ActivePad.selectedPad == newSelection)
|
|
return;
|
|
|
|
ActivePad.selectedPad = newSelection;
|
|
|
|
// Before firing and updating UI, run CheckConfiguringBothPads to see if we should
|
|
// sync the config and/or change the selection again.
|
|
CheckConfiguringBothPads(CurrentSMXDevice.singleton.GetState());
|
|
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
// If the user selects "Both", or connects a second pad while on "Both", both pads need
|
|
// to have the same configuration, since we're configuring them together. Check if the
|
|
// configuration is out of sync, and ask the user before syncing them up so we don't
|
|
// clobber P2's configuration if this wasn't intended.
|
|
//
|
|
// If the user cancels, change the pad selection to P1 so we don't clobber P2.
|
|
private void CheckConfiguringBothPads(LoadFromConfigDelegateArgs args)
|
|
{
|
|
// Check if we're actually in "Both" mode with two controllers connected. If not,
|
|
// we don't have to do anything.
|
|
bool Pad1Connected = args.controller[0].info.connected;
|
|
bool Pad2Connected = args.controller[1].info.connected;
|
|
if(ActivePad.selectedPad != ActivePad.SelectedPad.Both || !Pad1Connected || !Pad2Connected)
|
|
return;
|
|
|
|
// If the two pads have the same configuration, there's nothing to do.
|
|
SMX.SMXConfig config1 = args.controller[0].config;
|
|
SMX.SMXConfig config2 = args.controller[1].config;
|
|
if(ConfigurationsSynced(config1, config2))
|
|
return;
|
|
|
|
string messageBoxText = "The two pads have different settings. Do you want to " +
|
|
"match P2 settings to P1 and configure both pads together? (This won't affect panel colors.)";
|
|
MessageBoxResult result = MessageBox.Show(messageBoxText, "StepManiaX", MessageBoxButton.YesNo, MessageBoxImage.None);
|
|
if(result == MessageBoxResult.Yes)
|
|
{
|
|
SyncP2FromP1(config1, config2);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Switch to P1.
|
|
ActivePad.selectedPad = ActivePad.SelectedPad.P1;
|
|
RefreshConnectedPadList(CurrentSMXDevice.singleton.GetState());
|
|
}
|
|
|
|
}
|
|
|
|
// Return true if the two pads have the same configuration, so we can configure them together
|
|
// without clobbering separate configurations.
|
|
bool ConfigurationsSynced(SMX.SMXConfig config1, SMX.SMXConfig config2)
|
|
{
|
|
if(!Enumerable.SequenceEqual(config1.GetLowThresholds(), config2.GetLowThresholds()))
|
|
return false;
|
|
if(!Enumerable.SequenceEqual(config1.GetHighThresholds(), config2.GetHighThresholds()))
|
|
return false;
|
|
if(!Enumerable.SequenceEqual(config1.enabledSensors, config2.enabledSensors))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Copy the P2 pad configuration to P1.
|
|
//
|
|
// This only copies settings that we actually configure, and it doesn't copy pad
|
|
// colors, which is separate from pad selection.
|
|
void SyncP2FromP1(SMX.SMXConfig config1, SMX.SMXConfig config2)
|
|
{
|
|
// Copy P1's configuration to P2.
|
|
Array.Copy(config1.enabledSensors, config2.enabledSensors, config1.enabledSensors.Count());
|
|
config2.SetLowThresholds(config1.GetLowThresholds());
|
|
config2.SetHighThresholds(config1.GetHighThresholds());
|
|
SMX.SMX.SetConfig(1, config2);
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
// Refresh which items are visible in the connected pads list, and which item is displayed as selected.
|
|
private void RefreshConnectedPadList(LoadFromConfigDelegateArgs args)
|
|
{
|
|
bool TwoControllersConnected = args.controller[0].info.connected && args.controller[1].info.connected;
|
|
|
|
// Only show the dropdown if two controllers are connected.
|
|
ConnectedPadList.Visibility = TwoControllersConnected ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
// Only show the P1/P2 text if only one controller is connected, since it takes the place
|
|
// of the dropdown.
|
|
P1Connected.Visibility = (!TwoControllersConnected && args.controller[0].info.connected) ? Visibility.Visible : Visibility.Collapsed;
|
|
P2Connected.Visibility = (!TwoControllersConnected && args.controller[1].info.connected) ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
if(!TwoControllersConnected)
|
|
return;
|
|
|
|
// Set the current selection.
|
|
ActivePad.SelectedPad selectedPad = ActivePad.selectedPad;
|
|
switch(ActivePad.selectedPad)
|
|
{
|
|
case ActivePad.SelectedPad.P1: ConnectedPadList.SelectedItem = ConnectedPadList_P1; break;
|
|
case ActivePad.SelectedPad.P2: ConnectedPadList.SelectedItem = ConnectedPadList_P2; break;
|
|
case ActivePad.SelectedPad.Both: ConnectedPadList.SelectedItem = ConnectedPadList_Both; break;
|
|
}
|
|
}
|
|
|
|
|
|
private void FactoryReset_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
SMX.SMX.FactoryReset(pad);
|
|
}
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
private void AdvancedModeEnabledCheckbox_Changed(object sender, RoutedEventArgs e)
|
|
{
|
|
CreateThresholdSliders();
|
|
}
|
|
|
|
private void ExportSettings(object sender, RoutedEventArgs e)
|
|
{
|
|
// Save the current thresholds on the first available pad as a preset.
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
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 active pads.
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
SMX.SMXConfig config = activePad.Item2;
|
|
|
|
SMXHelpers.ImportSettingsFromJSON(json, ref config);
|
|
SMX.SMX.SetConfig(pad, config);
|
|
}
|
|
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
private void LoadGIF(object sender, RoutedEventArgs e)
|
|
{
|
|
// If the "load idle GIF" button was pressed, load the released animation.
|
|
// Otherwise, load the pressed animation.
|
|
bool pressed = sender == this.LoadPressed;
|
|
|
|
// Prompt for a file to read.
|
|
Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
|
|
dialog.FileName = "Select an animated GIF";
|
|
dialog.DefaultExt = ".gif";
|
|
dialog.Filter = "Animated GIF (.gif)|*.gif";
|
|
bool? result = dialog.ShowDialog();
|
|
if(result == null || !(bool)result)
|
|
return;
|
|
|
|
byte[] buf = Helpers.ReadBinaryFile(dialog.FileName);
|
|
SMX.SMX.LightsType type = pressed ? SMX.SMX.LightsType.LightsType_Pressed : SMX.SMX.LightsType.LightsType_Released;
|
|
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
|
|
// Load the animation.
|
|
string error;
|
|
if(!SMX.SMX.LightsAnimation_Load(buf, pad, type, out error))
|
|
{
|
|
// Any errors here are problems with the GIF, so there's no point trying
|
|
// to load it for the second pad if the first returns an error. Just show the
|
|
// error and stop.
|
|
MessageBox.Show(error, "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
|
|
// Return without saving to settings on error.
|
|
return;
|
|
}
|
|
|
|
// Save the GIF to disk so we can load it quickly later.
|
|
Helpers.SaveAnimationToDisk(pad, type, buf);
|
|
|
|
// Refresh after loading a GIF to update the "Leave this application running" text.
|
|
CurrentSMXDevice.singleton.FireConfigurationChanged(null);
|
|
}
|
|
|
|
// For firmwares that support it, upload the animation to the pad now. Otherwise,
|
|
// we'll run the animation directly.
|
|
foreach(Tuple<int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
|
|
SMX.SMXConfig config;
|
|
if(!SMX.SMX.GetConfig(pad, out config))
|
|
continue;
|
|
|
|
if(config.masterVersion >= 4)
|
|
UploadLatestGIF();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UploadLatestGIF()
|
|
{
|
|
// Create a progress window. Center it on top of the main window.
|
|
ProgressWindow dialog = new ProgressWindow();
|
|
dialog.Left = (Left + Width/2) - (dialog.Width/2);
|
|
dialog.Top = (Top + Height/2) - (dialog.Height/2);
|
|
dialog.Title = "Storing animations on pad...";
|
|
|
|
int[] CurrentProgress = new int[] { 0, 0 };
|
|
|
|
// Upload graphics for all connected pads. If two pads are connected
|
|
// we can start both of these simultaneously, and they'll be sent in
|
|
// parallel.
|
|
int total = 0;
|
|
foreach(Tuple<int,SMX.SMXConfig> activePad in ActivePad.ActivePads())
|
|
{
|
|
int pad = activePad.Item1;
|
|
SMX.SMX.LightsUpload_BeginUpload(pad, delegate(int progress) {
|
|
// This is called from a thread, so dispatch back to the main thread.
|
|
Dispatcher.Invoke(delegate() {
|
|
// Store progress, so we can sum both pads.
|
|
CurrentProgress[pad] = progress;
|
|
|
|
dialog.SetProgress(CurrentProgress[0] + CurrentProgress[1]);
|
|
if(progress == 100)
|
|
dialog.Close();
|
|
});
|
|
});
|
|
|
|
// Each pad that we start uploading to is 100 units of progress.
|
|
total += 100;
|
|
dialog.SetTotal(total);
|
|
}
|
|
|
|
// Show the progress window as a modal dialog. This function won't return
|
|
// until we call dialog.Close above.
|
|
dialog.ShowDialog();
|
|
}
|
|
|
|
const int WM_SYSCOMMAND = 0x0112;
|
|
const int SC_MINIMIZE = 0xF020;
|
|
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
|
|
{
|
|
App application = (App) Application.Current;
|
|
|
|
if(msg == WM_SYSCOMMAND && ((int)wparam & 0xFFF0) == SC_MINIMIZE)
|
|
{
|
|
// Cancel minimize, and call MinimizeToTray instead.
|
|
handled = true;
|
|
application.MinimizeToTray();
|
|
}
|
|
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
private void MainTab_Selected(object sender, RoutedEventArgs e)
|
|
{
|
|
if(Main.SelectedItem == SensitivityTab)
|
|
{
|
|
// Refresh the threshold sliders, in case the enabled panels were changed
|
|
// on the advanced tab.
|
|
CreateThresholdSliders();
|
|
}
|
|
}
|
|
}
|
|
}
|