Files
2026-07-13 13:13:17 +08:00

191 lines
6.6 KiB
C#

using System.IO;
using T3.Core.SystemUi;
using T3.Core.Settings;
using T3.Core.Utils;
using T3.Editor.Gui.UiHelpers;
using T3.Editor.SystemUi;
namespace T3.Editor.Gui.Interaction.StartupCheck;
/// <summary>
/// A helper that provides an option to restore the latest backups when the previous
/// startup failed.
/// </summary>
public static class StartUp
{
public static void FlagBeginStartupSequence()
{
WarnIfLowDiskSpace();
if (File.Exists(StartUpLockFilePath))
{
#if !DEBUG
ShowLastStartupFailedMessageBox();
#endif
}
File.WriteAllText(StartUpLockFilePath, "Startup " + DateTime.Now);
}
public static void FlagStartupSequenceComplete()
{
if (!File.Exists(StartUpLockFilePath))
{
Log.Debug("Strange. Startup file missing?");
}
else
{
File.Delete(StartUpLockFilePath);
}
}
private static void ShowLastStartupFailedMessageBox()
{
var isThereABackup = AutoBackup.AutoBackup.HasAnyBackups();
if (!isThereABackup)
{
var result2 = BlockingWindow.Instance.ShowMessageBox("It looks like the last startup failed.\nUnfortunately, there is no backup yet.", "Startup Failed", "Retry", "Cancel");
if (result2 != "Retry")
{
Log.Info("User cancelled startup.");
EditorUi.Instance.ExitApplication();
}
return;
}
Log.Debug("StartUpProgress lock file exists?");
var timeOfLastBackup = AutoBackup.AutoBackup.GetTimeOfLastBackup();
var timeSpan = StringUtils.GetReadableRelativeTime(timeOfLastBackup);
const string caption = "Oh no! Start up problems...";
string message = "It looks like the last startup was incomplete.\n\n" +
"You can choose:\n\n" +
$" YES to restore the latest backup of every project ({timeSpan})\n" +
" NO to open the documentation\n" +
" CANCEL to attempt starting anyway.\n";
const string restore = "Restore backups";
const string openDoc = "Open documentation";
const string startup = "I don't care do it anyway!!!!";
var result = BlockingWindow.Instance.ShowMessageBox(message, caption, restore, openDoc, startup);
switch (result)
{
case restore:
{
var restoredCount = AutoBackup.AutoBackup.RestoreLatestBackups();
if (restoredCount > 0)
{
FlagStartupSequenceComplete();
BlockingWindow.Instance.ShowMessageBox($"{restoredCount} project(s) restored. Click OK to restart.\nFingers crossed!", "Complete", "Ok");
Environment.Exit(0);
}
else
{
BlockingWindow.Instance.ShowMessageBox("Restoring backups failed.\nYou might want to try an earlier archive in <project>/.temp/Backup/...", "Failed",
"Ok");
Environment.Exit(0);
}
break;
}
case openDoc:
CoreUi.Instance.OpenWithDefaultApplication(HelpUrl);
Environment.Exit(0);
break;
case startup:
break;
}
}
/// <summary>
/// Warns when a drive holding the settings folder or a project directory is nearly full —
/// saving, backups, logs and compilation all fail in hard-to-diagnose ways once the disk
/// runs out, so surfacing it before any of that happens beats scattered write errors.
/// </summary>
private static void WarnIfLowDiskSpace()
{
var lowDrives = new List<string>();
foreach (var driveRoot in EnumerateRelevantDriveRoots())
{
try
{
var info = new DriveInfo(driveRoot);
if (!info.IsReady)
continue;
var freeMb = info.AvailableFreeSpace / (1024 * 1024);
if (freeMb < MinFreeDiskSpaceMb)
lowDrives.Add($"{info.Name} ({freeMb} MB free)");
}
catch (Exception e)
{
Log.Debug($"Could not check disk space for {driveRoot}: {e.Message}");
}
}
if (lowDrives.Count == 0)
return;
var driveList = string.Join("\n", lowDrives);
Log.Warning($"Low disk space:\n{driveList}");
var message = "These drives are running out of space:\n\n"
+ driveList + "\n\n"
+ "TiXL needs room for saving projects, backups and compiling. "
+ $"With less than {MinFreeDiskSpaceMb} MB free, saving is likely to fail and data could be lost.\n\n"
+ "Please free up some disk space.";
var result = BlockingWindow.Instance.ShowMessageBox(message, "Low disk space", "Continue anyway", "Exit");
if (result == "Exit")
{
Log.Info("User exited because of low disk space.");
EditorUi.Instance.ExitApplication();
}
}
/// <summary>Drive roots of the settings folder and every configured project directory, deduplicated.</summary>
private static IEnumerable<string> EnumerateRelevantDriveRoots()
{
var roots = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
TryAddDriveRoot(roots, FileLocations.SettingsDirectory);
TryAddDriveRoot(roots, FileLocations.DefaultProjectFolder);
var projectDirectories = UserSettings.Config?.ProjectDirectories;
if (projectDirectories != null)
{
foreach (var dir in projectDirectories)
{
TryAddDriveRoot(roots, dir);
}
}
return roots;
}
private static void TryAddDriveRoot(HashSet<string> roots, string? path)
{
if (string.IsNullOrWhiteSpace(path))
return;
try
{
var root = Path.GetPathRoot(Path.GetFullPath(path));
if (!string.IsNullOrEmpty(root))
roots.Add(root);
}
catch (Exception e)
{
Log.Debug($"Could not resolve drive root for {path}: {e.Message}");
}
}
private const long MinFreeDiskSpaceMb = 100;
private const string HelpUrl = "https://github.com/tixl3d/tixl/wiki/help.Installation";
private static string StartUpLockFilePath => Path.Combine(FileLocations.SettingsDirectory, "startingUp");
}