diff --git a/Program.Config.cs b/Program.Config.cs index f458672..13d40df 100644 --- a/Program.Config.cs +++ b/Program.Config.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Threading; +using System.Windows.Forms; using Hp.Bridge.Client.SDKs.PerformanceControl.DataStructure; using HP.Omen.Core.Common.NVidiaApi; using HP.Omen.Core.Model.Device.Models; @@ -341,6 +342,10 @@ public static void CleanUpAndRemoveTasks() { static void RestoreCPUPower() { // 恢复CPU功耗设定 + if (currentPreset == FanLockPresetKey && fanLockCurrentPower >= 10 && fanLockCurrentPower <= 254) { + SetCpuPowerLimit((byte)fanLockCurrentPower); + return; + } if (cpuPower.Contains(" W")) { int value = int.Parse(cpuPower.Replace(" W", "").Trim()); if (isCPUPowerControlSupported && value >= 10 && value <= 254) { @@ -349,6 +354,214 @@ static void RestoreCPUPower() { } } + static bool TryGetCurrentCpuPowerSetting(out int value) { + value = -1; + if (cpuPower == "max") { + value = 254; + return true; + } + if (!string.IsNullOrEmpty(cpuPower) && cpuPower.Contains(" W")) { + int parsed; + if (int.TryParse(cpuPower.Replace(" W", "").Trim(), out parsed) && parsed >= 10 && parsed <= 254) { + value = parsed; + return true; + } + } + return false; + } + + static void LoadFanLockConfig(RegistryKey key) { + if (key == null) return; + fanLockMinimumPower = Math.Max(10, Math.Min(254, Convert.ToInt32(key.GetValue("FanLockMinimumPower", 20)))); + fanLockTargetRpm = Math.Max(0, Convert.ToInt32(key.GetValue("FanLockTargetRpm", 3000))) / 100 * 100; + fanLockTargetTemperature = Math.Max(40, Convert.ToInt32(key.GetValue("FanLockTargetTemperature", 80))); + fanLockReturnPreset = (string)key.GetValue("FanLockReturnPreset", "PresetCustom1"); + if (fanLockReturnPreset == FanLockPresetKey || !PresetOrder.Contains(fanLockReturnPreset)) + fanLockReturnPreset = "PresetCustom1"; + if (fanLockCurrentPower < 0) { + int savedStartPower = Convert.ToInt32(key.GetValue("FanLockStartPower", -1)); + if (savedStartPower >= 10) + fanLockCurrentPower = Math.Max(fanLockMinimumPower, Math.Min(254, savedStartPower)); + } + } + + static void SaveFanLockConfig() { + try { + using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\OmenSuperHub")) { + if (key == null) return; + key.SetValue("FanLockMinimumPower", fanLockMinimumPower); + key.SetValue("FanLockTargetRpm", fanLockTargetRpm); + key.SetValue("FanLockTargetTemperature", fanLockTargetTemperature); + key.SetValue("FanLockReturnPreset", fanLockReturnPreset); + if (fanLockCurrentPower >= 10) + key.SetValue("FanLockStartPower", fanLockCurrentPower); + } + } catch (Exception ex) { + Logger.Error($"SaveFanLockConfig: {ex.Message}"); + } + } + + static bool ActivateFanLockPreset() { + if (!isCPUPowerControlSupported) return false; + + if (currentPreset != FanLockPresetKey) { + int startingPower; + if (!TryGetCurrentCpuPowerSetting(out startingPower)) { + MessageBox.Show( + Application.OpenForms.OfType().FirstOrDefault(), + Strings.FanLockNeedsCpuPower, + Strings.Hint, + MessageBoxButtons.OK, + MessageBoxIcon.Warning); + return false; + } + + fanLockReturnPreset = currentPreset; + fanLockCurrentPower = Math.Max(fanLockMinimumPower, Math.Min(254, startingPower)); + SavePresetToRegistry(FanLockPresetKey); + currentPreset = FanLockPresetKey; + SaveConfig("CurrentPreset"); + SaveFanLockConfig(); + } + + LoadMonitorMetricSettings(FanLockPresetKey); + UpdateMonitorMetricCheckedStates(); + ApplyFanLockTargets(); + UpdateTrayIconText(); + UpdateFloatingText(); + return true; + } + + static void EnsureCpuMonitorForFanLock() { + if (monitorCPU && hwMonitorProcess != null && !hwMonitorProcess.HasExited) return; + monitorCPU = true; + cpuTempReady = false; + if (hwMonitorProcess == null || hwMonitorProcess.HasExited) + StartHardwareMonitor(); + else + SetCpuMonitorState(true); + UpdateCheckedState("monitorCPUGroup", Strings.MonitorCpuOn); + } + + static void ApplyFanLockTargets() { + if (currentPreset != FanLockPresetKey) return; + EnsureCpuMonitorForFanLock(); + + int maximumRpm = platformMaxFanSpeed.HasValue && platformMaxFanSpeed.Value > 0 + ? (int)(platformMaxFanSpeed.Value * 1.1) + : 6400; + fanLockTargetRpm = Math.Max(0, Math.Min(maximumRpm, fanLockTargetRpm)); + fanLockTargetRpm = fanLockTargetRpm / 100 * 100; + fanLockTargetTemperature = Math.Max(40, Math.Min((maxCPUTemp ?? 97) - 3, fanLockTargetTemperature)); + fanLockCurrentPower = Math.Max(fanLockMinimumPower, Math.Min(254, fanLockCurrentPower)); + + fanControl = fanLockTargetRpm + " RPM"; + SetMaxFanSpeedOff(); + fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); + SetFanLevel(fanLockTargetRpm / 100, fanLockTargetRpm / 100, Is3FanNb); + SetCpuPowerLimit((byte)fanLockCurrentPower); + SetFanLockControlledMenusEnabled(false); + UpdateCheckedState("fanControlGroup", Strings.SetFanSpeedSlider); + fanLockUpdatingControls = true; + try { + if (fanTrackBar != null) + fanTrackBar.Value = Math.Max(fanTrackBar.Minimum, Math.Min(fanTrackBar.Maximum, fanLockTargetRpm / 100)); + if (cpuPowerTrackBar != null) + cpuPowerTrackBar.Value = Math.Max(cpuPowerTrackBar.Minimum, Math.Min(cpuPowerTrackBar.Maximum, fanLockCurrentPower)); + } finally { + fanLockUpdatingControls = false; + } + if (fanValueLabel != null) + fanValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{fanLockTargetRpm} RPM"); + if (cpuPowerValueLabel != null) + cpuPowerValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{fanLockCurrentPower} W"); + } + + static void AdjustFanLockPower() { + if (currentPreset != FanLockPresetKey || !monitorCPU || !cpuTempReady || fanLockCurrentPower < 0) + return; + + int nextPower = fanLockCurrentPower; + if (smoothedCPUTemp > fanLockTargetTemperature + 1f) + nextPower--; + else if (smoothedCPUTemp < fanLockTargetTemperature - 1f) + nextPower++; + + nextPower = Math.Max(fanLockMinimumPower, Math.Min(254, nextPower)); + if (nextPower == fanLockCurrentPower) return; + + fanLockCurrentPower = nextPower; + SetCpuPowerLimit((byte)fanLockCurrentPower); + if (cpuPowerValueLabel != null && uiContext != null) { + uiContext.Post(_ => { + fanLockUpdatingControls = true; + try { + if (cpuPowerTrackBar != null) + cpuPowerTrackBar.Value = Math.Max(cpuPowerTrackBar.Minimum, Math.Min(cpuPowerTrackBar.Maximum, fanLockCurrentPower)); + } finally { + fanLockUpdatingControls = false; + } + if (cpuPowerValueLabel != null) + cpuPowerValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{fanLockCurrentPower} W"); + }, null); + } + } + + static void LeaveFanLockPresetForSafety() { + if (currentPreset != FanLockPresetKey) return; + string targetPreset = fanLockReturnPreset; + if (targetPreset == FanLockPresetKey || !PresetOrder.Contains(targetPreset)) + targetPreset = "PresetCustom1"; + // 使用进入模式时保存的快照恢复全部设置,避免内置预设重新生成默认值。 + LoadPresetFields(FanLockPresetKey); + LoadMonitorMetricSettings(FanLockPresetKey); + currentPreset = targetPreset; + SaveConfig("CurrentPreset"); + SetFanLockControlledMenusEnabled(true); + + var item = FindMenuItemByName(trayIcon.ContextMenuStrip.Items, currentPreset); + if (item != null) + UpdateCheckedState("presetsGroup", null, item); + ApplyPresetSettings(FanLockPresetKey); + UpdateMonitorMetricCheckedStates(); + UpdateTrayIconText(); + UpdateFloatingText(); + } + + // 返回 true 表示功耗已经处于下限,可以进入风扇安全切换阶段。 + static bool PrepareFanLockOverheatProtection() { + if (currentPreset != FanLockPresetKey || fanLockCurrentPower <= fanLockMinimumPower) + return true; + + int distanceToMinimum = fanLockCurrentPower - fanLockMinimumPower; + int reduction = Math.Max(5, (distanceToMinimum + 1) / 2); + fanLockCurrentPower = Math.Max(fanLockMinimumPower, fanLockCurrentPower - reduction); + SetCpuPowerLimit((byte)fanLockCurrentPower); + + if (uiContext != null) { + uiContext.Post(_ => { + fanLockUpdatingControls = true; + try { + if (cpuPowerTrackBar != null) + cpuPowerTrackBar.Value = Math.Max(cpuPowerTrackBar.Minimum, Math.Min(cpuPowerTrackBar.Maximum, fanLockCurrentPower)); + } finally { + fanLockUpdatingControls = false; + } + if (cpuPowerValueLabel != null) + cpuPowerValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{fanLockCurrentPower} W"); + }, null); + } + return false; + } + + static void SetFanLockControlledMenusEnabled(bool enabled) { + if (trayIcon == null || trayIcon.ContextMenuStrip == null) return; + var fanMenu = FindMenuItemByName(trayIcon.ContextMenuStrip.Items, "FanControlMenu"); + var cpuPowerMenu = FindMenuItemByName(trayIcon.ContextMenuStrip.Items, "CpuPowerMenu"); + if (fanMenu != null) fanMenu.Enabled = enabled; + if (cpuPowerMenu != null) cpuPowerMenu.Enabled = enabled; + } + static void RestorePowerConfig() { SetUnleashMode(); System.Threading.Tasks.Task.Delay(1000).ContinueWith(_ => { @@ -1003,7 +1216,7 @@ static void UpdateMonitorMetricCheckedStates() { /// static void ApplyPresetSettings(string presetKey) { // 自定义预设特有字段:监控项、温度显示模式等 - if (presetKey == "Restore" || presetKey == "PresetCustom1" || presetKey == "PresetCustom2" || presetKey == "PresetCustom3") { + if (presetKey == "Restore" || presetKey == FanLockPresetKey || presetKey == "PresetCustom1" || presetKey == "PresetCustom2" || presetKey == "PresetCustom3") { if (presetKey == "Restore") { try { using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\OmenSuperHub")) { @@ -1077,22 +1290,24 @@ static void ApplyPresetSettings(string presetKey) { UpdateCheckedState("fanTableGroup", Strings.FanCustomMode); } - // 风扇控制模式 - if (fanControl == "auto") { - SetMaxFanSpeedOff(); - fanControlTimer.Change(0, 1000); - UpdateCheckedState("fanControlGroup", Strings.FanAuto); - } else if (fanControl.Contains("max")) { - SetMaxFanSpeedOn(); - fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); - UpdateCheckedState("fanControlGroup", Strings.FanMax); - } else if (fanControl.Contains(" RPM")) { - SetMaxFanSpeedOff(); - fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); - int rpmValue = int.Parse(fanControl.Replace(" RPM", "").Trim()); - SetFanLevel(rpmValue / 100, rpmValue / 100, Is3FanNb); - if (fanTrackBar != null) fanTrackBar.Value = rpmValue / 100; - UpdateCheckedState("fanControlGroup", Strings.SetFanSpeedSlider); + // 风扇控制模式;锁定风扇预设在本方法末尾单独应用。 + if (currentPreset != FanLockPresetKey) { + if (fanControl == "auto") { + SetMaxFanSpeedOff(); + fanControlTimer.Change(0, 1000); + UpdateCheckedState("fanControlGroup", Strings.FanAuto); + } else if (fanControl.Contains("max")) { + SetMaxFanSpeedOn(); + fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); + UpdateCheckedState("fanControlGroup", Strings.FanMax); + } else if (fanControl.Contains(" RPM")) { + SetMaxFanSpeedOff(); + fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); + int rpmValue = int.Parse(fanControl.Replace(" RPM", "").Trim()); + SetFanLevel(rpmValue / 100, rpmValue / 100, Is3FanNb); + if (fanTrackBar != null) fanTrackBar.Value = rpmValue / 100; + UpdateCheckedState("fanControlGroup", Strings.SetFanSpeedSlider); + } } // 风扇响应速度 @@ -1104,7 +1319,7 @@ static void ApplyPresetSettings(string presetKey) { } // CPU 功耗 - if (isCPUPowerControlSupported) { + if (isCPUPowerControlSupported && currentPreset != FanLockPresetKey) { if (cpuPower == "null") { UpdateCheckedState("cpuPowerGroup", Strings.NotSet); } else if (cpuPower == "max") { @@ -1214,6 +1429,9 @@ static void ApplyPresetSettings(string presetKey) { } } }); + + if (currentPreset == FanLockPresetKey) + ApplyFanLockTargets(); } /// @@ -1221,6 +1439,11 @@ static void ApplyPresetSettings(string presetKey) { /// 保存到注册表,然后应用到硬件。 /// static void applyPresetLogic(string targetPreset) { + if (targetPreset == FanLockPresetKey) { + ActivateFanLockPreset(); + return; + } + SetFanLockControlledMenusEnabled(true); currentPreset = targetPreset; if (targetPreset == "PresetExtreme" || targetPreset == "PresetGpuPriority" || targetPreset == "PresetLightUse") { @@ -1289,6 +1512,7 @@ static void RestoreConfig() { presetCustom1Name = (string)key.GetValue("PresetCustom1Name", Strings.PresetCustom1); presetCustom2Name = (string)key.GetValue("PresetCustom2Name", Strings.PresetCustom2); presetCustom3Name = (string)key.GetValue("PresetCustom3Name", Strings.PresetCustom3); + LoadFanLockConfig(key); // 旧版升级兼容:不存在 CurrentPreset 键时迁移 if (key.GetValue("CurrentPreset") == null) { @@ -1337,6 +1561,13 @@ static void RestoreConfig() { } else { LoadPresetFields(currentPreset); } + if (currentPreset == FanLockPresetKey && fanLockCurrentPower < 10) { + int restoredPower; + if (TryGetCurrentCpuPowerSetting(out restoredPower)) + fanLockCurrentPower = Math.Max(fanLockMinimumPower, restoredPower); + else + fanLockCurrentPower = fanLockMinimumPower; + } LoadMonitorMetricSettings(currentPreset); var item = FindMenuItemByName(trayIcon.ContextMenuStrip.Items, currentPreset); @@ -1438,6 +1669,8 @@ static void RestoreConfig() { /// static void SavePresetToRegistry(string presetKey) { if (presetKey == "PresetExtreme" || presetKey == "PresetGpuPriority" || presetKey == "PresetLightUse") return; + // 此子键在进入锁定模式前保存完整快照;模式运行期间不得被后续菜单修改覆盖。 + if (presetKey == FanLockPresetKey && currentPreset == FanLockPresetKey) return; try { using (RegistryKey key = Registry.CurrentUser.CreateSubKey($@"Software\OmenSuperHub\{presetKey}")) { if (key == null) return; diff --git a/Program.Menu.cs b/Program.Menu.cs index 86bd8c9..ffe74e3 100644 --- a/Program.Menu.cs +++ b/Program.Menu.cs @@ -194,6 +194,23 @@ static void BuildTrayMenu(ContextMenuStrip menu) { lightUseItem.Name = "PresetLightUse"; presetsMenu.DropDownItems.Add(lightUseItem); presetsMenu.DropDownItems.Add(new ToolStripSeparator()); + + var fanLockItem = new ToolStripMenuItem(Strings.PresetFanLock) { + Name = FanLockPresetKey, + Tag = "presetsGroup", + Checked = currentPreset == FanLockPresetKey, + ToolTipText = Strings.PresetFanLockTooltip + }; + fanLockItem.MouseUp += (s, e) => { + if (e.Button == MouseButtons.Left) { + if (ActivateFanLockPreset()) + UpdateCheckedState("presetsGroup", null, fanLockItem); + } else if (e.Button == MouseButtons.Right) { + ShowFanLockEditor(); + } + }; + presetsMenu.DropDownItems.Add(fanLockItem); + presetsMenu.DropDownItems.Add(new ToolStripSeparator()); } var custom1Item = CreateMenuItem(presetCustom1Name, "presetsGroup", (s, e) => applyPresetLogic("PresetCustom1"), currentPreset == "PresetCustom1"); @@ -344,7 +361,7 @@ void attachRename(ToolStripMenuItem item, string presetKey) { menu.Items.Add(fanConfigMenu); - ToolStripMenuItem fanControlMenu = new ToolStripMenuItem(Strings.FanControl); + ToolStripMenuItem fanControlMenu = new ToolStripMenuItem(Strings.FanControl) { Name = "FanControlMenu" }; if (isFanCleanSupported || isFanLegacyCleanSupported) { string menuText = Strings.CleanCreekMenuItem; if (!isFanCleanSupported && isFanLegacyCleanSupported) @@ -403,6 +420,7 @@ void attachRename(ToolStripMenuItem item, string presetKey) { fanValueLabel = new ToolStripMenuItem(string.Format(Strings.CurrentSliderValueTemp, $"{fanTrackBar.Value * 100} RPM")) { Enabled = false }; fanTrackBar.ValueChanged += (sender, e) => { + if (fanLockUpdatingControls) return; fanControl = fanTrackBar.Value * 100 + " RPM"; fanValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{fanTrackBar.Value * 100} RPM"); fanControlTimer.Change(Timeout.Infinite, Timeout.Infinite); @@ -609,7 +627,7 @@ void attachRename(ToolStripMenuItem item, string presetKey) { } if (isCPUPowerControlSupported) { - ToolStripMenuItem cpuPowerMenu = new ToolStripMenuItem(Strings.CpuPowerMenu); + ToolStripMenuItem cpuPowerMenu = new ToolStripMenuItem(Strings.CpuPowerMenu) { Name = "CpuPowerMenu" }; cpuPowerMenu.DropDownItems.Add(new ToolStripMenuItem(Strings.PerfCpuPowerTip) { Enabled = false }); cpuPowerMenu.DropDownItems.Add(new ToolStripSeparator()); @@ -638,6 +656,7 @@ void attachRename(ToolStripMenuItem item, string presetKey) { // 滑块值改变时更新标签并应用设置 cpuPowerTrackBar.ValueChanged += (sender, e) => { + if (fanLockUpdatingControls) return; int val = cpuPowerTrackBar.Value; cpuPowerValueLabel.Text = string.Format(Strings.CurrentSliderValueTemp, $"{val} W"); cpuPower = cpuPowerTrackBar.Value + " W"; @@ -2166,6 +2185,69 @@ ex is UnauthorizedAccessException || } } + static void ShowFanLockEditor() { + int maximumRpm = platformMaxFanSpeed.HasValue && platformMaxFanSpeed.Value > 0 + ? (int)(platformMaxFanSpeed.Value * 1.1) + : 6400; + maximumRpm = Math.Max(100, maximumRpm / 100 * 100); + int maximumTemperature = Math.Max(40, (maxCPUTemp ?? 97) - 3); + + using (Form form = new Form { + Width = 430, + Height = 260, + FormBorderStyle = FormBorderStyle.FixedDialog, + MaximizeBox = false, + MinimizeBox = false, + StartPosition = FormStartPosition.CenterScreen, + Text = Strings.FanLockEditorTitle + }) { + Label powerLabel = new Label { Left = 15, Top = 22, Width = 230, Text = Strings.FanLockMinimumPower }; + NumericUpDown powerInput = new NumericUpDown { + Left = 270, Top = 18, Width = 120, Minimum = 10, Maximum = 254, + Value = Math.Max(10, Math.Min(254, fanLockMinimumPower)) + }; + + Label rpmLabel = new Label { Left = 15, Top = 70, Width = 230, Text = Strings.FanLockTargetRpm }; + NumericUpDown rpmInput = new NumericUpDown { + Left = 270, Top = 66, Width = 120, Minimum = 0, Maximum = maximumRpm, + Increment = 100, + Value = Math.Max(0, Math.Min(maximumRpm, fanLockTargetRpm)) + }; + + Label temperatureLabel = new Label { Left = 15, Top = 118, Width = 230, Text = Strings.FanLockTargetTemperature }; + NumericUpDown temperatureInput = new NumericUpDown { + Left = 270, Top = 114, Width = 120, Minimum = 40, Maximum = maximumTemperature, + Value = Math.Max(40, Math.Min(maximumTemperature, fanLockTargetTemperature)) + }; + + Button okButton = new Button { + Text = Strings.OK, Left = 110, Top = 165, Width = 95, + DialogResult = DialogResult.OK + }; + Button cancelButton = new Button { + Text = Strings.Cancel, Left = 220, Top = 165, Width = 95, + DialogResult = DialogResult.Cancel + }; + + form.Controls.AddRange(new Control[] { + powerLabel, powerInput, rpmLabel, rpmInput, temperatureLabel, temperatureInput, + okButton, cancelButton + }); + form.AcceptButton = okButton; + form.CancelButton = cancelButton; + + if (form.ShowDialog() != DialogResult.OK) return; + + fanLockMinimumPower = (int)powerInput.Value; + fanLockTargetRpm = (int)rpmInput.Value / 100 * 100; + fanLockTargetTemperature = (int)temperatureInput.Value; + SaveFanLockConfig(); + + if (currentPreset == FanLockPresetKey) + ApplyFanLockTargets(); + } + } + static bool ApplyCustomFanConfig() { string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; diff --git a/Program.OmenKey.cs b/Program.OmenKey.cs index 00112e7..6e7bee7 100644 --- a/Program.OmenKey.cs +++ b/Program.OmenKey.cs @@ -253,6 +253,7 @@ static bool IsPresetAvailable(string presetKey) { case "PresetExtreme": case "PresetGpuPriority": case "PresetLightUse": + case FanLockPresetKey: return isCPUPowerControlSupported; case "PresetCustom1": case "PresetCustom2": @@ -268,7 +269,8 @@ static List GetAvailablePresetKeys() { } static string GetDefaultOmenKeyPresetCandidates() { - return string.Join(";", GetAvailablePresetKeys()); + // 锁定风扇模式会接管风扇与CPU功耗,不默认加入快捷键轮换,用户仍可手动勾选。 + return string.Join(";", GetAvailablePresetKeys().Where(key => key != FanLockPresetKey)); } static List GetOmenKeyPresetCandidateKeys() { diff --git a/Program.cs b/Program.cs index 469f29c..a3e3dba 100644 --- a/Program.cs +++ b/Program.cs @@ -100,7 +100,8 @@ struct MSLLHOOKSTRUCT { static int textSize = 40; static int countRestore = 0, gpuClock = 0, gpuCoreOverclock = -1, gpuMemoryOverclock = -1, maxFrameRate = -1, graphicsBoostClock = 0; static int alreadyRead = 0, alreadyReadCode = 1000; - static readonly string[] PresetOrder = { "PresetExtreme", "PresetGpuPriority", "PresetLightUse", "PresetCustom1", "PresetCustom2", "PresetCustom3" }; + const string FanLockPresetKey = "PresetFanLock"; + static readonly string[] PresetOrder = { "PresetExtreme", "PresetGpuPriority", "PresetLightUse", FanLockPresetKey, "PresetCustom1", "PresetCustom2", "PresetCustom3" }; static string currentPreset = "PresetCustom1", presetCustom1Name = Strings.PresetCustom1, presetCustom2Name = Strings.PresetCustom2, presetCustom3Name = Strings.PresetCustom3; static string fanTable = "cool", fanControl = "auto", tempSensitivity = "high", tppPower = "null", iccMax = "null", acLoadline = "null", cpuPower = "null", tgpPower = "on", ppabPower = "on", dState = "normal", autoStart = "off", customIcon = "original", floatingBar = "off", floatingBarLoc = "left", floatingBarScreen = "", omenKey = OmenKeyActions.Default, omenKeyAppPath = "", omenKeyAppName = "", omenKeyShortcut = "", omenKeyPresetCandidates = "", dataLocalize = "off", appLanguage = "zh-CN", autoFanProtect = "on"; static volatile bool monitorFan = false; @@ -117,6 +118,10 @@ struct MSLLHOOKSTRUCT { static int? maxGPUTemp = null; static float CPUTemp = 50, GPUTemp = 40, rawTempCPU = 50f, rawTempGPU = 40f; static float CPUPower = 0, GPUPower = 0, CPUFrequency = 0f, GPUFrequency = 0f, rawPowerCPU = 0f, rawPowerGPU = 0f, rawFrequencyCPU = 0f, rawFrequencyGPU = 0f; + static int fanLockMinimumPower = 20, fanLockTargetRpm = 3000, fanLockTargetTemperature = 80; + static int fanLockCurrentPower = -1; + static string fanLockReturnPreset = "PresetCustom1"; + static bool fanLockUpdatingControls = false; static bool rawGotGPU = false; static volatile bool tempReady = false; // 子进程首次输出有效温度后置 true static volatile bool cpuTempReady = false; // CPU 温度已初始化给平滑值,允许参与风扇控制 @@ -1186,6 +1191,8 @@ static void QueryHardware() { smoothedGPUTemp = rawTempGPU * respondSpeed + smoothedGPUTemp * (1.0f - respondSpeed); } + bool fanLockPowerHandledByProtection = false; + // 根据显示方式决定展示原始值或平滑值 if (monitorCPU && cpuTempReady) CPUTemp = (tempDisplayMode == "raw") ? tempCPU : smoothedCPUTemp; @@ -1203,26 +1210,47 @@ static void QueryHardware() { } if (fanSpeedCondition) { - // 先切换为降温模式(cool配置) - fanTable = "cool"; - LoadFanConfig("cool.txt"); - UpdateCheckedState("fanTableGroup", Strings.FanCoolMode); - SaveConfig("FanTable"); - - // 再切换为自动风扇控制 - fanControl = "auto"; - SetMaxFanSpeedOff(); - fanControlTimer.Change(0, 1000); - UpdateCheckedState("fanControlGroup", Strings.FanAuto); - SaveConfig("FanControl"); - - trayIcon.BalloonTipTitle = Strings.HighTempBalloonTitle; - trayIcon.BalloonTipText = Strings.HighTempBalloonText(currentMaxCPUTemp, smoothedCPUTemp); - trayIcon.BalloonTipIcon = ToolTipIcon.Warning; - trayIcon.ShowBalloonTip(3000); + bool exitedFanLockForSafety = false; + bool shouldSwitchFan = true; + if (currentPreset == FanLockPresetKey) { + shouldSwitchFan = PrepareFanLockOverheatProtection(); + if (shouldSwitchFan) { + LeaveFanLockPresetForSafety(); + exitedFanLockForSafety = true; + } else { + fanLockPowerHandledByProtection = true; + trayIcon.BalloonTipTitle = Strings.HighTempBalloonTitle; + trayIcon.BalloonTipText = Strings.FanLockPowerReducedBalloonText(currentMaxCPUTemp, smoothedCPUTemp, fanLockCurrentPower, fanLockMinimumPower); + trayIcon.BalloonTipIcon = ToolTipIcon.Warning; + trayIcon.ShowBalloonTip(3000); + } + } + + if (shouldSwitchFan) { + // 先切换为降温模式(cool配置) + fanTable = "cool"; + LoadFanConfig("cool.txt"); + UpdateCheckedState("fanTableGroup", Strings.FanCoolMode); + if (!exitedFanLockForSafety) SaveConfig("FanTable"); + + // 再切换为自动风扇控制 + fanControl = "auto"; + SetMaxFanSpeedOff(); + fanControlTimer.Change(0, 1000); + UpdateCheckedState("fanControlGroup", Strings.FanAuto); + if (!exitedFanLockForSafety) SaveConfig("FanControl"); + + trayIcon.BalloonTipTitle = Strings.HighTempBalloonTitle; + trayIcon.BalloonTipText = Strings.HighTempBalloonText(currentMaxCPUTemp, smoothedCPUTemp); + trayIcon.BalloonTipIcon = ToolTipIcon.Warning; + trayIcon.ShowBalloonTip(3000); + } } } + if (!fanLockPowerHandledByProtection) + AdjustFanLockPower(); + //通过countQuery延时来确保温度正常读取 if (countQuery <= 5 && monitorGPU) countQuery++; @@ -1428,6 +1456,7 @@ static string GetPresetDisplayName(string presetKey) { case "PresetExtreme": return Strings.PresetExtreme; case "PresetGpuPriority": return Strings.PresetGpuPriority; case "PresetLightUse": return Strings.PresetLightUse; + case FanLockPresetKey: return Strings.PresetFanLock; case "PresetCustom1": return presetCustom1Name; case "PresetCustom2": return presetCustom2Name; case "PresetCustom3": return presetCustom3Name; diff --git a/Strings.cs b/Strings.cs index cb2c8b1..c0c5715 100644 --- a/Strings.cs +++ b/Strings.cs @@ -59,6 +59,19 @@ public static class Strings { public static string PresetExtreme => T("极致性能", "極致性能", "Extreme Performance"); public static string PresetGpuPriority => T("GPU优先", "GPU優先", "GPU Priority"); public static string PresetLightUse => T("轻度使用", "輕度使用", "Light Use"); + public static string PresetFanLock => T("锁定风扇转速模式", "鎖定風扇轉速模式", "Locked Fan Speed Mode"); + public static string PresetFanLockTooltip => T( + "左键进入锁定风扇转速模式;右键修改目标转速、目标温度和CPU功耗墙下限。", + "左鍵進入鎖定風扇轉速模式;右鍵修改目標轉速、目標溫度和CPU功耗牆下限。", + "Left-click to activate locked fan speed mode; right-click to edit its fan, temperature, and minimum CPU power targets."); + public static string FanLockEditorTitle => T("锁定风扇转速模式", "鎖定風扇轉速模式", "Locked Fan Speed Mode"); + public static string FanLockMinimumPower => T("CPU功耗墙下限 (W)", "CPU功耗牆下限 (W)", "Minimum CPU power (W)"); + public static string FanLockTargetRpm => T("目标风扇转速 (RPM)", "目標風扇轉速 (RPM)", "Target fan speed (RPM)"); + public static string FanLockTargetTemperature => T("目标CPU温度 (℃)", "目標CPU溫度 (℃)", "Target CPU temperature (°C)"); + public static string FanLockNeedsCpuPower => T( + "当前预设没有设置CPU功率,无法确定锁定模式的PL1起点。请先设置一个CPU功率数值。", + "目前預設沒有設定CPU功率,無法確定鎖定模式的PL1起點。請先設定一個CPU功率數值。", + "The current preset has no CPU power setting, so the starting PL1 cannot be determined. Set a CPU power value first."); public static string PresetCustom1 => T("自定义预设1", "自定義預設1", "Custom 1"); public static string PresetCustom2 => T("自定义预设2", "自定義預設2", "Custom 2"); public static string PresetCustom3 => T("自定义预设3", "自定義預設3", "Custom 3"); @@ -288,9 +301,13 @@ public static string DbUnlockFailed(float w) => T( // 高温警告(气泡) public static string HighTempBalloonTitle => T("温度过高警告", "溫度過高警告", "High Temperature Warning"); public static string HighTempBalloonText(int limit, float temp) => T( - $"检测到CPU温度高于{limit - 5}℃ ({temp:F1}℃),且风扇处于固定转速状态,OSH已自动切换为降温模式并将风扇控制切换为自动模式。", - $"偵測到CPU溫度高於{limit - 5}℃ ({temp:F1}℃),且風扇處於固定轉速狀態,OSH已自動切換至降溫模式並將風扇控制改為自動。", - $"CPU temperature exceeded {limit - 5}°C ({temp:F1}°C) with a fixed fan speed. OSH has switched to Cool mode and Auto fan control."); + $"检测到CPU温度高于{limit - 2}℃ ({temp:F1}℃),且风扇处于固定转速状态,OSH已自动切换为降温模式并将风扇控制切换为自动模式。", + $"偵測到CPU溫度高於{limit - 2}℃ ({temp:F1}℃),且風扇處於固定轉速狀態,OSH已自動切換至降溫模式並將風扇控制改為自動。", + $"CPU temperature exceeded {limit - 2}°C ({temp:F1}°C) with a fixed fan speed. OSH has switched to Cool mode and Auto fan control."); + public static string FanLockPowerReducedBalloonText(int limit, float temp, int power, int minimum) => T( + $"检测到CPU温度高于{limit - 2}℃ ({temp:F1}℃)。锁定风扇转速模式已优先将CPU功耗墙降低至{power} W,下限为{minimum} W;到达下限后若仍过热才会切换风扇控制。", + $"偵測到CPU溫度高於{limit - 2}℃ ({temp:F1}℃)。鎖定風扇轉速模式已優先將CPU功耗牆降低至{power} W,下限為{minimum} W;到達下限後若仍過熱才會切換風扇控制。", + $"CPU temperature exceeded {limit - 2}°C ({temp:F1}°C). Locked fan speed mode reduced CPU power to {power} W first (minimum {minimum} W); fan control will change only if overheating continues at the minimum."); // ───────────────────────────────────────────────────────────────────────── // 性能控制 — 提示文本