551 lines
19 KiB
C#
551 lines
19 KiB
C#
using ScottPlot;
|
||
using ScottPlot.WinForms;
|
||
|
||
namespace ChromeboxFanControl;
|
||
|
||
public sealed class MainForm : Form
|
||
{
|
||
private AppConfig _config = AppConfig.LoadMerged();
|
||
private FanController _fan = null!;
|
||
private readonly FormsPlot _chart = new() { Dock = DockStyle.Fill };
|
||
private readonly FormsPlot _curveChart = new() { Dock = DockStyle.Fill };
|
||
private readonly System.Windows.Forms.Label _lblStatus = new();
|
||
private readonly DataGridView _gridCurve = new();
|
||
private readonly TextBox _txtEctool = new();
|
||
private readonly NumericUpDown _nudPoll = new();
|
||
private readonly NumericUpDown _nudRpmEvery = new();
|
||
private readonly TextBox _txtFanRpmArgs = new();
|
||
private readonly TextBox _txtFanDutyArgs = new();
|
||
private readonly TextBox _txtAutoFanArgs = new();
|
||
private readonly ComboBox _cmbTempSource = new();
|
||
private readonly NumericUpDown _nudFailCount = new();
|
||
private readonly NumericUpDown _nudFailPct = new();
|
||
private readonly CheckBox _chkFailAuto = new();
|
||
private readonly NumericUpDown _nudChartMin = new();
|
||
private readonly NumericUpDown _nudChartPoints = new();
|
||
private readonly NotifyIcon _tray = new();
|
||
private readonly ContextMenuStrip _trayMenu = new();
|
||
private readonly ToolStripMenuItem _miPause = new("暂停控制");
|
||
private readonly object _seriesLock = new();
|
||
private readonly List<double> _tSec = new();
|
||
private readonly List<double> _temps = new();
|
||
private readonly List<double> _duties = new();
|
||
private readonly List<double> _rpms = new();
|
||
private DateTime _chartAnchor = DateTime.UtcNow;
|
||
private bool _minimizeToTray = true;
|
||
private ScottPlot.IYAxis? _yRpmAxis;
|
||
|
||
public MainForm()
|
||
{
|
||
Text = "Chromebox 风扇温控";
|
||
Size = new Size(960, 700);
|
||
StartPosition = FormStartPosition.CenterScreen;
|
||
MinimumSize = new Size(800, 500);
|
||
|
||
BuildUi();
|
||
WireTray();
|
||
|
||
Load += (_, _) =>
|
||
{
|
||
_config = AppConfig.LoadMerged();
|
||
_chartAnchor = DateTime.UtcNow;
|
||
ApplyConfigToUi();
|
||
_fan = new FanController(CloneConfigFromUi());
|
||
_fan.Sample += FanOnSample;
|
||
_fan.Start();
|
||
};
|
||
|
||
FormClosing += MainForm_FormClosing;
|
||
}
|
||
|
||
private void BuildUi()
|
||
{
|
||
var tabs = new TabControl { Dock = DockStyle.Fill };
|
||
|
||
var tabMonitor = new TabPage("监控");
|
||
var tabCurve = new TabPage("曲线");
|
||
var tabAdv = new TabPage("高级");
|
||
|
||
tabs.TabPages.Add(tabMonitor);
|
||
tabs.TabPages.Add(tabCurve);
|
||
tabs.TabPages.Add(tabAdv);
|
||
|
||
_lblStatus.AutoSize = false;
|
||
_lblStatus.Height = 28;
|
||
_lblStatus.Dock = DockStyle.Bottom;
|
||
_lblStatus.Padding = new Padding(8, 4, 8, 4);
|
||
_lblStatus.AutoEllipsis = true;
|
||
_lblStatus.Text = "启动中…";
|
||
|
||
var panelChart = new Panel { Dock = DockStyle.Fill };
|
||
panelChart.Controls.Add(_chart);
|
||
panelChart.Controls.Add(_lblStatus);
|
||
|
||
tabMonitor.Controls.Add(panelChart);
|
||
|
||
_gridCurve.Dock = DockStyle.Top;
|
||
_gridCurve.Height = 260;
|
||
_gridCurve.AutoGenerateColumns = false;
|
||
_gridCurve.AllowUserToAddRows = false;
|
||
_gridCurve.RowHeadersVisible = false;
|
||
_gridCurve.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "colTemp",
|
||
HeaderText = "温度 (°C)",
|
||
ReadOnly = true,
|
||
Width = 80
|
||
});
|
||
_gridCurve.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "colDuty",
|
||
HeaderText = "占空比 0–100",
|
||
Width = 100
|
||
});
|
||
for (var i = 0; i < 14; i++)
|
||
_gridCurve.Rows.Add(FanCurve.TempBreakpoints[i].ToString(), "0");
|
||
_gridCurve.CellValueChanged += (_, _) => RebuildCurveChart();
|
||
|
||
var panelCurveBtns = new FlowLayoutPanel
|
||
{
|
||
Dock = DockStyle.Bottom,
|
||
AutoSize = true,
|
||
FlowDirection = FlowDirection.LeftToRight,
|
||
Padding = new Padding(8)
|
||
};
|
||
var btnSave = new Button { Text = "保存并应用", AutoSize = true };
|
||
btnSave.Click += (_, _) => SaveFromUi();
|
||
var btnDefault = new Button { Text = "恢复默认曲线", AutoSize = true };
|
||
btnDefault.Click += (_, _) =>
|
||
{
|
||
var def = new AppConfig();
|
||
for (var i = 0; i < 14; i++)
|
||
_gridCurve.Rows[i].Cells[1].Value = def.CurvePoints[i].ToString();
|
||
RebuildCurveChart();
|
||
};
|
||
panelCurveBtns.Controls.Add(btnSave);
|
||
panelCurveBtns.Controls.Add(btnDefault);
|
||
|
||
var panelCurveTop = new Panel { Dock = DockStyle.Top, Height = 360 };
|
||
panelCurveTop.Controls.Add(_gridCurve);
|
||
panelCurveTop.Controls.Add(panelCurveBtns);
|
||
|
||
var panelCurve = new Panel { Dock = DockStyle.Fill };
|
||
panelCurve.Controls.Add(_curveChart);
|
||
panelCurve.Controls.Add(panelCurveTop);
|
||
tabCurve.Controls.Add(panelCurve);
|
||
|
||
var flp = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 2,
|
||
RowCount = 12,
|
||
Padding = new Padding(12)
|
||
};
|
||
flp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 35));
|
||
flp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 65));
|
||
|
||
var row = 0;
|
||
AddRow(flp, row++, "ectool 路径", _txtEctool);
|
||
AddRow(flp, row++, "轮询间隔 (ms)", _nudPoll);
|
||
_nudPoll.Minimum = 250;
|
||
_nudPoll.Maximum = 60_000;
|
||
_nudPoll.Increment = 250;
|
||
|
||
AddRow(flp, row++, "每 N 轮读 RPM", _nudRpmEvery);
|
||
_nudRpmEvery.Minimum = 1;
|
||
_nudRpmEvery.Maximum = 100;
|
||
|
||
AddRow(flp, row++, "读转速参数 (空格分隔)", _txtFanRpmArgs);
|
||
AddRow(flp, row++, "读占空比参数 (空=跳过)", _txtFanDutyArgs);
|
||
|
||
AddRow(flp, row++, "恢复自动风扇参数", _txtAutoFanArgs);
|
||
|
||
AddRow(flp, row++, "温度来源", _cmbTempSource);
|
||
_cmbTempSource.DropDownStyle = ComboBoxStyle.DropDownList;
|
||
_cmbTempSource.Items.AddRange(["AverageCore", "MaxCore"]);
|
||
|
||
AddRow(flp, row++, "连续失败进入安全模式 (次)", _nudFailCount);
|
||
_nudFailCount.Minimum = 1;
|
||
_nudFailCount.Maximum = 100;
|
||
|
||
AddRow(flp, row++, "安全模式固定占空比 (%)", _nudFailPct);
|
||
_nudFailPct.Minimum = 0;
|
||
_nudFailPct.Maximum = 100;
|
||
|
||
_chkFailAuto.Text = "安全模式使用 autofanctrl(不用固定占空比)";
|
||
_chkFailAuto.AutoSize = true;
|
||
var failPanel = new FlowLayoutPanel
|
||
{
|
||
FlowDirection = FlowDirection.LeftToRight,
|
||
AutoSize = true,
|
||
Dock = DockStyle.Fill,
|
||
WrapContents = false
|
||
};
|
||
failPanel.Controls.Add(_chkFailAuto);
|
||
flp.Controls.Add(new System.Windows.Forms.Label { Text = "安全模式策略", AutoSize = true, Anchor = AnchorStyles.Left }, 0, row);
|
||
flp.Controls.Add(failPanel, 1, row++);
|
||
|
||
AddRow(flp, row++, "图表保留时长 (分钟)", _nudChartMin);
|
||
_nudChartMin.Minimum = 1;
|
||
_nudChartMin.Maximum = 120;
|
||
|
||
AddRow(flp, row++, "图表最大点数", _nudChartPoints);
|
||
_nudChartPoints.Minimum = 100;
|
||
_nudChartPoints.Maximum = 10_000;
|
||
_nudChartPoints.Increment = 100;
|
||
|
||
var btnSaveAdv = new Button { Text = "保存高级设置", AutoSize = true, Dock = DockStyle.Bottom };
|
||
btnSaveAdv.Click += (_, _) => SaveFromUi();
|
||
tabAdv.Controls.Add(btnSaveAdv);
|
||
tabAdv.Controls.Add(flp);
|
||
|
||
Controls.Add(tabs);
|
||
}
|
||
|
||
private static void AddRow(TableLayoutPanel t, int row, string label, Control editor)
|
||
{
|
||
t.Controls.Add(new System.Windows.Forms.Label { Text = label, AutoSize = true, Anchor = AnchorStyles.Left }, 0, row);
|
||
editor.Dock = DockStyle.Fill;
|
||
t.Controls.Add(editor, 1, row);
|
||
}
|
||
|
||
private void WireTray()
|
||
{
|
||
_tray.Text = "Chromebox 风扇温控";
|
||
_tray.Visible = true;
|
||
_tray.Icon = SystemIcons.Application;
|
||
_tray.DoubleClick += (_, _) => ShowFromTray();
|
||
_miPause.Click += (_, _) =>
|
||
{
|
||
TogglePause();
|
||
};
|
||
_trayMenu.Items.Add("打开主窗口", null, (_, _) => ShowFromTray());
|
||
_trayMenu.Items.Add(_miPause);
|
||
_trayMenu.Items.Add("关于", null, (_, _) =>
|
||
MessageBox.Show(this,
|
||
"Chromebox 风扇温控\n\n使用 LibreHardwareMonitor 读取 CPU 温度,通过 crosec ectool 控制风扇。\n\n请以管理员身份运行。",
|
||
Text, MessageBoxButtons.OK, MessageBoxIcon.Information));
|
||
_trayMenu.Items.Add("退出", null, (_, _) => Close());
|
||
_tray.ContextMenuStrip = _trayMenu;
|
||
}
|
||
|
||
private void ShowFromTray()
|
||
{
|
||
Show();
|
||
WindowState = FormWindowState.Normal;
|
||
ShowInTaskbar = true;
|
||
Activate();
|
||
}
|
||
|
||
private void TogglePause()
|
||
{
|
||
if (_fan == null)
|
||
return;
|
||
_fan.Paused = !_fan.Paused;
|
||
_miPause.Text = _fan.Paused ? "恢复控制" : "暂停控制";
|
||
}
|
||
|
||
private void ApplyConfigToUi()
|
||
{
|
||
_txtEctool.Text = _config.EctoolPath;
|
||
_nudPoll.Value = _config.PollIntervalMs;
|
||
_nudRpmEvery.Value = _config.FanRpmPollEveryNCycles;
|
||
_txtFanRpmArgs.Text = string.Join(' ', _config.FanRpmArgs);
|
||
_txtFanDutyArgs.Text = _config.FanDutyArgs is { Length: > 0 } da ? string.Join(' ', da) : "";
|
||
_txtAutoFanArgs.Text = string.Join(' ', _config.AutoFanCtrlArgs);
|
||
_cmbTempSource.SelectedItem = _config.TempSource;
|
||
if (_cmbTempSource.SelectedIndex < 0)
|
||
_cmbTempSource.SelectedIndex = 0;
|
||
_nudFailCount.Value = _config.FailSafeAfterConsecutiveErrors;
|
||
_nudFailPct.Value = _config.FailSafeFanPercent;
|
||
_chkFailAuto.Checked = _config.FailSafeRestoreAutoFan;
|
||
_nudChartMin.Value = _config.ChartHistoryMinutes;
|
||
_nudChartPoints.Value = _config.ChartMaxPoints;
|
||
for (var i = 0; i < 14; i++)
|
||
_gridCurve.Rows[i].Cells[1].Value = _config.CurvePoints[i].ToString();
|
||
RebuildCurveChart();
|
||
}
|
||
|
||
private AppConfig CloneConfigFromUi()
|
||
{
|
||
var c = new AppConfig
|
||
{
|
||
EctoolPath = _txtEctool.Text.Trim(),
|
||
PollIntervalMs = (int)_nudPoll.Value,
|
||
FanRpmPollEveryNCycles = (int)_nudRpmEvery.Value,
|
||
FanRpmArgs = SplitArgs(_txtFanRpmArgs.Text),
|
||
FanDutyArgs = SplitArgs(_txtFanDutyArgs.Text) is { Length: > 0 } fa ? fa : null,
|
||
AutoFanCtrlArgs = SplitArgs(_txtAutoFanArgs.Text),
|
||
TempSource = _cmbTempSource.SelectedItem?.ToString() ?? "AverageCore",
|
||
FailSafeAfterConsecutiveErrors = (int)_nudFailCount.Value,
|
||
FailSafeFanPercent = (int)_nudFailPct.Value,
|
||
FailSafeRestoreAutoFan = _chkFailAuto.Checked,
|
||
ChartHistoryMinutes = (int)_nudChartMin.Value,
|
||
ChartMaxPoints = (int)_nudChartPoints.Value,
|
||
CurvePoints = ReadCurveFromGrid()
|
||
};
|
||
return c;
|
||
}
|
||
|
||
private static string[] SplitArgs(string line) =>
|
||
string.IsNullOrWhiteSpace(line)
|
||
? []
|
||
: line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
private int[] ReadCurveFromGrid()
|
||
{
|
||
var pts = new int[14];
|
||
for (var i = 0; i < 14; i++)
|
||
{
|
||
var cell = _gridCurve.Rows[i].Cells[1].Value?.ToString();
|
||
if (!int.TryParse(cell, out var v))
|
||
v = 0;
|
||
pts[i] = Math.Clamp(v, 0, 100);
|
||
}
|
||
|
||
return pts;
|
||
}
|
||
|
||
private void RebuildCurveChart()
|
||
{
|
||
var temps = FanCurve.TempBreakpoints.Select(t => (double)t).ToArray();
|
||
var duties = ReadCurveFromGrid().Select(d => (double)d).ToArray();
|
||
|
||
var plt = _curveChart.Plot;
|
||
plt.Clear();
|
||
plt.Axes.Left.Label.Text = "占空比 (%)";
|
||
plt.Axes.Bottom.Label.Text = "温度 (°C)";
|
||
plt.Axes.SetLimitsX(0, 100);
|
||
plt.Axes.SetLimitsY(0, 100);
|
||
|
||
if (temps.Length > 0 && duties.Length > 0)
|
||
{
|
||
var scatter = plt.Add.Scatter(temps, duties);
|
||
scatter.Color = Colors.SteelBlue;
|
||
scatter.LineWidth = 2;
|
||
scatter.MarkerSize = 6;
|
||
}
|
||
|
||
_curveChart.Refresh();
|
||
}
|
||
|
||
private void SaveFromUi()
|
||
{
|
||
try
|
||
{
|
||
var rpmArgs = SplitArgs(_txtFanRpmArgs.Text);
|
||
if (rpmArgs.Length == 0)
|
||
{
|
||
MessageBox.Show(this, "读转速参数不能为空。", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
var autoArgs = SplitArgs(_txtAutoFanArgs.Text);
|
||
if (autoArgs.Length == 0)
|
||
{
|
||
MessageBox.Show(this, "恢复自动风扇参数不能为空。", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
_config = CloneConfigFromUi();
|
||
_config.SaveUser();
|
||
_fan?.UpdateConfig(_config);
|
||
MessageBox.Show(this, "已保存。", Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(this, ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
}
|
||
|
||
private void FanOnSample(object? sender, FanSampleEventArgs e)
|
||
{
|
||
if (IsDisposed)
|
||
return;
|
||
if (InvokeRequired)
|
||
BeginInvoke(new MethodInvoker(() => OnSampleUi(e)));
|
||
else
|
||
OnSampleUi(e);
|
||
}
|
||
|
||
private void OnSampleUi(FanSampleEventArgs e)
|
||
{
|
||
var sec = (e.Time.ToUniversalTime() - _chartAnchor).TotalSeconds;
|
||
var temp = e.TempC ?? double.NaN;
|
||
var duty = (e.ActualDutyPercent ?? e.TargetDutyPercent) is { } d ? d : double.NaN;
|
||
var rpm = e.Rpm.HasValue ? e.Rpm.Value : double.NaN;
|
||
|
||
lock (_seriesLock)
|
||
{
|
||
_tSec.Add(sec);
|
||
_temps.Add(temp);
|
||
_duties.Add(duty);
|
||
_rpms.Add(rpm);
|
||
TrimSeries();
|
||
}
|
||
|
||
var tStr = e.TempC.HasValue ? $"{e.TempC:F1} °C" : "—";
|
||
var dVal = e.ActualDutyPercent ?? e.TargetDutyPercent;
|
||
var dStr = dVal.HasValue ? $"{dVal} %" : "—";
|
||
var rStr = e.Rpm.HasValue ? $"{e.Rpm} RPM" : "—";
|
||
var state = e.Paused ? "已暂停" : e.FailSafe ? "安全模式" : "运行中";
|
||
_lblStatus.Text =
|
||
$"{state} 温度: {tStr} 目标占空比: {dStr} 转速: {rStr} ectool: {e.LastEctoolMessage ?? "—"}";
|
||
|
||
_tray.Text = $"Chromebox {tStr} {dStr}";
|
||
|
||
RebuildChart();
|
||
}
|
||
|
||
private void TrimSeries()
|
||
{
|
||
var maxPts = _config.ChartMaxPoints;
|
||
var winSec = Math.Max(60, _config.ChartHistoryMinutes * 60.0);
|
||
while (_tSec.Count > 0 && _tSec.Count > maxPts)
|
||
{
|
||
_tSec.RemoveAt(0);
|
||
_temps.RemoveAt(0);
|
||
_duties.RemoveAt(0);
|
||
_rpms.RemoveAt(0);
|
||
}
|
||
|
||
if (_tSec.Count == 0)
|
||
return;
|
||
var last = _tSec[^1];
|
||
while (_tSec.Count > 0 && last - _tSec[0] > winSec)
|
||
{
|
||
_tSec.RemoveAt(0);
|
||
_temps.RemoveAt(0);
|
||
_duties.RemoveAt(0);
|
||
_rpms.RemoveAt(0);
|
||
}
|
||
}
|
||
|
||
private void RebuildChart()
|
||
{
|
||
double[] x, t, d, r;
|
||
lock (_seriesLock)
|
||
{
|
||
x = _tSec.ToArray();
|
||
t = _temps.ToArray();
|
||
d = _duties.ToArray();
|
||
r = _rpms.ToArray();
|
||
}
|
||
|
||
var plt = _chart.Plot;
|
||
plt.Clear();
|
||
|
||
plt.Axes.Left.Label.Text = "Temperature (°C)";
|
||
plt.Axes.Bottom.Label.Text = "Time (s)";
|
||
plt.Axes.Right.IsVisible = true;
|
||
plt.Axes.Right.Label.Text = "Duty (%)";
|
||
|
||
if (_yRpmAxis == null)
|
||
_yRpmAxis = plt.Axes.AddRightAxis();
|
||
_yRpmAxis.Label.Text = "RPM";
|
||
|
||
if (x.Length > 0)
|
||
{
|
||
var st = plt.Add.Scatter(x, t);
|
||
st.Axes.YAxis = plt.Axes.Left;
|
||
st.LegendText = string.Empty;
|
||
st.Color = Colors.Blue;
|
||
st.LineWidth = 2;
|
||
st.MarkerSize = 0;
|
||
|
||
var sd = plt.Add.Scatter(x, d);
|
||
sd.Axes.YAxis = plt.Axes.Right;
|
||
sd.LegendText = string.Empty;
|
||
sd.Color = Colors.Orange;
|
||
sd.LineWidth = 2;
|
||
sd.MarkerSize = 0;
|
||
|
||
var sr = plt.Add.Scatter(x, r);
|
||
sr.Axes.YAxis = _yRpmAxis;
|
||
sr.LegendText = string.Empty;
|
||
sr.Color = Colors.Green;
|
||
sr.LineWidth = 2;
|
||
sr.MarkerSize = 0;
|
||
|
||
var tempValid = t.Where(v => !double.IsNaN(v)).ToArray();
|
||
if (tempValid.Length > 0)
|
||
{
|
||
var tMin = tempValid.Min();
|
||
var tMax = tempValid.Max();
|
||
var pad = Math.Max(5, (tMax - tMin) * 0.1);
|
||
plt.Axes.SetLimitsY(Math.Max(0, tMin - pad), tMax + pad, plt.Axes.Left);
|
||
}
|
||
else
|
||
{
|
||
plt.Axes.SetLimitsY(0, 100, plt.Axes.Left);
|
||
}
|
||
|
||
plt.Axes.SetLimitsY(0, 100, plt.Axes.Right);
|
||
|
||
var rpmValid = r.Where(v => !double.IsNaN(v) && v > 0).ToArray();
|
||
if (rpmValid.Length > 0)
|
||
{
|
||
var rMin = rpmValid.Min();
|
||
var rMax = rpmValid.Max();
|
||
var rpmRange = rMax - rMin;
|
||
var pad = Math.Max(500, Math.Max(rpmRange * 0.2, rMax * 0.15));
|
||
plt.Axes.SetLimitsY(Math.Max(0, rMin - pad * 0.2), rMax + pad, _yRpmAxis);
|
||
}
|
||
else
|
||
{
|
||
plt.Axes.SetLimitsY(0, 6000, _yRpmAxis);
|
||
}
|
||
|
||
var xMin = x[0];
|
||
var xMax = x[^1];
|
||
var range = xMax - xMin;
|
||
var xMargin = Math.Min(10, Math.Max(2, range * 0.03));
|
||
plt.Axes.SetLimitsX(Math.Max(0, xMin - xMargin * 0.5), xMax + xMargin);
|
||
}
|
||
else
|
||
{
|
||
plt.Axes.SetLimitsY(0, 6000, _yRpmAxis);
|
||
}
|
||
|
||
LegendItem[] leg =
|
||
[
|
||
new() { LineColor = Colors.Blue, LineWidth = 2, LabelText = "°C" },
|
||
new() { LineColor = Colors.Orange, LineWidth = 2, LabelText = "Duty %" },
|
||
new() { LineColor = Colors.Green, LineWidth = 2, LabelText = "RPM" }
|
||
];
|
||
plt.ShowLegend(leg, Alignment.UpperRight);
|
||
plt.Legend.Orientation = ScottPlot.Orientation.Horizontal;
|
||
_chart.Refresh();
|
||
}
|
||
|
||
private void MainForm_FormClosing(object? sender, FormClosingEventArgs e)
|
||
{
|
||
_tray.Visible = false;
|
||
_tray.Dispose();
|
||
|
||
try
|
||
{
|
||
_fan?.Stop();
|
||
var cfg = _config;
|
||
if (File.Exists(cfg.EctoolPath))
|
||
EctoolRunner.RunSync(cfg.EctoolPath, cfg.AutoFanCtrlArgs);
|
||
}
|
||
catch
|
||
{
|
||
/* best effort */
|
||
}
|
||
|
||
_fan?.Dispose();
|
||
}
|
||
|
||
protected override void OnResize(EventArgs e)
|
||
{
|
||
base.OnResize(e);
|
||
if (WindowState == FormWindowState.Minimized && _minimizeToTray)
|
||
{
|
||
Hide();
|
||
ShowInTaskbar = false;
|
||
}
|
||
}
|
||
}
|