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.
251 lines
9.3 KiB
C#
251 lines
9.3 KiB
C#
using System;
|
|
using System.Windows;
|
|
using System.Runtime.InteropServices;
|
|
using System.IO;
|
|
using System.Threading;
|
|
|
|
namespace smx_config
|
|
{
|
|
public partial class App: Application
|
|
{
|
|
[DllImport("SMX.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern void SMX_Internal_OpenConsole();
|
|
|
|
private System.Windows.Forms.NotifyIcon trayIcon;
|
|
private MainWindow window;
|
|
|
|
App()
|
|
{
|
|
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionEventHandler;
|
|
}
|
|
|
|
protected override void OnStartup(StartupEventArgs e)
|
|
{
|
|
base.OnStartup(e);
|
|
|
|
// If an instance is already running, foreground it and exit.
|
|
if(ForegroundExistingInstance())
|
|
{
|
|
Shutdown();
|
|
return;
|
|
}
|
|
|
|
// This is used by the installer to close a running instance automatically when updating.
|
|
ListenForShutdownRequest();
|
|
|
|
// If we're being launched on startup, but the LaunchOnStartup setting is false,
|
|
// then the user turned off auto-launching but we're still being launched for some
|
|
// reason (eg. a renamed launch shortcut that we couldn't find to remove). As
|
|
// a safety so we don't launch when the user doesn't want us to, just exit in this
|
|
// case.
|
|
if(Helpers.LaunchedOnStartup() && !LaunchOnStartup.Enable)
|
|
{
|
|
Shutdown();
|
|
return;
|
|
}
|
|
|
|
LaunchOnStartup.Enable = true;
|
|
if(!SMX.SMX.DLLExists())
|
|
{
|
|
MessageBox.Show("SMXConfig encountered an unexpected error.\n\nSMX.dll couldn't be found:\n\n" + Helpers.GetLastWin32ErrorString(), "SMXConfig");
|
|
Current.Shutdown();
|
|
return;
|
|
}
|
|
|
|
if(!SMX.SMX.DLLAvailable())
|
|
{
|
|
MessageBox.Show("SMXConfig encountered an unexpected error.\n\nSMX.dll failed to load:\n\n" + Helpers.GetLastWin32ErrorString(), "SMXConfig");
|
|
Current.Shutdown();
|
|
return;
|
|
}
|
|
|
|
if(Helpers.GetDebug())
|
|
SMX_Internal_OpenConsole();
|
|
|
|
CurrentSMXDevice.singleton = new CurrentSMXDevice();
|
|
|
|
// Load animations.
|
|
Helpers.LoadSavedPanelAnimations();
|
|
|
|
CreateTrayIcon();
|
|
|
|
// Create the main window.
|
|
if(!Helpers.LaunchedOnStartup())
|
|
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(IsMinimizedToTray())
|
|
{
|
|
window.Visibility = Visibility.Visible;
|
|
window.Activate();
|
|
}
|
|
else
|
|
{
|
|
MinimizeToTray();
|
|
}
|
|
}
|
|
|
|
public bool IsMinimizedToTray()
|
|
{
|
|
return window.Visibility == Visibility.Collapsed;
|
|
}
|
|
|
|
public void MinimizeToTray()
|
|
{
|
|
// Just hide the window. Don't actually set the window to minimized, since it
|
|
// won't do anything and it causes problems when restoring the window.
|
|
window.Visibility = Visibility.Collapsed;
|
|
}
|
|
|
|
public void BringToForeground()
|
|
{
|
|
// Restore or create the window. Don't minimize if we're already restored.
|
|
if(window == null || IsMinimizedToTray())
|
|
ToggleMainWindow();
|
|
|
|
// Focus the window.
|
|
window.WindowState = WindowState.Normal;
|
|
window.Activate();
|
|
}
|
|
|
|
private void MainWindowClosed(object sender, EventArgs e)
|
|
{
|
|
window = null;
|
|
}
|
|
|
|
private void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
|
|
{
|
|
string message = e.ExceptionObject.ToString();
|
|
MessageBox.Show("SMXConfig encountered an unexpected error:\n\n" + message, "SMXConfig");
|
|
}
|
|
|
|
protected override void OnExit(ExitEventArgs e)
|
|
{
|
|
base.OnExit(e);
|
|
|
|
Console.WriteLine("Application exiting");
|
|
|
|
// Remove the tray icon.
|
|
if(trayIcon != 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;
|
|
}
|
|
}
|
|
|
|
// If another instance other than this one is running, send it WM_USER to tell it to
|
|
// foreground itself. Return true if another instance was found.
|
|
private bool ForegroundExistingInstance()
|
|
{
|
|
bool createdNew = false;
|
|
EventWaitHandle SMXConfigEvent = new EventWaitHandle(false, EventResetMode.AutoReset, "SMXConfigEvent", out createdNew);
|
|
if(!createdNew)
|
|
{
|
|
// Signal the event to foreground the existing instance.
|
|
SMXConfigEvent.Set();
|
|
return true;
|
|
}
|
|
|
|
ThreadPool.RegisterWaitForSingleObject(SMXConfigEvent, ForegroundApplicationCallback, this, Timeout.Infinite, false);
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void ForegroundApplicationCallback(Object self, Boolean timedOut)
|
|
{
|
|
// This is called when another instance sends us a message over SMXConfigEvent.
|
|
Application.Current.Dispatcher.Invoke(new Action(() => {
|
|
App application = (App) Application.Current;
|
|
application.BringToForeground();
|
|
}));
|
|
}
|
|
|
|
private void ListenForShutdownRequest()
|
|
{
|
|
// We've already checked that we're the only instance when we get here, so this event shouldn't
|
|
// exist. If it already exists for some reason, we'll listen to it anyway.
|
|
EventWaitHandle SMXConfigShutdown = new EventWaitHandle(false, EventResetMode.AutoReset, "SMXConfigShutdown");
|
|
ThreadPool.RegisterWaitForSingleObject(SMXConfigShutdown, ShutdownApplicationCallback, this, Timeout.Infinite, false);
|
|
}
|
|
|
|
private static void ShutdownApplicationCallback(Object self, Boolean timedOut)
|
|
{
|
|
// This is called when another instance sends us a message over SMXConfigShutdown.
|
|
Application.Current.Dispatcher.Invoke(new Action(() => {
|
|
App application = (App) Application.Current;
|
|
application.Shutdown();
|
|
}));
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|