Skip to content
1 change: 1 addition & 0 deletions resources/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@

"HardwareSettings": "Source",
"DiskSource": "Disk",
"GpuSource": "GPU",
"NetworkSource": "Network",
"Auto": "Auto",
"UseWinPerCounters": "Use Sys Counters",
Expand Down
1 change: 1 addition & 0 deletions resources/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@

"HardwareSettings": "硬件与传感器设置",
"DiskSource": "首选磁盘",
"GpuSource": "首选显卡",
"NetworkSource": "首选网卡",
"Auto": "自动模式",
"UseWinPerCounters": "优先使用系统计数器",
Expand Down
24 changes: 24 additions & 0 deletions scripts/package.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
param(
[string]$Runtime = "win-x64",
[string]$Configuration = "Release",
[string]$Version = "dev"
)

$ErrorActionPreference = "Stop"

if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) {
Write-Error "dotnet CLI not found. Please install .NET SDK 8+ and retry."
}

$outDir = Join-Path $PSScriptRoot "artifacts/$Runtime"
$publishDir = Join-Path $outDir "publish"
New-Item -ItemType Directory -Force -Path $publishDir | Out-Null

dotnet publish "$PSScriptRoot/../LiteMonitor.csproj" -c $Configuration -r $Runtime --self-contained false -o $publishDir

$zipName = "LiteMonitor_v$Version-$Runtime.zip"
$zipPath = Join-Path $outDir $zipName
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path (Join-Path $publishDir '*') -DestinationPath $zipPath

Write-Host "Package created: $zipPath"
1 change: 1 addition & 0 deletions src/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Settings
public string LastAutoNetwork { get; set; } = "";
public string PreferredDisk { get; set; } = "";
public string LastAutoDisk { get; set; } = "";
public string PreferredGpu { get; set; } = ""; // ★★★ [新增] 首选显卡 (空=自动选第一块) ★★★

// ★★★ [新增] 首选风扇 ★★★
public string PreferredCpuFan { get; set; } = "";
Expand Down
2 changes: 2 additions & 0 deletions src/System/HardwareMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ public static string GenerateSmartName(ISensor sensor, IHardware hardware) =>

public static List<string> ListAllDisks() => HardwareScanner.ListAllDisks(Instance!._computer);

public static List<string> ListAllGpus() => HardwareScanner.ListAllGpus(Instance!._computer); // ★★★ [新增] ★★★

public static List<string> ListAllFans() => HardwareScanner.ListAllFans(Instance!._computer, Instance!._lock);

public static List<string> ListAllMoboTemps() => HardwareScanner.ListAllMoboTemps(Instance!._computer, Instance!._lock);
Expand Down
42 changes: 30 additions & 12 deletions src/System/HardwareServices/HardwareScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static class HardwareScanner
private static List<string>? _cachedNetworkList = null;
private static List<string>? _cachedDiskList = null;
private static List<string>? _cachedMoboTempList = null;
private static List<string>? _cachedGpuList = null; // ★★★ [新增] GPU 列表缓存 ★★★

/// <summary>
/// 清除所有扫描缓存
Expand All @@ -24,6 +25,7 @@ public static void ClearCache()
_cachedNetworkList = null;
_cachedDiskList = null;
_cachedMoboTempList = null;
_cachedGpuList = null; // ★★★ [新增] ★★★
}

/// <summary>
Expand All @@ -47,14 +49,14 @@ public static string GenerateSmartName(ISensor sensor, IHardware hardware, IComp
public static List<string> ListAllNetworks(IComputer computer)
{
if (_cachedNetworkList != null && _cachedNetworkList.Count > 0)
return _cachedNetworkList.ToList();
return new List<string>(_cachedNetworkList);

var list = computer.Hardware
.Where(h => h.HardwareType == HardwareType.Network)
.Select(h => h.Name).Distinct().ToList();

if (list.Count > 0) _cachedNetworkList = list;
return list.ToList();
return list;
}

/// <summary>
Expand All @@ -63,14 +65,32 @@ public static List<string> ListAllNetworks(IComputer computer)
public static List<string> ListAllDisks(IComputer computer)
{
if (_cachedDiskList != null && _cachedDiskList.Count > 0)
return _cachedDiskList.ToList();
return new List<string>(_cachedDiskList);

var list = computer.Hardware
.Where(h => h.HardwareType == HardwareType.Storage)
.Select(h => h.Name).Distinct().ToList();

if (list.Count > 0) _cachedDiskList = list;
return list.ToList();
return list;
}

/// <summary>
/// ★★★ [新增] 列出所有显卡名称 (支持 NVIDIA / AMD / Intel) ★★★
/// </summary>
public static List<string> ListAllGpus(IComputer computer)
{
if (_cachedGpuList != null && _cachedGpuList.Count > 0)
return new List<string>(_cachedGpuList);

var list = computer.Hardware
.Where(h => h.HardwareType == HardwareType.GpuNvidia ||
h.HardwareType == HardwareType.GpuAmd ||
h.HardwareType == HardwareType.GpuIntel)
.Select(h => h.Name).Distinct().ToList();

if (list.Count > 0) _cachedGpuList = list;
return list;
}

/// <summary>
Expand All @@ -79,7 +99,7 @@ public static List<string> ListAllDisks(IComputer computer)
public static List<string> ListAllFans(IComputer computer, object syncLock)
{
if (_cachedFanList != null && _cachedFanList.Count > 0)
return _cachedFanList.ToList();
return new List<string>(_cachedFanList);

var list = new List<string>();
lock (syncLock)
Expand All @@ -104,10 +124,9 @@ void Scan(IHardware hw)
foreach (var hw in computer.Hardware) Scan(hw);
}

list.Sort();
var final = list.Distinct().ToList();
var final = list.Distinct().OrderBy(name => name).ToList();
if (final.Count > 0) _cachedFanList = final;
return final.ToList();
return final;
}

/// <summary>
Expand All @@ -116,7 +135,7 @@ void Scan(IHardware hw)
public static List<string> ListAllMoboTemps(IComputer computer, object syncLock)
{
if (_cachedMoboTempList != null && _cachedMoboTempList.Count > 0)
return _cachedMoboTempList.ToList();
return new List<string>(_cachedMoboTempList);

var list = new List<string>();
lock (syncLock)
Expand All @@ -141,10 +160,9 @@ void Scan(IHardware hw)
foreach (var hw in computer.Hardware) Scan(hw);
}

list.Sort();
var final = list.Distinct().ToList();
var final = list.Distinct().OrderBy(name => name).ToList();
if (final.Count > 0) _cachedMoboTempList = final;
return final.ToList();
return final;
}
}
}
14 changes: 12 additions & 2 deletions src/System/HardwareServices/HardwareValueProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class HardwareValueProvider : IDisposable
private string _lastPrefMoboTemp = "";
private string _lastPrefDisk = "";
private string _lastPrefNet = "";
private string _lastPrefGpu = ""; // ★★★ [新增] 用户首选显卡 ★★★

public HardwareValueProvider(Computer c, Settings s, SensorMap map, NetworkManager net, DiskManager disk, FpsCounter fpsCounter,PerformanceCounterManager perfManager, object syncLock, Dictionary<string, float> lastValid)
{
Expand Down Expand Up @@ -65,6 +66,7 @@ public void PreCacheAllSensors(SensorMap map)
_lastPrefMoboTemp = _cfg.PreferredMoboTemp;
_lastPrefDisk = _cfg.PreferredDisk;
_lastPrefNet = _cfg.PreferredNetwork;
_lastPrefGpu = _cfg.PreferredGpu ?? ""; // ★★★ [新增] ★★★

// 1. 预查找用户指定的首选传感器 (风扇、水泵、主板温度)
string[] preferredKeys = { "CPU.Fan", "CPU.Pump", "CASE.Fan", "MOBO.Temp" };
Expand Down Expand Up @@ -188,8 +190,16 @@ public void OnUpdateTickStarted()
{
_tickCache.Clear();

// 自动检测配置变更:如果用户更改了首选风扇/磁盘,立即自动预热
if (_lastPrefCpuFan != _cfg.PreferredCpuFan ||
// ★★★ [新增] 检测 PreferredGpu 变更:需要重建 SensorMap (过滤逻辑在 Rebuild 里) ★★★
bool gpuChanged = _lastPrefGpu != (_cfg.PreferredGpu ?? "");
if (gpuChanged)
{
_sensorMap.Rebuild(_computer, _cfg);
}

// 自动检测配置变更:如果用户更改了首选风扇/磁盘/显卡,立即自动预热
if (gpuChanged ||
_lastPrefCpuFan != _cfg.PreferredCpuFan ||
_lastPrefCpuPump != _cfg.PreferredCpuPump ||
_lastPrefCaseFan != _cfg.PreferredCaseFan ||
_lastPrefMoboTemp != _cfg.PreferredMoboTemp ||
Expand Down
29 changes: 28 additions & 1 deletion src/System/HardwareServices/SensorMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ public void Clear()
}
}

/// <summary>
/// ★★★ [新增] 使映射失效,强制下次 EnsureFresh 时重建 ★★★
/// 用于 PreferredGpu 等配置变更后立即生效
/// </summary>
public void Invalidate()
{
lock (_lock)
{
_lastMapBuild = DateTime.MinValue;
}
}



public bool TryGetSensor(string key, out ISensor? sensor)
Expand Down Expand Up @@ -100,11 +112,26 @@ public void Rebuild(Computer computer, Settings cfg) // ★ 签名修改
// 但为了保持原代码结构,我们依然用candidates收集主板相关数据
var candidatesMoboTemps = new List<ISensor>(capacity: 10); // 预设容量,减少扩容开销

// ★★★ [新增] 读取用户首选显卡配置 (空=自动选第一块) ★★★
string prefGpu = cfg.PreferredGpu ?? "";

// 局部递归函数
void RegisterTo(IHardware hw)
{
//hw.Update();

// ★★★ [新增] 首选显卡过滤:如果用户指定了首选 GPU,非匹配 GPU 完全跳过 ★★★
// 这样 newGpu 和 newMap["GPU.*"] 都只会来自用户指定的显卡
bool isGpuHw = hw.HardwareType == HardwareType.GpuNvidia ||
hw.HardwareType == HardwareType.GpuAmd ||
hw.HardwareType == HardwareType.GpuIntel;
if (isGpuHw && !string.IsNullOrEmpty(prefGpu) &&
!hw.Name.Equals(prefGpu, StringComparison.OrdinalIgnoreCase))
{
// 不处理该 GPU 的任何数据,也不递归其 SubHardware
return;
}

// --- 填充 CPU 缓存 (用于加权平均) ---
if (hw.HardwareType == HardwareType.Cpu)
{
Expand Down Expand Up @@ -282,4 +309,4 @@ void RegisterTo(IHardware hw)
// 复用 HardwareRules 的字符串匹配,避免重复造轮子
public static bool Has(string source, string sub) => HardwareRules.Has(source, sub);
}
}
}
10 changes: 9 additions & 1 deletion src/UI/Settings/SystemHardwarPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class SystemHardwarPage : SettingsPageBase

// ★★★ 修复:类型更正为 LiteComboBox ★★★
private LiteComboBox _cbDisk, _cbNet, _cbMobo;
private LiteComboBox _cbGpu; // ★★★ [新增] 显卡下拉 ★★★
private LiteComboBox _cbFanCpu, _cbFanPump, _cbFanCase;

public SystemHardwarPage()
Expand Down Expand Up @@ -54,10 +55,11 @@ private async void PopulateAsyncData()
// 1. 并行等待所有数据返回 (使用 HardwareScanner)
var taskDisks = Task.Run(() => HardwareScanner.ListAllDisks(HardwareMonitor.Instance.ComputerInstance));
var taskNets = Task.Run(() => HardwareScanner.ListAllNetworks(HardwareMonitor.Instance.ComputerInstance));
var taskGpus = Task.Run(() => HardwareScanner.ListAllGpus(HardwareMonitor.Instance.ComputerInstance)); // ★★★ [新增] ★★★
var taskFans = Task.Run(() => HardwareScanner.ListAllFans(HardwareMonitor.Instance.ComputerInstance, HardwareMonitor.Instance.SyncLock));
var taskMobo = Task.Run(() => HardwareScanner.ListAllMoboTemps(HardwareMonitor.Instance.ComputerInstance, HardwareMonitor.Instance.SyncLock));

await Task.WhenAll(taskDisks, taskNets, taskFans, taskMobo);
await Task.WhenAll(taskDisks, taskNets, taskGpus, taskFans, taskMobo);

// 2. ★★★ 锁定全局布局 (防止每填一个框就重绘一次) ★★★
this.SuspendLayout();
Expand Down Expand Up @@ -86,6 +88,7 @@ void FillSync(LiteComboBox combo, List<string> data, string currentVal)
// 3. 瞬间填入所有数据 (因为布局被挂起,用户看不见中间过程)
FillSync(_cbDisk, taskDisks.Result, Config.PreferredDisk);
FillSync(_cbNet, taskNets.Result, Config.PreferredNetwork);
FillSync(_cbGpu, taskGpus.Result, Config.PreferredGpu); // ★★★ [新增] ★★★
FillSync(_cbMobo, taskMobo.Result, Config.PreferredMoboTemp);

// Fan 的数据是复用的
Expand Down Expand Up @@ -137,6 +140,11 @@ private void CreateSourceCard()
() => Config?.PreferredDisk ?? strAuto,
v => { if(Config!=null) Config.PreferredDisk = (v == strAuto ? "" : v); });

// ★★★ [新增] 显卡来源下拉:多显卡设备 (笔记本核显+独显) 可选择显示哪块 ★★★
_cbGpu = (LiteComboBox)group.AddCombo(this, "Menu.GpuSource", new List<string> { strAuto },
() => Config?.PreferredGpu ?? strAuto,
v => { if (Config != null) Config.PreferredGpu = (v == strAuto ? "" : v); });

_cbNet = (LiteComboBox)group.AddCombo(this, "Menu.NetworkSource", new List<string> { strAuto },
() => Config?.PreferredNetwork ?? strAuto,
v => { if (Config != null) Config.PreferredNetwork = (v == strAuto ? "" : v); });
Expand Down