Ionite
12 months ago
committed by
GitHub
40 changed files with 1373 additions and 531 deletions
@ -0,0 +1,126 @@
|
||||
using System.Linq; |
||||
using Avalonia; |
||||
using Avalonia.Controls; |
||||
using Avalonia.Input; |
||||
using Avalonia.Interactivity; |
||||
using Avalonia.VisualTree; |
||||
using JetBrains.Annotations; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Controls.Extensions; |
||||
|
||||
/// <summary> |
||||
/// Show tooltip on Controls with IsEffectivelyEnabled = false |
||||
/// https://github.com/AvaloniaUI/Avalonia/issues/3847#issuecomment-1618790059 |
||||
/// </summary> |
||||
[PublicAPI] |
||||
public static class ShowDisabledTooltipExtension |
||||
{ |
||||
static ShowDisabledTooltipExtension() |
||||
{ |
||||
ShowOnDisabledProperty.Changed.AddClassHandler<Control>(HandleShowOnDisabledChanged); |
||||
} |
||||
|
||||
public static bool GetShowOnDisabled(AvaloniaObject obj) |
||||
{ |
||||
return obj.GetValue(ShowOnDisabledProperty); |
||||
} |
||||
|
||||
public static void SetShowOnDisabled(AvaloniaObject obj, bool value) |
||||
{ |
||||
obj.SetValue(ShowOnDisabledProperty, value); |
||||
} |
||||
|
||||
public static readonly AttachedProperty<bool> ShowOnDisabledProperty = |
||||
AvaloniaProperty.RegisterAttached<object, Control, bool>("ShowOnDisabled"); |
||||
|
||||
private static void HandleShowOnDisabledChanged( |
||||
Control control, |
||||
AvaloniaPropertyChangedEventArgs e |
||||
) |
||||
{ |
||||
if (e.GetNewValue<bool>()) |
||||
{ |
||||
control.DetachedFromVisualTree += AttachedControl_DetachedFromVisualOrExtension; |
||||
control.AttachedToVisualTree += AttachedControl_AttachedToVisualTree; |
||||
if (control.IsInitialized) |
||||
{ |
||||
// enabled after visual attached |
||||
AttachedControl_AttachedToVisualTree(control, null!); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
AttachedControl_DetachedFromVisualOrExtension(control, null!); |
||||
} |
||||
} |
||||
|
||||
private static void AttachedControl_AttachedToVisualTree( |
||||
object? sender, |
||||
VisualTreeAttachmentEventArgs e |
||||
) |
||||
{ |
||||
if (sender is not Control control || TopLevel.GetTopLevel(control) is not { } tl) |
||||
{ |
||||
return; |
||||
} |
||||
// NOTE pointermove needed to be tunneled for me but you may not need to... |
||||
tl.AddHandler( |
||||
InputElement.PointerMovedEvent, |
||||
TopLevel_PointerMoved, |
||||
RoutingStrategies.Tunnel |
||||
); |
||||
} |
||||
|
||||
private static void AttachedControl_DetachedFromVisualOrExtension( |
||||
object? s, |
||||
VisualTreeAttachmentEventArgs e |
||||
) |
||||
{ |
||||
if (s is not Control control) |
||||
{ |
||||
return; |
||||
} |
||||
control.DetachedFromVisualTree -= AttachedControl_DetachedFromVisualOrExtension; |
||||
control.AttachedToVisualTree -= AttachedControl_AttachedToVisualTree; |
||||
if (TopLevel.GetTopLevel(control) is not { } tl) |
||||
{ |
||||
return; |
||||
} |
||||
tl.RemoveHandler(InputElement.PointerMovedEvent, TopLevel_PointerMoved); |
||||
} |
||||
|
||||
private static void TopLevel_PointerMoved(object? sender, PointerEventArgs e) |
||||
{ |
||||
if (sender is not Control tl) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
var attachedControls = tl.GetVisualDescendants() |
||||
.Where(GetShowOnDisabled) |
||||
.Cast<Control>() |
||||
.ToList(); |
||||
|
||||
// find disabled children under pointer w/ this extension enabled |
||||
var disabledChildUnderPointer = attachedControls.FirstOrDefault( |
||||
x => |
||||
x.Bounds.Contains(e.GetPosition(x.Parent as Visual)) |
||||
&& x is { IsEffectivelyVisible: true, IsEffectivelyEnabled: false } |
||||
); |
||||
|
||||
if (disabledChildUnderPointer != null) |
||||
{ |
||||
// manually show tooltip |
||||
ToolTip.SetIsOpen(disabledChildUnderPointer, true); |
||||
} |
||||
|
||||
var disabledTooltipsToHide = attachedControls.Where( |
||||
x => ToolTip.GetIsOpen(x) && x != disabledChildUnderPointer && !x.IsEffectivelyEnabled |
||||
); |
||||
|
||||
foreach (var control in disabledTooltipsToHide) |
||||
{ |
||||
ToolTip.SetIsOpen(control, false); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,28 @@
|
||||
using System; |
||||
using System.Diagnostics.CodeAnalysis; |
||||
using System.Globalization; |
||||
using Avalonia.Data.Converters; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Converters; |
||||
|
||||
public class CustomStringFormatConverter<T>([StringSyntax("CompositeFormat")] string format) |
||||
: IValueConverter |
||||
where T : IFormatProvider, new() |
||||
{ |
||||
/// <inheritdoc /> |
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) |
||||
{ |
||||
return value is null ? null : string.Format(new T(), format, value); |
||||
} |
||||
|
||||
/// <inheritdoc /> |
||||
public object? ConvertBack( |
||||
object? value, |
||||
Type targetType, |
||||
object? parameter, |
||||
CultureInfo culture |
||||
) |
||||
{ |
||||
return value is null ? null : throw new NotImplementedException(); |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
using System; |
||||
using System.Globalization; |
||||
using Avalonia.Data.Converters; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Converters; |
||||
|
||||
/// <summary> |
||||
/// Converts an index to index + 1 |
||||
/// </summary> |
||||
public class IndexPlusOneConverter : IValueConverter |
||||
{ |
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) |
||||
{ |
||||
if (value is int i) |
||||
{ |
||||
return i + 1; |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
|
||||
public object? ConvertBack( |
||||
object? value, |
||||
Type targetType, |
||||
object? parameter, |
||||
CultureInfo culture |
||||
) |
||||
{ |
||||
if (value is int i) |
||||
{ |
||||
return i - 1; |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
using System; |
||||
using Size = StabilityMatrix.Core.Helper.Size; |
||||
|
||||
namespace StabilityMatrix.Avalonia.Converters; |
||||
|
||||
public class MemoryBytesFormatter : ICustomFormatter, IFormatProvider |
||||
{ |
||||
/// <inheritdoc /> |
||||
public object? GetFormat(Type? formatType) |
||||
{ |
||||
return formatType == typeof(ICustomFormatter) ? this : null; |
||||
} |
||||
|
||||
/// <inheritdoc /> |
||||
public string Format(string? format, object? arg, IFormatProvider? formatProvider) |
||||
{ |
||||
if (format == null || !format.Trim().StartsWith('M')) |
||||
{ |
||||
if (arg is IFormattable formatArg) |
||||
{ |
||||
return formatArg.ToString(format, formatProvider); |
||||
} |
||||
|
||||
return arg?.ToString() ?? string.Empty; |
||||
} |
||||
|
||||
var value = Convert.ToUInt64(arg); |
||||
|
||||
var result = format.Trim().EndsWith("10", StringComparison.OrdinalIgnoreCase) |
||||
? Size.FormatBase10Bytes(value) |
||||
: Size.FormatBytes(value); |
||||
|
||||
// Strip i if not Mi |
||||
if (!format.Trim().Contains('I', StringComparison.OrdinalIgnoreCase)) |
||||
{ |
||||
result = result.Replace("i", string.Empty, StringComparison.OrdinalIgnoreCase); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
} |
@ -1,172 +0,0 @@
|
||||
using System.Diagnostics; |
||||
using System.Runtime.Versioning; |
||||
using System.Text.RegularExpressions; |
||||
using Microsoft.Win32; |
||||
|
||||
namespace StabilityMatrix.Core.Helper; |
||||
|
||||
public static partial class HardwareHelper |
||||
{ |
||||
private static IReadOnlyList<GpuInfo>? cachedGpuInfos; |
||||
|
||||
private static string RunBashCommand(string command) |
||||
{ |
||||
var processInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"") |
||||
{ |
||||
UseShellExecute = false, |
||||
RedirectStandardOutput = true |
||||
}; |
||||
|
||||
var process = Process.Start(processInfo); |
||||
|
||||
process.WaitForExit(); |
||||
|
||||
var output = process.StandardOutput.ReadToEnd(); |
||||
|
||||
return output; |
||||
} |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
private static IEnumerable<GpuInfo> IterGpuInfoWindows() |
||||
{ |
||||
const string gpuRegistryKeyPath = |
||||
@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}"; |
||||
|
||||
using var baseKey = Registry.LocalMachine.OpenSubKey(gpuRegistryKeyPath); |
||||
|
||||
if (baseKey == null) yield break; |
||||
|
||||
foreach (var subKeyName in baseKey.GetSubKeyNames().Where(k => k.StartsWith("0"))) |
||||
{ |
||||
using var subKey = baseKey.OpenSubKey(subKeyName); |
||||
if (subKey != null) |
||||
{ |
||||
yield return new GpuInfo |
||||
{ |
||||
Name = subKey.GetValue("DriverDesc")?.ToString(), |
||||
MemoryBytes = Convert.ToUInt64(subKey.GetValue("HardwareInformation.qwMemorySize")), |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
|
||||
[SupportedOSPlatform("linux")] |
||||
private static IEnumerable<GpuInfo> IterGpuInfoLinux() |
||||
{ |
||||
var output = RunBashCommand("lspci | grep VGA"); |
||||
var gpuLines = output.Split("\n"); |
||||
|
||||
foreach (var line in gpuLines) |
||||
{ |
||||
if (string.IsNullOrWhiteSpace(line)) continue; |
||||
|
||||
var gpuId = line.Split(' ')[0]; // The GPU ID is the first part of the line |
||||
var gpuOutput = RunBashCommand($"lspci -v -s {gpuId}"); |
||||
|
||||
ulong memoryBytes = 0; |
||||
string? name = null; |
||||
|
||||
// Parse output with regex |
||||
var match = Regex.Match(gpuOutput, @"VGA compatible controller: ([^\n]*)"); |
||||
if (match.Success) |
||||
{ |
||||
name = match.Groups[1].Value.Trim(); |
||||
} |
||||
|
||||
match = Regex.Match(gpuOutput, @"prefetchable\) \[size=(\\d+)M\]"); |
||||
if (match.Success) |
||||
{ |
||||
memoryBytes = ulong.Parse(match.Groups[1].Value) * 1024 * 1024; |
||||
} |
||||
|
||||
yield return new GpuInfo { Name = name, MemoryBytes = memoryBytes }; |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Yields GpuInfo for each GPU in the system. |
||||
/// </summary> |
||||
public static IEnumerable<GpuInfo> IterGpuInfo() |
||||
{ |
||||
if (Compat.IsWindows) |
||||
{ |
||||
return IterGpuInfoWindows(); |
||||
} |
||||
else if (Compat.IsLinux) |
||||
{ |
||||
// Since this requires shell commands, fetch cached value if available. |
||||
if (cachedGpuInfos is not null) |
||||
{ |
||||
return cachedGpuInfos; |
||||
} |
||||
|
||||
// No cache, fetch and cache. |
||||
cachedGpuInfos = IterGpuInfoLinux().ToList(); |
||||
return cachedGpuInfos; |
||||
} |
||||
// TODO: Implement for macOS |
||||
return Enumerable.Empty<GpuInfo>(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Return true if the system has at least one Nvidia GPU. |
||||
/// </summary> |
||||
public static bool HasNvidiaGpu() |
||||
{ |
||||
return IterGpuInfo().Any(gpu => gpu.IsNvidia); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Return true if the system has at least one AMD GPU. |
||||
/// </summary> |
||||
public static bool HasAmdGpu() |
||||
{ |
||||
return IterGpuInfo().Any(gpu => gpu.IsAmd); |
||||
} |
||||
|
||||
// Set ROCm for default if AMD and Linux |
||||
public static bool PreferRocm() => !HasNvidiaGpu() |
||||
&& HasAmdGpu() |
||||
&& Compat.IsLinux; |
||||
|
||||
// Set DirectML for default if AMD and Windows |
||||
public static bool PreferDirectML() => !HasNvidiaGpu() |
||||
&& HasAmdGpu() |
||||
&& Compat.IsWindows; |
||||
} |
||||
|
||||
public enum Level |
||||
{ |
||||
Unknown, |
||||
Low, |
||||
Medium, |
||||
High |
||||
} |
||||
|
||||
public record GpuInfo |
||||
{ |
||||
public string? Name { get; init; } = string.Empty; |
||||
public ulong MemoryBytes { get; init; } |
||||
public Level? MemoryLevel => MemoryBytes switch |
||||
{ |
||||
<= 0 => Level.Unknown, |
||||
< 4 * Size.GiB => Level.Low, |
||||
< 8 * Size.GiB => Level.Medium, |
||||
_ => Level.High |
||||
}; |
||||
|
||||
public bool IsNvidia |
||||
{ |
||||
get |
||||
{ |
||||
var name = Name?.ToLowerInvariant(); |
||||
|
||||
if (string.IsNullOrEmpty(name)) return false; |
||||
|
||||
return name.Contains("nvidia") |
||||
|| name.Contains("tesla"); |
||||
} |
||||
} |
||||
|
||||
public bool IsAmd => Name?.ToLowerInvariant().Contains("amd") ?? false; |
||||
} |
@ -0,0 +1,7 @@
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
public readonly record struct CpuInfo |
||||
{ |
||||
public string ProcessorCaption { get; init; } |
||||
public string ProcessorName { get; init; } |
||||
} |
@ -0,0 +1,31 @@
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
public record GpuInfo |
||||
{ |
||||
public int Index { get; init; } |
||||
public string? Name { get; init; } = string.Empty; |
||||
public ulong MemoryBytes { get; init; } |
||||
public MemoryLevel? MemoryLevel => |
||||
MemoryBytes switch |
||||
{ |
||||
<= 0 => HardwareInfo.MemoryLevel.Unknown, |
||||
< 4 * Size.GiB => HardwareInfo.MemoryLevel.Low, |
||||
< 8 * Size.GiB => HardwareInfo.MemoryLevel.Medium, |
||||
_ => HardwareInfo.MemoryLevel.High |
||||
}; |
||||
|
||||
public bool IsNvidia |
||||
{ |
||||
get |
||||
{ |
||||
var name = Name?.ToLowerInvariant(); |
||||
|
||||
if (string.IsNullOrEmpty(name)) |
||||
return false; |
||||
|
||||
return name.Contains("nvidia") || name.Contains("tesla"); |
||||
} |
||||
} |
||||
|
||||
public bool IsAmd => Name?.Contains("amd", StringComparison.OrdinalIgnoreCase) ?? false; |
||||
} |
@ -0,0 +1,241 @@
|
||||
using System.ComponentModel; |
||||
using System.Diagnostics; |
||||
using System.Runtime.InteropServices; |
||||
using System.Runtime.Versioning; |
||||
using System.Text.RegularExpressions; |
||||
using Hardware.Info; |
||||
using Microsoft.Win32; |
||||
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
public static partial class HardwareHelper |
||||
{ |
||||
private static IReadOnlyList<GpuInfo>? cachedGpuInfos; |
||||
|
||||
private static readonly Lazy<IHardwareInfo> HardwareInfoLazy = |
||||
new(() => new Hardware.Info.HardwareInfo()); |
||||
|
||||
public static IHardwareInfo HardwareInfo => HardwareInfoLazy.Value; |
||||
|
||||
private static string RunBashCommand(string command) |
||||
{ |
||||
var processInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"") |
||||
{ |
||||
UseShellExecute = false, |
||||
RedirectStandardOutput = true |
||||
}; |
||||
|
||||
var process = Process.Start(processInfo); |
||||
|
||||
process.WaitForExit(); |
||||
|
||||
var output = process.StandardOutput.ReadToEnd(); |
||||
|
||||
return output; |
||||
} |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
private static IEnumerable<GpuInfo> IterGpuInfoWindows() |
||||
{ |
||||
const string gpuRegistryKeyPath = |
||||
@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}"; |
||||
|
||||
using var baseKey = Registry.LocalMachine.OpenSubKey(gpuRegistryKeyPath); |
||||
|
||||
if (baseKey == null) |
||||
yield break; |
||||
|
||||
var gpuIndex = 0; |
||||
|
||||
foreach (var subKeyName in baseKey.GetSubKeyNames().Where(k => k.StartsWith("0"))) |
||||
{ |
||||
using var subKey = baseKey.OpenSubKey(subKeyName); |
||||
if (subKey != null) |
||||
{ |
||||
yield return new GpuInfo |
||||
{ |
||||
Index = gpuIndex++, |
||||
Name = subKey.GetValue("DriverDesc")?.ToString(), |
||||
MemoryBytes = Convert.ToUInt64( |
||||
subKey.GetValue("HardwareInformation.qwMemorySize") |
||||
), |
||||
}; |
||||
} |
||||
} |
||||
} |
||||
|
||||
[SupportedOSPlatform("linux")] |
||||
private static IEnumerable<GpuInfo> IterGpuInfoLinux() |
||||
{ |
||||
var output = RunBashCommand("lspci | grep VGA"); |
||||
var gpuLines = output.Split("\n"); |
||||
|
||||
var gpuIndex = 0; |
||||
|
||||
foreach (var line in gpuLines) |
||||
{ |
||||
if (string.IsNullOrWhiteSpace(line)) |
||||
continue; |
||||
|
||||
var gpuId = line.Split(' ')[0]; // The GPU ID is the first part of the line |
||||
var gpuOutput = RunBashCommand($"lspci -v -s {gpuId}"); |
||||
|
||||
ulong memoryBytes = 0; |
||||
string? name = null; |
||||
|
||||
// Parse output with regex |
||||
var match = Regex.Match(gpuOutput, @"VGA compatible controller: ([^\n]*)"); |
||||
if (match.Success) |
||||
{ |
||||
name = match.Groups[1].Value.Trim(); |
||||
} |
||||
|
||||
match = Regex.Match(gpuOutput, @"prefetchable\) \[size=(\\d+)M\]"); |
||||
if (match.Success) |
||||
{ |
||||
memoryBytes = ulong.Parse(match.Groups[1].Value) * 1024 * 1024; |
||||
} |
||||
|
||||
yield return new GpuInfo |
||||
{ |
||||
Index = gpuIndex++, |
||||
Name = name, |
||||
MemoryBytes = memoryBytes |
||||
}; |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Yields GpuInfo for each GPU in the system. |
||||
/// </summary> |
||||
public static IEnumerable<GpuInfo> IterGpuInfo() |
||||
{ |
||||
if (Compat.IsWindows) |
||||
{ |
||||
return IterGpuInfoWindows(); |
||||
} |
||||
else if (Compat.IsLinux) |
||||
{ |
||||
// Since this requires shell commands, fetch cached value if available. |
||||
if (cachedGpuInfos is not null) |
||||
{ |
||||
return cachedGpuInfos; |
||||
} |
||||
|
||||
// No cache, fetch and cache. |
||||
cachedGpuInfos = IterGpuInfoLinux().ToList(); |
||||
return cachedGpuInfos; |
||||
} |
||||
// TODO: Implement for macOS |
||||
return Enumerable.Empty<GpuInfo>(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Return true if the system has at least one Nvidia GPU. |
||||
/// </summary> |
||||
public static bool HasNvidiaGpu() |
||||
{ |
||||
return IterGpuInfo().Any(gpu => gpu.IsNvidia); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Return true if the system has at least one AMD GPU. |
||||
/// </summary> |
||||
public static bool HasAmdGpu() |
||||
{ |
||||
return IterGpuInfo().Any(gpu => gpu.IsAmd); |
||||
} |
||||
|
||||
// Set ROCm for default if AMD and Linux |
||||
public static bool PreferRocm() => !HasNvidiaGpu() && HasAmdGpu() && Compat.IsLinux; |
||||
|
||||
// Set DirectML for default if AMD and Windows |
||||
public static bool PreferDirectML() => !HasNvidiaGpu() && HasAmdGpu() && Compat.IsWindows; |
||||
|
||||
/// <summary> |
||||
/// Gets the total and available physical memory in bytes. |
||||
/// </summary> |
||||
public static MemoryInfo GetMemoryInfo() => |
||||
Compat.IsWindows ? GetMemoryInfoImplWindows() : GetMemoryInfoImplGeneric(); |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
private static MemoryInfo GetMemoryInfoImplWindows() |
||||
{ |
||||
var memoryStatus = new Win32MemoryStatusEx(); |
||||
|
||||
if (!GlobalMemoryStatusEx(ref memoryStatus)) |
||||
{ |
||||
throw new Win32Exception(Marshal.GetLastWin32Error()); |
||||
} |
||||
|
||||
if (!GetPhysicallyInstalledSystemMemory(out var installedMemoryKb)) |
||||
{ |
||||
throw new Win32Exception(Marshal.GetLastWin32Error()); |
||||
} |
||||
|
||||
return new MemoryInfo |
||||
{ |
||||
TotalInstalledBytes = (ulong)installedMemoryKb * 1024, |
||||
TotalPhysicalBytes = memoryStatus.UllTotalPhys, |
||||
AvailablePhysicalBytes = memoryStatus.UllAvailPhys |
||||
}; |
||||
} |
||||
|
||||
private static MemoryInfo GetMemoryInfoImplGeneric() |
||||
{ |
||||
HardwareInfo.RefreshMemoryList(); |
||||
|
||||
return new MemoryInfo |
||||
{ |
||||
TotalPhysicalBytes = HardwareInfo.MemoryStatus.TotalPhysical, |
||||
AvailablePhysicalBytes = HardwareInfo.MemoryStatus.AvailablePhysical |
||||
}; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Gets cpu info |
||||
/// </summary> |
||||
public static Task<CpuInfo> GetCpuInfoAsync() => |
||||
Compat.IsWindows ? Task.FromResult(GetCpuInfoImplWindows()) : GetCpuInfoImplGenericAsync(); |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
private static CpuInfo GetCpuInfoImplWindows() |
||||
{ |
||||
var info = new CpuInfo(); |
||||
|
||||
using var processorKey = Registry.LocalMachine.OpenSubKey( |
||||
@"Hardware\Description\System\CentralProcessor\0", |
||||
RegistryKeyPermissionCheck.ReadSubTree |
||||
); |
||||
|
||||
if (processorKey?.GetValue("ProcessorNameString") is string processorName) |
||||
{ |
||||
info = info with { ProcessorCaption = processorName.Trim() }; |
||||
} |
||||
|
||||
return info; |
||||
} |
||||
|
||||
private static Task<CpuInfo> GetCpuInfoImplGenericAsync() |
||||
{ |
||||
return Task.Run(() => |
||||
{ |
||||
HardwareInfo.RefreshCPUList(); |
||||
|
||||
return new CpuInfo |
||||
{ |
||||
ProcessorCaption = HardwareInfo.CpuList.FirstOrDefault()?.Caption.Trim() ?? "" |
||||
}; |
||||
}); |
||||
} |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
[LibraryImport("kernel32.dll", SetLastError = true)] |
||||
[return: MarshalAs(UnmanagedType.Bool)] |
||||
private static partial bool GetPhysicallyInstalledSystemMemory(out long totalMemoryInKilobytes); |
||||
|
||||
[SupportedOSPlatform("windows")] |
||||
[LibraryImport("kernel32.dll", SetLastError = true)] |
||||
[return: MarshalAs(UnmanagedType.Bool)] |
||||
private static partial bool GlobalMemoryStatusEx(ref Win32MemoryStatusEx lpBuffer); |
||||
} |
@ -0,0 +1,10 @@
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
public readonly record struct MemoryInfo |
||||
{ |
||||
public ulong TotalInstalledBytes { get; init; } |
||||
|
||||
public ulong TotalPhysicalBytes { get; init; } |
||||
|
||||
public ulong AvailablePhysicalBytes { get; init; } |
||||
} |
@ -0,0 +1,9 @@
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
public enum MemoryLevel |
||||
{ |
||||
Unknown, |
||||
Low, |
||||
Medium, |
||||
High |
||||
} |
@ -0,0 +1,19 @@
|
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace StabilityMatrix.Core.Helper.HardwareInfo; |
||||
|
||||
[StructLayout(LayoutKind.Sequential)] |
||||
public struct Win32MemoryStatusEx |
||||
{ |
||||
public uint DwLength = (uint)Marshal.SizeOf(typeof(Win32MemoryStatusEx)); |
||||
public uint DwMemoryLoad = 0; |
||||
public ulong UllTotalPhys = 0; |
||||
public ulong UllAvailPhys = 0; |
||||
public ulong UllTotalPageFile = 0; |
||||
public ulong UllAvailPageFile = 0; |
||||
public ulong UllTotalVirtual = 0; |
||||
public ulong UllAvailVirtual = 0; |
||||
public ulong UllAvailExtendedVirtual = 0; |
||||
|
||||
public Win32MemoryStatusEx() { } |
||||
} |
@ -1,8 +1,11 @@
|
||||
namespace StabilityMatrix.Core.Models; |
||||
using System.Text.Json.Serialization; |
||||
|
||||
namespace StabilityMatrix.Core.Models; |
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<LaunchOptionType>))] |
||||
public enum LaunchOptionType |
||||
{ |
||||
Bool, |
||||
String, |
||||
Int, |
||||
Int |
||||
} |
||||
|
Loading…
Reference in new issue