diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs
new file mode 100644
index 00000000..66b9c654
--- /dev/null
+++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs
@@ -0,0 +1,337 @@
+namespace GHelper.AutoTDP
+{
+ partial class AutoTDPGameProfileUI
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ panelPerformanceHeader = new Panel();
+ pictureKeyboard = new PictureBox();
+ labelSettings = new Label();
+ checkBoxEnabled = new CheckBox();
+ panelLightingContent = new Panel();
+ labelMinTDP = new Label();
+ labelMaxTDP = new Label();
+ sliderMaxTDP = new UI.Slider();
+ numericUpDownFPS = new NumericUpDown();
+ sliderMinTDP = new UI.Slider();
+ label2 = new Label();
+ label3 = new Label();
+ label1 = new Label();
+ textBoxTitle = new TextBox();
+ textBoxProcessName = new TextBox();
+ labelFPSSource = new Label();
+ labelLimiter = new Label();
+ buttonSave = new UI.RButton();
+ buttonDelete = new UI.RButton();
+ panelPerformanceHeader.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit();
+ panelLightingContent.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).BeginInit();
+ SuspendLayout();
+ //
+ // panelPerformanceHeader
+ //
+ panelPerformanceHeader.BackColor = SystemColors.ControlLight;
+ panelPerformanceHeader.Controls.Add(pictureKeyboard);
+ panelPerformanceHeader.Controls.Add(labelSettings);
+ panelPerformanceHeader.Controls.Add(checkBoxEnabled);
+ panelPerformanceHeader.Dock = DockStyle.Top;
+ panelPerformanceHeader.Location = new Point(0, 0);
+ panelPerformanceHeader.Margin = new Padding(2);
+ panelPerformanceHeader.Name = "panelPerformanceHeader";
+ panelPerformanceHeader.Size = new Size(386, 30);
+ panelPerformanceHeader.TabIndex = 53;
+ //
+ // pictureKeyboard
+ //
+ pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32;
+ pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom;
+ pictureKeyboard.Location = new Point(3, 8);
+ pictureKeyboard.Margin = new Padding(2);
+ pictureKeyboard.Name = "pictureKeyboard";
+ pictureKeyboard.Size = new Size(16, 16);
+ pictureKeyboard.TabIndex = 35;
+ pictureKeyboard.TabStop = false;
+ //
+ // labelSettings
+ //
+ labelSettings.AutoSize = true;
+ labelSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point);
+ labelSettings.Location = new Point(22, 8);
+ labelSettings.Margin = new Padding(4, 0, 4, 0);
+ labelSettings.Name = "labelSettings";
+ labelSettings.Size = new Size(89, 15);
+ labelSettings.TabIndex = 34;
+ labelSettings.Text = "Game Settings";
+ //
+ // checkBoxEnabled
+ //
+ checkBoxEnabled.Location = new Point(241, 2);
+ checkBoxEnabled.Margin = new Padding(4, 0, 4, 0);
+ checkBoxEnabled.Name = "checkBoxEnabled";
+ checkBoxEnabled.Size = new Size(136, 25);
+ checkBoxEnabled.TabIndex = 53;
+ checkBoxEnabled.Text = "Enabled";
+ checkBoxEnabled.UseVisualStyleBackColor = true;
+ //
+ // panelLightingContent
+ //
+ panelLightingContent.AutoSize = true;
+ panelLightingContent.Controls.Add(labelMinTDP);
+ panelLightingContent.Controls.Add(labelMaxTDP);
+ panelLightingContent.Controls.Add(sliderMaxTDP);
+ panelLightingContent.Controls.Add(numericUpDownFPS);
+ panelLightingContent.Controls.Add(sliderMinTDP);
+ panelLightingContent.Controls.Add(label2);
+ panelLightingContent.Controls.Add(label3);
+ panelLightingContent.Controls.Add(label1);
+ panelLightingContent.Controls.Add(textBoxTitle);
+ panelLightingContent.Controls.Add(textBoxProcessName);
+ panelLightingContent.Controls.Add(labelFPSSource);
+ panelLightingContent.Controls.Add(labelLimiter);
+ panelLightingContent.Dock = DockStyle.Top;
+ panelLightingContent.Location = new Point(0, 30);
+ panelLightingContent.Margin = new Padding(2);
+ panelLightingContent.Name = "panelLightingContent";
+ panelLightingContent.Padding = new Padding(0, 0, 0, 7);
+ panelLightingContent.Size = new Size(386, 146);
+ panelLightingContent.TabIndex = 57;
+ //
+ // labelMinTDP
+ //
+ labelMinTDP.Location = new Point(342, 91);
+ labelMinTDP.Name = "labelMinTDP";
+ labelMinTDP.Size = new Size(39, 22);
+ labelMinTDP.TabIndex = 67;
+ labelMinTDP.Text = "30W";
+ labelMinTDP.TextAlign = ContentAlignment.MiddleRight;
+ //
+ // labelMaxTDP
+ //
+ labelMaxTDP.Location = new Point(341, 117);
+ labelMaxTDP.Name = "labelMaxTDP";
+ labelMaxTDP.Size = new Size(40, 22);
+ labelMaxTDP.TabIndex = 66;
+ labelMaxTDP.Text = "150W";
+ labelMaxTDP.TextAlign = ContentAlignment.MiddleRight;
+ //
+ // sliderMaxTDP
+ //
+ sliderMaxTDP.AccessibleName = "DPI Slider";
+ sliderMaxTDP.Location = new Point(140, 117);
+ sliderMaxTDP.Margin = new Padding(2);
+ sliderMaxTDP.Max = 200;
+ sliderMaxTDP.Min = 5;
+ sliderMaxTDP.Name = "sliderMaxTDP";
+ sliderMaxTDP.Size = new Size(204, 20);
+ sliderMaxTDP.Step = 1;
+ sliderMaxTDP.TabIndex = 65;
+ sliderMaxTDP.TabStop = false;
+ sliderMaxTDP.Text = "sliderBattery";
+ sliderMaxTDP.Value = 0;
+ //
+ // numericUpDownFPS
+ //
+ numericUpDownFPS.BorderStyle = BorderStyle.None;
+ numericUpDownFPS.Location = new Point(291, 66);
+ numericUpDownFPS.Margin = new Padding(2);
+ numericUpDownFPS.Maximum = new decimal(new int[] { 1000, 0, 0, 0 });
+ numericUpDownFPS.Minimum = new decimal(new int[] { 20, 0, 0, 0 });
+ numericUpDownFPS.Name = "numericUpDownFPS";
+ numericUpDownFPS.Size = new Size(86, 19);
+ numericUpDownFPS.TabIndex = 64;
+ numericUpDownFPS.TextAlign = HorizontalAlignment.Center;
+ numericUpDownFPS.Value = new decimal(new int[] { 60, 0, 0, 0 });
+ //
+ // sliderMinTDP
+ //
+ sliderMinTDP.AccessibleName = "DPI Slider";
+ sliderMinTDP.Location = new Point(140, 93);
+ sliderMinTDP.Margin = new Padding(2);
+ sliderMinTDP.Max = 200;
+ sliderMinTDP.Min = 5;
+ sliderMinTDP.Name = "sliderMinTDP";
+ sliderMinTDP.Size = new Size(204, 20);
+ sliderMinTDP.Step = 1;
+ sliderMinTDP.TabIndex = 63;
+ sliderMinTDP.TabStop = false;
+ sliderMinTDP.Text = "sliderBattery";
+ sliderMinTDP.Value = 0;
+ //
+ // label2
+ //
+ label2.Location = new Point(5, 117);
+ label2.Margin = new Padding(4, 0, 4, 0);
+ label2.Name = "label2";
+ label2.Size = new Size(129, 22);
+ label2.TabIndex = 61;
+ label2.Text = "Max TDP:";
+ label2.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // label3
+ //
+ label3.Location = new Point(5, 91);
+ label3.Margin = new Padding(4, 0, 4, 0);
+ label3.Name = "label3";
+ label3.Size = new Size(129, 22);
+ label3.TabIndex = 62;
+ label3.Text = "Min TDP:";
+ label3.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // label1
+ //
+ label1.Location = new Point(5, 63);
+ label1.Margin = new Padding(4, 0, 4, 0);
+ label1.Name = "label1";
+ label1.Size = new Size(129, 22);
+ label1.TabIndex = 60;
+ label1.Text = "Target FPS:";
+ label1.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // textBoxTitle
+ //
+ textBoxTitle.Location = new Point(140, 36);
+ textBoxTitle.Name = "textBoxTitle";
+ textBoxTitle.Size = new Size(237, 23);
+ textBoxTitle.TabIndex = 59;
+ //
+ // textBoxProcessName
+ //
+ textBoxProcessName.Location = new Point(140, 6);
+ textBoxProcessName.Name = "textBoxProcessName";
+ textBoxProcessName.ReadOnly = true;
+ textBoxProcessName.Size = new Size(237, 23);
+ textBoxProcessName.TabIndex = 58;
+ textBoxProcessName.WordWrap = false;
+ //
+ // labelFPSSource
+ //
+ labelFPSSource.Location = new Point(4, 37);
+ labelFPSSource.Margin = new Padding(4, 0, 4, 0);
+ labelFPSSource.Name = "labelFPSSource";
+ labelFPSSource.Size = new Size(129, 22);
+ labelFPSSource.TabIndex = 57;
+ labelFPSSource.Text = "Name:";
+ labelFPSSource.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // labelLimiter
+ //
+ labelLimiter.Location = new Point(4, 7);
+ labelLimiter.Margin = new Padding(4, 0, 4, 0);
+ labelLimiter.Name = "labelLimiter";
+ labelLimiter.Size = new Size(129, 22);
+ labelLimiter.TabIndex = 47;
+ labelLimiter.Text = "Process";
+ labelLimiter.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // buttonSave
+ //
+ buttonSave.AccessibleName = "Keyboard Color";
+ buttonSave.Activated = false;
+ buttonSave.Anchor = AnchorStyles.Top | AnchorStyles.Right;
+ buttonSave.BackColor = SystemColors.ButtonHighlight;
+ buttonSave.BorderColor = Color.Transparent;
+ buttonSave.BorderRadius = 2;
+ buttonSave.FlatStyle = FlatStyle.Flat;
+ buttonSave.ForeColor = SystemColors.ControlText;
+ buttonSave.Location = new Point(279, 182);
+ buttonSave.Margin = new Padding(2, 4, 2, 4);
+ buttonSave.Name = "buttonSave";
+ buttonSave.Secondary = false;
+ buttonSave.Size = new Size(103, 25);
+ buttonSave.TabIndex = 61;
+ buttonSave.Text = "Save";
+ buttonSave.UseVisualStyleBackColor = false;
+ //
+ // buttonDelete
+ //
+ buttonDelete.AccessibleName = "Keyboard Color";
+ buttonDelete.Activated = false;
+ buttonDelete.Anchor = AnchorStyles.Top | AnchorStyles.Right;
+ buttonDelete.BackColor = SystemColors.ButtonHighlight;
+ buttonDelete.BorderColor = Color.Transparent;
+ buttonDelete.BorderRadius = 2;
+ buttonDelete.FlatStyle = FlatStyle.Flat;
+ buttonDelete.ForeColor = SystemColors.ControlText;
+ buttonDelete.Location = new Point(4, 182);
+ buttonDelete.Margin = new Padding(2, 4, 2, 4);
+ buttonDelete.Name = "buttonDelete";
+ buttonDelete.Secondary = false;
+ buttonDelete.Size = new Size(103, 25);
+ buttonDelete.TabIndex = 62;
+ buttonDelete.Text = "Delete";
+ buttonDelete.UseVisualStyleBackColor = false;
+ //
+ // AutoTDPGameProfileUI
+ //
+ AutoScaleDimensions = new SizeF(7F, 15F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(386, 217);
+ Controls.Add(buttonDelete);
+ Controls.Add(buttonSave);
+ Controls.Add(panelLightingContent);
+ Controls.Add(panelPerformanceHeader);
+ FormBorderStyle = FormBorderStyle.FixedSingle;
+ MaximizeBox = false;
+ MinimizeBox = false;
+ Name = "AutoTDPGameProfileUI";
+ ShowIcon = false;
+ ShowInTaskbar = false;
+ Text = "Game Profile";
+ panelPerformanceHeader.ResumeLayout(false);
+ panelPerformanceHeader.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit();
+ panelLightingContent.ResumeLayout(false);
+ panelLightingContent.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private Panel panelPerformanceHeader;
+ private PictureBox pictureKeyboard;
+ private Label labelSettings;
+ private CheckBox checkBoxEnabled;
+ private Panel panelLightingContent;
+ private Label labelFPSSource;
+ private Label labelLimiter;
+ private TextBox textBoxTitle;
+ private TextBox textBoxProcessName;
+ private UI.RButton buttonSave;
+ private Label label2;
+ private Label label3;
+ private Label label1;
+ private UI.Slider sliderMinTDP;
+ private UI.Slider sliderMaxTDP;
+ private NumericUpDown numericUpDownFPS;
+ private Label labelMinTDP;
+ private Label labelMaxTDP;
+ private UI.RButton buttonDelete;
+ }
+}
\ No newline at end of file
diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs
new file mode 100644
index 00000000..95f96677
--- /dev/null
+++ b/app/AutoTDP/AutoTDPGameProfileUI.cs
@@ -0,0 +1,90 @@
+using GHelper.UI;
+
+namespace GHelper.AutoTDP
+{
+ public partial class AutoTDPGameProfileUI : RForm
+ {
+ public GameProfile GameProfile;
+ private AutoTDPUI AutoTDPUI;
+
+ public AutoTDPGameProfileUI(GameProfile profile, AutoTDPUI parent)
+ {
+ AutoTDPUI = parent;
+ GameProfile = profile;
+ InitializeComponent();
+
+ sliderMinTDP.ValueChanged += SliderMinTDP_ValueChanged;
+ sliderMaxTDP.ValueChanged += SliderMaxTDP_ValueChanged;
+ buttonSave.Click += ButtonSave_Click;
+ buttonDelete.Click += ButtonDelete_Click;
+
+ InitTheme();
+
+
+ Shown += AutoTDPGameProfileUI_Shown;
+
+ VisualizeGameProfile();
+ }
+
+ private void ButtonDelete_Click(object? sender, EventArgs e)
+ {
+ AutoTDPUI.DeleteGameProfile(GameProfile);
+ Close();
+ }
+
+ private void ButtonSave_Click(object? sender, EventArgs e)
+ {
+ GameProfile.Enabled = checkBoxEnabled.Checked;
+ GameProfile.GameTitle = textBoxTitle.Text;
+ GameProfile.TargetFPS = ((int)numericUpDownFPS.Value);
+ GameProfile.MinTdp = sliderMinTDP.Value;
+ GameProfile.MaxTdp = sliderMaxTDP.Value;
+
+ AutoTDPUI.UpdateGameProfile(GameProfile);
+
+ Close();
+ }
+
+ private void SliderMaxTDP_ValueChanged(object? sender, EventArgs e)
+ {
+ labelMaxTDP.Text = sliderMaxTDP.Value + "W";
+ if (sliderMaxTDP.Value < sliderMinTDP.Value)
+ {
+ sliderMinTDP.Value = sliderMaxTDP.Value;
+ }
+ }
+
+ private void SliderMinTDP_ValueChanged(object? sender, EventArgs e)
+ {
+ labelMinTDP.Text = sliderMinTDP.Value + "W";
+ if (sliderMaxTDP.Value > sliderMinTDP.Value)
+ {
+ sliderMaxTDP.Value = sliderMinTDP.Value;
+ }
+ }
+
+ private void AutoTDPGameProfileUI_Shown(object? sender, EventArgs e)
+ {
+ if (Height > Program.settingsForm.Height)
+ {
+ Top = Program.settingsForm.Top + Program.settingsForm.Height - Height;
+ }
+ else
+ {
+ Top = Program.settingsForm.Top + 60;
+ }
+
+ Left = Program.settingsForm.Left - Width - ((AutoTDPUI.Width - Width) / 2);
+ }
+
+ private void VisualizeGameProfile()
+ {
+ sliderMinTDP.Value = GameProfile.MinTdp;
+ sliderMaxTDP.Value = GameProfile.MaxTdp;
+ numericUpDownFPS.Value = GameProfile.TargetFPS;
+ textBoxProcessName.Text = GameProfile.ProcessName;
+ textBoxTitle.Text = GameProfile.GameTitle;
+ checkBoxEnabled.Checked = GameProfile.Enabled;
+ }
+ }
+}
diff --git a/app/AutoTDP/AutoTDPGameProfileUI.resx b/app/AutoTDP/AutoTDPGameProfileUI.resx
new file mode 100644
index 00000000..af32865e
--- /dev/null
+++ b/app/AutoTDP/AutoTDPGameProfileUI.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs
new file mode 100644
index 00000000..dd9eb2be
--- /dev/null
+++ b/app/AutoTDP/AutoTDPService.cs
@@ -0,0 +1,435 @@
+using System.Text.Json;
+using GHelper.AutoTDP.FramerateSource;
+using GHelper.AutoTDP.PowerLimiter;
+
+namespace GHelper.AutoTDP
+{
+ internal class AutoTDPService : IDisposable
+ {
+
+ private static readonly int INTERVAL_APP_CHECK = 5_000;
+ private static readonly int INTERVAL_FPS_CHECK = 2_500;
+
+ string GameProfileFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper\\AutoTDP.json";
+
+ IFramerateSource? framerateSouce;
+ IPowerLimiter? powerLimiter;
+
+ public List GameProfiles = new List();
+
+ private bool Running = false;
+ private Thread? checkerThread;
+ private Thread? tdpThread;
+
+ private double GameFPSPrevious = double.NaN;
+ private double GameFPS;
+
+ private int FramerateTargetReachedCounter;
+ private int FramerateDipCounter;
+
+ private static readonly int FPSDipHistorySize = 8;
+
+ private List FramerateLog = new List();
+
+ private double CurrentTDP;
+
+ private GameInstance? currentGame;
+
+ public AutoTDPService()
+ {
+ LoadGameProfiles();
+
+ Start();
+ }
+
+ ///
+ /// Whether the system is enabled and currently running.
+ ///
+ ///
+ public bool IsRunning()
+ {
+ return Running;
+ }
+
+ ///
+ /// Whether a supported game is actively monitored and TDP is adjusted
+ ///
+ ///
+ public bool IsActive()
+ {
+ return currentGame is not null;
+ }
+
+ public void Start()
+ {
+ if (!IsEnabled() || IsRunning())
+ {
+ return;
+ }
+
+ Running = true;
+
+ InitFramerateSource();
+
+ InitLimiter();
+
+
+ checkerThread = new Thread(() =>
+ {
+ while (Running)
+ {
+ CheckForGame();
+ try
+ {
+ Thread.Sleep(INTERVAL_APP_CHECK);
+ }
+ catch (ThreadInterruptedException)
+ {
+ continue;
+ }
+ }
+ });
+ checkerThread.Start();
+ }
+
+ public bool IsEnabled()
+ {
+ return AppConfig.Get("auto_tdp_enabled", 0) == 1;
+ }
+
+ public void InitFramerateSource()
+ {
+ string? source = AppConfig.GetString("auto_tdp_fps_source");
+
+ if ((source is null || source.Equals("rtss")) && RTSSFramerateSource.IsAvailable())
+ {
+ RTSSFramerateSource rtss = new RTSSFramerateSource();
+ RTSSFramerateSource.Start();
+ framerateSouce = rtss;
+ return;
+ }
+ }
+
+ public void InitLimiter()
+ {
+ string? limiter = AppConfig.GetString("auto_tdp_limiter");
+
+ if (limiter is null || limiter.Equals("asus_acpi"))
+ {
+ powerLimiter = new ASUSACPIPowerLimiter();
+ return;
+ }
+
+ if (limiter is not null && limiter.Equals("intel_msr"))
+ {
+ powerLimiter = new IntelMSRPowerLimiter();
+ return;
+ }
+ }
+
+ public void SaveGameProfiles()
+ {
+ string json = JsonSerializer.Serialize(GameProfiles);
+
+ File.WriteAllText(GameProfileFile, json);
+ }
+
+
+ public void LoadGameProfiles()
+ {
+ if (!File.Exists(GameProfileFile))
+ {
+ return;
+ }
+
+ string? json = File.ReadAllText(GameProfileFile);
+
+ if (json == null)
+ {
+ return;
+ }
+
+ try
+ {
+ GameProfiles = JsonSerializer.Deserialize>(json);
+ }
+ catch (Exception e)
+ {
+ Logger.WriteLine("[AutoTDPService] Deserialization failed. Creating empty list. Message: " + e.Message);
+ GameProfiles = new List();
+ }
+
+ }
+
+ public void CheckForGame()
+ {
+ if (currentGame is not null)
+ {
+ //Already handling a running game. No need to check for other games
+ return;
+ }
+ List runningGames = framerateSouce.GetRunningGames();
+
+ if (runningGames.Count == 0)
+ {
+ Logger.WriteLine("[AutoTDPService] No games detected");
+ return;
+ }
+
+ foreach (GameInstance gi in runningGames)
+ {
+ Logger.WriteLine("[AutoTDPService] Detected App: " + gi.ProcessName + " PID: " + gi.ProcessID);
+
+ if (IsGameInList(gi.ProcessName))
+ {
+ Logger.WriteLine("[AutoTDPService] Detected Supported Game: " + gi.ProcessName + " PID: " + gi.ProcessID);
+ HandleGame(gi);
+ return;
+ }
+ }
+ }
+
+ public GameProfile? ProfileForGame(String? processName)
+ {
+ if (processName is null)
+ {
+ return null;
+ }
+
+ foreach (GameProfile gp in GameProfiles)
+ {
+ if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName))
+ {
+ return gp;
+ }
+ }
+
+ return null;
+ }
+
+ public bool IsGameInList(String? processName)
+ {
+ return ProfileForGame(processName) is not null;
+ }
+
+
+ public void HandleGame(GameInstance instance)
+ {
+ if (currentGame is not null)
+ {
+ Logger.WriteLine("[AutoTDPService] Already handling a game");
+ return;
+ }
+
+ if (tdpThread is not null)
+ {
+ tdpThread.Join();
+ tdpThread = null;
+ }
+ currentGame = instance;
+
+ StartGameHandler(instance);
+ }
+
+ public void Reset()
+ {
+ currentGame = null;
+ GameFPSPrevious = double.NaN;
+ GameFPS = 0;
+
+ if (powerLimiter is not null)
+ {
+ powerLimiter.ResetPowerLimits();
+ CurrentTDP = powerLimiter.GetCPUPowerLimit();
+ }
+ }
+
+ public void StartGameHandler(GameInstance instance)
+ {
+ GameProfile? profile = ProfileForGame(instance.ProcessName);
+ if (profile is null || powerLimiter is null || framerateSouce is null)
+ {
+ return;
+ }
+
+ tdpThread = new Thread(() =>
+ {
+ CurrentTDP = powerLimiter.GetCPUPowerLimit();
+ while (currentGame is not null && Running)
+ {
+ GameFPS = framerateSouce.GetFramerate(profile.ProcessName);
+
+
+ Logger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS);
+
+ if (GameFPS < 0.0d)
+ {
+ //Game is not running anymore or RTSS lost its hook
+ Reset();
+ return;
+ }
+ AdjustPowerLimit(profile);
+
+ try
+ {
+ Thread.Sleep(INTERVAL_FPS_CHECK);
+ }
+ catch (ThreadInterruptedException)
+ {
+ continue;
+ }
+
+ }
+ });
+ tdpThread.Start();
+ }
+
+ private double FPSDipCorrection(double currentFramerate, double targetFPS)
+ {
+ double correction = 0.0d;
+
+
+ FramerateLog.Insert(0, currentFramerate);
+
+ //Remove last entry when exceeding the desired size.
+ if (FramerateLog.Count > FPSDipHistorySize)
+ {
+ FramerateLog.RemoveAt(FramerateLog.Count - 1);
+ }
+
+ if (targetFPS - 1 <= currentFramerate && currentFramerate <= targetFPS + 1)
+ {
+ FramerateTargetReachedCounter++;
+
+ if (FramerateTargetReachedCounter >= 4
+ && FramerateTargetReachedCounter < FPSDipHistorySize
+ && targetFPS - 0.75 <= FramerateLog.Take(4).Average()
+ && FramerateLog.Take(3).Average() <= targetFPS + 0.15)
+ {
+ //short dip
+ FramerateDipCounter++;
+ correction = targetFPS + 0.75 - currentFramerate;
+ }
+ else if (FramerateDipCounter >= 5
+ && targetFPS - 0.75 <= FramerateLog.Average()
+ && FramerateLog.Average() <= targetFPS + 0.15)
+ {
+ //long dip
+ correction = targetFPS + 1.5 - currentFramerate;
+ FramerateTargetReachedCounter = FPSDipHistorySize;
+ }
+ }
+ else
+ {
+ //No dip, no correction
+ correction = 0.0;
+ FramerateTargetReachedCounter = 0;
+ FramerateDipCounter = 0;
+ }
+
+ return correction;
+ }
+
+
+ private double TDPDamper(double currentFramerate)
+ {
+ if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate;
+ double dF = -0.1d;
+
+ // Calculation
+ double deltaError = currentFramerate - GameFPSPrevious;
+ double dT = deltaError / (1010.0 / 1000.0);
+ double damping = CurrentTDP / currentFramerate * dF * dT;
+
+ GameFPSPrevious = currentFramerate;
+
+ return damping;
+ }
+
+ public void AdjustPowerLimit(GameProfile profile)
+ {
+
+ if (powerLimiter is null)
+ {
+ //Should not happen... but we also don't want it to crash
+ return;
+ }
+
+ double newPL = CurrentTDP;
+
+
+ Logger.WriteLine("[AutoTDPService] Current: " + (int)GameFPS + "FPS");
+
+
+ double delta = profile.TargetFPS - GameFPS - FPSDipCorrection(GameFPS, profile.TargetFPS);
+ delta = Math.Clamp(delta, -15, 15);
+
+
+ double adjustment = (delta * CurrentTDP / GameFPS) * 0.85;
+ //Dampen the changes to not change TDP too aggressively which would cause performance issues
+ adjustment += TDPDamper(GameFPS);
+
+
+ newPL += adjustment;
+
+ //Respect the limits that the user chose
+ newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp);
+
+ Logger.WriteLine("[AutoTDPService] Setting Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment);
+
+ //We only limit to full watts, no fractions. In this case, we will cut off the fractional part
+ powerLimiter.SetCPUPowerLimit((int)newPL);
+ CurrentTDP = newPL;
+ }
+
+ public void StopGameHandler()
+ {
+ if (tdpThread is not null)
+ {
+ currentGame = null;
+ tdpThread.Join();
+ tdpThread = null;
+ }
+
+ }
+
+ public void Shutdown()
+ {
+ Running = false;
+
+ if (checkerThread is not null)
+ {
+ checkerThread.Interrupt();
+ checkerThread.Join();
+ }
+
+
+ if (tdpThread is not null)
+ {
+ tdpThread.Interrupt();
+ tdpThread.Join();
+ }
+
+ if (powerLimiter is not null)
+ {
+ powerLimiter.ResetPowerLimits();
+ powerLimiter.Dispose();
+ powerLimiter = null;
+ }
+
+ if (framerateSouce is not null)
+ {
+ framerateSouce = null;
+ }
+
+ //Kill RTSS instance if we started one
+ RTSSFramerateSource.Stop();
+
+ }
+
+ public void Dispose()
+ {
+ Shutdown();
+ }
+ }
+}
diff --git a/app/AutoTDP/AutoTDPUI.Designer.cs b/app/AutoTDP/AutoTDPUI.Designer.cs
new file mode 100644
index 00000000..0b48a2b1
--- /dev/null
+++ b/app/AutoTDP/AutoTDPUI.Designer.cs
@@ -0,0 +1,311 @@
+using GHelper.UI;
+
+namespace GHelper.AutoTDP
+{
+ partial class AutoTDPUI
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ panelPerformanceHeader = new Panel();
+ pictureKeyboard = new PictureBox();
+ labelGlobalSettings = new Label();
+ checkBoxEnabled = new CheckBox();
+ panelLightingContent = new Panel();
+ comboBoxFPSSource = new RComboBox();
+ labelFPSSource = new Label();
+ comboBoxLimiter = new RComboBox();
+ labelLimiter = new Label();
+ buttonAddGame = new RButton();
+ panelGamesHeader = new Panel();
+ pictureBox1 = new PictureBox();
+ labelGames = new Label();
+ tableLayoutGames = new TableLayoutPanel();
+ buttonGameDummy = new RButton();
+ panelPerformanceHeader.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureKeyboard).BeginInit();
+ panelLightingContent.SuspendLayout();
+ panelGamesHeader.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit();
+ tableLayoutGames.SuspendLayout();
+ SuspendLayout();
+ //
+ // panelPerformanceHeader
+ //
+ panelPerformanceHeader.BackColor = SystemColors.ControlLight;
+ panelPerformanceHeader.Controls.Add(pictureKeyboard);
+ panelPerformanceHeader.Controls.Add(labelGlobalSettings);
+ panelPerformanceHeader.Controls.Add(checkBoxEnabled);
+ panelPerformanceHeader.Dock = DockStyle.Top;
+ panelPerformanceHeader.Location = new Point(0, 0);
+ panelPerformanceHeader.Margin = new Padding(2);
+ panelPerformanceHeader.Name = "panelPerformanceHeader";
+ panelPerformanceHeader.Size = new Size(446, 30);
+ panelPerformanceHeader.TabIndex = 52;
+ //
+ // pictureKeyboard
+ //
+ pictureKeyboard.BackgroundImage = Properties.Resources.icons8_automation_32;
+ pictureKeyboard.BackgroundImageLayout = ImageLayout.Zoom;
+ pictureKeyboard.Location = new Point(3, 8);
+ pictureKeyboard.Margin = new Padding(2);
+ pictureKeyboard.Name = "pictureKeyboard";
+ pictureKeyboard.Size = new Size(16, 16);
+ pictureKeyboard.TabIndex = 35;
+ pictureKeyboard.TabStop = false;
+ //
+ // labelGlobalSettings
+ //
+ labelGlobalSettings.AutoSize = true;
+ labelGlobalSettings.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point);
+ labelGlobalSettings.Location = new Point(22, 8);
+ labelGlobalSettings.Margin = new Padding(4, 0, 4, 0);
+ labelGlobalSettings.Name = "labelGlobalSettings";
+ labelGlobalSettings.Size = new Size(100, 15);
+ labelGlobalSettings.TabIndex = 34;
+ labelGlobalSettings.Text = "General Settings";
+ //
+ // checkBoxEnabled
+ //
+ checkBoxEnabled.Location = new Point(244, 4);
+ checkBoxEnabled.Margin = new Padding(4, 0, 4, 0);
+ checkBoxEnabled.Name = "checkBoxEnabled";
+ checkBoxEnabled.Size = new Size(198, 25);
+ checkBoxEnabled.TabIndex = 53;
+ checkBoxEnabled.Text = "Enabled";
+ checkBoxEnabled.UseVisualStyleBackColor = true;
+ //
+ // panelLightingContent
+ //
+ panelLightingContent.AutoSize = true;
+ panelLightingContent.Controls.Add(comboBoxFPSSource);
+ panelLightingContent.Controls.Add(labelFPSSource);
+ panelLightingContent.Controls.Add(comboBoxLimiter);
+ panelLightingContent.Controls.Add(labelLimiter);
+ panelLightingContent.Dock = DockStyle.Top;
+ panelLightingContent.Location = new Point(0, 30);
+ panelLightingContent.Margin = new Padding(2);
+ panelLightingContent.Name = "panelLightingContent";
+ panelLightingContent.Padding = new Padding(0, 0, 0, 7);
+ panelLightingContent.Size = new Size(446, 67);
+ panelLightingContent.TabIndex = 56;
+ //
+ // comboBoxFPSSource
+ //
+ comboBoxFPSSource.BorderColor = Color.White;
+ comboBoxFPSSource.ButtonColor = Color.FromArgb(255, 255, 255);
+ comboBoxFPSSource.DropDownStyle = ComboBoxStyle.DropDownList;
+ comboBoxFPSSource.FlatStyle = FlatStyle.Flat;
+ comboBoxFPSSource.FormattingEnabled = true;
+ comboBoxFPSSource.Location = new Point(244, 37);
+ comboBoxFPSSource.Margin = new Padding(11, 0, 11, 0);
+ comboBoxFPSSource.Name = "comboBoxFPSSource";
+ comboBoxFPSSource.Size = new Size(191, 23);
+ comboBoxFPSSource.TabIndex = 56;
+ //
+ // labelFPSSource
+ //
+ labelFPSSource.Location = new Point(4, 37);
+ labelFPSSource.Margin = new Padding(4, 0, 4, 0);
+ labelFPSSource.Name = "labelFPSSource";
+ labelFPSSource.Size = new Size(211, 22);
+ labelFPSSource.TabIndex = 57;
+ labelFPSSource.Text = "FPS Source";
+ //
+ // comboBoxLimiter
+ //
+ comboBoxLimiter.BorderColor = Color.White;
+ comboBoxLimiter.ButtonColor = Color.FromArgb(255, 255, 255);
+ comboBoxLimiter.DropDownStyle = ComboBoxStyle.DropDownList;
+ comboBoxLimiter.FlatStyle = FlatStyle.Flat;
+ comboBoxLimiter.FormattingEnabled = true;
+ comboBoxLimiter.Location = new Point(244, 7);
+ comboBoxLimiter.Margin = new Padding(11, 0, 11, 0);
+ comboBoxLimiter.Name = "comboBoxLimiter";
+ comboBoxLimiter.Size = new Size(191, 23);
+ comboBoxLimiter.TabIndex = 46;
+ //
+ // labelLimiter
+ //
+ labelLimiter.Location = new Point(4, 7);
+ labelLimiter.Margin = new Padding(4, 0, 4, 0);
+ labelLimiter.Name = "labelLimiter";
+ labelLimiter.Size = new Size(211, 22);
+ labelLimiter.TabIndex = 47;
+ labelLimiter.Text = "Power Limiter";
+ //
+ // buttonAddGame
+ //
+ buttonAddGame.AccessibleName = "Keyboard Color";
+ buttonAddGame.Activated = false;
+ buttonAddGame.Anchor = AnchorStyles.Top | AnchorStyles.Right;
+ buttonAddGame.BackColor = SystemColors.ButtonHighlight;
+ buttonAddGame.BorderColor = Color.Transparent;
+ buttonAddGame.BorderRadius = 2;
+ buttonAddGame.FlatStyle = FlatStyle.Flat;
+ buttonAddGame.ForeColor = SystemColors.ControlText;
+ buttonAddGame.Location = new Point(339, 3);
+ buttonAddGame.Margin = new Padding(2, 4, 2, 4);
+ buttonAddGame.Name = "buttonAddGame";
+ buttonAddGame.Secondary = false;
+ buttonAddGame.Size = new Size(103, 25);
+ buttonAddGame.TabIndex = 60;
+ buttonAddGame.Text = "Add Game";
+ buttonAddGame.UseVisualStyleBackColor = false;
+ //
+ // panelGamesHeader
+ //
+ panelGamesHeader.BackColor = SystemColors.ControlLight;
+ panelGamesHeader.Controls.Add(pictureBox1);
+ panelGamesHeader.Controls.Add(buttonAddGame);
+ panelGamesHeader.Controls.Add(labelGames);
+ panelGamesHeader.Dock = DockStyle.Top;
+ panelGamesHeader.Location = new Point(0, 97);
+ panelGamesHeader.Margin = new Padding(2);
+ panelGamesHeader.Name = "panelGamesHeader";
+ panelGamesHeader.Size = new Size(446, 30);
+ panelGamesHeader.TabIndex = 61;
+ //
+ // pictureBox1
+ //
+ pictureBox1.BackgroundImage = Properties.Resources.icons8_software_32_white;
+ pictureBox1.BackgroundImageLayout = ImageLayout.Zoom;
+ pictureBox1.Location = new Point(3, 8);
+ pictureBox1.Margin = new Padding(2);
+ pictureBox1.Name = "pictureBox1";
+ pictureBox1.Size = new Size(16, 16);
+ pictureBox1.TabIndex = 35;
+ pictureBox1.TabStop = false;
+ //
+ // labelGames
+ //
+ labelGames.AutoSize = true;
+ labelGames.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point);
+ labelGames.Location = new Point(22, 8);
+ labelGames.Margin = new Padding(4, 0, 4, 0);
+ labelGames.Name = "labelGames";
+ labelGames.Size = new Size(45, 15);
+ labelGames.TabIndex = 34;
+ labelGames.Text = "Games";
+ //
+ // tableLayoutGames
+ //
+ tableLayoutGames.AutoSize = true;
+ tableLayoutGames.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ tableLayoutGames.ColumnCount = 4;
+ tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
+ tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
+ tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
+ tableLayoutGames.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
+ tableLayoutGames.Controls.Add(buttonGameDummy, 0, 0);
+ tableLayoutGames.Dock = DockStyle.Top;
+ tableLayoutGames.Location = new Point(0, 127);
+ tableLayoutGames.Margin = new Padding(4, 2, 4, 2);
+ tableLayoutGames.Name = "tableLayoutGames";
+ tableLayoutGames.RowCount = 7;
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableLayoutGames.Size = new Size(446, 420);
+ tableLayoutGames.TabIndex = 62;
+ //
+ // buttonGameDummy
+ //
+ buttonGameDummy.AccessibleName = "DPI Setting 4";
+ buttonGameDummy.Activated = false;
+ buttonGameDummy.AutoSize = true;
+ buttonGameDummy.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ buttonGameDummy.BackColor = SystemColors.ControlLightLight;
+ buttonGameDummy.BorderColor = Color.LightGreen;
+ buttonGameDummy.BorderRadius = 5;
+ buttonGameDummy.Dock = DockStyle.Fill;
+ buttonGameDummy.FlatAppearance.BorderSize = 0;
+ buttonGameDummy.FlatStyle = FlatStyle.Flat;
+ buttonGameDummy.ForeColor = SystemColors.ControlText;
+ buttonGameDummy.ImageAlign = ContentAlignment.BottomCenter;
+ buttonGameDummy.Location = new Point(2, 2);
+ buttonGameDummy.Margin = new Padding(2);
+ buttonGameDummy.Name = "buttonGameDummy";
+ buttonGameDummy.Secondary = false;
+ buttonGameDummy.Size = new Size(107, 56);
+ buttonGameDummy.TabIndex = 7;
+ buttonGameDummy.Text = "Genshin Impact\r\n60FPS\r\n";
+ buttonGameDummy.TextImageRelation = TextImageRelation.ImageAboveText;
+ buttonGameDummy.UseVisualStyleBackColor = false;
+ buttonGameDummy.Visible = false;
+ //
+ // AutoTDPUI
+ //
+ AutoScaleDimensions = new SizeF(7F, 15F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(446, 547);
+ Controls.Add(tableLayoutGames);
+ Controls.Add(panelGamesHeader);
+ Controls.Add(panelLightingContent);
+ Controls.Add(panelPerformanceHeader);
+ FormBorderStyle = FormBorderStyle.FixedSingle;
+ MaximizeBox = false;
+ MinimizeBox = false;
+ Name = "AutoTDPUI";
+ ShowIcon = false;
+ Text = "Auto TDP Settings";
+ panelPerformanceHeader.ResumeLayout(false);
+ panelPerformanceHeader.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureKeyboard).EndInit();
+ panelLightingContent.ResumeLayout(false);
+ panelGamesHeader.ResumeLayout(false);
+ panelGamesHeader.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit();
+ tableLayoutGames.ResumeLayout(false);
+ tableLayoutGames.PerformLayout();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+ private Panel panelPerformanceHeader;
+ private PictureBox pictureKeyboard;
+ private Label labelGlobalSettings;
+ private Panel panelLightingContent;
+ private TableLayoutPanel tableLayoutGames;
+ private UI.RButton rButton1;
+ private CheckBox checkBoxEnabled;
+ private UI.RComboBox comboBoxLightingMode;
+ private Label labelLimiter;
+ private UI.RComboBox comboBoxFPSSource;
+ private Label labelFPSSource;
+ private RComboBox comboBoxLimiter;
+ private RButton buttonAddGame;
+ private Panel panelGamesHeader;
+ private PictureBox pictureBox1;
+ private Label labelGames;
+ private RButton buttonGameDummy;
+ }
+}
\ No newline at end of file
diff --git a/app/AutoTDP/AutoTDPUI.cs b/app/AutoTDP/AutoTDPUI.cs
new file mode 100644
index 00000000..525cb299
--- /dev/null
+++ b/app/AutoTDP/AutoTDPUI.cs
@@ -0,0 +1,222 @@
+using GHelper.AutoTDP.FramerateSource;
+using GHelper.UI;
+using Ryzen;
+
+namespace GHelper.AutoTDP
+{
+ public partial class AutoTDPUI : RForm
+ {
+ private AutoTDPGameProfileUI? profileUI;
+ public AutoTDPUI()
+ {
+ InitializeComponent();
+
+ InitTheme();
+
+ checkBoxEnabled.CheckedChanged += CheckBoxEnabled_CheckedChanged;
+ buttonAddGame.Click += ButtonAddGame_Click;
+
+ comboBoxLimiter.DropDownClosed += ComboBoxLimiter_DropDownClosed;
+
+ comboBoxFPSSource.DropDownClosed += ComboBoxFPSSource_DropDownClosed;
+
+ Shown += AutoTDPUI_Shown;
+
+ VisualizeGeneralSettings();
+ VizualizeGameList();
+ }
+
+ private void ComboBoxFPSSource_DropDownClosed(object? sender, EventArgs e)
+ {
+ if ((comboBoxFPSSource.SelectedItem as string).StartsWith("Riva"))
+ {
+ AppConfig.Set("auto_tdp_fps_source", "rtss");
+ }
+ }
+
+ private void ComboBoxLimiter_DropDownClosed(object? sender, EventArgs e)
+ {
+ if ((comboBoxLimiter.SelectedItem as string).StartsWith("Intel"))
+ {
+ AppConfig.Set("auto_tdp_limiter", "intel_msr");
+ }
+
+
+ if ((comboBoxLimiter.SelectedItem as string).StartsWith("ASUS ACPI"))
+ {
+ AppConfig.Set("auto_tdp_limiter", "asus_acpi");
+ }
+ }
+
+ private void CheckBoxEnabled_CheckedChanged(object? sender, EventArgs e)
+ {
+ AppConfig.Set("auto_tdp_enabled", checkBoxEnabled.Checked ? 1 : 0);
+
+ if (Program.autoTDPService.IsEnabled())
+ {
+ Program.autoTDPService.Start();
+ }
+ else
+ {
+ Program.autoTDPService.Shutdown();
+ }
+ }
+
+ private void ButtonAddGame_Click(object? sender, EventArgs e)
+ {
+ string? path = null;
+ Thread t = new Thread(() =>
+ {
+ OpenFileDialog ofd = new OpenFileDialog();
+ ofd.Filter = "Executables (*.exe)|*.exe";
+
+ if (ofd.ShowDialog() == DialogResult.OK)
+ {
+ path = ofd.FileName;
+ }
+ });
+
+ t.SetApartmentState(ApartmentState.STA);
+ t.Start();
+ t.Join();
+
+ if (path is null)
+ {
+ //User did not select a file
+ return;
+ }
+
+ GameProfile p = new GameProfile();
+ p.ProcessName = Path.GetFileName(path);
+ p.GameTitle = Path.GetFileName(path);
+ p.Enabled = true;
+ p.TargetFPS = 60;
+ p.MaxTdp = 40;
+ p.MinTdp = 20;
+
+ profileUI = new AutoTDPGameProfileUI(p, this);
+ profileUI.TopMost = true;
+ profileUI.FormClosed += ProfileUI_FormClosed;
+ profileUI.Show();
+ }
+
+ private void ProfileUI_FormClosed(object? sender, FormClosedEventArgs e)
+ {
+ profileUI = null;
+ }
+
+ private void AutoTDPUI_Shown(object? sender, EventArgs e)
+ {
+ if (Height > Program.settingsForm.Height)
+ {
+ Top = Program.settingsForm.Top + Program.settingsForm.Height - Height;
+ }
+ else
+ {
+ Top = Program.settingsForm.Top;
+ }
+
+ Left = Program.settingsForm.Left - Width - 5;
+ }
+
+ private void VisualizeGeneralSettings()
+ {
+ checkBoxEnabled.Checked = AppConfig.Get("auto_tdp_enabled", 0) == 1;
+
+ if (!RyzenControl.IsAMD())
+ comboBoxLimiter.Items.Add("Intel MSR Power Limiter");
+
+
+ comboBoxLimiter.Items.Add("ASUS ACPI Power Limiter");
+
+ string? limiter = AppConfig.GetString("auto_tdp_limiter");
+
+ if (comboBoxLimiter.Items.Count > 0 && limiter is null)
+ comboBoxLimiter.SelectedIndex = 0;
+
+ if (!RyzenControl.IsAMD() && limiter is not null && limiter.Equals("intel_msr"))
+ {
+ comboBoxLimiter.SelectedIndex = 0;
+ }
+
+ if (limiter is not null && limiter.Equals("asus_acpi"))
+ {
+ comboBoxLimiter.SelectedIndex = !RyzenControl.IsAMD() ? 1 : 0;
+ }
+
+
+
+ if (RTSSFramerateSource.IsAvailable())
+ comboBoxFPSSource.Items.Add("Riva Tuner Statistics Server");
+
+
+ string? source = AppConfig.GetString("auto_tdp_fps_source", null);
+
+ if (comboBoxFPSSource.Items.Count > 0 && source is null)
+ comboBoxFPSSource.SelectedIndex = 0;
+
+ if (source is not null && source.Equals("rtss"))
+ {
+ comboBoxFPSSource.SelectedIndex = 0;
+ }
+ }
+
+
+ private void VizualizeGameList()
+ {
+ //Due to my lousy skills in UI design, the game table is limited to 7x4 games.
+ buttonAddGame.Enabled = Program.autoTDPService.GameProfiles.Count < 7 * 4;
+
+ tableLayoutGames.Controls.Clear();
+
+ foreach (GameProfile gp in Program.autoTDPService.GameProfiles)
+ {
+ RButton bt = new RButton();
+ bt.Text = gp.GameTitle + "\n" + gp.TargetFPS + " FPS";
+
+ bt.Dock = DockStyle.Fill;
+ bt.FlatStyle = FlatStyle.Flat;
+ bt.FlatAppearance.BorderColor = RForm.borderMain;
+ bt.UseVisualStyleBackColor = false;
+ bt.AutoSize = true;
+ bt.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ bt.BackColor = RForm.buttonMain;
+ bt.ForeColor = RForm.foreMain;
+ bt.Click += Bt_Click;
+ bt.Tag = gp;
+
+ tableLayoutGames.Controls.Add(bt);
+ }
+ }
+
+ private void Bt_Click(object? sender, EventArgs e)
+ {
+ GameProfile gp = (GameProfile)((RButton)sender).Tag;
+ profileUI = new AutoTDPGameProfileUI(gp, this);
+ profileUI.TopMost = true;
+ profileUI.FormClosed += ProfileUI_FormClosed;
+ profileUI.Show();
+ }
+
+ public void DeleteGameProfile(GameProfile gp)
+ {
+ if (Program.autoTDPService.IsGameInList(gp.ProcessName))
+ {
+ Program.autoTDPService.GameProfiles.Remove(gp);
+ }
+
+ Program.autoTDPService.SaveGameProfiles();
+ VizualizeGameList();
+ }
+
+ public void UpdateGameProfile(GameProfile gp)
+ {
+ if (!Program.autoTDPService.IsGameInList(gp.ProcessName))
+ {
+ Program.autoTDPService.GameProfiles.Add(gp);
+ }
+ Program.autoTDPService.SaveGameProfiles();
+ VizualizeGameList();
+ }
+ }
+}
diff --git a/app/AutoTDP/AutoTDPUI.resx b/app/AutoTDP/AutoTDPUI.resx
new file mode 100644
index 00000000..af32865e
--- /dev/null
+++ b/app/AutoTDP/AutoTDPUI.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/app/AutoTDP/FramerateSource/IFramerateSource.cs b/app/AutoTDP/FramerateSource/IFramerateSource.cs
new file mode 100644
index 00000000..3bf7aa85
--- /dev/null
+++ b/app/AutoTDP/FramerateSource/IFramerateSource.cs
@@ -0,0 +1,17 @@
+namespace GHelper.AutoTDP.FramerateSource
+{
+
+ internal class GameInstance
+ {
+ public string? ProcessName { get; set; }
+
+ public int ProcessID { get; set; }
+ }
+
+ internal interface IFramerateSource
+ {
+ public double GetFramerate(string processName);
+
+ public List GetRunningGames();
+ }
+}
diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs
new file mode 100644
index 00000000..e70ab6ac
--- /dev/null
+++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using RTSSSharedMemoryNET;
+
+namespace GHelper.AutoTDP.FramerateSource
+{
+ internal class RTSSFramerateSource : IFramerateSource
+ {
+ private static Process? rtssInstance;
+
+ private static OSD? osd;
+
+ public static string RTSSPath { get; set; }
+
+
+ public static bool IsRunning => Process.GetProcessesByName("RTSS").Length != 0;
+
+
+ static RTSSFramerateSource()
+ {
+ RTSSPath = @"C:\Program Files (x86)\RivaTuner Statistics Server\RTSS.exe";
+ }
+
+ public static bool IsAvailable()
+ {
+ return File.Exists(RTSSPath);
+
+ }
+
+ public static void Start()
+ {
+ if ((rtssInstance == null || rtssInstance.HasExited) && !IsRunning && File.Exists(RTSSPath))
+ {
+ try
+ {
+ rtssInstance = Process.Start(RTSSPath);
+ Thread.Sleep(2000); // If it works, don't touch it
+ }
+ catch (Exception exc)
+ {
+ Logger.WriteLine("Could not start RTSS Service" + exc.Message);
+ }
+
+ RunOSD();
+ }
+ else
+ {
+ RunOSD();
+ }
+ }
+
+ public List GetRunningGames()
+ {
+ if (!IsRunning)
+ {
+ return new List();
+ }
+
+ List giL = new List();
+
+ foreach (AppEntry appEntry in OSD.GetAppEntries())
+ {
+ GameInstance i = new GameInstance();
+ i.ProcessID = appEntry.ProcessId;
+ i.ProcessName = appEntry.Name;
+
+ giL.Add(i);
+ }
+
+ return giL;
+ }
+
+ public double GetFramerate(string processName)
+ {
+ if (!IsRunning)
+ {
+ return -1.0d;
+ }
+
+ try
+ {
+ var appE = OSD.GetAppEntries()
+ .Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.Name.EndsWith(processName));
+ if (appE is null)
+ return -1.0d;
+
+ return (double)appE.StatFrameTimeBufFramerate / 10;
+ }
+ catch (InvalidDataException)
+ {
+ }
+ catch (FileNotFoundException)
+ {
+ }
+
+ return -1.0d;
+ }
+
+ public static void RunOSD()
+ {
+ if (osd == null)
+ {
+ try
+ {
+ osd = new OSD("GHELPER");
+ }
+ catch (Exception exc)
+ {
+ Logger.WriteLine("Could not start OSD" + exc.Message);
+ }
+ }
+ }
+
+ public static void Stop()
+ {
+ if (rtssInstance != null && !rtssInstance.HasExited)
+ {
+ try
+ {
+ rtssInstance.Kill();
+ rtssInstance = null;
+ var proc = Process.GetProcessesByName("RTSSHooksLoader64");
+ proc[0].Kill();
+ }
+ catch (Exception)
+ {
+ // Ignored
+ }
+ }
+ }
+ }
+}
diff --git a/app/AutoTDP/GameProfile.cs b/app/AutoTDP/GameProfile.cs
new file mode 100644
index 00000000..c0165592
--- /dev/null
+++ b/app/AutoTDP/GameProfile.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GHelper.AutoTDP
+{
+ public class GameProfile
+ {
+ public string GameTitle { get; set; }
+ public string ProcessName { get; set; }
+ public int TargetFPS { get; set; }
+ public int MinTdp { get; set; }
+ public int MaxTdp { get; set; }
+ public bool Enabled { get; set; }
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs
new file mode 100644
index 00000000..4c5ab738
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GHelper.AutoTDP.PowerLimiter
+{
+ internal class ASUSACPIPowerLimiter : IPowerLimiter
+ {
+
+ private int DefaultA0 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0);
+ private int DefaultA3 = Program.acpi.DeviceGet(AsusACPI.PPT_APUA0);
+
+ public void SetCPUPowerLimit(int watts)
+ {
+ if (Program.acpi.DeviceGet(AsusACPI.PPT_APUA0) >= 0)
+ {
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, watts, "PowerLimit A3");
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, watts, "PowerLimit A0");
+ }
+ }
+
+
+ public int GetCPUPowerLimit()
+ {
+ return Program.acpi.DeviceGet(AsusACPI.PPT_APUA0);
+ }
+
+ public void Dispose()
+ {
+ //Nothing to dispose here
+ }
+
+ public void ResetPowerLimits()
+ {
+ //Load limits that were set before the limiter engaged
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, DefaultA0, "PowerLimit A3");
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, DefaultA3, "PowerLimit A0");
+ }
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs
new file mode 100644
index 00000000..05e01996
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs
@@ -0,0 +1,12 @@
+namespace GHelper.AutoTDP.PowerLimiter
+{
+ internal interface IPowerLimiter : IDisposable
+ {
+ public void SetCPUPowerLimit(int watts);
+
+ public int GetCPUPowerLimit();
+
+ public void ResetPowerLimits();
+
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs
new file mode 100644
index 00000000..5d93ae5b
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs
@@ -0,0 +1,106 @@
+using HidSharp.Reports.Units;
+using Ryzen;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GHelper.AutoTDP.PowerLimiter
+{
+ internal class IntelMSRPowerLimiter : IPowerLimiter
+ {
+ private Ols ols;
+
+ private uint DefaultEax = 0; // Set on first reading
+ private uint DefaultEdx = 0;
+
+ //Lower 14 bits are the power limits
+ private uint PL1_MASK = 0x3FFF;
+ private uint PL2_MASK = 0x3FFF;
+
+ //The power unit factor (Default is 0.125 for most Intel CPUs).
+ private double PowerUnit = 0x0;
+
+ public IntelMSRPowerLimiter()
+ {
+ ols = new Ols();
+ ols.InitializeOls();
+ ReadPowerUnit();
+ }
+
+ public void ReadPowerUnit()
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ ols.Rdmsr(0x606, ref eax, ref edx);
+
+
+ uint pwr = eax & 0x03;
+
+ PowerUnit = 1 / Math.Pow(2, pwr);
+ }
+
+ public void SetCPUPowerLimit(int watts)
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+
+ ols.Rdmsr(0x610, ref eax, ref edx);
+
+ uint watsRapl = (uint)(watts / PowerUnit);
+
+ //Set limits for both PL1 and PL2
+ uint eaxFilterd = eax & ~PL1_MASK;
+ uint edxFilterd = edx & ~PL2_MASK;
+
+ eaxFilterd |= watsRapl;
+ edxFilterd |= watsRapl;
+
+ //Enable clamping
+ eaxFilterd |= 0x8000;
+ edxFilterd |= 0x8000;
+
+ ols.Wrmsr(0x610, eaxFilterd, edxFilterd);
+ }
+
+
+ public int GetCPUPowerLimit()
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ ols.Rdmsr(0x610, ref eax, ref edx);
+
+ if (DefaultEax == 0)
+ {
+ //Store default settings to reset them on exit
+ DefaultEax = eax;
+ DefaultEdx = edx;
+ }
+
+ uint pl1 = eax & PL1_MASK;
+ uint pl2 = edx & PL2_MASK;
+
+ return (int)(pl1 * PowerUnit);
+ }
+
+
+ public void ResetPowerLimits()
+ {
+ if (DefaultEax == 0)
+ {
+ return;
+ }
+ ols.Wrmsr(0x610, DefaultEax, DefaultEdx);
+ }
+
+ public void Dispose()
+ {
+ ols.DeinitializeOls();
+ ols.Dispose();
+ }
+ }
+}
diff --git a/app/GHelper.csproj b/app/GHelper.csproj
index 56011b39..656a5a4d 100644
--- a/app/GHelper.csproj
+++ b/app/GHelper.csproj
@@ -88,6 +88,14 @@
+
+
+ RTSSSharedMemoryNET.dll
+ PreserveNewest
+ true
+
+
+
True
diff --git a/app/GHelper.sln b/app/GHelper.sln
index 0b4abb97..64effb6e 100644
--- a/app/GHelper.sln
+++ b/app/GHelper.sln
@@ -14,18 +14,24 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|Any CPU.ActiveCfg = Debug|x64
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|Any CPU.Build.0 = Debug|x64
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x64.ActiveCfg = Debug|Any CPU
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x64.Build.0 = Debug|Any CPU
+ {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Debug|x86.Build.0 = Debug|Any CPU
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|Any CPU.ActiveCfg = Release|x64
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|Any CPU.Build.0 = Release|x64
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x64.ActiveCfg = Release|x64
{D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x64.Build.0 = Release|x64
+ {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x86.ActiveCfg = Release|Any CPU
+ {D6138BB1-8FDB-4835-87EF-2FE41A3DD604}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/app/Program.cs b/app/Program.cs
index 7cd18809..2f07fda8 100644
--- a/app/Program.cs
+++ b/app/Program.cs
@@ -1,4 +1,5 @@
using GHelper.Ally;
+using GHelper.AutoTDP;
using GHelper.Battery;
using GHelper.Display;
using GHelper.Gpu;
@@ -46,6 +47,8 @@ namespace GHelper
private static PowerLineStatus isPlugged = SystemInformation.PowerStatus.PowerLineStatus;
+ public static AutoTDPService autoTDPService = new AutoTDPService();
+
// The main entry point for the application
public static void Main(string[] args)
{
@@ -306,6 +309,7 @@ namespace GHelper
static void OnExit(object sender, EventArgs e)
{
+ autoTDPService.Shutdown();
trayIcon.Visible = false;
PeripheralsProvider.UnregisterForDeviceEvents();
clamshellControl.UnregisterDisplayEvents();
diff --git a/app/RTSSSharedMemoryNET.dll b/app/RTSSSharedMemoryNET.dll
new file mode 100644
index 00000000..37197eaa
Binary files /dev/null and b/app/RTSSSharedMemoryNET.dll differ
diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs
index e08037a2..6e2f9ed0 100644
--- a/app/Settings.Designer.cs
+++ b/app/Settings.Designer.cs
@@ -67,6 +67,8 @@ namespace GHelper
labelCPUFan = new Label();
panelGPU = new Panel();
labelTipGPU = new Label();
+ tableAdditionalGPUFeature = new TableLayoutPanel();
+ buttonAutoTDP = new RButton();
tableAMD = new TableLayoutPanel();
buttonOverlay = new RButton();
buttonFPS = new RButton();
@@ -143,6 +145,7 @@ namespace GHelper
panelCPUTitle.SuspendLayout();
((System.ComponentModel.ISupportInitialize)picturePerf).BeginInit();
panelGPU.SuspendLayout();
+ tableAdditionalGPUFeature.SuspendLayout();
tableAMD.SuspendLayout();
tableGPU.SuspendLayout();
panelGPUTitle.SuspendLayout();
@@ -723,6 +726,7 @@ namespace GHelper
panelGPU.AutoSize = true;
panelGPU.AutoSizeMode = AutoSizeMode.GrowAndShrink;
panelGPU.Controls.Add(labelTipGPU);
+ panelGPU.Controls.Add(tableAdditionalGPUFeature);
panelGPU.Controls.Add(tableAMD);
panelGPU.Controls.Add(tableGPU);
panelGPU.Controls.Add(panelGPUTitle);
@@ -745,6 +749,46 @@ namespace GHelper
labelTipGPU.Size = new Size(787, 36);
labelTipGPU.TabIndex = 20;
//
+ // tableAdditionalGPUFeature
+ //
+ tableAdditionalGPUFeature.AutoSize = true;
+ tableAdditionalGPUFeature.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ tableAdditionalGPUFeature.ColumnCount = 3;
+ tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalGPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalGPUFeature.Controls.Add(buttonAutoTDP, 0, 0);
+ tableAdditionalGPUFeature.Dock = DockStyle.Top;
+ tableAdditionalGPUFeature.Location = new Point(10, 198);
+ tableAdditionalGPUFeature.Margin = new Padding(2, 1, 2, 1);
+ tableAdditionalGPUFeature.Name = "tableAdditionalGPUFeature";
+ tableAdditionalGPUFeature.RowCount = 1;
+ tableAdditionalGPUFeature.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableAdditionalGPUFeature.Size = new Size(392, 60);
+ tableAdditionalGPUFeature.TabIndex = 25;
+ //
+ // buttonAutoTDP
+ //
+ buttonAutoTDP.Activated = false;
+ buttonAutoTDP.BackColor = SystemColors.ControlLightLight;
+ buttonAutoTDP.BorderColor = Color.Transparent;
+ buttonAutoTDP.BorderRadius = 5;
+ buttonAutoTDP.Dock = DockStyle.Fill;
+ buttonAutoTDP.FlatAppearance.BorderSize = 0;
+ buttonAutoTDP.FlatStyle = FlatStyle.Flat;
+ buttonAutoTDP.ForeColor = SystemColors.ControlText;
+ buttonAutoTDP.Image = Properties.Resources.icons8_processor_32;
+ buttonAutoTDP.ImageAlign = ContentAlignment.MiddleRight;
+ buttonAutoTDP.Location = new Point(1, 1);
+ buttonAutoTDP.Margin = new Padding(1, 1, 1, 1);
+ buttonAutoTDP.Name = "buttonAutoTDP";
+ buttonAutoTDP.Secondary = false;
+ buttonAutoTDP.Size = new Size(128, 38);
+ buttonAutoTDP.TabIndex = 11;
+ buttonAutoTDP.Text = "Auto TDP";
+ buttonAutoTDP.TextImageRelation = TextImageRelation.ImageBeforeText;
+ buttonAutoTDP.UseVisualStyleBackColor = false;
+ //
// tableAMD
//
tableAMD.AutoSize = true;
@@ -1790,6 +1834,7 @@ namespace GHelper
((System.ComponentModel.ISupportInitialize)picturePerf).EndInit();
panelGPU.ResumeLayout(false);
panelGPU.PerformLayout();
+ tableAdditionalGPUFeature.ResumeLayout(false);
tableAMD.ResumeLayout(false);
tableGPU.ResumeLayout(false);
panelGPUTitle.ResumeLayout(false);
@@ -1931,5 +1976,7 @@ namespace GHelper
private Label labelGammaTitle;
private CheckBox checkMatrixLid;
private Panel panelMatrixAuto;
+ private TableLayoutPanel tableAdditionalGPUFeature;
+ private RButton buttonAutoTDP;
}
}
diff --git a/app/Settings.cs b/app/Settings.cs
index b5c34a81..3355764a 100644
--- a/app/Settings.cs
+++ b/app/Settings.cs
@@ -1,5 +1,6 @@
using GHelper.Ally;
using GHelper.AnimeMatrix;
+using GHelper.AutoTDP;
using GHelper.AutoUpdate;
using GHelper.Battery;
using GHelper.Display;
@@ -28,6 +29,7 @@ namespace GHelper
AutoUpdateControl updateControl;
AsusMouseSettings? mouseSettings;
+ AutoTDPUI? autoTdpUi;
public AniMatrixControl matrixControl;
@@ -244,6 +246,8 @@ namespace GHelper
buttonFPS.Click += ButtonFPS_Click;
buttonOverlay.Click += ButtonOverlay_Click;
+ buttonAutoTDP.Click += ButtonAutoTDP_Click;
+
Text = "G-Helper " + (ProcessHelper.IsUserAdministrator() ? "—" : "-") + " " + AppConfig.GetModelShort();
TopMost = AppConfig.Is("topmost");
@@ -261,6 +265,32 @@ namespace GHelper
panelPerformance.Focus();
}
+ private void ButtonAutoTDP_Click(object? sender, EventArgs e)
+ {
+ autoTdpUi = new AutoTDPUI();
+ autoTdpUi.TopMost = true;
+ autoTdpUi.FormClosed += AutoTdpUi_FormClosed;
+ autoTdpUi.Disposed += AutoTdpUi_Disposed;
+ if (!autoTdpUi.IsDisposed)
+ {
+ autoTdpUi.Show();
+ }
+ else
+ {
+ autoTdpUi = null;
+ }
+ }
+
+ private void AutoTdpUi_Disposed(object? sender, EventArgs e)
+ {
+ autoTdpUi = null;
+ }
+
+ private void AutoTdpUi_FormClosed(object? sender, FormClosedEventArgs e)
+ {
+ autoTdpUi = null;
+ }
+
private void SliderGamma_ValueChanged(object? sender, EventArgs e)
{
screenControl.SetGamma(sliderGamma.Value);
@@ -1081,6 +1111,7 @@ namespace GHelper
private void ButtonQuit_Click(object? sender, EventArgs e)
{
+ Program.autoTDPService.Shutdown();
matrixControl.Dispose();
Close();
Program.trayIcon.Visible = false;