Allow minimizing to the tray.

This allows running GIF animations in the background.
master
Glenn Maynard 6 years ago
parent 4691ce5ecf
commit 5cf5eddc81
  1. 3
      smx-config/App.xaml
  2. 109
      smx-config/App.xaml.cs
  3. 45
      smx-config/MainWindow.xaml.cs
  4. BIN
      smx-config/Resources/window icon grey.ico
  5. BIN
      smx-config/Resources/window icon grey.png
  6. 7
      smx-config/SMXConfig.csproj

@ -1,8 +1,7 @@
<Application x:Class="smx_config.App" <Application x:Class="smx_config.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:smx_config" xmlns:local="clr-namespace:smx_config">
StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
</Application.Resources> </Application.Resources>

@ -1,6 +1,7 @@
using System; using System;
using System.Windows; using System.Windows;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.IO;
namespace smx_config namespace smx_config
{ {
@ -11,9 +12,17 @@ namespace smx_config
[DllImport("SMX.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport("SMX.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void SMX_Internal_OpenConsole(); private static extern void SMX_Internal_OpenConsole();
private System.Windows.Forms.NotifyIcon trayIcon;
private MainWindow window;
App() App()
{ {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionEventHandler; AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionEventHandler;
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if(!SMX.SMX.DLLExists()) if(!SMX.SMX.DLLExists())
{ {
@ -38,6 +47,40 @@ namespace smx_config
// we're running. // we're running.
Helpers.LoadSavedPanelAnimations(); Helpers.LoadSavedPanelAnimations();
SMX.SMX.LightsAnimation_SetAuto(true); SMX.SMX.LightsAnimation_SetAuto(true);
CreateTrayIcon();
// Create the main window.
ToggleMainWindow();
}
// Open or close the main window.
//
// We don't create our UI until the first time it's opened, so we use
// less memory when we're launched on startup. However, when we're minimized
// back to the tray, we don't destroy the main window. WPF is just too
// leaky to recreate the main window each time it's called due to internal
// circular references. Instead, we just focus on minimizing CPU overhead.
void ToggleMainWindow()
{
if(window == null)
{
window = new MainWindow();
window.Closed += MainWindowClosed;
window.Show();
}
else if(window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
window.Activate();
}
else
window.WindowState = WindowState.Minimized;
}
private void MainWindowClosed(object sender, EventArgs e)
{
window = null;
} }
private void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e) private void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
@ -50,13 +93,69 @@ namespace smx_config
{ {
base.OnExit(e); base.OnExit(e);
// Shut down cleanly, to make sure we don't run any threaded callbacks during shutdown.
Console.WriteLine("Application exiting"); Console.WriteLine("Application exiting");
if(CurrentSMXDevice.singleton == null)
return; // Remove the tray icon.
CurrentSMXDevice.singleton.Shutdown(); if(trayIcon != null)
CurrentSMXDevice.singleton = null; {
trayIcon.Visible = false;
trayIcon = null;
}
// Shut down cleanly, to make sure we don't run any threaded callbacks during shutdown.
if(CurrentSMXDevice.singleton != null)
{
CurrentSMXDevice.singleton.Shutdown();
CurrentSMXDevice.singleton = null;
}
}
// Create a tray icon. For some reason there's no WPF interface for this,
// so we have to use Forms.
void CreateTrayIcon()
{
Stream iconStream = GetResourceStream(new Uri( "pack://application:,,,/Resources/window%20icon%20grey.ico")).Stream;
System.Drawing.Icon icon = new System.Drawing.Icon(iconStream);
trayIcon = new System.Windows.Forms.NotifyIcon();
trayIcon.Text = "StepManiaX";
trayIcon.Visible = true;
// Show or hide the application window on click.
trayIcon.Click += delegate (object sender, EventArgs e) { ToggleMainWindow(); };
trayIcon.DoubleClick += delegate (object sender, EventArgs e) { ToggleMainWindow(); };
CurrentSMXDevice.singleton.ConfigurationChanged += delegate(LoadFromConfigDelegateArgs args) {
RefreshTrayIcon(args);
};
// Do the initial refresh.
RefreshTrayIcon(CurrentSMXDevice.singleton.GetState(), true);
} }
// Refresh the tray icon when we're connected or disconnected.
bool wasConnected;
void RefreshTrayIcon(LoadFromConfigDelegateArgs args, bool force=false)
{
if(trayIcon == null)
return;
bool EitherControllerConnected = false;
for(int pad = 0; pad < 2; ++pad)
if(args.controller[pad].info.connected)
EitherControllerConnected = true;
// Skip the refresh if the connected state didn't change.
if(wasConnected == EitherControllerConnected && !force)
return;
wasConnected = EitherControllerConnected;
trayIcon.Text = EitherControllerConnected? "StepManiaX (connected)":"StepManiaX (disconnected)";
// Set the tray icon.
string filename = EitherControllerConnected? "window%20icon.ico":"window%20icon%20grey.ico";
Stream iconStream = GetResourceStream(new Uri( "pack://application:,,,/Resources/" + filename)).Stream;
trayIcon.Icon = new System.Drawing.Icon(iconStream);
}
} }
} }

@ -18,6 +18,51 @@ namespace smx_config
onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) { onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
LoadUIFromConfig(args); LoadUIFromConfig(args);
}); });
// If we're controlling GIF animations, confirm exiting, since you can minimize
// to tray to keep playing animations. If we're not controlling animations,
// 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 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.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;
};
StateChanged += delegate(object sender, EventArgs e)
{
// Closing the main window entirely when minimized to the tray would be
// nice, but with WPF we don't really save memory by doing that, so
// just hide the window.
ShowInTaskbar = WindowState != WindowState.Minimized;
};
} }
public override void OnApplyTemplate() public override void OnApplyTemplate()

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -81,6 +81,7 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@ -195,6 +196,12 @@
<Resource Include="Resources\pressed.gif" /> <Resource Include="Resources\pressed.gif" />
<Resource Include="Resources\released.gif" /> <Resource Include="Resources\released.gif" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Resource Include="Resources\window icon grey.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\window icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

Loading…
Cancel
Save