using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; using SkiaSharp; namespace Artemis.Core; /// /// Provides a few general utilities for ease of use /// public static class Utilities { /// /// Call this before even initializing the Core to make sure the folders required for operation are in place /// public static void PrepareFirstLaunch() { CreateAccessibleDirectory(Constants.DataFolder); CreateAccessibleDirectory(Constants.PluginsFolder); CreateAccessibleDirectory(Constants.LayoutsFolder); CreateAccessibleDirectory(Constants.UpdatingFolder); } /// /// Attempts to gracefully shut down the application with a delayed kill to ensure the application shut down /// /// This is required because not all SDKs shut down properly, it is too unpredictable to just assume we can /// gracefully shut down /// /// public static void Shutdown() { // Request a graceful shutdown, whatever UI we're running can pick this up OnShutdownRequested(); } /// /// Restarts the application /// /// Whether the application should be restarted with elevated permissions /// Delay in seconds before killing process and restarting /// A list of extra arguments to pass to Artemis when restarting public static void Restart(bool elevate, TimeSpan delay, params string[] extraArgs) { if (!OperatingSystem.IsWindows() && elevate) throw new ArtemisCoreException("Elevation on non-Windows platforms is not supported."); OnRestartRequested(new RestartEventArgs(elevate, delay, extraArgs.ToList())); } /// /// Applies a pending update /// /// A boolean indicating whether to silently update or not. public static void ApplyUpdate(bool silent) { OnUpdateRequested(new UpdateEventArgs(silent)); } /// /// Opens the provided URL in the default web browser /// /// The URL to open /// The process created to open the URL public static Process? OpenUrl(string url) { ProcessStartInfo processInfo = new() { FileName = url, UseShellExecute = true }; return Process.Start(processInfo); } /// /// Creates all directories and subdirectories in the specified path unless they already exist with permissions /// allowing access by everyone. /// /// The directory to create. public static void CreateAccessibleDirectory(string path) { if (!Directory.Exists(path)) { DirectoryInfo dataDirectory = Directory.CreateDirectory(path); if (!OperatingSystem.IsWindows()) return; // On Windows, ensure everyone has permission (important when running as admin) DirectorySecurity security = dataDirectory.GetAccessControl(); SecurityIdentifier everyone = new(WellKnownSidType.WorldSid, null); security.AddAccessRule(new FileSystemAccessRule( everyone, FileSystemRights.Modify | FileSystemRights.Synchronize, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow) ); dataDirectory.SetAccessControl(security); } } /// /// Occurs when the core has requested an application shutdown /// public static event EventHandler? ShutdownRequested; /// /// Occurs when the core has requested an application restart /// public static event EventHandler? RestartRequested; /// /// Occurs when the core has requested a pending application update to be applied /// public static event EventHandler? UpdateRequested; /// /// Opens the provided folder in the user's file explorer /// /// The full path of the folder to open public static void OpenFolder(string path) { if (OperatingSystem.IsWindows()) Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path); else if (OperatingSystem.IsMacOS()) Process.Start("open", path); else if (OperatingSystem.IsLinux()) Process.Start("xdg-open", path); else throw new PlatformNotSupportedException("Can't open folders on this platform"); } /// /// Gets the current application location /// /// internal static string GetCurrentLocation() { return Process.GetCurrentProcess().MainModule!.FileName!; } private static void OnRestartRequested(RestartEventArgs e) { RestartRequested?.Invoke(null, e); } private static void OnShutdownRequested() { ShutdownRequested?.Invoke(null, EventArgs.Empty); } private static void OnUpdateRequested(UpdateEventArgs e) { UpdateRequested?.Invoke(null, e); } }