using CustomControls; using GHelper.Gpu; using System.Diagnostics; using System.Windows.Forms.DataVisualization.Charting; namespace GHelper { public partial class Fans : RForm { DataPoint curPoint = null; Series seriesCPU; Series seriesGPU; Series seriesMid; static int MinRPM, MaxRPM; const int fansMax = 100; NvidiaGpuControl? nvControl = null; public Fans() { InitializeComponent(); Text = Properties.Strings.FansAndPower; labelPowerLimits.Text = Properties.Strings.PowerLimits; labelInfo.Text = Properties.Strings.PPTExperimental; checkApplyPower.Text = Properties.Strings.ApplyPowerLimits; labelFans.Text = Properties.Strings.FanCurves; labelBoost.Text = Properties.Strings.CPUBoost; buttonReset.Text = Properties.Strings.FactoryDefaults; checkApplyFans.Text = Properties.Strings.ApplyFanCurve; labelGPU.Text = Properties.Strings.GPUSettings; labelGPUCoreTitle.Text = Properties.Strings.GPUCoreClockOffset; labelGPUMemoryTitle.Text = Properties.Strings.GPUMemoryClockOffset; labelGPUBoostTitle.Text = Properties.Strings.GPUBoost; labelGPUTempTitle.Text = Properties.Strings.GPUTempTarget; InitTheme(); MinRPM = 18; MaxRPM = HardwareControl.GetFanMax(); labelTip.Visible = false; labelTip.BackColor = Color.Transparent; FormClosing += Fans_FormClosing; seriesCPU = chartCPU.Series.Add("CPU"); seriesGPU = chartGPU.Series.Add("GPU"); seriesMid = chartMid.Series.Add("Mid"); seriesCPU.Color = colorStandard; seriesGPU.Color = colorTurbo; seriesMid.Color = colorEco; chartCPU.MouseMove += ChartCPU_MouseMove; chartCPU.MouseUp += ChartCPU_MouseUp; chartGPU.MouseMove += ChartCPU_MouseMove; chartGPU.MouseUp += ChartCPU_MouseUp; chartMid.MouseMove += ChartCPU_MouseMove; chartMid.MouseUp += ChartCPU_MouseUp; buttonReset.Click += ButtonReset_Click; trackTotal.Maximum = ASUSWmi.MaxTotal; trackTotal.Minimum = ASUSWmi.MinTotal; trackCPU.Maximum = ASUSWmi.MaxCPU; trackCPU.Minimum = ASUSWmi.MinCPU; trackCPU.Scroll += TrackPower_Scroll; trackTotal.Scroll += TrackPower_Scroll; trackCPU.MouseUp += TrackPower_MouseUp; trackTotal.MouseUp += TrackPower_MouseUp; checkApplyFans.Click += CheckApplyFans_Click; checkApplyPower.Click += CheckApplyPower_Click; trackGPUCore.Minimum = NvidiaGpuControl.MinCoreOffset; trackGPUCore.Maximum = NvidiaGpuControl.MaxCoreOffset; trackGPUMemory.Minimum = NvidiaGpuControl.MinMemoryOffset; trackGPUMemory.Maximum = NvidiaGpuControl.MaxMemoryOffset; trackGPUBoost.Minimum = ASUSWmi.MinGPUBoost; trackGPUBoost.Maximum = ASUSWmi.MaxGPUBoost; trackGPUTemp.Minimum = ASUSWmi.MinGPUTemp; trackGPUTemp.Maximum = ASUSWmi.MaxGPUTemp; trackGPUCore.Scroll += trackGPU_Scroll; trackGPUMemory.Scroll += trackGPU_Scroll; trackGPUBoost.Scroll += trackGPUPower_Scroll; trackGPUTemp.Scroll += trackGPUPower_Scroll; trackGPUCore.MouseUp += TrackGPU_MouseUp; trackGPUMemory.MouseUp += TrackGPU_MouseUp; trackGPUBoost.MouseUp += TrackGPU_MouseUp; trackGPUTemp.MouseUp += TrackGPU_MouseUp; //labelInfo.MaximumSize = new Size(280, 0); labelInfo.Text = Properties.Strings.PPTExperimental; labelFansResult.Visible = false; InitFans(); InitPower(); InitBoost(); InitGPU(true); comboBoost.SelectedValueChanged += ComboBoost_Changed; Shown += Fans_Shown; } private void TrackGPU_MouseUp(object? sender, MouseEventArgs e) { Program.settingsForm.SetGPUPower(); Program.settingsForm.SetGPUClocks(true); } public void InitGPU(bool readClocks = false) { if (HardwareControl.GpuControl is not null && HardwareControl.GpuControl.IsNvidia) { nvControl = (NvidiaGpuControl)HardwareControl.GpuControl; } else { panelGPU.Visible = false; return; } try { panelGPU.Visible = true; int gpu_boost = Program.config.getConfigPerf("gpu_boost"); int gpu_temp = Program.config.getConfigPerf("gpu_temp"); int core = Program.config.getConfigPerf("gpu_core"); int memory = Program.config.getConfigPerf("gpu_memory"); if (gpu_boost < 0) gpu_boost = ASUSWmi.MaxGPUBoost; if (gpu_temp < 0) gpu_temp = ASUSWmi.MaxGPUTemp; if (core == -1) core = 0; if (memory == -1) memory = 0; //if (readClocks) //{ int status = nvControl.GetClocks(out int current_core, out int current_memory); if (status != -1) { core = current_core; memory = current_memory; } try { labelGPU.Text = nvControl.FullName; } catch { } //} trackGPUCore.Value = Math.Max(Math.Min(core, NvidiaGpuControl.MaxCoreOffset), NvidiaGpuControl.MinCoreOffset); trackGPUMemory.Value = Math.Max(Math.Min(memory, NvidiaGpuControl.MaxMemoryOffset), NvidiaGpuControl.MinMemoryOffset); trackGPUBoost.Value = Math.Max(Math.Min(gpu_boost, ASUSWmi.MaxGPUBoost), ASUSWmi.MinGPUBoost); trackGPUTemp.Value = Math.Max(Math.Min(gpu_temp, ASUSWmi.MaxGPUTemp), ASUSWmi.MinGPUTemp); panelGPUBoost.Visible = (Program.wmi.DeviceGet(ASUSWmi.PPT_GPUC0) >= 0); panelGPUTemp.Visible = (Program.wmi.DeviceGet(ASUSWmi.PPT_GPUC2) >= 0); VisualiseGPUSettings(); } catch (Exception ex) { Logger.WriteLine(ex.ToString()); panelGPU.Visible = false; } } private void VisualiseGPUSettings() { labelGPUCore.Text = $"{trackGPUCore.Value} MHz"; labelGPUMemory.Text = $"{trackGPUMemory.Value} MHz"; labelGPUBoost.Text = $"{trackGPUBoost.Value}W"; labelGPUTemp.Text = $"{trackGPUTemp.Value}°C"; } private void trackGPU_Scroll(object? sender, EventArgs e) { if (sender is null) return; TrackBar track = (TrackBar)sender; track.Value = (int)Math.Round((float)track.Value / 5) * 5; Program.config.setConfigPerf("gpu_core", trackGPUCore.Value); Program.config.setConfigPerf("gpu_memory", trackGPUMemory.Value); VisualiseGPUSettings(); } private void trackGPUPower_Scroll(object? sender, EventArgs e) { Program.config.setConfigPerf("gpu_boost", trackGPUBoost.Value); Program.config.setConfigPerf("gpu_temp", trackGPUTemp.Value); VisualiseGPUSettings(); } static string ChartPercToRPM(int percentage, string unit = "") { if (percentage == 0) return "OFF"; return (200 * Math.Round((float)(MinRPM * 100 + (MaxRPM - MinRPM) * percentage) / 200)).ToString() + unit; } void SetChart(Chart chart, int device) { string title; if (device == 1) title = Properties.Strings.FanProfileGPU; else if (device == 2) title = Properties.Strings.FanProfileMid; else title = Properties.Strings.FanProfileCPU; if (Program.settingsForm.perfName.Length > 0) labelFans.Text = Properties.Strings.FanProfiles + ": " + Program.settingsForm.perfName; chart.Titles[0].Text = title; chart.ChartAreas[0].AxisX.Minimum = 10; chart.ChartAreas[0].AxisX.Maximum = 100; chart.ChartAreas[0].AxisX.Interval = 10; chart.ChartAreas[0].AxisY.Minimum = 0; chart.ChartAreas[0].AxisY.Maximum = fansMax; chart.ChartAreas[0].AxisY.LabelStyle.Font = new Font("Arial", 7F); chart.ChartAreas[0].AxisX.MajorGrid.LineColor = chartGrid; chart.ChartAreas[0].AxisY.MajorGrid.LineColor = chartGrid; chart.ChartAreas[0].AxisX.LineColor = chartGrid; chart.ChartAreas[0].AxisY.LineColor = chartGrid; for (int i = 0; i <= fansMax - 10; i += 10) chart.ChartAreas[0].AxisY.CustomLabels.Add(i - 2, i + 2, ChartPercToRPM(i)); chart.ChartAreas[0].AxisY.CustomLabels.Add(fansMax - 2, fansMax + 2, Properties.Strings.RPM); chart.ChartAreas[0].AxisY.Interval = 10; if (chart.Legends.Count > 0) chart.Legends[0].Enabled = false; } private void Fans_Shown(object? sender, EventArgs e) { panelSliders.Visible = panelGPU.Visible || panelPower.Visible; if (Height > Program.settingsForm.Height) { Top = Program.settingsForm.Top + Program.settingsForm.Height - Height; } else { MinimumSize = new Size(0, Program.settingsForm.Height); Height = Program.settingsForm.Height; Top = Program.settingsForm.Top; } Left = Program.settingsForm.Left - Width - 5; } private void TrackPower_MouseUp(object? sender, MouseEventArgs e) { Program.settingsForm.AutoPower(); } public void InitBoost() { int boost = NativeMethods.GetCPUBoost(); if (boost >= 0) comboBoost.SelectedIndex = Math.Min(boost, comboBoost.Items.Count - 1); } private void ComboBoost_Changed(object? sender, EventArgs e) { if (Program.config.getConfigPerf("auto_boost") != comboBoost.SelectedIndex) { NativeMethods.SetCPUBoost(comboBoost.SelectedIndex); Program.config.setConfigPerf("auto_boost", comboBoost.SelectedIndex); } } private void CheckApplyPower_Click(object? sender, EventArgs e) { if (sender is null) return; CheckBox chk = (CheckBox)sender; Program.config.setConfigPerf("auto_apply_power", chk.Checked ? 1 : 0); if (chk.Checked) { Program.settingsForm.AutoPower(); } else { Program.wmi.DeviceSet(ASUSWmi.PerformanceMode, Program.config.getConfig("performance_mode"), "PerfMode"); Program.settingsForm.AutoFans(); } } private void CheckApplyFans_Click(object? sender, EventArgs e) { if (sender is null) return; CheckBox chk = (CheckBox)sender; Program.config.setConfigPerf("auto_apply", chk.Checked ? 1 : 0); if (chk.Checked) { Program.settingsForm.AutoFans(); } else { Program.wmi.DeviceSet(ASUSWmi.PerformanceMode, Program.config.getConfig("performance_mode"), "PerfMode"); Program.settingsForm.AutoPower(); } } public void LabelFansResult(string text) { labelFansResult.Text = text; labelFansResult.Visible = (text.Length > 0); } private void Fans_FormClosing(object? sender, FormClosingEventArgs e) { /* if (e.CloseReason == CloseReason.UserClosing) { e.Cancel = true; Hide(); }*/ } public void InitPower(bool changed = false) { bool cpuBmode = (Program.wmi.DeviceGet(ASUSWmi.PPT_CPUB0) >= 0); // 2022 model + bool cpuAmode = (Program.wmi.DeviceGet(ASUSWmi.PPT_TotalA0) >= 0); // 2021 model + panelPower.Visible = cpuAmode; panelCPU.Visible = cpuBmode; // Yes, that's stupid, but Total slider on 2021 model actually adjusts CPU PPT if (!cpuBmode) { labelPlatform.Text = "CPU PPT"; } int limit_total; int limit_cpu; bool apply = Program.config.getConfigPerf("auto_apply_power") == 1; if (changed) { limit_total = trackTotal.Value; limit_cpu = trackCPU.Value; } else { limit_total = Program.config.getConfigPerf("limit_total"); limit_cpu = Program.config.getConfigPerf("limit_cpu"); } if (limit_total < 0) limit_total = ASUSWmi.DefaultTotal; if (limit_total > ASUSWmi.MaxTotal) limit_total = ASUSWmi.MaxTotal; if (limit_total < ASUSWmi.MinTotal) limit_total = ASUSWmi.MinTotal; if (limit_cpu < 0) limit_cpu = ASUSWmi.DefaultCPU; if (limit_cpu > ASUSWmi.MaxCPU) limit_cpu = ASUSWmi.MaxCPU; if (limit_cpu < ASUSWmi.MinCPU) limit_cpu = ASUSWmi.MinCPU; if (limit_cpu > limit_total) limit_cpu = limit_total; trackTotal.Value = limit_total; trackCPU.Value = limit_cpu; checkApplyPower.Checked = apply; labelTotal.Text = trackTotal.Value.ToString() + "W"; labelCPU.Text = trackCPU.Value.ToString() + "W"; Program.config.setConfigPerf("limit_total", limit_total); Program.config.setConfigPerf("limit_cpu", limit_cpu); } private void TrackPower_Scroll(object? sender, EventArgs e) { InitPower(true); } public void InitFans() { byte[] curve = Program.wmi.GetFanCurve(2); if (curve.All(singleByte => singleByte == 0)) { Program.config.setConfig("mid_fan", 0); } else { Program.config.setConfig("mid_fan", 1); chartMid.Visible = true; SetChart(chartMid, 2); LoadProfile(seriesMid, 2); } SetChart(chartCPU, 0); SetChart(chartGPU, 1); LoadProfile(seriesCPU, 0); LoadProfile(seriesGPU, 1); int auto_apply = Program.config.getConfigPerf("auto_apply"); checkApplyFans.Checked = (auto_apply == 1); } void LoadProfile(Series series, int device, int def = 0) { series.ChartType = SeriesChartType.Line; series.MarkerSize = 10; series.MarkerStyle = MarkerStyle.Circle; series.Points.Clear(); int mode = Program.config.getConfig("performance_mode"); byte[] curve = Program.config.getFanConfig(device); if (def == 1 || ASUSWmi.IsEmptyCurve(curve)) { curve = Program.wmi.GetFanCurve(device, mode); if (ASUSWmi.IsEmptyCurve(curve)) curve = Program.config.getDefaultCurve(device); curve = ASUSWmi.FixFanCurve(curve); } //Debug.WriteLine(BitConverter.ToString(curve)); byte old = 0; for (int i = 0; i < 8; i++) { if (curve[i] == old) curve[i]++; // preventing 2 points in same spot from default asus profiles series.Points.AddXY(curve[i], curve[i + 8]); old = curve[i]; } SaveProfile(series, device); } void SaveProfile(Series series, int device) { byte[] curve = new byte[16]; int i = 0; foreach (DataPoint point in series.Points) { curve[i] = (byte)point.XValue; curve[i + 8] = (byte)point.YValues.First(); i++; } Program.config.setFanConfig(device, curve); //Program.wmi.SetFanCurve(device, curve); } private void ButtonReset_Click(object? sender, EventArgs e) { LoadProfile(seriesCPU, 0, 1); LoadProfile(seriesGPU, 1, 1); if (Program.config.getConfig("mid_fan") == 1) LoadProfile(seriesMid, 2, 1); checkApplyFans.Checked = false; checkApplyPower.Checked = false; Program.config.setConfigPerf("auto_apply", 0); Program.config.setConfigPerf("auto_apply_power", 0); Program.wmi.DeviceSet(ASUSWmi.PerformanceMode, Program.config.getConfig("performance_mode"), "PerfMode"); trackGPUCore.Value = 0; trackGPUMemory.Value = 0; trackGPUBoost.Value = ASUSWmi.MaxGPUBoost; trackGPUTemp.Value = ASUSWmi.MaxGPUTemp; Program.config.setConfigPerf("gpu_boost", trackGPUBoost.Value); Program.config.setConfigPerf("gpu_temp", trackGPUTemp.Value); Program.config.setConfigPerf("gpu_core", trackGPUCore.Value); Program.config.setConfigPerf("gpu_memory", trackGPUMemory.Value); VisualiseGPUSettings(); Program.settingsForm.SetGPUClocks(true); Program.settingsForm.SetGPUPower(); } private void ChartCPU_MouseUp(object? sender, MouseEventArgs e) { curPoint = null; labelTip.Visible = false; SaveProfile(seriesCPU, 0); SaveProfile(seriesGPU, 1); if (Program.config.getConfig("mid_fan") == 1) SaveProfile(seriesMid, 2); Program.settingsForm.AutoFans(); } private void ChartCPU_MouseMove(object? sender, MouseEventArgs e) { if (sender is null) return; Chart chart = (Chart)sender; ChartArea ca = chart.ChartAreas[0]; Axis ax = ca.AxisX; Axis ay = ca.AxisY; bool tip = false; HitTestResult hit = chart.HitTest(e.X, e.Y); if (hit.Series is not null && hit.PointIndex >= 0) { curPoint = hit.Series.Points[hit.PointIndex]; tip = true; } if (curPoint != null) { double dx, dy, dymin; try { dx = ax.PixelPositionToValue(e.X); dy = ay.PixelPositionToValue(e.Y); if (dx < 20) dx = 20; if (dx > 100) dx = 100; if (dy < 0) dy = 0; if (dy > fansMax) dy = fansMax; dymin = (dx - 65) * 1.2; if (dy < dymin) dy = dymin; if (e.Button.HasFlag(MouseButtons.Left)) { curPoint.XValue = dx; curPoint.YValues[0] = dy; if (hit.Series is not null) AdjustAllLevels(hit.PointIndex, dx, dy, hit.Series); tip = true; } labelTip.Text = Math.Round(curPoint.XValue) + "C, " + ChartPercToRPM((int)curPoint.YValues[0], " " + Properties.Strings.RPM); labelTip.Top = e.Y + ((Control)sender).Top; labelTip.Left = e.X - 50; } catch { Debug.WriteLine(e.Y); tip = false; } } labelTip.Visible = tip; } private void AdjustAllLevels(int index, double curXVal, double curYVal, Series series) { // Get the neighboring DataPoints of the hit point DataPoint upperPoint = null; DataPoint lowerPoint = null; if (index > 0) { lowerPoint = series.Points[index - 1]; } if (index < series.Points.Count - 1) { upperPoint = series.Points[index + 1]; } // Adjust the values according to the comparison between the value and its neighbors if (upperPoint != null) { if (curYVal > upperPoint.YValues[0]) { for (int i = index + 1; i < series.Points.Count; i++) { DataPoint curUpper = series.Points[i]; if (curUpper.YValues[0] >= curYVal) break; curUpper.YValues[0] = curYVal; } } if (curXVal > upperPoint.XValue) { for (int i = index + 1; i < series.Points.Count; i++) { DataPoint curUpper = series.Points[i]; if (curUpper.XValue >= curXVal) break; curUpper.XValue = curXVal; } } } if (lowerPoint != null) { //Debug.WriteLine(curYVal + " = 0; i--) { DataPoint curLower = series.Points[i]; if (curLower.YValues[0] < curYVal) break; curLower.YValues[0] = Math.Floor(curYVal); } } if (curXVal < lowerPoint.XValue) { for (int i = index - 1; i >= 0; i--) { DataPoint curLower = series.Points[i]; if (curLower.XValue <= curXVal) break; curLower.XValue = curXVal; } } } } } }