mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
175 lines
6.2 KiB
C#
175 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading;
|
|
using Artemis.Core;
|
|
using Artemis.Core.Services;
|
|
using Artemis.UI.Shared.Services;
|
|
using Avalonia;
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
using Avalonia.Threading;
|
|
using DryIoc;
|
|
|
|
namespace Artemis.UI.Linux;
|
|
|
|
public class ApplicationStateManager
|
|
{
|
|
private readonly IWindowService _windowService;
|
|
|
|
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
|
private Mutex? _artemisMutex;
|
|
|
|
public ApplicationStateManager(IContainer container, string[] startupArguments)
|
|
{
|
|
_windowService = container.Resolve<IWindowService>();
|
|
StartupArguments = startupArguments;
|
|
|
|
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
|
|
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
|
|
Core.Utilities.UpdateRequested += UtilitiesOnUpdateRequested;
|
|
|
|
// On OS shutdown dispose the IOC container just so device providers get a chance to clean up
|
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
|
controlledApplicationLifetime.Exit += (_, _) =>
|
|
{
|
|
RunForcedShutdownIfEnabled();
|
|
|
|
// Dispose plugins before disposing the IOC container because plugins might access services during dispose
|
|
container.Resolve<IPluginManagementService>().Dispose();
|
|
container.Dispose();
|
|
};
|
|
}
|
|
|
|
public string[] StartupArguments { get; }
|
|
|
|
public bool FocusExistingInstance()
|
|
{
|
|
_artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535d", out bool createdNew);
|
|
if (createdNew)
|
|
return false;
|
|
|
|
return RemoteFocus();
|
|
}
|
|
|
|
public void DisplayException(Exception e)
|
|
{
|
|
try
|
|
{
|
|
_windowService.ShowExceptionDialog("An unhandled exception occured", e);
|
|
}
|
|
catch
|
|
{
|
|
// ignored, we tried
|
|
}
|
|
}
|
|
|
|
private bool RemoteFocus()
|
|
{
|
|
// At this point we cannot read the database yet to retrieve the web server port.
|
|
// Instead use the method external applications should use as well.
|
|
if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt")))
|
|
{
|
|
KillOtherInstances();
|
|
return false;
|
|
}
|
|
|
|
string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt"));
|
|
using HttpClient client = new();
|
|
try
|
|
{
|
|
CancellationTokenSource cts = new();
|
|
cts.CancelAfter(2000);
|
|
|
|
HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token);
|
|
httpResponseMessage.EnsureSuccessStatusCode();
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
KillOtherInstances();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void KillOtherInstances()
|
|
{
|
|
// Kill everything else heh
|
|
List<Process> processes = Process.GetProcessesByName("Artemis.UI").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList();
|
|
foreach (Process process in processes)
|
|
{
|
|
try
|
|
{
|
|
process.Kill(true);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
|
{
|
|
List<string> argsList = new();
|
|
argsList.AddRange(StartupArguments);
|
|
if (e.ExtraArgs != null)
|
|
argsList.AddRange(e.ExtraArgs.Except(argsList));
|
|
|
|
//TODO: start new instance with correct arguments
|
|
|
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
|
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
|
}
|
|
|
|
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
|
{
|
|
RunForcedShutdownIfEnabled();
|
|
|
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
|
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
|
}
|
|
|
|
private void RunForcedShutdownIfEnabled()
|
|
{
|
|
if (StartupArguments.Contains("--disable-forced-shutdown"))
|
|
return;
|
|
|
|
//todo
|
|
}
|
|
|
|
private void UtilitiesOnUpdateRequested(object? sender, UpdateEventArgs e)
|
|
{
|
|
List<string> argsList = new(StartupArguments);
|
|
if (e.Silent && !argsList.Contains("--minimized"))
|
|
argsList.Add("--minimized");
|
|
|
|
// Retain startup arguments after update by providing them to the script
|
|
string script = Path.Combine(Constants.UpdatingFolder, "installing", "Scripts", "update.sh");
|
|
string source = $"\"{Path.Combine(Constants.UpdatingFolder, "installing")}\"";
|
|
string destination = $"\"{Constants.ApplicationFolder}\"";
|
|
string args = argsList.Any() ? $"\"{string.Join(' ', argsList)}\"" : "";
|
|
|
|
RunScriptWithOutputFile(script, $"{source} {destination} {args}", Path.Combine(Constants.DataFolder, "update-log.txt"));
|
|
|
|
// Lets try a graceful shutdown, PowerShell will kill if needed
|
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
|
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
|
}
|
|
|
|
private void RunScriptWithOutputFile(string script, string arguments, string outputFile)
|
|
{
|
|
// Use > for files that are bigger than 200kb to start fresh, otherwise use >> to append
|
|
string redirectSymbol = File.Exists(outputFile) && new FileInfo(outputFile).Length > 200000 ? ">" : ">>";
|
|
ProcessStartInfo info = new()
|
|
{
|
|
Arguments = $"\"{script}\" {arguments} {redirectSymbol} \"{outputFile}\"",
|
|
FileName = "/bin/bash",
|
|
WindowStyle = ProcessWindowStyle.Hidden,
|
|
CreateNoWindow = true,
|
|
};
|
|
Process.Start(info);
|
|
}
|
|
} |