diff --git a/app/AppConfig.cs b/app/AppConfig.cs
index 98be6f6bd..68b4c001d 100644
--- a/app/AppConfig.cs
+++ b/app/AppConfig.cs
@@ -1,3 +1,4 @@
+using GHelper;
using GHelper.Mode;
using System.Management;
using System.Text.Json;
@@ -575,7 +576,17 @@ public static bool NoAutoUltimate()
public static bool IsManualModeRequired()
{
- if (!IsMode("auto_apply_power"))
+ bool isAutoTDPActive = false;
+
+
+ if (Program.autoTDPService is not null)
+ {
+ //If using ASUS ACPI and AutoTDP is running right now, ignore current power mode as the auto tdp power settings are temporary
+ isAutoTDPActive = AppConfig.GetString("auto_tdp_limiter", "").Equals("asus_acpi") && Program.autoTDPService.IsActive();
+ }
+
+
+ if (!IsMode("auto_apply_power") || isAutoTDPActive)
return false;
return
diff --git a/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs
new file mode 100644
index 000000000..7688fd6ba
--- /dev/null
+++ b/app/AutoTDP/AutoTDPGameProfileUI.Designer.cs
@@ -0,0 +1,404 @@
+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();
+ panelGameSettings = new Panel();
+ sliderCheckInterval = new UI.Slider();
+ numericCheckInterval = new NumericUpDown();
+ labelCheckInterval = new Label();
+ sliderFPS = new UI.Slider();
+ labelMinTDP = new Label();
+ labelMaxTDP = new Label();
+ sliderMaxTDP = new UI.Slider();
+ numericUpDownFPS = new NumericUpDown();
+ sliderMinTDP = new UI.Slider();
+ labelMaxTDPText = new Label();
+ labeMinTDPText = new Label();
+ labelTargetFPS = 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();
+ panelGameSettings.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)numericCheckInterval).BeginInit();
+ ((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;
+ //
+ // panelGameSettings
+ //
+ panelGameSettings.AutoSize = true;
+ panelGameSettings.Controls.Add(sliderCheckInterval);
+ panelGameSettings.Controls.Add(numericCheckInterval);
+ panelGameSettings.Controls.Add(labelCheckInterval);
+ panelGameSettings.Controls.Add(sliderFPS);
+ panelGameSettings.Controls.Add(labelMinTDP);
+ panelGameSettings.Controls.Add(labelMaxTDP);
+ panelGameSettings.Controls.Add(sliderMaxTDP);
+ panelGameSettings.Controls.Add(numericUpDownFPS);
+ panelGameSettings.Controls.Add(sliderMinTDP);
+ panelGameSettings.Controls.Add(labelMaxTDPText);
+ panelGameSettings.Controls.Add(labeMinTDPText);
+ panelGameSettings.Controls.Add(labelTargetFPS);
+ panelGameSettings.Controls.Add(textBoxTitle);
+ panelGameSettings.Controls.Add(textBoxProcessName);
+ panelGameSettings.Controls.Add(labelFPSSource);
+ panelGameSettings.Controls.Add(labelLimiter);
+ panelGameSettings.Dock = DockStyle.Top;
+ panelGameSettings.Location = new Point(0, 30);
+ panelGameSettings.Margin = new Padding(2);
+ panelGameSettings.Name = "panelGameSettings";
+ panelGameSettings.Padding = new Padding(0, 0, 0, 7);
+ panelGameSettings.Size = new Size(386, 174);
+ panelGameSettings.TabIndex = 57;
+ //
+ // sliderCheckInterval
+ //
+ sliderCheckInterval.AccessibleName = "DPI Slider";
+ sliderCheckInterval.Location = new Point(140, 145);
+ sliderCheckInterval.Margin = new Padding(2);
+ sliderCheckInterval.Max = 5000;
+ sliderCheckInterval.Min = 16;
+ sliderCheckInterval.Name = "sliderCheckInterval";
+ sliderCheckInterval.Size = new Size(168, 20);
+ sliderCheckInterval.Step = 1;
+ sliderCheckInterval.TabIndex = 71;
+ sliderCheckInterval.TabStop = false;
+ sliderCheckInterval.Text = "FPS Slider";
+ sliderCheckInterval.Value = 500;
+ //
+ // numericCheckInterval
+ //
+ numericCheckInterval.BorderStyle = BorderStyle.None;
+ numericCheckInterval.Location = new Point(312, 145);
+ numericCheckInterval.Margin = new Padding(2);
+ numericCheckInterval.Maximum = new decimal(new int[] { 5000, 0, 0, 0 });
+ numericCheckInterval.Minimum = new decimal(new int[] { 16, 0, 0, 0 });
+ numericCheckInterval.Name = "numericCheckInterval";
+ numericCheckInterval.Size = new Size(65, 19);
+ numericCheckInterval.TabIndex = 70;
+ numericCheckInterval.TextAlign = HorizontalAlignment.Center;
+ numericCheckInterval.Value = new decimal(new int[] { 500, 0, 0, 0 });
+ //
+ // labelCheckInterval
+ //
+ labelCheckInterval.Location = new Point(5, 142);
+ labelCheckInterval.Margin = new Padding(4, 0, 4, 0);
+ labelCheckInterval.Name = "labelCheckInterval";
+ labelCheckInterval.Size = new Size(129, 22);
+ labelCheckInterval.TabIndex = 69;
+ labelCheckInterval.Text = "Check Interval (ms):";
+ labelCheckInterval.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // sliderFPS
+ //
+ sliderFPS.AccessibleName = "DPI Slider";
+ sliderFPS.Location = new Point(140, 66);
+ sliderFPS.Margin = new Padding(2);
+ sliderFPS.Max = 1000;
+ sliderFPS.Min = 20;
+ sliderFPS.Name = "sliderFPS";
+ sliderFPS.Size = new Size(168, 20);
+ sliderFPS.Step = 1;
+ sliderFPS.TabIndex = 68;
+ sliderFPS.TabStop = false;
+ sliderFPS.Text = "FPS Slider";
+ sliderFPS.Value = 60;
+ //
+ // 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 = "Max TDP Slider";
+ sliderMaxTDP.Value = 0;
+ //
+ // numericUpDownFPS
+ //
+ numericUpDownFPS.BorderStyle = BorderStyle.None;
+ numericUpDownFPS.Location = new Point(312, 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(65, 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 = "Min TDP Slider";
+ sliderMinTDP.Value = 0;
+ //
+ // labelMaxTDPText
+ //
+ labelMaxTDPText.Location = new Point(5, 117);
+ labelMaxTDPText.Margin = new Padding(4, 0, 4, 0);
+ labelMaxTDPText.Name = "labelMaxTDPText";
+ labelMaxTDPText.Size = new Size(129, 22);
+ labelMaxTDPText.TabIndex = 61;
+ labelMaxTDPText.Text = "Max TDP:";
+ labelMaxTDPText.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // labeMinTDPText
+ //
+ labeMinTDPText.Location = new Point(5, 91);
+ labeMinTDPText.Margin = new Padding(4, 0, 4, 0);
+ labeMinTDPText.Name = "labeMinTDPText";
+ labeMinTDPText.Size = new Size(129, 22);
+ labeMinTDPText.TabIndex = 62;
+ labeMinTDPText.Text = "Min TDP:";
+ labeMinTDPText.TextAlign = ContentAlignment.MiddleLeft;
+ //
+ // labelTargetFPS
+ //
+ labelTargetFPS.Location = new Point(5, 63);
+ labelTargetFPS.Margin = new Padding(4, 0, 4, 0);
+ labelTargetFPS.Name = "labelTargetFPS";
+ labelTargetFPS.Size = new Size(129, 22);
+ labelTargetFPS.TabIndex = 60;
+ labelTargetFPS.Text = "Target FPS:";
+ labelTargetFPS.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(278, 225);
+ 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(3, 225);
+ 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, 263);
+ Controls.Add(buttonDelete);
+ Controls.Add(buttonSave);
+ Controls.Add(panelGameSettings);
+ 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();
+ panelGameSettings.ResumeLayout(false);
+ panelGameSettings.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)numericCheckInterval).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numericUpDownFPS).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private Panel panelPerformanceHeader;
+ private PictureBox pictureKeyboard;
+ private Label labelSettings;
+ private CheckBox checkBoxEnabled;
+ private Panel panelGameSettings;
+ private Label labelFPSSource;
+ private Label labelLimiter;
+ private TextBox textBoxTitle;
+ private TextBox textBoxProcessName;
+ private UI.RButton buttonSave;
+ private Label labelMaxTDPText;
+ private Label labeMinTDPText;
+ private Label labelTargetFPS;
+ private UI.Slider sliderMinTDP;
+ private UI.Slider sliderMaxTDP;
+ private NumericUpDown numericUpDownFPS;
+ private Label labelMinTDP;
+ private Label labelMaxTDP;
+ private UI.RButton buttonDelete;
+ private UI.Slider sliderFPS;
+ private UI.Slider sliderCheckInterval;
+ private NumericUpDown numericCheckInterval;
+ private Label labelCheckInterval;
+ }
+}
\ No newline at end of file
diff --git a/app/AutoTDP/AutoTDPGameProfileUI.cs b/app/AutoTDP/AutoTDPGameProfileUI.cs
new file mode 100644
index 000000000..41db4f70d
--- /dev/null
+++ b/app/AutoTDP/AutoTDPGameProfileUI.cs
@@ -0,0 +1,124 @@
+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;
+
+ sliderFPS.ValueChanged += SliderFPS_ValueChanged;
+ numericUpDownFPS.ValueChanged += NumericUpDownFPS_ValueChanged;
+
+ sliderCheckInterval.ValueChanged += SliderCheckInterval_ValueChanged;
+ numericCheckInterval.ValueChanged += NumericCheckInterval_ValueChanged;
+
+ InitTheme();
+
+
+ Shown += AutoTDPGameProfileUI_Shown;
+
+ VisualizeGameProfile();
+ }
+
+ private void NumericCheckInterval_ValueChanged(object? sender, EventArgs e)
+ {
+ sliderCheckInterval.Value = (int)numericCheckInterval.Value;
+ }
+
+ private void SliderCheckInterval_ValueChanged(object? sender, EventArgs e)
+ {
+ numericCheckInterval.Value = sliderCheckInterval.Value;
+ }
+
+ private void NumericUpDownFPS_ValueChanged(object? sender, EventArgs e)
+ {
+ sliderFPS.Value = (int)numericUpDownFPS.Value;
+ }
+
+ private void SliderFPS_ValueChanged(object? sender, EventArgs e)
+ {
+ numericUpDownFPS.Value = sliderFPS.Value;
+ }
+
+ 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;
+ GameProfile.Interval = ((int)numericCheckInterval.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()
+ {
+ if (GameProfile.Interval < sliderCheckInterval.Min || GameProfile.Interval > sliderCheckInterval.Max)
+ {
+ GameProfile.Interval = AutoTDPService.INTERVAL_FPS_CHECK;
+ }
+
+ sliderMinTDP.Value = GameProfile.MinTdp;
+ sliderMaxTDP.Value = GameProfile.MaxTdp;
+ numericUpDownFPS.Value = GameProfile.TargetFPS;
+ textBoxProcessName.Text = GameProfile.ProcessName;
+ textBoxTitle.Text = GameProfile.GameTitle;
+ checkBoxEnabled.Checked = GameProfile.Enabled;
+ sliderCheckInterval.Value = GameProfile.Interval;
+ numericCheckInterval.Value = GameProfile.Interval;
+ }
+ }
+}
diff --git a/app/AutoTDP/AutoTDPGameProfileUI.resx b/app/AutoTDP/AutoTDPGameProfileUI.resx
new file mode 100644
index 000000000..af32865ec
--- /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/AutoTDPLogger.cs b/app/AutoTDP/AutoTDPLogger.cs
new file mode 100644
index 000000000..76660ed80
--- /dev/null
+++ b/app/AutoTDP/AutoTDPLogger.cs
@@ -0,0 +1,39 @@
+using System.Diagnostics;
+
+public static class AutoTDPLogger
+{
+ public static string appPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\GHelper";
+ public static string logFile = appPath + "\\auto_tdp_log.txt";
+
+ public static void WriteLine(string logMessage)
+ {
+ Debug.WriteLine(logMessage);
+ if (!Directory.Exists(appPath)) Directory.CreateDirectory(appPath);
+
+ try
+ {
+ using (StreamWriter w = File.AppendText(logFile))
+ {
+ w.WriteLine($"{DateTime.Now}: {logMessage}");
+ w.Close();
+ }
+ }
+ catch { }
+
+ if (new Random().Next(100) == 1) Cleanup();
+
+
+ }
+
+ public static void Cleanup()
+ {
+ try
+ {
+ var file = File.ReadAllLines(logFile);
+ int skip = Math.Max(0, file.Count() - 5000);
+ File.WriteAllLines(logFile, file.Skip(skip).ToArray());
+ }
+ catch { }
+ }
+
+}
diff --git a/app/AutoTDP/AutoTDPService.cs b/app/AutoTDP/AutoTDPService.cs
new file mode 100644
index 000000000..5050c0946
--- /dev/null
+++ b/app/AutoTDP/AutoTDPService.cs
@@ -0,0 +1,733 @@
+using System.Text.Json;
+using GHelper.AutoTDP.FramerateSource;
+using GHelper.AutoTDP.PowerLimiter;
+using Ryzen;
+
+namespace GHelper.AutoTDP
+{
+ internal class AutoTDPService : IDisposable
+ {
+
+ private static readonly bool LOG_AUTO_TDP = true;
+ private static readonly int INTERVAL_MIN_CHECK = 15 * 1_000;
+ private static readonly int INTERVAL_APP_CHECK = 5_000;
+ public static readonly int INTERVAL_FPS_CHECK = 500;
+
+ private static readonly int INTERVAL_LOG = 1_000;
+ private int LogCounter = 0;
+
+ 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 = 6;
+
+ private List FramerateLog = new List();
+
+ private double LowestTDP;
+ private double LowestStableTDP;
+ private long LowestStableStability = 0;
+ private int LowestStableChecks = 0;
+ private double CurrentTDP;
+ private double LastAdjustment;
+ private double LastAdjustmentTotal = 0;
+ private int LastAdjustmentsWithoutImprovement = 0;
+
+ private GameInstance? currentGame = null;
+ private GameProfile? currentGameProfile = null;
+
+ 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 static bool IsAvailable()
+ {
+
+ if (AppConfig.IsAlly())
+ {
+ //Not yet supported
+ return false;
+ }
+
+ return AvailablePowerLimiters().Count > 0 && AvailableFramerateSources().Count > 0;
+ }
+
+ private int FPSCheckInterval()
+ {
+ if (powerLimiter is null)
+ {
+ return INTERVAL_FPS_CHECK;
+ }
+
+ int interval = INTERVAL_FPS_CHECK;
+
+ if (currentGame is not null && currentGameProfile is not null && currentGameProfile.Interval > 0)
+ {
+ interval = currentGameProfile.Interval;
+ }
+
+ return (int)Math.Max(interval, powerLimiter.GetMinInterval());
+ }
+
+ public static List AvailableFramerateSources()
+ {
+ List l = new List();
+
+ if (RTSSFramerateSource.IsAvailable()) l.Add("rtss");
+
+ Logger.WriteLine("[AutoTDPService] Available Framerate Sources: " + string.Join(", ", l.ToArray()));
+ return l;
+ }
+
+
+ public static List AvailablePowerLimiters()
+ {
+ List l = new List();
+
+ if (IntelMSRPowerLimiter.IsAvailable()) l.Add("intel_msr");
+
+ if (ASUSACPIPowerLimiter.IsAvailable()) l.Add("asus_acpi");
+
+
+ Logger.WriteLine("[AutoTDPService] Available Power Limiters: " + string.Join(", ", l.ToArray()));
+
+ return l;
+ }
+
+ public void SwapPowerLimiter()
+ {
+ IPowerLimiter? ipl = powerLimiter;
+ powerLimiter = null;
+
+ if (ipl is not null)
+ {
+ ipl.ResetPowerLimits();
+ ipl.Dispose();
+ }
+
+
+ InitLimiter();
+
+ if (powerLimiter is not null && IsActive())
+ {
+ powerLimiter.SavePowerLimits();
+ powerLimiter.Prepare();
+ }
+
+ }
+
+ public void Start()
+ {
+ if (!IsEnabled() || IsRunning() || !IsAvailable())
+ {
+ Logger.WriteLine("[AutoTDPService] Refusing startup. Stats: Enabled: " + IsEnabled() + ", Running: " + IsRunning() + " ,Available: " + IsAvailable());
+ 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)
+ {
+ Logger.WriteLine("[AutoTDPService] No source defined in settings. Using Default");
+ }
+ else
+ {
+ Logger.WriteLine("[AutoTDPService] Framerate Source Setting: " + source);
+ }
+
+ if ((source is null || source.Equals("rtss")) && RTSSFramerateSource.IsAvailable())
+ {
+ Logger.WriteLine("[AutoTDPService] Initializing RTSSFramerateSource...");
+ RTSSFramerateSource rtss = new RTSSFramerateSource();
+ RTSSFramerateSource.Start();
+ framerateSouce = rtss;
+ return;
+ }
+ }
+
+ public void InitLimiter()
+ {
+ string? limiter = AppConfig.GetString("auto_tdp_limiter");
+
+ if (limiter is null)
+ {
+ Logger.WriteLine("[AutoTDPService] No limiter defined in settings. Using Default");
+ }
+ else
+ {
+ Logger.WriteLine("[AutoTDPService] Limiter Setting: " + limiter);
+ }
+
+ if (limiter is null || (limiter.Equals("asus_acpi") && ASUSACPIPowerLimiter.IsAvailable()))
+ {
+ Logger.WriteLine("[AutoTDPService] Initializing ASUSACPIPowerLimiter...");
+ powerLimiter = new ASUSACPIPowerLimiter();
+ return;
+ }
+
+ if (limiter is not null && limiter.Equals("intel_msr") && IntelMSRPowerLimiter.IsAvailable())
+ {
+ Logger.WriteLine("[AutoTDPService] Initializing IntelMSRPowerLimiter...");
+ powerLimiter = new IntelMSRPowerLimiter();
+ return;
+ }
+ }
+
+ public void SaveGameProfiles()
+ {
+ string json = JsonSerializer.Serialize(GameProfiles, new JsonSerializerOptions { WriteIndented = true });
+
+ File.WriteAllText(GameProfileFile, json);
+ }
+
+ public void SortGameProfiles()
+ {
+ GameProfiles.Sort();
+ }
+
+
+ public void LoadGameProfiles()
+ {
+ if (!File.Exists(GameProfileFile))
+ {
+ if (GameProfiles is null) GameProfiles = new List();
+ 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)
+ {
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.WriteLine("[AutoTDPService] No games detected");
+ return;
+ }
+
+ foreach (GameInstance gi in runningGames)
+ {
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.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, bool checkForEnabled = true)
+ {
+ if (processName is null)
+ {
+ return null;
+ }
+
+ foreach (GameProfile gp in GameProfiles)
+ {
+ if (gp.ProcessName is not null && processName.EndsWith(gp.ProcessName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ if (!gp.Enabled && checkForEnabled)
+ {
+ return null;
+ }
+ return gp;
+ }
+ }
+
+ return null;
+ }
+
+ public bool IsGameInList(String? processName, bool checkForEnabled = true)
+ {
+ return ProfileForGame(processName, checkForEnabled) is not null;
+ }
+
+
+ public void HandleGame(GameInstance instance)
+ {
+ if (currentGame is not null)
+ {
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.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;
+ currentGameProfile = null;
+ GameFPSPrevious = double.NaN;
+ GameFPS = 0;
+ LastAdjustmentsWithoutImprovement = 0;
+ LastAdjustment = 0.0;
+ FramerateLog = new List();
+ FramerateTargetReachedCounter = 0;
+ FramerateDipCounter = 0;
+ LowestStableStability = 0;
+ LowestStableChecks = 0;
+ LogCounter = 0;
+
+ if (powerLimiter is not null)
+ {
+ powerLimiter.ResetPowerLimits();
+ CurrentTDP = powerLimiter.GetCPUPowerLimit();
+ }
+ }
+
+ public void StartGameHandler(GameInstance instance)
+ {
+ currentGameProfile = ProfileForGame(instance.ProcessName);
+ if (currentGameProfile is null || powerLimiter is null || framerateSouce is null)
+ {
+ return;
+ }
+
+ Logger.WriteLine("[AutoTDPService] Start handling game: " + instance.ProcessName + " PID: " + instance.ProcessID);
+
+ tdpThread = new Thread(() =>
+ {
+ CurrentTDP = powerLimiter.GetCPUPowerLimit();
+ powerLimiter.SavePowerLimits(); // save current power limits to restore them afterwards
+
+ powerLimiter.Prepare();
+
+ Logger.WriteLine("[AutoTDPService] Backing up Power limit: " + CurrentTDP + "W");
+
+ LowestStableTDP = currentGameProfile.MaxTdp;
+ LowestTDP = currentGameProfile.MaxTdp;
+
+ while (currentGame is not null && Running)
+ {
+
+ if (!currentGameProfile.Enabled)
+ {
+ //Game was disabled during session. Stop AutoTDP
+ Logger.WriteLine("[AutoTDPService] Game profile disabled: " + currentGameProfile.GameTitle + ". Disengaging.");
+ Reset();
+ return;
+ }
+
+ double fps = framerateSouce.GetFramerate(instance);
+
+ if (LOG_AUTO_TDP && LogCounter * FPSCheckInterval() > INTERVAL_LOG)
+ AutoTDPLogger.WriteLine("[AutoTDPService] (" + instance.ProcessName + ") Framerate " + GameFPS);
+
+ if (fps < 0.0d)
+ {
+ //Game is not running anymore or RTSS lost its hook
+ Logger.WriteLine("[AutoTDPService] Game exited: " + instance.ProcessName + " PID: " + instance.ProcessID);
+ Reset();
+ return;
+ }
+
+ //prevent FPS from going to 0 which causes issues with the math
+ GameFPS = Math.Max(5, fps);
+ AdjustPowerLimit(currentGameProfile);
+
+ try
+ {
+ Thread.Sleep(FPSCheckInterval());
+ }
+ 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)
+ {
+ //Framerate is inside ideal range
+ FramerateTargetReachedCounter++;
+
+ if (FramerateTargetReachedCounter >= 3
+ && FramerateTargetReachedCounter < FPSDipHistorySize
+ && targetFPS - 0.5 <= FramerateLog.Take(3).Average()
+ && FramerateLog.Take(3).Average() - 0.05 <= targetFPS)
+ {
+ //short dip
+ FramerateDipCounter++;
+ FramerateUnstable();
+
+ correction = targetFPS + 0.25 - currentFramerate;
+ }
+ else if (FramerateDipCounter >= 2
+ && targetFPS - 0.5 <= FramerateLog.Average()
+ && FramerateLog.Average() - 0.1 <= targetFPS)
+ {
+ //long dip
+ correction = targetFPS + 0.45 - currentFramerate;
+ FramerateTargetReachedCounter = FPSDipHistorySize;
+ FramerateVeryUnstable();
+ }
+ else
+ {
+ FramerateStable();
+ }
+ }
+ else
+ {
+ //Framerate not in target range
+ correction = 0.0;
+ FramerateTargetReachedCounter = 0;
+ FramerateDipCounter = 0;
+ FramerateStable();
+ }
+
+ ProcessStability();
+ return correction;
+ }
+
+ private void FramerateStable()
+ {
+ LowestStableStability++;
+ }
+
+ private void FramerateUnstable()
+ {
+ LowestStableStability -= 30;
+ }
+ private void FramerateVeryUnstable()
+ {
+ LowestStableStability = -10;
+ }
+
+ private bool Stabilize()
+ {
+ return LowestStableChecks * FPSCheckInterval() > INTERVAL_MIN_CHECK;
+ }
+
+ private void ProcessStability()
+ {
+ if (!Stabilize()) LowestStableChecks++;
+
+ if (LowestStableStability < 0 && Stabilize())
+ {
+ //If unstable for too often increase lowest stable TDP
+ LowestStableTDP += 1;
+ LowestTDP += 1;
+ LowestStableStability = 0;
+ return;
+ }
+
+ if (CurrentTDP > LowestStableTDP - 0.1 && CurrentTDP < LowestStableTDP + 0.1 && Stabilize())
+ {
+ LowestStableStability++;
+
+ //Stable for at least 120s
+ if (LowestStableStability * FPSCheckInterval() > (120 * 1_000))
+ {
+ //if stable for long time try to reduce it again
+ LowestStableTDP = currentGameProfile.MaxTdp;
+ LowestStableStability = 0;
+ }
+ }
+
+ if (LowestTDP - 0.25 <= CurrentTDP && CurrentTDP <= LowestTDP + 0.25)
+ {
+ LowestStableStability++;
+
+ if (LowestStableStability * FPSCheckInterval() > (5 * 1_000) && Stabilize())
+ {
+ LowestStableTDP = LowestTDP + (LowestTDP * 0.10); // Add 10% additional wattage to get a smoother framerate
+ }
+
+ }
+
+ if (CurrentTDP < LowestTDP - 0.1 && LowestStableStability > 0)
+ {
+ LowestStableStability = 0;
+ LowestTDP = CurrentTDP;
+ }
+
+ LowestStableStability = Math.Min(LowestStableStability, (125 * 1_000) / FPSCheckInterval());
+ }
+
+
+ private double TDPDamper(double currentFramerate)
+ {
+ if (double.IsNaN(GameFPSPrevious)) GameFPSPrevious = currentFramerate;
+ double dF = -0.12d;
+
+ // PID Compute
+ double deltaError = currentFramerate - GameFPSPrevious;
+ double dT = deltaError / (1020.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;
+ }
+
+ //For the case that someone thinks they're super smart and sets the max lower than the min, we treat them as equal and consider the MAX to be the fixed TDP.
+ if (profile.MaxTdp <= profile.MinTdp)
+ {
+ if (CurrentTDP == profile.MaxTdp)
+ {
+ return;
+ }
+ powerLimiter?.SetCPUPowerLimit(profile.MaxTdp);
+ CurrentTDP = profile.MaxTdp;
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.WriteLine("[AutoTDPService] Power Limit fixed at " + profile.MaxTdp + "W");
+
+ return;
+ }
+
+ double newPL = CurrentTDP;
+ double fpsCorrection = FPSDipCorrection(GameFPS, profile.GetTDPFPS());
+ double delta = profile.GetTDPFPS() - GameFPS - fpsCorrection - 1;
+ delta = Math.Clamp(delta, -15, 15);
+
+
+ double adjustment = (delta * CurrentTDP / GameFPS) * 0.7;
+ //Dampen the changes to not change TDP too aggressively which would cause performance issues
+ adjustment += TDPDamper(GameFPS);
+
+ adjustment = Math.Min(adjustment, (CurrentTDP * 0.1));
+
+ if (GameFPSPrevious > profile.GetTDPFPS() && GameFPS < profile.GetTDPFPS() && FPSCheckInterval() <= 250)
+ {
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.WriteLine("[AutoTDPService] Single Dip, Ignore");
+ //single dip. Ignore
+ return;
+ }
+
+ if (LastAdjustment > 0 && GameFPS <= GameFPSPrevious + 0.1 && adjustment > 0)
+ {
+ LastAdjustmentsWithoutImprovement++;
+ LastAdjustmentTotal += adjustment;
+
+ //Wait for 10 consecutive power increases and at least 5W increased TDP before judging that increasing power does nothing.
+ if (LastAdjustmentsWithoutImprovement >= 10 && LastAdjustmentTotal > 5)
+ {
+ //Do not adjust if increasing power does not improve framerate.
+ if (LOG_AUTO_TDP)
+ AutoTDPLogger.WriteLine("[AutoTDPService] Not adjusting because no improvement from last increase");
+ return;
+ }
+
+ }
+ else
+ {
+ LastAdjustmentsWithoutImprovement = 0;
+ LastAdjustmentTotal = 0;
+ }
+
+ newPL += adjustment;
+ LastAdjustment = adjustment;
+
+ //Respect the limits that the user chose
+ newPL = Math.Clamp(newPL, profile.MinTdp, profile.MaxTdp);
+
+ if (newPL < LowestStableTDP && LowestStableTDP < profile.MaxTdp - 1)
+ {
+ newPL = LowestStableTDP;
+ }
+
+ if (LOG_AUTO_TDP)
+ {
+ if (LogCounter * FPSCheckInterval() > INTERVAL_LOG)
+ {
+ LogCounter = 0;
+ AutoTDPLogger.WriteLine("[AutoTDPService] Power Limit from " + CurrentTDP + "W to " + newPL + "W, Delta:" + adjustment
+ + " Lowest: " + LowestTDP + "W, Lowest Stable(" + LowestStableStability + "): " + LowestStableTDP + "W");
+ }
+ else
+ {
+ LogCounter++;
+ }
+ }
+
+
+ //Apply power limits
+ powerLimiter?.SetCPUPowerLimit(newPL);
+ CurrentTDP = newPL;
+ }
+
+ public void StopGameHandler()
+ {
+ if (tdpThread is not null)
+ {
+ currentGame = null;
+ currentGameProfile = null;
+ tdpThread.Join();
+ tdpThread = null;
+ }
+
+ }
+
+ public void Shutdown()
+ {
+ Running = false;
+ currentGame = null;
+ currentGameProfile = null;
+
+ 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;
+ }
+
+ Reset();
+
+ //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 000000000..0b48a2b1b
--- /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 000000000..811abbae0
--- /dev/null
+++ b/app/AutoTDP/AutoTDPUI.cs
@@ -0,0 +1,233 @@
+using GHelper.AutoTDP.FramerateSource;
+using GHelper.AutoTDP.PowerLimiter;
+using GHelper.UI;
+using Ryzen;
+using System.Linq;
+
+namespace GHelper.AutoTDP
+{
+ public partial class AutoTDPUI : RForm
+ {
+
+ private Dictionary ModeTexts = new Dictionary();
+
+
+ private AutoTDPGameProfileUI? profileUI;
+ public AutoTDPUI()
+ {
+ InitializeComponent();
+
+ InitTheme();
+
+
+ ModeTexts.Add("intel_msr", "Intel MSR Power Limiter");
+ ModeTexts.Add("asus_acpi", "ASUS ACPI Power Limiter");
+ ModeTexts.Add("rtss", "Riva Tuner Statistics Server");
+
+ 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)
+ {
+ AppConfig.Set("auto_tdp_fps_source", AutoTDPService.AvailableFramerateSources().ElementAt(comboBoxFPSSource.SelectedIndex));
+
+ }
+
+ private void ComboBoxLimiter_DropDownClosed(object? sender, EventArgs e)
+ {
+ string? limiter = AppConfig.GetString("auto_tdp_limiter");
+ string newLimiter = AutoTDPService.AvailablePowerLimiters().ElementAt(comboBoxLimiter.SelectedIndex);
+
+ AppConfig.Set("auto_tdp_limiter", newLimiter);
+
+ if (!newLimiter.Equals(limiter))
+ {
+ Program.autoTDPService.SwapPowerLimiter();
+ }
+ }
+
+ 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, *.dat)|*.exe; *.dat";
+
+ 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).Replace(".exe", "").Replace(".dat", "");
+ p.Enabled = true;
+ p.TargetFPS = 60;
+ p.MaxTdp = 40;
+ p.MinTdp = 15;
+ p.Interval = AutoTDPService.INTERVAL_FPS_CHECK;
+
+ profileUI = new AutoTDPGameProfileUI(p, this);
+ profileUI.FormClosed += ProfileUI_FormClosed;
+ profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost;
+ profileUI.ShowDialog(this);
+ }
+
+ 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;
+
+ comboBoxLimiter.Items.Clear();
+ comboBoxFPSSource.Items.Clear();
+
+ foreach (string s in AutoTDPService.AvailablePowerLimiters())
+ {
+ comboBoxLimiter.Items.Add(ModeTexts[s]);
+ }
+
+ foreach (string s in AutoTDPService.AvailableFramerateSources())
+ {
+ comboBoxFPSSource.Items.Add(ModeTexts[s]);
+ }
+
+
+ string? limiter = AppConfig.GetString("auto_tdp_limiter", null);
+ string? source = AppConfig.GetString("auto_tdp_fps_source", null);
+
+
+ if (limiter is not null && AutoTDPService.AvailablePowerLimiters().Contains(limiter))
+ {
+ comboBoxLimiter.SelectedIndex = AutoTDPService.AvailablePowerLimiters().IndexOf(limiter);
+ }
+ else
+ {
+ comboBoxLimiter.SelectedIndex = 0;
+ }
+
+ if (source is not null && AutoTDPService.AvailableFramerateSources().Contains(source))
+ {
+ comboBoxFPSSource.SelectedIndex = AutoTDPService.AvailableFramerateSources().IndexOf(source);
+ }
+ else
+ {
+ 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.FormClosed += ProfileUI_FormClosed;
+ profileUI.TopMost = AppConfig.Is("topmost") || this.TopMost;
+ profileUI.ShowDialog(this);
+ }
+
+ public void DeleteGameProfile(GameProfile gp)
+ {
+ if (Program.autoTDPService.IsGameInList(gp.ProcessName, false))
+ {
+ Program.autoTDPService.GameProfiles.Remove(gp);
+ }
+
+ Program.autoTDPService.SortGameProfiles();
+ Program.autoTDPService.SaveGameProfiles();
+ VizualizeGameList();
+ }
+
+ public void UpdateGameProfile(GameProfile gp)
+ {
+ if (!Program.autoTDPService.IsGameInList(gp.ProcessName, false))
+ {
+ Program.autoTDPService.GameProfiles.Add(gp);
+ }
+ Program.autoTDPService.SortGameProfiles();
+ Program.autoTDPService.SaveGameProfiles();
+ VizualizeGameList();
+ }
+ }
+}
diff --git a/app/AutoTDP/AutoTDPUI.resx b/app/AutoTDP/AutoTDPUI.resx
new file mode 100644
index 000000000..af32865ec
--- /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 000000000..85e647255
--- /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(GameInstance instance);
+
+ public List GetRunningGames();
+ }
+}
diff --git a/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs
new file mode 100644
index 000000000..1e5f6cec6
--- /dev/null
+++ b/app/AutoTDP/FramerateSource/RTSSFramerateSource.cs
@@ -0,0 +1,166 @@
+using System.Diagnostics;
+using GHelper.Helpers;
+using Microsoft.Win32;
+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;
+
+
+ private static string GetInstallPath()
+ {
+ try
+ {
+ return (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Unwinder\\RTSS", "InstallPath", null);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+
+ static RTSSFramerateSource()
+ {
+ string path = GetInstallPath();
+
+ if (path is not null)
+ {
+ RTSSPath = path;
+ }
+ else
+ {
+ RTSSPath = @"C:\Program Files (x86)\RivaTuner Statistics Server\RTSS.exe";
+ }
+ }
+
+ public static bool IsAvailable()
+ {
+ if (IsRunning)
+ {
+ return true;
+ }
+
+ return File.Exists(RTSSPath) && ProcessHelper.IsUserAdministrator();
+ }
+
+ 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)
+ {
+ Start();
+ }
+
+ List giL = new List();
+ try
+ {
+ foreach (AppEntry appEntry in OSD.GetAppEntries())
+ {
+ GameInstance i = new GameInstance();
+ i.ProcessID = appEntry.ProcessId;
+ i.ProcessName = appEntry.Name;
+
+ giL.Add(i);
+ }
+ }
+ catch (InvalidDataException)
+ {
+ }
+ catch (FileNotFoundException)
+ {
+ }
+
+ return giL;
+ }
+
+ public double GetFramerate(GameInstance instance)
+ {
+ if (!IsRunning)
+ {
+ return -1.0d;
+ }
+
+ try
+ {
+ var appE = OSD.GetAppEntries().FirstOrDefault(a => a.ProcessId == instance.ProcessID);
+ 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 000000000..b3ed552ad
--- /dev/null
+++ b/app/AutoTDP/GameProfile.cs
@@ -0,0 +1,24 @@
+
+namespace GHelper.AutoTDP
+{
+ public class GameProfile : IComparable
+ {
+ 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; }
+ public int Interval { get; set; }
+
+ public int CompareTo(GameProfile? other)
+ {
+ return GameTitle.CompareTo(other?.GameTitle);
+ }
+
+ public int GetTDPFPS()
+ {
+ return TargetFPS - 1;
+ }
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs
new file mode 100644
index 000000000..cd4cb6ed0
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/ASUSACPIPowerLimiter.cs
@@ -0,0 +1,81 @@
+using GHelper.Mode;
+
+namespace GHelper.AutoTDP.PowerLimiter
+{
+ internal class ASUSACPIPowerLimiter : IPowerLimiter
+ {
+
+ private bool allAmd;
+ private bool fPPT;
+
+ private int lastPowerLimit = 0;
+
+ public ASUSACPIPowerLimiter()
+ {
+ allAmd = Program.acpi.IsAllAmdPPT();
+ fPPT = Program.acpi.DeviceGet(AsusACPI.PPT_APUC1) >= 0;
+ }
+
+ public static bool IsAvailable()
+ {
+ return AppConfig.IsASUS();
+ }
+ public int GetMinInterval()
+ {
+ return 250;
+ }
+
+ public void Prepare()
+ {
+ //Program.modeControl.AutoFans(AppConfig.IsManualModeRequired() || AppConfig.IsFanRequired());
+ Program.modeControl.SetPerformanceMode(Modes.GetCurrent());
+ }
+
+ public void SavePowerLimits()
+ {
+ }
+
+ public void SetCPUPowerLimit(double watts)
+ {
+
+ int pl = (int)Math.Ceiling(watts);
+
+ if (lastPowerLimit == pl)
+ {
+ //Do not set the same limit twice to reduce load on the ACPI driver
+ return;
+ }
+
+ if (allAmd) // CPU limit all amd models
+ {
+ Program.acpi.DeviceSet(AsusACPI.PPT_CPUB0, pl, "PowerLimit B0");
+ }
+ else
+ {
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA3, pl, "PowerLimit A3");
+ Program.acpi.DeviceSet(AsusACPI.PPT_APUA0, pl, "PowerLimit A0");
+ if (fPPT) Program.acpi.DeviceSet(AsusACPI.PPT_APUC1, pl, "PowerLimit C1");
+ }
+
+ lastPowerLimit = pl;
+ }
+
+
+ // You can't read PPTs on ASUS :) endpoints just return 0 if they are available
+ public int GetCPUPowerLimit()
+ {
+ return 200;
+ }
+
+ public void Dispose()
+ {
+ //Nothing to dispose here
+ }
+
+ // Correct Asus way to reset everything, is just to set mode again
+ public void ResetPowerLimits()
+ {
+ Program.modeControl.SetPerformanceMode(Modes.GetCurrent());
+ }
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/IPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs
new file mode 100644
index 000000000..919990f08
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/IPowerLimiter.cs
@@ -0,0 +1,18 @@
+namespace GHelper.AutoTDP.PowerLimiter
+{
+ internal interface IPowerLimiter : IDisposable
+ {
+ public void SetCPUPowerLimit(double watts);
+
+ public int GetCPUPowerLimit();
+
+ public void ResetPowerLimits();
+
+ public void SavePowerLimits();
+
+ public int GetMinInterval();
+
+ public void Prepare();
+
+ }
+}
diff --git a/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs
new file mode 100644
index 000000000..3acf187e9
--- /dev/null
+++ b/app/AutoTDP/PowerLimiter/IntelMSRPowerLimiter.cs
@@ -0,0 +1,86 @@
+using GHelper.Helpers;
+using GHelper.Intel;
+using Ryzen;
+
+namespace GHelper.AutoTDP.PowerLimiter
+{
+
+ internal class IntelMSRPowerLimiter : IPowerLimiter
+ {
+
+ uint backupPl1 = 0;
+ uint backupPl2 = 0;
+
+ public IntelMSRPowerLimiter()
+ {
+ IntelCoreControl.LogCurrentState();
+ }
+
+ public int GetMinInterval()
+ {
+ return 33;
+ }
+
+ public void Prepare()
+ {
+
+ }
+
+ public static bool IsAvailable()
+ {
+ if (!ProcessHelper.IsUserAdministrator() || !IntelCoreControl.IsIntel())
+ {
+ return false;
+ }
+
+ Ols o = new Ols();
+
+ o.InitializeOls();
+
+ uint err = o.GetDllStatus();
+ o.DeinitializeOls();
+ o.Dispose();
+
+ if (err == (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void SavePowerLimits()
+ {
+ backupPl1 = IntelCoreControl.GetPL1();
+ backupPl2 = IntelCoreControl.GetPL2();
+ }
+
+
+ public void SetCPUPowerLimit(double watts)
+ {
+ IntelCoreControl.SetPowerLimits((uint)Math.Ceiling(watts), (uint)Math.Ceiling(watts), clampPl1: true, clampPl2: true);
+ }
+
+
+ public int GetCPUPowerLimit()
+ {
+ Logger.WriteLine("[AutoTDPService] Read Power Limit - PL1: " + IntelCoreControl.GetPL1() + "W, PL2: " + IntelCoreControl.GetPL2() + "W");
+
+ return (int)IntelCoreControl.GetPL1();
+ }
+
+
+ public void ResetPowerLimits()
+ {
+ if (backupPl1 == 0)
+ {
+ return;
+ }
+ IntelCoreControl.SetPowerLimits(backupPl1, backupPl2);
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/app/GHelper.csproj b/app/GHelper.csproj
index a622d18d7..e478b41a9 100644
--- a/app/GHelper.csproj
+++ b/app/GHelper.csproj
@@ -76,6 +76,14 @@
+
+
+ RTSSSharedMemoryNET.dll
+ PreserveNewest
+ true
+
+
+
True
diff --git a/app/GHelper.sln b/app/GHelper.sln
index 0b4abb975..64effb6ea 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/Intel/IntelCoreControl.cs b/app/Intel/IntelCoreControl.cs
new file mode 100644
index 000000000..8ba966fce
--- /dev/null
+++ b/app/Intel/IntelCoreControl.cs
@@ -0,0 +1,457 @@
+using GHelper.Helpers;
+using NvAPIWrapper.Native.GPU;
+using NvAPIWrapper.Native.GPU.Structures;
+using Ryzen;
+using System;
+
+namespace GHelper.Intel
+{
+ internal class IntelCoreControl
+ {
+ public static readonly uint MSR_PKG_POWER_LIMIT = 0x610;
+ public static readonly uint MSR_RAPL_POWER_UNIT = 0x606;
+
+ public static readonly uint INTEL_PACKAGE_RAPL_LIMIT_0_0_0_MCHBAR_PCU = 0x59a0;
+
+
+ //Lower 14 bits are the power limits
+ private static readonly uint PL1_MASK = 0x3FFF;
+ private static readonly uint PL2_MASK = 0x3FFF;
+
+ private static double POWER_UNIT = 0;
+ private static double TIME_UNIT = 0;
+
+ public static bool IsIntel()
+ {
+ return !RyzenControl.IsAMD(); // There is no GHeloper supported ASUS laptop that has a CPU which is neither AMD nor Intel. Therefore, no need to re-invent the wheel.
+ }
+
+ public static void Initialize()
+ {
+ if (!ProcessHelper.IsUserAdministrator() || RyzenControl.IsAMD())
+ {
+ //Only for Admins and not for AMD CPUs.
+ return;
+ }
+
+ if (POWER_UNIT > 0 && TIME_UNIT > 0)
+ {
+ return;
+ }
+
+ Ols ols = new Ols();
+ ols.InitializeOls();
+
+ POWER_UNIT = GetPowerRAPLUnit(ols);
+ TIME_UNIT = GetTimeUnit(ols);
+
+ ols.DeinitializeOls();
+ ols.Dispose();
+ }
+
+ private static void LogOLSState(uint err)
+ {
+ switch (err)
+ {
+ case (uint)Ols.OlsDllStatus.OLS_DLL_NO_ERROR:
+ return;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DRIVER_NOT_LOADED");
+ break;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_UNSUPPORTED_PLATFORM:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_UNSUPPORTED_PLATFORM");
+ break;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_FOUND:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_NOT_FOUND");
+ break;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_UNLOADED:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_UNLOADED");
+ break;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK");
+ break;
+ case (uint)Ols.OlsDllStatus.OLS_DLL_UNKNOWN_ERROR:
+ Logger.WriteLine("[IntelCoreControl] Intel MSR Error: OLS_DLL_UNKNOWN_ERROR");
+ break;
+ }
+ }
+
+ /*
+ * Due to Winring0 shipped with GHelper not implementing readphysicalmemory, we cannot fix the MMIO.
+ * Therefore, Intel Dynamic Tuning can lower the power limits below what we configure.
+ * However, it cannot set the limits higher than we set them in the MSRs.
+ *
+ * This function is incomplete due to testing not possible
+ *
+ * See: https://github.com/horshack-dpreview/setPL/blob/master/setPL.sh for how to erase the MMIO to let MSR take full control over power limits.
+ public static unsafe void MMIOFix()
+ {
+
+ Ols ols = new Ols();
+ ols.InitializeOls();
+
+ uint mchbar = ols.ReadPciConfigDword(0x00, 0x48);
+
+ bool enabled = (((mchbar & 0x1) != 0));
+
+ //Remove enabled bit to get phyiscal address
+ mchbar = (uint)((mchbar & ~1));
+
+
+ uint raplLimitAddr = ((mchbar + INTEL_PACKAGE_RAPL_LIMIT_0_0_0_MCHBAR_PCU));
+
+ byte[] buffer = new byte[4];
+
+ fixed (byte* pBuf = buffer)
+ {
+ ols.ReadPhysicalMemory(raplLimitAddr + 0, pBuf, 4, 1);
+ }
+
+ byte[] buffer2 = new byte[4];
+
+ fixed (byte* pBuf = buffer2)
+ {
+ ols.ReadPhysicalMemory(raplLimitAddr + 4, pBuf, 4, 1);
+ }
+
+
+ ols.DeinitializeOls();
+ ols.Dispose();
+
+ }
+ */
+
+ public static void LogCurrentState()
+ {
+ uint pl1 = GetPL1();
+ uint pl2 = GetPL2();
+
+ bool msrLocked = IsMSRLocked();
+ bool pl1Clamped = IsPL1Clamped();
+ bool pl2Clamped = IsPL2Clamped();
+
+ double TAU = GetTAU();
+
+
+ Logger.WriteLine("[IntelCoreControl] MSR Power State: Pl1: " + pl1 + "W " + (pl1Clamped ? "(Clamped)" : "(Unclamped)") + ", PL2: " + pl2 + "W" + (pl2Clamped ? "(Clamped)" : "(Unclamped)") + ", TAU: " + TAU + "s. MSR State: " + (msrLocked ? "Locked" : "Unlocked"));
+
+ }
+
+ private static double GetPowerRAPLUnit(Ols ols)
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0)
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_RAPL_POWER_UNIT");
+ LogOLSState(ols.GetDllStatus());
+ return -1;
+ }
+
+ //Bits 0:3
+ uint pwr = eax & 0x3;
+
+ return 1 / Math.Pow(2, pwr);
+ }
+
+ private static double GetTimeUnit(Ols ols)
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ if (ols.Rdmsr(MSR_RAPL_POWER_UNIT, ref eax, ref edx) == 0)
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_RAPL_POWER_UNIT");
+ LogOLSState(ols.GetDllStatus());
+ return -1;
+ }
+
+ //Bits 16:19
+ uint pwr = (eax & 0xF0000) >> 16;
+
+ return 1 / Math.Pow(2, pwr);
+ }
+
+ public static List AllowedTimeUnits()
+ {
+ Initialize();
+
+ if (TIME_UNIT <= 0)
+ {
+ //Timeunit was invalid
+ return new List();
+ }
+
+ List lst = new List();
+ //y has 5 bits, z has 2 bits.
+ for (int y = 0; y <= 31; ++y)
+ {
+ for (int z = 0; z <= 3; ++z)
+ {
+ //Formula as specified by Intel
+ double val = Math.Pow(2, y) * (1.0 + (z / 4.0)) * TIME_UNIT;
+ lst.Add(val);
+ }
+ }
+
+ return lst;
+ }
+
+ // Forumla 2^Y * (1.0 + Z/4.0) * Time_Unit
+ // See intel specification
+ // Y has 5 bits, Z has 2 bits.
+ private static uint ComputeTimeWindowRAPL(double seconds)
+ {
+ int t = (int)(seconds / TIME_UNIT);
+
+ double r = Math.Log2(t);
+
+ int y = (int)r;
+
+ if (y < 0 || y > 31)
+ {
+ //Value is out of range for bit range
+ }
+
+ int exp = (int)Math.Pow(2, y);
+
+ double fac = t / (double)exp;
+
+ int z = (int)((fac - 1) * 4);
+
+ if (z < 0 || z > 3)
+ {
+ //Value is out of range for bit range
+ }
+
+ uint val = 0;
+
+ //y are the first 5 bits, then z the next 2 bits for a total of 7 bits
+ val |= (uint)y;
+ val |= (uint)(z << 5);
+
+ return val;
+ }
+
+ public static bool ReadPowerMSR(ref uint eax, ref uint edx)
+ {
+ Ols ols = new Ols();
+ ols.InitializeOls();
+
+ bool result = ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 1;
+
+ ols.DeinitializeOls();
+ ols.Dispose();
+
+ return result;
+ }
+
+ public static bool IsMSRLocked()
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return true;
+ }
+
+ //Bit 31 of edx marks whether the MSR is locked and read only or whether it is writeable
+ return (edx >> 31) != 0;
+ }
+
+ public static bool IsPL1Clamped()
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return true;
+ }
+
+ //Bit 16 of eax
+ return (eax >> 16) != 0;
+ }
+
+ public static bool IsPL2Clamped()
+ {
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return true;
+ }
+
+ //Bit 16 of edx
+ return (edx >> 16) != 0;
+ }
+
+ public static uint GetPL2()
+ {
+ Initialize();
+
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return 0;
+ }
+
+ uint pl2 = edx & PL2_MASK;
+
+ return (uint)(pl2 * POWER_UNIT);
+ }
+
+ public static uint GetPL1()
+ {
+ Initialize();
+
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return 0;
+ }
+
+ uint pl1 = eax & PL1_MASK;
+
+ return (uint)(pl1 * POWER_UNIT);
+ }
+
+ public static double GetTAU()
+ {
+ Initialize();
+
+ uint eax = 0;
+ uint edx = 0;
+
+ if (!ReadPowerMSR(ref eax, ref edx))
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ return 0;
+ }
+
+ uint y = (eax & 0x00000000003E0000) >> 17;
+ uint z = (eax & 0x0000000000C00000) >> 22;
+
+
+ double val = Math.Pow(2, y) * (1.0 + (z / 4.0)) * TIME_UNIT;
+
+ return val;
+ }
+
+ ///
+ /// Sets the power limts for the CPU via MSR
+ ///
+ /// Longterm power limit in watts
+ /// Shortterm power limit in watts
+ /// PL2 turbo time limit in seconds. See: AllowedTimeUnits() for possible TAU values
+ /// Allow going below OS-requested P/T state setting during time window of PL1
+ /// Allow going below OS-requested P/T state setting during time window of PL2
+ /// true if changes were applied successfully
+ public static bool SetPowerLimits(uint pl1, uint pl2, double tau = 0, bool clampPl1 = false, bool clampPl2 = false)
+ {
+ Initialize();
+
+ if (POWER_UNIT <= 0 || TIME_UNIT <= 0)
+ {
+ Logger.WriteLine("[IntelCoreControl] POWER_UNIT or TIME_UNIT could not be initialized.");
+ return false;
+ }
+
+ bool status = false;
+
+ Ols ols = new Ols();
+ ols.InitializeOls();
+
+
+ uint eax = 0;
+ uint edx = 0;
+
+
+ if (ols.Rdmsr(MSR_PKG_POWER_LIMIT, ref eax, ref edx) == 0)
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Read MSR_PKG_POWER_LIMIT");
+ LogOLSState(ols.GetDllStatus());
+ ols.DeinitializeOls();
+ ols.Dispose();
+ return false;
+ }
+
+ //Bit 31 of edx marks whether the MSR is locked and read only or whether it is writeable
+ bool msrLocked = (edx >> 31) != 0;
+
+ if (msrLocked)
+ {
+ Logger.WriteLine("[IntelCoreControl] MSR_PKG_POWER_LIMIT is locked. Cannot change power limits");
+ ols.DeinitializeOls();
+ ols.Dispose();
+ return false;
+ }
+
+
+ uint pl1Rapl = (uint)(pl1 / POWER_UNIT);
+ uint pl2Rapl = (uint)(pl2 / POWER_UNIT);
+
+ //Set power limits
+ eax = (eax & ~PL1_MASK) | pl1Rapl;
+ edx = (edx & ~PL1_MASK) | pl2Rapl;
+
+
+ //Set clamp for PL1
+ if (clampPl1)
+ eax |= 0x8000;
+ else
+ eax = eax & ~((uint)1 << 16);
+
+
+ //Set clamp for PL2
+ if (clampPl2)
+ edx |= 0x8000;
+ else
+ edx = edx & ~((uint)1 << 16);
+
+
+ if (tau > 0)
+ {
+ //Set power limit time window for PL2
+ uint tauBits = ComputeTimeWindowRAPL(tau);
+
+ tauBits = tauBits << 17;
+
+ //Clear old tau bits
+ eax = (uint)(eax & (0xFFFFFFFFFF01FFFF));
+
+ //Apply new tau bits
+ eax |= tauBits;
+ }
+
+
+ if (ols.Wrmsr(0x610, eax, edx) == 0)
+ {
+ Logger.WriteLine("[IntelCoreControl] Failed to Write MSR_PKG_POWER_LIMIT");
+ LogOLSState(ols.GetDllStatus());
+ }
+ else
+ {
+ status = true;
+ }
+
+ ols.DeinitializeOls();
+ ols.Dispose();
+
+ return status;
+
+ }
+ }
+}
diff --git a/app/Peripherals/PeripheralsProvider.cs b/app/Peripherals/PeripheralsProvider.cs
index 1e2c23dd1..a25ec5b16 100644
--- a/app/Peripherals/PeripheralsProvider.cs
+++ b/app/Peripherals/PeripheralsProvider.cs
@@ -219,6 +219,7 @@ public static void DetectAllAsusMice()
DetectMouse(new PugioIIWired());
DetectMouse(new StrixImpactII());
DetectMouse(new StrixImpactIIElectroPunk());
+ DetectMouse(new StrixEvolve());
DetectMouse(new Chakram());
DetectMouse(new ChakramWired());
DetectMouse(new ChakramCore());
diff --git a/app/Program.cs b/app/Program.cs
index 916252803..dd3e59ce3 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;
@@ -47,6 +48,8 @@ static class Program
private static PowerLineStatus isPlugged = SystemInformation.PowerStatus.PowerLineStatus;
+ public static AutoTDPService autoTDPService = null;
+
// The main entry point for the application
public static void Main(string[] args)
{
@@ -82,6 +85,7 @@ public static void Main(string[] args)
Logger.WriteLine("Start Count: " + startCount);
acpi = new AsusACPI();
+ autoTDPService = new AutoTDPService();
if (!acpi.IsConnected() && AppConfig.IsASUS())
{
@@ -340,6 +344,7 @@ static void TrayIcon_MouseClick(object? sender, MouseEventArgs e)
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 000000000..37197eaaf
Binary files /dev/null and b/app/RTSSSharedMemoryNET.dll differ
diff --git a/app/Settings.Designer.cs b/app/Settings.Designer.cs
index b09be6033..9e2de51bb 100644
--- a/app/Settings.Designer.cs
+++ b/app/Settings.Designer.cs
@@ -67,6 +67,8 @@ private void InitializeComponent()
labelCPUFan = new Label();
panelGPU = new Panel();
labelTipGPU = new Label();
+ tableAdditionalCPUFeature = new TableLayoutPanel();
+ buttonCPUAutoTDP = new RButton();
tableAMD = new TableLayoutPanel();
buttonAutoTDP = new RButton();
buttonOverlay = new RButton();
@@ -154,6 +156,7 @@ private void InitializeComponent()
panelCPUTitle.SuspendLayout();
((System.ComponentModel.ISupportInitialize)picturePerf).BeginInit();
panelGPU.SuspendLayout();
+ tableAdditionalCPUFeature.SuspendLayout();
tableAMD.SuspendLayout();
tableGPU.SuspendLayout();
panelGPUTitle.SuspendLayout();
@@ -571,6 +574,7 @@ private void InitializeComponent()
panelPerformance.AccessibleRole = AccessibleRole.Grouping;
panelPerformance.AutoSize = true;
panelPerformance.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ panelPerformance.Controls.Add(tableAdditionalCPUFeature);
panelPerformance.Controls.Add(tablePerf);
panelPerformance.Controls.Add(panelCPUTitle);
panelPerformance.Dock = DockStyle.Top;
@@ -770,6 +774,46 @@ private void InitializeComponent()
labelTipGPU.Size = new Size(787, 36);
labelTipGPU.TabIndex = 20;
//
+ // tableAdditionalGPUFeature
+ //
+ tableAdditionalCPUFeature.AutoSize = true;
+ tableAdditionalCPUFeature.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ tableAdditionalCPUFeature.ColumnCount = 3;
+ tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalCPUFeature.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.3333321F));
+ tableAdditionalCPUFeature.Controls.Add(buttonCPUAutoTDP, 0, 0);
+ tableAdditionalCPUFeature.Dock = DockStyle.Top;
+ tableAdditionalCPUFeature.Location = new Point(10, 198);
+ tableAdditionalCPUFeature.Margin = new Padding(2, 1, 2, 1);
+ tableAdditionalCPUFeature.Name = "tableAdditionalGPUFeature";
+ tableAdditionalCPUFeature.RowCount = 1;
+ tableAdditionalCPUFeature.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
+ tableAdditionalCPUFeature.Size = new Size(392, 60);
+ tableAdditionalCPUFeature.TabIndex = 25;
+ //
+ // buttonAutoTDP
+ //
+ buttonCPUAutoTDP.Activated = false;
+ buttonCPUAutoTDP.BackColor = SystemColors.ControlLightLight;
+ buttonCPUAutoTDP.BorderColor = Color.Transparent;
+ buttonCPUAutoTDP.BorderRadius = 5;
+ buttonCPUAutoTDP.Dock = DockStyle.Fill;
+ buttonCPUAutoTDP.FlatAppearance.BorderSize = 0;
+ buttonCPUAutoTDP.FlatStyle = FlatStyle.Flat;
+ buttonCPUAutoTDP.ForeColor = SystemColors.ControlText;
+ buttonCPUAutoTDP.Image = Properties.Resources.icons8_processor_32;
+ buttonCPUAutoTDP.ImageAlign = ContentAlignment.MiddleRight;
+ buttonCPUAutoTDP.Location = new Point(1, 1);
+ buttonCPUAutoTDP.Margin = new Padding(1, 1, 1, 1);
+ buttonCPUAutoTDP.Name = "buttonAutoTDP";
+ buttonCPUAutoTDP.Secondary = false;
+ buttonCPUAutoTDP.Size = new Size(128, 38);
+ buttonCPUAutoTDP.TabIndex = 11;
+ buttonCPUAutoTDP.Text = "Auto TDP";
+ buttonCPUAutoTDP.TextImageRelation = TextImageRelation.ImageBeforeText;
+ buttonCPUAutoTDP.UseVisualStyleBackColor = false;
+ //
// tableAMD
//
tableAMD.AutoSize = true;
@@ -2025,6 +2069,7 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)picturePerf).EndInit();
panelGPU.ResumeLayout(false);
panelGPU.PerformLayout();
+ tableAdditionalCPUFeature.ResumeLayout(false);
tableAMD.ResumeLayout(false);
tableGPU.ResumeLayout(false);
panelGPUTitle.ResumeLayout(false);
@@ -2169,6 +2214,8 @@ private void InitializeComponent()
private Label labelGammaTitle;
private CheckBox checkMatrixLid;
private Panel panelMatrixAuto;
+ private TableLayoutPanel tableAdditionalCPUFeature;
+ private RButton buttonCPUAutoTDP;
private TableLayoutPanel tableVisual;
private RComboBox comboVisual;
private RComboBox comboGamut;
diff --git a/app/Settings.cs b/app/Settings.cs
index 91703ead0..bef02b1c8 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 @@ public partial class SettingsForm : RForm
AutoUpdateControl updateControl;
AsusMouseSettings? mouseSettings;
+ AutoTDPUI? autoTdpUi;
public AniMatrixControl matrixControl;
@@ -258,6 +260,8 @@ public SettingsForm()
buttonAutoTDP.Click += ButtonAutoTDP_Click;
buttonAutoTDP.BorderColor = colorTurbo;
+ buttonCPUAutoTDP.Click += ButtonCPUAutoTDP_Click;
+
Text = "G-Helper " + (ProcessHelper.IsUserAdministrator() ? "—" : "-") + " " + AppConfig.GetModelShort();
TopMost = AppConfig.Is("topmost");
@@ -469,6 +473,38 @@ public void VisualiseBrightness()
});
}
+ private void ButtonCPUAutoTDP_Click(object? sender, EventArgs e)
+ {
+ if (autoTdpUi is not null)
+ {
+ return;
+ }
+
+ autoTdpUi = new AutoTDPUI();
+ autoTdpUi.FormClosed += AutoTdpUi_FormClosed;
+ autoTdpUi.Disposed += AutoTdpUi_Disposed;
+ autoTdpUi.TopMost = AppConfig.Is("topmost");
+
+ 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;
+ }
+
public void VisualiseDisabled()
{
comboGamut.Enabled = comboColorTemp.Enabled = (SplendidCommand)AppConfig.Get("visual") != SplendidCommand.Disabled;
@@ -638,6 +674,8 @@ private void SettingsForm_VisibleChanged(object? sender, EventArgs e)
Task.Run((Action)RefreshPeripheralsBattery);
updateControl.CheckForUpdates();
+
+ tableAdditionalCPUFeature.Visible = AutoTDPService.IsAvailable();
}
}
@@ -1338,6 +1376,7 @@ public void VisualiseScreen(bool screenEnabled, bool screenAuto, int frequency,
private void ButtonQuit_Click(object? sender, EventArgs e)
{
+ Program.autoTDPService.Shutdown();
matrixControl.Dispose();
Close();
Program.trayIcon.Visible = false;