feat(linux): add graphical platform services
This commit is contained in:
parent
1628d9f587
commit
66f114b258
28 changed files with 1328 additions and 155 deletions
171
src/AcDream.Runtime/Platform/ApplicationPathSet.cs
Normal file
171
src/AcDream.Runtime/Platform/ApplicationPathSet.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
namespace AcDream.Runtime.Platform;
|
||||
|
||||
/// <summary>
|
||||
/// BCL-only view of the process environment used to resolve portable
|
||||
/// application paths exactly once at a host composition root.
|
||||
/// </summary>
|
||||
public interface IApplicationPathEnvironment
|
||||
{
|
||||
bool IsWindows { get; }
|
||||
|
||||
string CurrentDirectory { get; }
|
||||
|
||||
string? GetEnvironmentVariable(string name);
|
||||
|
||||
string GetFolderPath(Environment.SpecialFolder folder);
|
||||
}
|
||||
|
||||
internal sealed class ApplicationPathEnvironment
|
||||
: IApplicationPathEnvironment
|
||||
{
|
||||
internal static ApplicationPathEnvironment Instance { get; } = new();
|
||||
|
||||
private ApplicationPathEnvironment()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsWindows => OperatingSystem.IsWindows();
|
||||
|
||||
public string CurrentDirectory => Environment.CurrentDirectory;
|
||||
|
||||
public string? GetEnvironmentVariable(string name) =>
|
||||
Environment.GetEnvironmentVariable(name);
|
||||
|
||||
public string GetFolderPath(Environment.SpecialFolder folder) =>
|
||||
Environment.GetFolderPath(folder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Canonical portable per-user paths shared by graphical and no-window hosts.
|
||||
/// Hosts may override the three roots, but feature code only receives the
|
||||
/// resolved immutable paths and never reads operating-system state itself.
|
||||
/// </summary>
|
||||
public sealed record ApplicationPathSet(
|
||||
string ConfigDirectory,
|
||||
string DataDirectory,
|
||||
string CacheDirectory,
|
||||
string? LegacyConfigDirectory)
|
||||
{
|
||||
public string SettingsFile =>
|
||||
Path.Combine(ConfigDirectory, "settings.json");
|
||||
|
||||
public string KeyBindingsFile =>
|
||||
Path.Combine(ConfigDirectory, "keybinds.json");
|
||||
|
||||
public string LogsDirectory =>
|
||||
Path.Combine(DataDirectory, "logs");
|
||||
|
||||
public string ScreenshotsDirectory =>
|
||||
Path.Combine(DataDirectory, "screenshots");
|
||||
|
||||
public string PluginsDirectory =>
|
||||
Path.Combine(DataDirectory, "plugins");
|
||||
|
||||
public string DiagnosticsDirectory =>
|
||||
Path.Combine(CacheDirectory, "diagnostics");
|
||||
|
||||
public static ApplicationPathSet Resolve(
|
||||
string? configDirectory = null,
|
||||
string? dataDirectory = null,
|
||||
string? cacheDirectory = null,
|
||||
IApplicationPathEnvironment? platform = null)
|
||||
{
|
||||
platform ??= ApplicationPathEnvironment.Instance;
|
||||
|
||||
string config;
|
||||
string data;
|
||||
string cache;
|
||||
if (platform.IsWindows)
|
||||
{
|
||||
string roaming = RequireFolder(
|
||||
platform,
|
||||
Environment.SpecialFolder.ApplicationData);
|
||||
string local = RequireFolder(
|
||||
platform,
|
||||
Environment.SpecialFolder.LocalApplicationData);
|
||||
config = Path.Combine(roaming, "acdream");
|
||||
data = Path.Combine(local, "acdream");
|
||||
cache = Path.Combine(local, "acdream", "cache");
|
||||
}
|
||||
else
|
||||
{
|
||||
string home = RequireFolder(
|
||||
platform,
|
||||
Environment.SpecialFolder.UserProfile);
|
||||
config = ResolveXdg(
|
||||
platform,
|
||||
"XDG_CONFIG_HOME",
|
||||
Path.Combine(home, ".config"),
|
||||
"acdream");
|
||||
data = ResolveXdg(
|
||||
platform,
|
||||
"XDG_DATA_HOME",
|
||||
Path.Combine(home, ".local", "share"),
|
||||
"acdream");
|
||||
cache = ResolveXdg(
|
||||
platform,
|
||||
"XDG_CACHE_HOME",
|
||||
Path.Combine(home, ".cache"),
|
||||
"acdream");
|
||||
}
|
||||
|
||||
string? legacyConfigDirectory =
|
||||
platform.IsWindows && configDirectory is null
|
||||
? Path.Combine(
|
||||
RequireFolder(
|
||||
platform,
|
||||
Environment.SpecialFolder.LocalApplicationData),
|
||||
"acdream")
|
||||
: null;
|
||||
|
||||
return new ApplicationPathSet(
|
||||
Normalize(configDirectory ?? config, platform),
|
||||
Normalize(dataDirectory ?? data, platform),
|
||||
Normalize(cacheDirectory ?? cache, platform),
|
||||
legacyConfigDirectory is null
|
||||
? null
|
||||
: Normalize(legacyConfigDirectory, platform));
|
||||
}
|
||||
|
||||
private static string ResolveXdg(
|
||||
IApplicationPathEnvironment platform,
|
||||
string variable,
|
||||
string fallback,
|
||||
string leaf)
|
||||
{
|
||||
string? configured = platform.GetEnvironmentVariable(variable);
|
||||
string root = string.IsNullOrWhiteSpace(configured)
|
||||
? fallback
|
||||
: configured;
|
||||
return Path.Combine(root, leaf);
|
||||
}
|
||||
|
||||
private static string RequireFolder(
|
||||
IApplicationPathEnvironment platform,
|
||||
Environment.SpecialFolder folder)
|
||||
{
|
||||
string value = platform.GetFolderPath(folder);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The operating system did not provide {folder}.");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string Normalize(
|
||||
string path,
|
||||
IApplicationPathEnvironment platform)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Application directories cannot be empty.",
|
||||
nameof(path));
|
||||
}
|
||||
|
||||
return Path.TrimEndingDirectorySeparator(
|
||||
Path.GetFullPath(path, platform.CurrentDirectory));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue