Removes unused projects

This commit is contained in:
your_username 2026-02-21 23:54:45 -05:00
parent f52cc23504
commit 253346b786
196 changed files with 84 additions and 15146 deletions

View file

@ -4,7 +4,7 @@ namespace CommonHelpers
{
public class AntiCheatSettings : BaseSettings
{
public const String HelpURL = "https://steam-deck-tools.ayufan.dev/#anti-cheat-and-antivirus-software";
public const String HelpURL = "https://github.com/nicklavoie/steam-deck-tools";
public static readonly AntiCheatSettings Default = new AntiCheatSettings();
@ -109,4 +109,4 @@ namespace CommonHelpers
return true;
}
}
}
}

View file

@ -4,18 +4,16 @@
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
</PropertyGroup>
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
<Exec Command="git describe --long --always --dirty --exclude=* --abbrev=8" ConsoleToMSBuild="True" IgnoreExitCode="True">
<Output PropertyName="SourceRevisionId" TaskParameter="ConsoleOutput" />
</Exec>
</Target>
<ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.6" />
<PackageReference Include="LibreHardwareMonitorLib" Version="0.9.1" />
<PackageReference Include="Sentry" Version="3.24.0" />
<PackageReference Include="TaskScheduler" Version="2.10.1" />

View file

@ -25,7 +25,7 @@ namespace CommonHelpers
"RTSSHooks64.dll"
};
public static string SDTURL = "https://steam-deck-tools.ayufan.dev";
public static string SDTURL = "https://github.com/nicklavoie/steam-deck-tools";
public static string VCRuntimeURL = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
public static string RTSSURL = "https://www.guru3d.com/files-details/rtss-rivatuner-statistics-server-download.html";

View file

@ -2,14 +2,6 @@
namespace CommonHelpers
{
public enum FanMode : uint
{
Default = 17374,
Silent,
SteamOS,
Max
}
public enum KernelDriversLoaded : uint
{
Yes = 4363232,
@ -32,19 +24,6 @@ namespace CommonHelpers
No
}
public enum PowerControlVisible : uint
{
Yes = 371313,
No
}
[StructLayout(LayoutKind.Sequential)]
public struct FanModeSetting
{
public FanMode Current, Desired;
public KernelDriversLoaded KernelDriversLoaded;
}
[StructLayout(LayoutKind.Sequential)]
public struct OverlayModeSetting
{
@ -53,23 +32,4 @@ namespace CommonHelpers
public KernelDriversLoaded KernelDriversLoaded;
public KernelDriversLoaded DesiredKernelDriversLoaded;
}
[StructLayout(LayoutKind.Sequential)]
public struct PowerControlSetting
{
public PowerControlVisible Current;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SteamControllerSetting
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public String CurrentProfile;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2048)]
public String SelectableProfiles;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public String DesiredProfile;
}
}

View file

@ -0,0 +1 @@
global using System.IO;

View file

@ -1,6 +1,5 @@
using System.Security.Principal;
using System.Security.AccessControl;
using Microsoft.Win32;
using System.Diagnostics;
using System.Reflection;
@ -220,37 +219,6 @@ namespace CommonHelpers
return File.Exists(uninstallExe);
}
private static System.Timers.Timer? updateTimer;
public static void RunUpdater(string Title, bool user = false, int recheckIntervalHours = 24)
{
// Schedule updater in 24h
if (updateTimer == null && !user && recheckIntervalHours > 0)
{
updateTimer = new System.Timers.Timer
{
Interval = recheckIntervalHours * 60 * 60 * 1000 // 24h
};
updateTimer.Elapsed += delegate { RunUpdater(Title, false); };
updateTimer.Start();
}
try
{
Process.Start(new ProcessStartInfo()
{
FileName = "Updater.exe",
ArgumentList = {
user ? "-user" : "-first",
"-app", ApplicationName,
"-version", ProductVersion
},
UseShellExecute = false
});
}
catch { }
}
public static void Fatal(String? title, String message, bool capture = true)
{
if (capture)

View file

@ -0,0 +1 @@
Disabled by default in this fork.

View file

@ -0,0 +1 @@
Disabled by default in this fork.

View file

@ -1,41 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWindowsForms>True</UseWindowsForms>
<StartupObject>FanControl.Program</StartupObject>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>0.1.0</Version>
<ApplicationIcon>FanControl.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CommonHelpers\CommonHelpers.csproj" />
<ProjectReference Include="..\ExternalHelpers\ExternalHelpers.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="inpoutx64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,361 +0,0 @@
namespace FanControl
{
partial class FanControlForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FanControlForm));
this.fanLoopTimer = new System.Windows.Forms.Timer(this.components);
this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripSeparatorEndOfModes = new System.Windows.Forms.ToolStripSeparator();
this.toolStripMenuItemStartupOnBootContext = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItemAlwaysOnTopContext = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fanModeSelectMenu = new System.Windows.Forms.ToolStripComboBox();
this.controlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItemStartupOnBoot = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItemAlwaysOnTop = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.propertyGridUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.helpLabel = new System.Windows.Forms.Label();
this.sensorWarningLabel = new System.Windows.Forms.Label();
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenu.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// fanLoopTimer
//
this.fanLoopTimer.Enabled = true;
this.fanLoopTimer.Interval = 250;
this.fanLoopTimer.Tick += new System.EventHandler(this.fanLoopTimer_Tick);
//
// notifyIcon
//
this.notifyIcon.BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info;
this.notifyIcon.BalloonTipText = "Test";
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Text = "Steam Deck Fan Control";
this.notifyIcon.DoubleClick += new System.EventHandler(this.formShow_Event);
//
// contextMenu
//
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem2,
this.toolStripSeparator3,
this.toolStripSeparatorEndOfModes,
this.toolStripMenuItemAlwaysOnTopContext,
this.toolStripMenuItemStartupOnBootContext,
this.toolStripMenuItem3,
this.toolStripMenuItem5,
this.toolStripSeparator1,
this.toolStripMenuItem1});
this.contextMenu.Name = "fanModeSelectMenu";
this.contextMenu.Size = new System.Drawing.Size(211, 194);
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItem2.Text = "&Show";
this.toolStripMenuItem2.Click += new System.EventHandler(this.formShow_Event);
//
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(207, 6);
//
// toolStripSeparatorEndOfModes
//
this.toolStripSeparatorEndOfModes.Name = "toolStripSeparatorEndOfModes";
this.toolStripSeparatorEndOfModes.Size = new System.Drawing.Size(207, 6); //
// toolStripMenuItemAlwaysOnTopContext
//
this.toolStripMenuItemAlwaysOnTopContext.Name = "toolStripMenuItemAlwaysOnTopContext";
this.toolStripMenuItemAlwaysOnTopContext.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItemAlwaysOnTopContext.Text = "&Always on Top";
this.toolStripMenuItemAlwaysOnTopContext.Click += new System.EventHandler(this.toolStripMenuItemAlwaysOnTop_Click);
//
// toolStripMenuItemStartupOnBootContext
//
this.toolStripMenuItemStartupOnBootContext.Name = "toolStripMenuItemStartupOnBootContext";
this.toolStripMenuItemStartupOnBootContext.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItemStartupOnBootContext.Text = "Run On &Startup";
this.toolStripMenuItemStartupOnBootContext.Click += new System.EventHandler(this.toolStripMenuItemStartupOnBoot_Click);
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItem3.Text = "&Check for Updates";
this.toolStripMenuItem3.Click += new System.EventHandler(this.checkForUpdates_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(207, 6);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItem1.Text = "&Exit";
this.toolStripMenuItem1.Click += new System.EventHandler(this.formClose_Event);
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fanModeSelectMenu,
this.controlToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Padding = new System.Windows.Forms.Padding(4, 1, 0, 1);
this.menuStrip1.Size = new System.Drawing.Size(438, 30);
this.menuStrip1.TabIndex = 3;
this.menuStrip1.Text = "menuStrip1";
//
// fanModeSelectMenu
//
this.fanModeSelectMenu.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.fanModeSelectMenu.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.fanModeSelectMenu.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.fanModeSelectMenu.Name = "fanModeSelectMenu";
this.fanModeSelectMenu.Size = new System.Drawing.Size(155, 28);
this.fanModeSelectMenu.SelectedIndexChanged += new System.EventHandler(this.fanModeSelectMenu_SelectedIndexChanged);
//
// controlToolStripMenuItem
//
this.controlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItemAlwaysOnTop,
this.toolStripMenuItemStartupOnBoot,
this.toolStripMenuItem4,
this.toolStripSeparator2,
this.exitToolStripMenuItem});
this.controlToolStripMenuItem.Name = "controlToolStripMenuItem";
this.controlToolStripMenuItem.Size = new System.Drawing.Size(72, 28);
this.controlToolStripMenuItem.Text = "&Control";
//
// toolStripMenuItemAlwaysOnTop
//
this.toolStripMenuItemAlwaysOnTop.Name = "toolStripMenuItemAlwaysOnTop";
this.toolStripMenuItemAlwaysOnTop.Size = new System.Drawing.Size(192, 26);
this.toolStripMenuItemAlwaysOnTop.Text = "&Always on Top";
this.toolStripMenuItemAlwaysOnTop.Click += new System.EventHandler(this.toolStripMenuItemAlwaysOnTop_Click);
//
// toolStripMenuItemStartupOnBoot
//
this.toolStripMenuItemStartupOnBoot.Name = "toolStripMenuItemStartupOnBoot";
this.toolStripMenuItemStartupOnBoot.Size = new System.Drawing.Size(192, 26);
this.toolStripMenuItemStartupOnBoot.Text = "Run On &Startup";
this.toolStripMenuItemStartupOnBoot.Click += new System.EventHandler(this.toolStripMenuItemStartupOnBoot_Click);
//
// toolStripMenuItem4
//
this.toolStripMenuItem4.Name = "toolStripMenuItem4";
this.toolStripMenuItem4.Size = new System.Drawing.Size(192, 26);
this.toolStripMenuItem4.Text = "Help";
this.toolStripMenuItem4.Click += new System.EventHandler(this.help_DoubleClick);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(189, 6);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(192, 26);
this.exitToolStripMenuItem.Text = "&Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.formClose_Event);
//
// propertyGridUpdateTimer
//
this.propertyGridUpdateTimer.Enabled = true;
this.propertyGridUpdateTimer.Interval = 1000;
this.propertyGridUpdateTimer.Tick += new System.EventHandler(this.propertyGridUpdateTimer_Tick);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.helpLabel, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.sensorWarningLabel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.propertyGrid1, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 30);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(2, 1, 2, 1);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(438, 454);
this.tableLayoutPanel1.TabIndex = 5;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.label1.ForeColor = System.Drawing.Color.Red;
this.label1.Location = new System.Drawing.Point(2, 394);
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(434, 40);
this.label1.TabIndex = 9;
this.label1.Text = "This application is highly experimental.\r\nUse at your own risk!";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// helpLabel
//
this.helpLabel.AutoSize = true;
this.helpLabel.Cursor = System.Windows.Forms.Cursors.Hand;
this.helpLabel.Dock = System.Windows.Forms.DockStyle.Fill;
this.helpLabel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.helpLabel.Font = new System.Drawing.Font("Segoe UI", 9F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point);
this.helpLabel.ForeColor = System.Drawing.SystemColors.HotTrack;
this.helpLabel.Location = new System.Drawing.Point(2, 434);
this.helpLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.helpLabel.Name = "helpLabel";
this.helpLabel.Size = new System.Drawing.Size(434, 20);
this.helpLabel.TabIndex = 8;
this.helpLabel.Text = "https://steam-deck-tools.ayufan.dev";
this.helpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.helpLabel.DoubleClick += new System.EventHandler(this.help_DoubleClick);
//
// sensorWarningLabel
//
this.sensorWarningLabel.AutoSize = true;
this.sensorWarningLabel.Dock = System.Windows.Forms.DockStyle.Fill;
this.sensorWarningLabel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.sensorWarningLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this.sensorWarningLabel.ForeColor = System.Drawing.Color.Red;
this.sensorWarningLabel.Location = new System.Drawing.Point(2, 334);
this.sensorWarningLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.sensorWarningLabel.Name = "sensorWarningLabel";
this.sensorWarningLabel.Size = new System.Drawing.Size(434, 60);
this.sensorWarningLabel.TabIndex = 6;
this.sensorWarningLabel.Text = "Some sensors are missing.\r\nThe fan behavior might be incorrect.\r\nWhich might resu" +
"lt in device overheating.\r\n";
this.sensorWarningLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.sensorWarningLabel.Visible = false;
//
// propertyGrid1
//
this.propertyGrid1.DisabledItemForeColor = System.Drawing.SystemColors.ControlText;
this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid1.HelpVisible = false;
this.propertyGrid1.Location = new System.Drawing.Point(2, 1);
this.propertyGrid1.Margin = new System.Windows.Forms.Padding(2, 1, 2, 1);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.Size = new System.Drawing.Size(434, 332);
this.propertyGrid1.TabIndex = 1;
this.propertyGrid1.ToolbarVisible = false;
//
// toolStripMenuItem5
//
this.toolStripMenuItem5.Name = "toolStripMenuItem5";
this.toolStripMenuItem5.Size = new System.Drawing.Size(210, 24);
this.toolStripMenuItem5.Text = "Help";
//
// FanControlForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(438, 484);
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.menuStrip1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FanControlForm";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Steam Deck Fan Control";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FanControlForm_FormClosing);
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FanControlForm_FormClosed);
this.contextMenu.ResumeLayout(false);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Timer fanLoopTimer;
private NotifyIcon notifyIcon;
private ContextMenuStrip contextMenu;
private MenuStrip menuStrip1;
private ToolStripSeparator toolStripSeparatorEndOfModes;
private ToolStripMenuItem toolStripMenuItem1;
private ToolStripComboBox fanModeSelectMenu;
private ToolStripMenuItem controlToolStripMenuItem;
private ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.Timer propertyGridUpdateTimer;
private TableLayoutPanel tableLayoutPanel1;
private Label sensorWarningLabel;
private PropertyGrid propertyGrid1;
private ToolStripMenuItem toolStripMenuItemStartupOnBoot;
private ToolStripSeparator toolStripSeparator2;
private Label label1;
private Label helpLabel;
private ToolStripMenuItem toolStripMenuItemAlwaysOnTop;
private ToolStripMenuItem toolStripMenuItem2;
private ToolStripSeparator toolStripSeparator3;
private ToolStripMenuItem toolStripMenuItemStartupOnBootContext;
private ToolStripMenuItem toolStripMenuItemAlwaysOnTopContext;
private ToolStripMenuItem toolStripMenuItem3;
private ToolStripSeparator toolStripSeparator1;
private ToolStripMenuItem toolStripMenuItem4;
private ToolStripMenuItem toolStripMenuItem5;
}
}

View file

@ -1,234 +0,0 @@
using CommonHelpers;
using ExternalHelpers;
namespace FanControl
{
public partial class FanControlForm : Form
{
private FanController fanControl;
private StartupManager startupManager = new StartupManager(
"Steam Deck Fan Control",
"Starts Steam Deck Fan Control on Windows startup."
);
private SharedData<FanModeSetting> sharedData = SharedData<FanModeSetting>.CreateNew();
public FanControlForm()
{
Instance.OnUninstall(() =>
{
startupManager.Startup = false;
});
InitializeComponent();
Text += " v" + Application.ProductVersion.ToString();
Instance.Open(Text, true, "Global\\FanControlOnce");
Instance.RunUpdater(Text);
if (Instance.WantsRunOnStartup)
startupManager.Startup = true;
fanControl = new FanController();
SharedData_Update();
notifyIcon.Text = Text;
notifyIcon.Visible = true;
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.fan_white : Resources.fan;
TopMost = Settings.Default.AlwaysOnTop;
toolStripMenuItemAlwaysOnTop.Checked = TopMost;
toolStripMenuItemAlwaysOnTopContext.Checked = TopMost;
toolStripMenuItemStartupOnBoot.Visible = startupManager.IsAvailable;
toolStripMenuItemStartupOnBoot.Checked = startupManager.Startup;
toolStripMenuItemStartupOnBootContext.Visible = startupManager.IsAvailable;
toolStripMenuItemStartupOnBootContext.Checked = startupManager.Startup;
foreach (var item in Enum.GetValues(typeof(FanMode)))
{
var menuItem = new ToolStripMenuItem(item.ToString()) { Tag = item };
menuItem.Click += FanMode_Click;
int insertIndex = contextMenu.Items.IndexOf(toolStripSeparatorEndOfModes);
contextMenu.Items.Insert(insertIndex, menuItem);
fanModeSelectMenu.Items.Add(item);
}
propertyGrid1.SelectedObject = fanControl;
propertyGrid1.ExpandAllGridItems();
Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Opacity = 0;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
setFanMode(Settings.Default.FanMode, AntiCheatSettings.Default.NotYet);
notifyIcon.ShowBalloonTip(3000, Text, "Fan Control Started", ToolTipIcon.Info);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
Visible = false;
Opacity = 100;
}
private bool AckAntiCheat()
{
return AntiCheatSettings.Default.AckAntiCheat(
Text,
"Usage of SteamOS or Max Fan Curve might trigger anti-cheat protection in some games.",
"Ensure that you USE DEFAULT FAN when playing games with ANTI-CHEAT PROTECTION.");
}
private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e)
{
// Restore fan mode on resume
if (e.Mode == Microsoft.Win32.PowerModes.Resume)
{
Instance.HardwareComputer.Reset();
fanControl.SetMode(fanControl.Mode);
}
}
private void setFanMode(FanMode mode, bool requireAck = true)
{
if (mode != FanMode.Default && mode != fanControl.Mode)
{
if (requireAck && !AckAntiCheat())
return;
}
fanControl.SetMode(mode);
Settings.Default.FanMode = mode;
foreach (ToolStripItem menuItem in contextMenu.Items)
{
if (menuItem is ToolStripMenuItem && menuItem.Tag is FanMode)
((ToolStripMenuItem)menuItem).Checked = ((FanMode)menuItem.Tag == mode);
}
fanModeSelectMenu.SelectedItem = mode;
}
private void FanMode_Click(object? sender, EventArgs e)
{
var menuItem = (ToolStripMenuItem)sender;
setFanMode((FanMode)menuItem.Tag);
}
private void fanModeSelectMenu_SelectedIndexChanged(object sender, EventArgs e)
{
var menuItem = (ToolStripComboBox)sender;
setFanMode((FanMode)menuItem.SelectedItem);
}
private void FanControlForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing && Visible)
{
e.Cancel = true;
Hide();
}
}
private void formShow_Event(object sender, EventArgs e)
{
Show();
Activate();
propertyGrid1.Refresh();
}
private void formClose_Event(object sender, EventArgs e)
{
Hide();
Close();
}
private void FanControlForm_FormClosed(object sender, FormClosedEventArgs e)
{
// Always revert to default on closing
fanControl.SetMode(FanMode.Default);
}
private void SharedData_Update()
{
if (sharedData.GetValue(out var value) && Enum.IsDefined<FanMode>(value.Desired))
{
setFanMode((FanMode)value.Desired);
}
sharedData.SetValue(new FanModeSetting()
{
Current = fanControl.Mode,
KernelDriversLoaded = Instance.UseKernelDrivers ? KernelDriversLoaded.Yes : KernelDriversLoaded.No
});
}
private void fanLoopTimer_Tick(object sender, EventArgs e)
{
if (fanControl is null)
return;
try
{
fanLoopTimer.Enabled = false;
SharedData_Update();
}
finally
{
fanLoopTimer.Enabled = true;
}
#if DEBUG
fanControl.Update(Visible);
#else
fanControl.Update();
#endif
}
private void propertyGridUpdateTimer_Tick(object sender, EventArgs e)
{
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.fan_white : Resources.fan;
if (!Visible)
return;
propertyGrid1.Refresh();
sensorWarningLabel.Visible = fanControl.IsAnyInvalid();
if (fanControl.IsActive)
notifyIcon.Text = String.Format("Fan: {0} RPM Mode: {1}", fanControl.CurrentRPM, fanControl.Mode);
else
notifyIcon.Text = String.Format("Mode: {0}", fanControl.Mode);
}
private void toolStripMenuItemStartupOnBoot_Click(object sender, EventArgs e)
{
startupManager.Startup = !startupManager.Startup;
toolStripMenuItemStartupOnBoot.Checked = startupManager.Startup;
toolStripMenuItemStartupOnBootContext.Checked = startupManager.Startup;
}
private void help_DoubleClick(object sender, EventArgs e)
{
Dependencies.OpenLink(Dependencies.SDTURL);
}
private void toolStripMenuItemAlwaysOnTop_Click(object sender, EventArgs e)
{
TopMost = !TopMost;
toolStripMenuItemAlwaysOnTop.Checked = TopMost;
toolStripMenuItemAlwaysOnTopContext.Checked = TopMost;
Settings.Default.AlwaysOnTop = toolStripMenuItemAlwaysOnTop.Checked;
}
private void checkForUpdates_Click(object sender, EventArgs e)
{
Instance.RunUpdater(Text, true);
}
}
}

View file

@ -1,158 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="fanLoopTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1188, 17</value>
</metadata>
<metadata name="notifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>955, 18</value>
</metadata>
<metadata name="contextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>8, 6</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="notifyIcon.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAAAAAAAEAIAB5CAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAABAAAAAQAgGAAAAqmlx3gAAAAFz
UkdCAK7OHOkAAAgzSURBVHhe5VtvbFPXFf+9Z2zlOQ8nIRG0E0RJGhQEW0kBW0QiJVoZAy2ZNiSDsvQD
SzdpoqLF+UDVbGWPrYVtSJjCisSHpkhdGoElSpt9YBNdghIpk42QQ0eUaFnClLApNAl18D8587vTtWzL
dvznPvs9L2jnQz74nXPuOb977zn3nHvD4X9AjY2NJ91uN7Zu3fqTsbGxzakmbNmy5W/j4+Mf79mzZ3Z4
ePgTLU3ktFSeqruurs42NTV1XsmYOp0O4XD4NQA9SuRYeYsFQENNTc34w4cPWe1awWcwGBZDoVA7gD/n
rSSNYDEAqAPwDxWNbgLwV7X0aQrAiRMnnr9w4cK/1DKW6uF5Hu3t7WW9vb1LaujVFAAAnwL4gRqGJupo
aWnB4OCgKraroiSLg0Rt5xP0qWK7KkoyOPkFgG9rBYDdbq+12Wz5R9WoYZoBoNfrfcvLy0atAOjs7Hyh
p6dnqlD9mgEAQMvlj5aWFv3g4OB//m8BAKDK5KmiJMMsaLYC2tvbn+vr65srdPap/DMHwIYNG96dm5t7
Rw3nnzkAeJ4/LcuypJbzWgMwCGCvisa+B+AXKuqLqNJyC6h5DjgLoFtt5zUF4Pz580JXV5dfBaM1cz4n
AGfPnq1xuVy8Tqf7p8PhCOfhjBdAaR5yMZFfAfhlAfI5RVdsgY6ODtOjR488g4N0CydTc3Pz+qGhoa9y
ao0yCILwSSAQoDW8YiopKZGCweBpxYIKBVIB2M3z/IgsyxnVrFu37vTi4iJTJCaE6DiOU3xa4zgOsixT
2cyGKHQ0E3siAPsB/IlFb1lZ2W88Hs/bLLylpaU9Pp/vxyy8MR6e59+UZfmiEpl8eeMAGAyG+VAoVKlA
EXNa0ul0JBxWFEK0zE5JLsYG6gTwoQLnY6ysEboWAFPlZjQa5/x+/3MstthsNsFut/+xsrKyYmFh4aU0
MjQL0fbZK1m3gMFgIKFQiGXMdDxMINTX138wOTl5jGEQajDt+6WlgYGBNa2trb0+n+97CjPMzebm5mND
Q0P/TlTMNTU1tY+MjBTae8+aru7cufPy3r176ZbZQwfnOG6WEEKBixPHcW8TQjZGfxienZ2VNm7cSA9T
cTIajR/5/f6jDCBmY3khcTXSLXASwG8LVAqDwSB1d3f/WpKkpMhNCHm5urr685mZmTIA9wH8DkBvuvFM
JlPH0tIStefF6upq3/37979fXl7+FwDMW4jRjy0AJiKTsX379i9HR0e/ySjIwvYmgMQIPhSd+VEAjSwK
ALgBbAcwXFdX9/epqSlFWSTXGJs2bcLMzEwk/tE/mtTtVVVVKC8vX5qcnDRFZ546FCer1So4HI4f0h/a
2tpu9vf3px6bKWAv5nKmgO+R2ybNAEgx7NWUZU+D4QcpPK8DuJzwWweAPxTgIIsopzkA0YC3KcGadM7H
PieBIIrigtfrXcfiST48ra2tP9UcAABxp5qamoSRkZGsFaLVajU6HI5A1KFsYOXjc6rMW0UFAMCPMmWA
lKUfS8uaArB58+ZpbteuXVN3796laUYrSlzWqwqAWBZQ5RyQBb04AJIklUiSFFveaUVsNpvRbrcXawuA
W79+/cnHjx8XfBDKBIDRaFzw+/1Vse88zx+TZTk1A6QNgoIgfBUIBOKyWizRWDGkyVkgweCcaZDn+ddl
WS52Gow3RfOtBpkmheO4LwkhSYcaWsldvHgxchA6d+7cp11dXalbgx6bv0WrOVEUa71e7wamwRQyxevu
xsZGv9vtFhTKZ2XfsWPHlXv37m0r5CgMoDlh+7xBCHmfEHUWrNlsnkhsPDQAGFcDAPqKQ5Zl+jRmOhAI
vLJz587PxsbGSqMrgcabtMUQAHr6e4vOvNlsDrpcrhUTQgjhBUF4JxgMMrXlcvgTOQckUsHvedauXfvR
06dP6ZZKpVhRBFEUF71eb9L1liAIpxMC3nDizGdwgjZMTxU4YftXtJ66u7ufP3PmDA1GSp62+ARB6Pf7
/a9yHJet9zUCYHcuoxsaGq5MTEz8LBcfgDMAmHqTqbpKS0vh8/kitUA2+kKv1+9OfehAu7YAhgghyw8e
PLBu27ZtkcFY1NbWLk9PT69h4QUQ2UIMvHmBoNPpXguHw5FqsJjEHL0oyIQQVvveBfBzVkdEUfza6/VW
UH7WAVh1Z+N7A8D7ShRFW+q0bs9JPM9Lsiyz3iJ9N/bgsigASJLES5KkqC8e8/j69etrDh8+zCRbVVUl
zc/PZwQh+uw26aFlUQAwGAynQqFQXtdcJSUlfcFgkBZRTNTW1lbV39+/4vru4MGDqK+vL7t06VLSA8ti
AFBouvIBEJm8T2CSJGmNx+OpHhgYWHS73V9nktcagLwidKqxt27dqjxw4ABTplEKlJYAqOJ81CHaGs94
u6PU6UR+rQBQlJZyOVBRUTH65MkT1pZ6LnVJ31UHQBTF97xerxbPWVS3VfVzgMlkOrW0tJRXtGeYttUN
gMViqXQ6nfMMjuTLsroB0OqGKQGt1QuA1WrVORwOxU9hFC6F1QuA2Wyuc7lcav5fUHKk5jg/IaSQ12YZ
sVYF1aNHj9ZcvXqVpXRVOOlx9mfiHMBc6uaBgioTlW5c1RTv27eP3L59Ow/fcorcBBDpHmtBqgFw/Phx
0+XLlz0KX4Pl9OnQoUPfuHHjRtK7npxCChhUAyA6Ju330b6fWpT0nkctpVrXAvtFUewr5F7fYrEEnU4n
PftH3vFoSWqvgLitRqOxU6/Xf+jxeJTar9k/SmsaBLN4+R0AL1kslk6n00kvX5KooaFhZmJi4vdHjhzx
XLt27YpStArl/y+pZ32X/qZ1gAAAAABJRU5ErkJggg==
</value>
</data>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>651, 17</value>
</metadata>
<metadata name="propertyGridUpdateTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1477, 16</value>
</metadata>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAAAAAAAEAIAB5CAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAABAAAAAQAgGAAAAqmlx3gAAAAFz
UkdCAK7OHOkAAAgzSURBVHhe5VtvbFPXFf+9Z2zlOQ8nIRG0E0RJGhQEW0kBW0QiJVoZAy2ZNiSDsvQD
SzdpoqLF+UDVbGWPrYVtSJjCisSHpkhdGoElSpt9YBNdghIpk42QQ0eUaFnClLApNAl18D8587vTtWzL
dvznPvs9L2jnQz74nXPuOb977zn3nHvD4X9AjY2NJ91uN7Zu3fqTsbGxzakmbNmy5W/j4+Mf79mzZ3Z4
ePgTLU3ktFSeqruurs42NTV1XsmYOp0O4XD4NQA9SuRYeYsFQENNTc34w4cPWe1awWcwGBZDoVA7gD/n
rSSNYDEAqAPwDxWNbgLwV7X0aQrAiRMnnr9w4cK/1DKW6uF5Hu3t7WW9vb1LaujVFAAAnwL4gRqGJupo
aWnB4OCgKraroiSLg0Rt5xP0qWK7KkoyOPkFgG9rBYDdbq+12Wz5R9WoYZoBoNfrfcvLy0atAOjs7Hyh
p6dnqlD9mgEAQMvlj5aWFv3g4OB//m8BAKDK5KmiJMMsaLYC2tvbn+vr65srdPap/DMHwIYNG96dm5t7
Rw3nnzkAeJ4/LcuypJbzWgMwCGCvisa+B+AXKuqLqNJyC6h5DjgLoFtt5zUF4Pz580JXV5dfBaM1cz4n
AGfPnq1xuVy8Tqf7p8PhCOfhjBdAaR5yMZFfAfhlAfI5RVdsgY6ODtOjR488g4N0CydTc3Pz+qGhoa9y
ao0yCILwSSAQoDW8YiopKZGCweBpxYIKBVIB2M3z/IgsyxnVrFu37vTi4iJTJCaE6DiOU3xa4zgOsixT
2cyGKHQ0E3siAPsB/IlFb1lZ2W88Hs/bLLylpaU9Pp/vxyy8MR6e59+UZfmiEpl8eeMAGAyG+VAoVKlA
EXNa0ul0JBxWFEK0zE5JLsYG6gTwoQLnY6ysEboWAFPlZjQa5/x+/3MstthsNsFut/+xsrKyYmFh4aU0
MjQL0fbZK1m3gMFgIKFQiGXMdDxMINTX138wOTl5jGEQajDt+6WlgYGBNa2trb0+n+97CjPMzebm5mND
Q0P/TlTMNTU1tY+MjBTae8+aru7cufPy3r176ZbZQwfnOG6WEEKBixPHcW8TQjZGfxienZ2VNm7cSA9T
cTIajR/5/f6jDCBmY3khcTXSLXASwG8LVAqDwSB1d3f/WpKkpMhNCHm5urr685mZmTIA9wH8DkBvuvFM
JlPH0tIStefF6upq3/37979fXl7+FwDMW4jRjy0AJiKTsX379i9HR0e/ySjIwvYmgMQIPhSd+VEAjSwK
ALgBbAcwXFdX9/epqSlFWSTXGJs2bcLMzEwk/tE/mtTtVVVVKC8vX5qcnDRFZ546FCer1So4HI4f0h/a
2tpu9vf3px6bKWAv5nKmgO+R2ybNAEgx7NWUZU+D4QcpPK8DuJzwWweAPxTgIIsopzkA0YC3KcGadM7H
PieBIIrigtfrXcfiST48ra2tP9UcAABxp5qamoSRkZGsFaLVajU6HI5A1KFsYOXjc6rMW0UFAMCPMmWA
lKUfS8uaArB58+ZpbteuXVN3796laUYrSlzWqwqAWBZQ5RyQBb04AJIklUiSFFveaUVsNpvRbrcXawuA
W79+/cnHjx8XfBDKBIDRaFzw+/1Vse88zx+TZTk1A6QNgoIgfBUIBOKyWizRWDGkyVkgweCcaZDn+ddl
WS52Gow3RfOtBpkmheO4LwkhSYcaWsldvHgxchA6d+7cp11dXalbgx6bv0WrOVEUa71e7wamwRQyxevu
xsZGv9vtFhTKZ2XfsWPHlXv37m0r5CgMoDlh+7xBCHmfEHUWrNlsnkhsPDQAGFcDAPqKQ5Zl+jRmOhAI
vLJz587PxsbGSqMrgcabtMUQAHr6e4vOvNlsDrpcrhUTQgjhBUF4JxgMMrXlcvgTOQckUsHvedauXfvR
06dP6ZZKpVhRBFEUF71eb9L1liAIpxMC3nDizGdwgjZMTxU4YftXtJ66u7ufP3PmDA1GSp62+ARB6Pf7
/a9yHJet9zUCYHcuoxsaGq5MTEz8LBcfgDMAmHqTqbpKS0vh8/kitUA2+kKv1+9OfehAu7YAhgghyw8e
PLBu27ZtkcFY1NbWLk9PT69h4QUQ2UIMvHmBoNPpXguHw5FqsJjEHL0oyIQQVvveBfBzVkdEUfza6/VW
UH7WAVh1Z+N7A8D7ShRFW+q0bs9JPM9Lsiyz3iJ9N/bgsigASJLES5KkqC8e8/j69etrDh8+zCRbVVUl
zc/PZwQh+uw26aFlUQAwGAynQqFQXtdcJSUlfcFgkBZRTNTW1lbV39+/4vru4MGDqK+vL7t06VLSA8ti
AFBouvIBEJm8T2CSJGmNx+OpHhgYWHS73V9nktcagLwidKqxt27dqjxw4ABTplEKlJYAqOJ81CHaGs94
u6PU6UR+rQBQlJZyOVBRUTH65MkT1pZ6LnVJ31UHQBTF97xerxbPWVS3VfVzgMlkOrW0tJRXtGeYttUN
gMViqXQ6nfMMjuTLsroB0OqGKQGt1QuA1WrVORwOxU9hFC6F1QuA2Wyuc7lcav5fUHKk5jg/IaSQ12YZ
sVYF1aNHj9ZcvXqVpXRVOOlx9mfiHMBc6uaBgioTlW5c1RTv27eP3L59Ow/fcorcBBDpHmtBqgFw/Phx
0+XLlz0KX4Pl9OnQoUPfuHHjRtK7npxCChhUAyA6Ju330b6fWpT0nkctpVrXAvtFUewr5F7fYrEEnU4n
PftH3vFoSWqvgLitRqOxU6/Xf+jxeJTar9k/SmsaBLN4+R0AL1kslk6n00kvX5KooaFhZmJi4vdHjhzx
XLt27YpStArl/y+pZ32X/qZ1gAAAAABJRU5ErkJggg==
</value>
</data>
</root>

View file

@ -1,163 +0,0 @@
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Hardware.CPU;
using Microsoft.VisualBasic.Devices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonHelpers;
namespace FanControl
{
[TypeConverter(typeof(ExpandableObjectConverter))]
[RefreshProperties(RefreshProperties.Repaint)]
internal partial class FanController : IDisposable
{
[CategoryAttribute("Fan")]
public FanMode Mode { get; private set; } = FanMode.Default;
[CategoryAttribute("Fan")]
public bool KernelDriversLoaded
{
get => Instance.UseKernelDrivers;
}
[CategoryAttribute("Fan")]
[NotifyParentProperty(true)]
public ushort? CurrentRPM { get; private set; }
[CategoryAttribute("Fan")]
[NotifyParentProperty(true)]
public ushort DesiredRPM { get; private set; }
[CategoryAttribute("Board")]
public String PDVersion { get; private set; } = Vlv0100.Instance.FirmwareVersion.ToString("X");
public FanController()
{
}
private void visitHardware(IHardware hardware)
{
Dictionary<FanSensor, ISensor> matched = new Dictionary<FanSensor, ISensor>();
foreach (ISensor hwSensor in hardware.Sensors)
{
foreach (var sensor in allSensors.Values)
{
if (sensor.Matches(hwSensor))
matched[sensor] = hwSensor;
}
}
if (matched.Any())
{
hardware.Update();
foreach (var sensor in matched)
sensor.Key.Update(sensor.Value, Mode);
}
foreach (IHardware subhardware in hardware.SubHardware)
{
visitHardware(subhardware);
}
}
private ushort getDesiredRPM()
{
ushort rpm = 0;
foreach (var sensor in allSensors.Values)
if (sensor.CalculatedRPM.HasValue)
rpm = Math.Max(rpm, sensor.CalculatedRPM.Value);
return rpm;
}
[Browsable(false)]
public bool IsActive
{
get { return Vlv0100.Instance.IsOpen; }
}
public void Update(bool showForDefault = false)
{
var mutex = Instance.WaitGlobalMutex(200);
if (mutex is null)
{
// If we cannot acquire mutex slightly increase FAN to compensate just in case
Vlv0100.Instance.SetFanDesiredRPM((ushort)(Vlv0100.Instance.GetFanDesiredRPM() * 110 / 100));
return;
}
try
{
if (AntiCheatSettings.Default.NotYet || Mode == FanMode.Default && !showForDefault)
{
Instance.UseKernelDrivers = false;
CurrentRPM = null;
DesiredRPM = 0;
foreach (var sensor in allSensors.Values)
sensor.Reset();
return;
}
else if (!Vlv0100.Instance.IsOpen)
{
Instance.UseKernelDrivers = true;
SetMode(Mode);
}
foreach (var sensor in allSensors.Values)
sensor.Reset();
foreach (var hardware in Instance.HardwareComputer.Hardware)
visitHardware(hardware);
}
finally
{
mutex.ReleaseMutex();
}
allSensors["Batt"].Update("VLV0100", Vlv0100.Instance.GetBattTemperature(), Mode);
Vlv0100.Instance.SetFanDesiredRPM(getDesiredRPM());
CurrentRPM = Vlv0100.Instance.GetFanRPM();
DesiredRPM = Vlv0100.Instance.GetFanDesiredRPM();
}
public void SetMode(FanMode mode)
{
switch (mode)
{
case FanMode.Default:
Vlv0100.Instance.SetFanControl(false);
break;
default:
Instance.UseKernelDrivers = true;
Vlv0100.Instance.SetFanControl(true);
break;
}
this.Mode = mode;
}
public bool IsAnyInvalid()
{
foreach (var sensor in allSensors.Values)
{
if (!sensor.IsValid(Mode))
return true;
}
return false;
}
public void Dispose()
{
}
}
}

View file

@ -1,222 +0,0 @@
using CommonHelpers;
using LibreHardwareMonitor.Hardware;
using System.ComponentModel;
namespace FanControl
{
internal partial class FanController
{
private Dictionary<string, FanSensor> allSensors = new Dictionary<string, FanSensor>
{
{
"APU", new FanSensor()
{
// TODO: Is this correct?
HardwareNames = { "AMD Custom APU 0405", "AMD Custom APU 0932" },
HardwareType = HardwareType.Cpu,
SensorName = "Package",
SensorType = SensorType.Power,
ValueDeadZone = 0.1f,
AvgSamples = 20,
MaxValue = 25, // TODO: On resume a bogus value is returned
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
{
{
FanMode.Max, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Constant,
MinRPM = CommonHelpers.Vlv0100.MAX_FAN_RPM
}
},
{
FanMode.SteamOS, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Constant,
MinRPM = 1500
}
},
{
FanMode.Silent, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Constant,
MinRPM = 1500
}
},
}
}
},
{
"CPU", new FanSensor()
{
HardwareNames = { "AMD Custom APU 0405", "AMD Custom APU 0932" },
HardwareType = HardwareType.Cpu,
SensorName = "Core (Tctl/Tdie)",
SensorType = SensorType.Temperature,
ValueDeadZone = 0.0f,
AvgSamples = 20,
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
{
{
FanMode.SteamOS, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Quadratic,
MinInput = 55,
MaxInput = 90,
A = 2.286f,
B = -188.6f,
C = 5457.0f
}
},
{
FanMode.Silent, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Exponential,
MinInput = 40,
MaxInput = 95,
A = 1.28f,
B = Settings.Default.Silent4000RPMTemp - 28,
C = 3000f
}
},
}
}
},
{
"GPU", new FanSensor()
{
HardwareNames = { "AMD Custom GPU 0405", "AMD Custom GPU 0932" },
HardwareType = HardwareType.GpuAmd,
SensorName = "GPU Core",
SensorType = SensorType.Temperature,
ValueDeadZone = 0.0f,
InvalidValue = 5.0f,
AvgSamples = 20,
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
{
{
FanMode.SteamOS, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Quadratic,
MinInput = 55,
MaxInput = 90,
A = 2.286f,
B = -188.6f,
C = 5457.0f
}
},
{
FanMode.Silent, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Exponential,
MinInput = 40,
MaxInput = 95,
A = 1.28f,
B = Settings.Default.Silent4000RPMTemp - 28,
C = 3000f
}
},
}
}
},
{
"SSD", new FanSensor()
{
HardwareType = HardwareType.Storage,
SensorName = "Temperature",
SensorType = SensorType.Temperature,
ValueDeadZone = 0.5f,
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
{
{
FanMode.SteamOS, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Pid,
MinInput = 30,
MaxInput = 70,
MaxRPM = 3000,
PidSetPoint = 70,
Kp = 0,
Ki = -20,
Kd = 0
}
},
{
FanMode.Silent, new FanSensor.Profile()
{
Type = FanSensor.Profile.ProfileType.Pid,
MinInput = 30,
MaxInput = 70,
MaxRPM = 3000,
PidSetPoint = 70,
Kp = 0,
Ki = -20,
Kd = 0
}
}
}
}
},
{
"Batt", new FanSensor()
{
HardwareType = HardwareType.Battery,
SensorName = "Temperature",
SensorType = SensorType.Temperature,
ValueDeadZone = 0.0f,
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
{
{
FanMode.SteamOS, new FanSensor.Profile()
{
// If battery goes over 40oC require 2kRPM
Type = FanSensor.Profile.ProfileType.Constant,
MinInput = 0,
MaxInput = 40,
MinRPM = 0,
MaxRPM = 2000,
}
},
{
FanMode.Silent, new FanSensor.Profile()
{
// If battery goes over 40oC require 2kRPM
Type = FanSensor.Profile.ProfileType.Constant,
MinInput = 0,
MaxInput = 40,
MinRPM = 0,
MaxRPM = 2000,
}
}
}
}
}
};
#region Sensor Properties for Property Grid
[CategoryAttribute("Sensor - APU"), DisplayName("Name")]
public String? APUName { get { return allSensors["APU"].Name; } }
[CategoryAttribute("Sensor - APU"), DisplayName("Power")]
public String? APUPower { get { return allSensors["APU"].FormattedValue(); } }
[CategoryAttribute("Sensor - CPU"), DisplayName("Name")]
public String? CPUName { get { return allSensors["CPU"].Name; } }
[CategoryAttribute("Sensor - CPU"), DisplayName("Temperature")]
public String? CPUTemperature { get { return allSensors["CPU"].FormattedValue(); } }
[CategoryAttribute("Sensor - GPU"), DisplayName("Name")]
public String? GPUName { get { return allSensors["GPU"].Name; } }
[CategoryAttribute("Sensor - GPU"), DisplayName("Temperature")]
public String? GPUTemperature { get { return allSensors["GPU"].FormattedValue(); } }
[CategoryAttribute("Sensor - SSD"), DisplayName("Name")]
public String? SSDName { get { return allSensors["SSD"].Name; } }
[CategoryAttribute("Sensor - SSD"), DisplayName("Temperature")]
public String? SSDTemperature { get { return allSensors["SSD"].FormattedValue(); } }
[CategoryAttribute("Sensor - Battery"), DisplayName("Name")]
public String? BatteryName { get { return allSensors["Batt"].Name; } }
[CategoryAttribute("Sensor - Battery"), DisplayName("Temperature")]
public String? BatteryTemperature { get { return allSensors["Batt"].FormattedValue(); } }
#endregion Sensor Properties for Property Grid
}
}

View file

@ -1,254 +0,0 @@
using CommonHelpers;
using LibreHardwareMonitor.Hardware;
using System.ComponentModel;
namespace FanControl
{
[TypeConverter(typeof(ExpandableObjectConverter))]
internal class FanSensor
{
public string? Name { get; internal set; }
public float? Value { get; internal set; }
public ushort? CalculatedRPM { get; internal set; }
public float ValueDeadZone { get; set; }
public int AvgSamples { get; set; } = 5;
public float InvalidValue { get; set; } = 0.0f;
public float? MaxValue { get; set; }
internal string HardwareName { get; set; } = "";
internal IList<string> HardwareNames { get; set; } = new List<string>();
internal HardwareType HardwareType { get; set; }
internal string SensorName { get; set; } = "";
internal SensorType SensorType { get; set; }
private List<float> AllSamples = new List<float>();
internal Dictionary<FanMode, Profile> Profiles { get; set; } = new Dictionary<FanMode, Profile>();
internal class Profile
{
public enum ProfileType
{
Constant,
Quadratic,
Pid,
Exponential
}
public ProfileType Type { get; set; }
public float MinInput { get; set; }
public float MaxInput { get; set; } = 90;
public ushort MinRPM { get; set; }
public ushort MaxRPM { get; set; } = ushort.MaxValue;
public float A { get; set; }
public float B { get; set; }
public float C { get; set; }
public float Kp { get; set; }
public float Ki { get; set; }
public float Kd { get; set; }
public float PidSetPoint { get; set; }
private float? pidLastInput { get; set; }
private float pidLastError { get; set; }
private DateTime pidLastTime { get; set; }
private float pidP { get; set; }
private float pidI { get; set; }
private float pidD { get; set; }
public ushort CalculateRPM(float input)
{
float rpm = 0;
switch (Type)
{
case ProfileType.Constant:
rpm = MinRPM;
break;
case ProfileType.Quadratic:
rpm = calculateQuadraticRPM(input);
break;
case ProfileType.Pid:
rpm = calculatePidRPM(input);
break;
case ProfileType.Exponential:
rpm = calculateExponentialRPM(input);
break;
}
if (input < MinInput)
rpm = MinRPM;
else if (input > MaxInput)
rpm = MaxRPM;
rpm = Math.Clamp(rpm, (float)MinRPM, (float)MaxRPM);
return (ushort)rpm;
}
private float calculateQuadraticRPM(float input)
{
return A * input * input + B * input + C;
}
private float calculatePidRPM(float input)
{
if (!pidLastInput.HasValue)
{
pidLastInput = input;
pidLastTime = DateTime.Now;
return 0;
}
float error = PidSetPoint - input;
float dInput = input - pidLastInput.Value;
float dt = Math.Min((float)(DateTime.Now - pidLastTime).TotalSeconds, 1.0f);
this.pidP = Kp * error;
this.pidI += Ki * error * dt;
this.pidI = Math.Min(this.pidI, this.MaxRPM);
this.pidD -= Kd * dInput / dt;
pidLastInput = input;
pidLastError = error;
pidLastTime = DateTime.Now;
return pidP + pidI + pidD;
}
private float calculateExponentialRPM(float input)
{
return (float)(Math.Pow(A, input - B) + C);
}
}
public void Reset()
{
Name = null;
Value = null;
CalculatedRPM = 0;
}
private bool MatchesHardwareName(string sensorHardwareName)
{
if (HardwareNames.Count > 0)
{
if (HardwareNames.Any(hardwareName => sensorHardwareName.StartsWith(hardwareName)))
return true;
}
// Empty string matches always
if (HardwareName.Length == 0)
return true;
if (sensorHardwareName.StartsWith(HardwareName))
return true;
return false;
}
public bool Matches(ISensor sensor)
{
return sensor != null &&
sensor.Hardware.HardwareType == HardwareType &&
MatchesHardwareName(sensor.Hardware.Name) &&
sensor.SensorType == SensorType &&
sensor.Name == SensorName;
}
public bool Update(ISensor hwSensor, FanMode mode)
{
if (!Matches(hwSensor))
return false;
System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1} {2}, value: {3}, type: {4}",
hwSensor.Identifier, hwSensor.Hardware.Name, hwSensor.Name, hwSensor.Value, hwSensor.SensorType));
return Update(
String.Format("{0} {1}", hwSensor.Hardware.Name, hwSensor.Name),
hwSensor.Value, mode);
}
public bool Update(string name, float? newValue, FanMode mode)
{
if (!newValue.HasValue || newValue <= InvalidValue)
return false;
if (MaxValue.HasValue)
newValue = Math.Min(newValue.Value, MaxValue.Value);
if (AllSamples.Count == 0 || Math.Abs(AllSamples.Last() - newValue.Value) >= ValueDeadZone)
{
AllSamples.Add(newValue.Value);
while (AllSamples.Count > AvgSamples)
AllSamples.RemoveAt(0);
}
float avgValue = 0.0f;
foreach (var value in AllSamples)
avgValue += value;
Name = name;
Value = avgValue / AllSamples.Count;
CalculatedRPM = CalculateRPM(mode);
return true;
}
public bool IsValid(FanMode mode)
{
// If we have profile, but no sensor value to consume it
// it is invalid
if (Profiles.ContainsKey(mode) && !Value.HasValue)
return false;
return true;
}
private String Unit()
{
switch (SensorType)
{
case SensorType.Temperature:
return "℃";
case SensorType.Power:
return "W";
default:
return "";
}
}
public String FormattedValue()
{
if (!Value.HasValue)
return "";
String value = "";
if (AllSamples.Count > 0)
value += AllSamples.Last().ToString("F1") + Unit();
value += " (avg: " + Value.Value.ToString("F1") + Unit() + ")";
if (CalculatedRPM.HasValue)
value += " (" + CalculatedRPM.ToString() + "RPM)";
return value;
}
public ushort? CalculateRPM(FanMode mode)
{
if (!Profiles.ContainsKey(mode) || !Value.HasValue)
return null;
var profile = Profiles[mode];
return profile.CalculateRPM(Value.Value);
}
}
}

View file

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibreHardwareMonitor.Hardware;
using CommonHelpers;
namespace FanControl
{
internal class Program
{
static void Main(string[] args)
{
Instance.WithSentry(() =>
{
ApplicationConfiguration.Initialize();
Application.Run(new FanControlForm());
});
}
}
}

View file

@ -1,83 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FanControl {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FanControl.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon fan {
get {
object obj = ResourceManager.GetObject("fan", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon fan_white {
get {
object obj = ResourceManager.GetObject("fan_white", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View file

@ -1,127 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="fan" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\fan.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="fan_white" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\fan-white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,42 +0,0 @@
using CommonHelpers;
namespace FanControl
{
internal sealed class Settings : BaseSettings
{
public static readonly Settings Default = new Settings();
public Settings() : base("Settings")
{
TouchSettings = true;
}
public FanMode FanMode
{
get { return Get<FanMode>("FanMode", CommonHelpers.FanMode.Default); }
set { Set("FanMode", value); }
}
public bool AlwaysOnTop
{
get { return Get<bool>("AlwaysOnTop", true); }
set { Set("AlwaysOnTop", value); }
}
public int Silent4000RPMTemp
{
get { return ClampSilent4000RPMTemp(Get("Silent4000RPMTemp", 85)); }
set { Set("Silent4000RPMTemp", ClampSilent4000RPMTemp(value)); }
}
public bool EnableExperimentalFeatures
{
get { return Instance.IsDEBUG; }
}
private int ClampSilent4000RPMTemp(int value)
{
return Math.Clamp(value, 70, 90);
}
}
}

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="0.1.0.0" name="FanControl.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View file

@ -40,7 +40,6 @@ namespace PerformanceOverlay
SharedData_Update();
Instance.Open(TitleWithVersion, Settings.Default.EnableKernelDrivers, "Global\\PerformanceOverlay");
Instance.RunUpdater(TitleWithVersion);
if (Instance.WantsRunOnStartup)
startupManager.Startup = true;
@ -261,13 +260,6 @@ namespace PerformanceOverlay
var osdMode = Settings.Default.OSDMode;
// If Power Control is visible use temporarily full OSD
if (Settings.Default.EnableFullOnPowerControl)
{
if (SharedData<PowerControlSetting>.GetExistingValue(out var value) && value.Current == PowerControlVisible.Yes)
osdMode = OverlayMode.Full;
}
var osdOverlay = Overlays.GetOSD(osdMode, sensors);
try

View file

@ -49,4 +49,13 @@
</EmbeddedResource>
</ItemGroup>
</Project>
<ItemGroup>
<None Include="..\DisableCheckForUpdates.txt" Link="DisableCheckForUpdates.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\DisableSentryTracking.txt" Link="DisableSentryTracking.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -35,12 +35,6 @@ namespace PerformanceOverlay
set { Set("ShowOSD", value); }
}
public bool EnableFullOnPowerControl
{
get { return Get<bool>("EnableFullOnPowerControl", false); }
set { Set("EnableFullOnPowerControl", value); }
}
public bool EnableKernelDrivers
{
get { return Get<bool>("EnableKernelDrivers", false); }

View file

@ -1,488 +0,0 @@
using CommonHelpers;
using ExternalHelpers;
using hidapi;
using Microsoft.Win32;
using PowerControl.External;
using PowerControl.Helpers;
using RTSSSharedMemoryNET;
using System.ComponentModel;
using System.Diagnostics;
namespace PowerControl
{
internal class Controller : IDisposable
{
public const String Title = "Power Control";
public static readonly String TitleWithVersion = Title + " v" + Application.ProductVersion.ToString();
public const int KeyPressRepeatTime = 400;
public const int KeyPressNextRepeatTime = 90;
Container components = new Container();
System.Windows.Forms.NotifyIcon notifyIcon;
StartupManager startupManager = new StartupManager(Title);
Menu.MenuRoot rootMenu = MenuStack.Root;
OSD osd;
System.Windows.Forms.Timer osdDismissTimer;
bool isOSDToggled = false;
bool wasInternalDisplayConnected;
hidapi.HidDevice neptuneDevice = new hidapi.HidDevice(0x28de, 0x1205, 64);
SDCInput neptuneDeviceState = new SDCInput();
DateTime? neptuneDeviceNextKey;
System.Windows.Forms.Timer neptuneTimer;
ProfilesController? profilesController;
SharedData<PowerControlSetting> sharedData = SharedData<PowerControlSetting>.CreateNew();
static Controller()
{
//Dependencies.ValidateHidapi(TitleWithVersion);
//Dependencies.ValidateRTSSSharedMemoryNET(TitleWithVersion);
}
public Controller()
{
Instance.OnUninstall(() =>
{
startupManager.Startup = false;
});
Log.CleanupLogFiles(DateTime.UtcNow.AddDays(-7));
Log.LogToFile = true;
Log.LogToFileDebug = true;
Instance.RunOnce(TitleWithVersion, "Global\\PowerControl");
Instance.RunUpdater(TitleWithVersion);
if (Instance.WantsRunOnStartup)
startupManager.Startup = true;
var contextMenu = new System.Windows.Forms.ContextMenuStrip(components);
var notRunningRTSSItem = contextMenu.Items.Add("&RTSS is not running");
notRunningRTSSItem.Enabled = false;
contextMenu.Opening += delegate { notRunningRTSSItem.Visible = Dependencies.EnsureRTSS(null) && !OSDHelpers.IsLoaded; };
rootMenu.Init();
rootMenu.Visible = false;
rootMenu.Update();
rootMenu.CreateMenu(contextMenu);
rootMenu.VisibleChanged += delegate { updateOSD(); };
contextMenu.Items.Add(new ToolStripSeparator());
if (Settings.Default.EnableExperimentalFeatures)
{
var installEDIDItem = contextMenu.Items.Add("Install &Resolutions");
installEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); };
var replaceEDIDItem = contextMenu.Items.Add("Replace &Resolutions");
replaceEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); };
var uninstallEDIDItem = contextMenu.Items.Add("Revert &Resolutions");
uninstallEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); };
contextMenu.Opening += delegate
{
if (ExternalHelpers.DisplayConfig.IsInternalConnected == true)
{
var edid = Helpers.AMD.EDID.GetEDID() ?? new byte[0];
var edidInstalled = Resources.CRU_SteamDeck.SequenceEqual(edid);
installEDIDItem.Visible = edid.Length <= 128;
replaceEDIDItem.Visible = !edidInstalled && edid.Length > 128;
uninstallEDIDItem.Visible = edid.Length > 128;
}
else
{
installEDIDItem.Visible = false;
replaceEDIDItem.Visible = false;
uninstallEDIDItem.Visible = false;
}
};
contextMenu.Items.Add(new ToolStripSeparator());
}
if (startupManager.IsAvailable)
{
var startupItem = new ToolStripMenuItem("Run On Startup");
startupItem.Checked = startupManager.Startup;
startupItem.Click += delegate
{
startupManager.Startup = !startupManager.Startup;
startupItem.Checked = startupManager.Startup;
};
contextMenu.Items.Add(startupItem);
}
var missingRTSSItem = contextMenu.Items.Add("&Install missing RTSS");
missingRTSSItem.Click += delegate { Dependencies.OpenLink(Dependencies.RTSSURL); };
contextMenu.Opening += delegate { missingRTSSItem.Visible = !Dependencies.EnsureRTSS(null); };
var showGameProfilesItem = contextMenu.Items.Add("Show Game &Profiles");
showGameProfilesItem.Click += delegate { Dependencies.OpenLink(Helper.ProfileSettings.UserProfilesPath); };
contextMenu.Items.Add(new ToolStripSeparator());
var checkForUpdatesItem = contextMenu.Items.Add("&Check for Updates");
checkForUpdatesItem.Click += delegate { Instance.RunUpdater(TitleWithVersion, true); };
var helpItem = contextMenu.Items.Add("&Help");
helpItem.Click += delegate { Dependencies.OpenLink(Dependencies.SDTURL); };
contextMenu.Items.Add(new ToolStripSeparator());
var exitItem = contextMenu.Items.Add("&Exit");
exitItem.Click += ExitItem_Click;
notifyIcon = new System.Windows.Forms.NotifyIcon(components);
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.traffic_light_outline_light : Resources.traffic_light_outline;
notifyIcon.Text = TitleWithVersion;
notifyIcon.Visible = true;
notifyIcon.ContextMenuStrip = contextMenu;
// Fix first time context menu position
contextMenu.Show();
contextMenu.Close();
osdDismissTimer = new System.Windows.Forms.Timer(components);
osdDismissTimer.Interval = 3000;
osdDismissTimer.Tick += delegate (object? sender, EventArgs e)
{
if (!isOSDToggled)
{
hideOSD();
}
};
var osdTimer = new System.Windows.Forms.Timer(components);
osdTimer.Tick += OsdTimer_Tick;
osdTimer.Interval = 250;
osdTimer.Enabled = true;
profilesController = new ProfilesController();
GlobalHotKey.RegisterHotKey(Settings.Default.MenuUpKey, () =>
{
if (!OSDHelpers.IsOSDForeground())
return;
rootMenu.Next(-1);
setDismissTimer();
dismissNeptuneInput();
}, true);
GlobalHotKey.RegisterHotKey(Settings.Default.MenuDownKey, () =>
{
if (!OSDHelpers.IsOSDForeground())
return;
rootMenu.Next(1);
setDismissTimer();
dismissNeptuneInput();
}, true);
GlobalHotKey.RegisterHotKey(Settings.Default.MenuLeftKey, () =>
{
if (!OSDHelpers.IsOSDForeground())
return;
rootMenu.SelectNext(-1);
setDismissTimer();
dismissNeptuneInput();
});
GlobalHotKey.RegisterHotKey(Settings.Default.MenuRightKey, () =>
{
if (!OSDHelpers.IsOSDForeground())
return;
rootMenu.SelectNext(1);
setDismissTimer();
dismissNeptuneInput();
});
GlobalHotKey.RegisterHotKey(Settings.Default.MenuToggle, () =>
{
isOSDToggled = !rootMenu.Visible;
if (!OSDHelpers.IsOSDForeground())
return;
if (isOSDToggled)
{
showOSD();
}
else
{
hideOSD();
}
}, true);
if (Settings.Default.EnableNeptuneController)
{
neptuneTimer = new System.Windows.Forms.Timer(components);
neptuneTimer.Interval = 1000 / 60;
neptuneTimer.Tick += NeptuneTimer_Tick;
neptuneTimer.Enabled = true;
neptuneDevice.OnInputReceived += NeptuneDevice_OnInputReceived;
neptuneDevice.OpenDevice();
neptuneDevice.BeginRead();
}
if (Settings.Default.EnableVolumeControls)
{
GlobalHotKey.RegisterHotKey("VolumeUp", () =>
{
if (neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
rootMenu.Select("Brightness");
else
rootMenu.Select("Volume");
rootMenu.SelectNext(1);
setDismissTimer();
dismissNeptuneInput();
});
GlobalHotKey.RegisterHotKey("VolumeDown", () =>
{
if (neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
rootMenu.Select("Brightness");
else
rootMenu.Select("Volume");
rootMenu.SelectNext(-1);
setDismissTimer();
dismissNeptuneInput();
});
}
wasInternalDisplayConnected = ExternalHelpers.DisplayConfig.IsInternalConnected.GetValueOrDefault(false);
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
}
private void OsdTimer_Tick(object? sender, EventArgs e)
{
try
{
notifyIcon.Text = TitleWithVersion + ". RTSS Version: " + OSD.Version;
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.traffic_light_outline_light : Resources.traffic_light_outline;
}
catch
{
notifyIcon.Text = TitleWithVersion + ". RTSS Not Available.";
notifyIcon.Icon = Resources.traffic_light_outline_red;
}
var watchedProfiles = profilesController?.WatchedProfiles ?? new string[0];
if (watchedProfiles.Any())
notifyIcon.Text += ". Profile: " + string.Join(", ", watchedProfiles);
updateOSD();
}
private Task NeptuneDevice_OnInputReceived(hidapi.HidDeviceInputReceivedEventArgs e)
{
var input = SDCInput.FromBuffer(e.Buffer);
var filteredInput = new SDCInput()
{
buttons0 = input.buttons0,
buttons1 = input.buttons1,
buttons2 = input.buttons2,
buttons3 = input.buttons3,
buttons4 = input.buttons4,
buttons5 = input.buttons5
};
if (!neptuneDeviceState.Equals(filteredInput))
{
neptuneDeviceState = filteredInput;
neptuneDeviceNextKey = null;
}
// Consume only some events to avoid under-running SWICD
if (!neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
Thread.Sleep(50);
return new Task(() => { });
}
private void dismissNeptuneInput()
{
neptuneDeviceNextKey = DateTime.UtcNow.AddDays(1);
}
private void NeptuneTimer_Tick(object? sender, EventArgs e)
{
var input = neptuneDeviceState;
if (neptuneDeviceNextKey == null)
neptuneDeviceNextKey = DateTime.UtcNow.AddMilliseconds(KeyPressRepeatTime);
else if (neptuneDeviceNextKey < DateTime.UtcNow)
neptuneDeviceNextKey = DateTime.UtcNow.AddMilliseconds(KeyPressNextRepeatTime);
else
return; // otherwise it did not yet trigger
// Reset sequence: 3 dots + L4|R4|L5|R5
if (input.buttons0 == SDCButton0.BTN_L5 &&
input.buttons1 == SDCButton1.BTN_R5 &&
input.buttons2 == 0 &&
input.buttons3 == 0 &&
input.buttons4 == (SDCButton4.BTN_L4 | SDCButton4.BTN_R4) &&
input.buttons5 == SDCButton5.BTN_QUICK_ACCESS)
{
dismissNeptuneInput();
rootMenu.Show();
rootMenu.Reset();
notifyIcon.ShowBalloonTip(3000, TitleWithVersion, "Settings were reset to default.", ToolTipIcon.Info);
return;
}
// Display reset sequence
if (input.buttons0 == (SDCButton0.BTN_L1 | SDCButton0.BTN_R1) &&
input.buttons1 == 0 &&
input.buttons2 == 0 &&
input.buttons3 == 0 &&
input.buttons4 == 0 &&
input.buttons5 == SDCButton5.BTN_QUICK_ACCESS)
{
dismissNeptuneInput();
DisplayResolutionController.ResetCurrentResolution();
notifyIcon.ShowBalloonTip(3000, TitleWithVersion, "Resolution was reset.", ToolTipIcon.Info);
return;
}
if (!neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS) || !OSDHelpers.IsOSDForeground())
{
// schedule next repeat far in the future
dismissNeptuneInput();
hideOSD();
return;
}
rootMenu.Show();
setDismissTimer(false);
if (input.buttons1 != 0 || input.buttons2 != 0 || input.buttons3 != 0 || input.buttons4 != 0)
{
return;
}
else if (input.buttons0 == SDCButton0.BTN_DPAD_LEFT)
{
rootMenu.SelectNext(-1);
}
else if (input.buttons0 == SDCButton0.BTN_DPAD_RIGHT)
{
rootMenu.SelectNext(1);
}
else if (input.buttons0 == SDCButton0.BTN_DPAD_UP)
{
rootMenu.Next(-1);
}
else if (input.buttons0 == SDCButton0.BTN_DPAD_DOWN)
{
rootMenu.Next(1);
}
}
private void setDismissTimer(bool enabled = true)
{
osdDismissTimer.Stop();
if (enabled)
osdDismissTimer.Start();
}
private void hideOSD()
{
if (!rootMenu.Visible)
return;
Trace.WriteLine("Hide OSD");
rootMenu.Visible = false;
osdDismissTimer.Stop();
updateOSD();
}
private void showOSD()
{
if (rootMenu.Visible)
return;
Trace.WriteLine("Show OSD");
rootMenu.Update();
rootMenu.Visible = true;
updateOSD();
}
public void updateOSD()
{
sharedData.SetValue(new PowerControlSetting()
{
Current = rootMenu.Visible ? PowerControlVisible.Yes : PowerControlVisible.No
});
if (!rootMenu.Visible)
{
osdClose();
return;
}
try
{
// recreate OSD if index 0
if (OSDHelpers.OSDIndex("Power Control") == 0 && OSD.GetOSDCount() > 1)
osdClose();
if (osd == null)
{
osd = new OSD("Power Control");
Trace.WriteLine("Show OSD");
}
osd.Update(rootMenu.Render(null));
}
catch (SystemException)
{
}
}
private void ExitItem_Click(object? sender, EventArgs e)
{
Application.Exit();
}
public void Dispose()
{
using (profilesController) { }
components.Dispose();
osdClose();
}
private void osdClose()
{
try
{
if (osd != null)
{
osd.Dispose();
Trace.WriteLine("Close OSD");
}
osd = null;
}
catch (SystemException)
{
}
}
private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e)
{
var isInternalDisplayConnected = ExternalHelpers.DisplayConfig.IsInternalConnected.GetValueOrDefault(false);
if (wasInternalDisplayConnected == isInternalDisplayConnected)
return;
Log.TraceLine("SystemEvents_DisplaySettingsChanged: wasConnected={0}, isConnected={1}",
wasInternalDisplayConnected, isInternalDisplayConnected);
wasInternalDisplayConnected = isInternalDisplayConnected;
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(() =>
{
Options.RefreshRate.Instance?.Reset();
Options.FPSLimit.Instance?.Reset();
rootMenu.Update();
})
);
}
}
}

View file

@ -1,328 +0,0 @@
#region Copyright
/*******************************************************************************
Copyright(c) 2008 - 2022 Advanced Micro Devices, Inc. All Rights Reserved.
Copyright (c) 2002 - 2006 ATI Technologies Inc. All Rights Reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDED BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
File: ADL.cs
Purpose: Implements ADL interface
Description: Implements some of the methods defined in ADL interface.
********************************************************************************/
#endregion Copyright
#region Using
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using FARPROC = System.IntPtr;
using HMODULE = System.IntPtr;
#endregion Using
#region ATI.ADL
namespace PowerControl.Helpers.AMD
{
#region Export Struct
#region ADLAdapterInfo
/// <summary> ADLAdapterInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfo
{
/// <summary>The size of the structure</summary>
int Size;
/// <summary> Adapter Index</summary>
internal int AdapterIndex;
/// <summary> Adapter UDID</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string UDID;
/// <summary> Adapter Bus Number</summary>
internal int BusNumber;
/// <summary> Adapter Driver Number</summary>
internal int DriverNumber;
/// <summary> Adapter Function Number</summary>
internal int FunctionNumber;
/// <summary> Adapter Vendor ID</summary>
internal int VendorID;
/// <summary> Adapter Adapter name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string AdapterName;
/// <summary> Adapter Display name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Adapter Present status</summary>
internal int Present;
/// <summary> Adapter Exist status</summary>
internal int Exist;
/// <summary> Adapter Driver Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPath;
/// <summary> Adapter Driver Ext Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPathExt;
/// <summary> Adapter PNP String</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string PNPString;
/// <summary> OS Display Index</summary>
internal int OSDisplayIndex;
}
/// <summary> ADLAdapterInfo Array</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfoArray
{
/// <summary> ADLAdapterInfo Array </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_ADAPTERS)]
internal ADLAdapterInfo[] ADLAdapterInfo;
}
#endregion ADLAdapterInfo
#region ADLDisplayInfo
/// <summary> ADLDisplayID Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayID
{
/// <summary> Display Logical Index </summary>
internal int DisplayLogicalIndex;
/// <summary> Display Physical Index </summary>
internal int DisplayPhysicalIndex;
/// <summary> Adapter Logical Index </summary>
internal int DisplayLogicalAdapterIndex;
/// <summary> Adapter Physical Index </summary>
internal int DisplayPhysicalAdapterIndex;
}
/// <summary> ADLDisplayInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayInfo
{
/// <summary> Display Index </summary>
internal ADLDisplayID DisplayID;
/// <summary> Display Controller Index </summary>
internal int DisplayControllerIndex;
/// <summary> Display Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Display Manufacturer Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayManufacturerName;
/// <summary> Display Type : < The Display type. CRT, TV,CV,DFP are some of display types,</summary>
internal int DisplayType;
/// <summary> Display output type </summary>
internal int DisplayOutputType;
/// <summary> Connector type</summary>
internal int DisplayConnector;
///<summary> Indicating the display info bits' mask.<summary>
internal int DisplayInfoMask;
///<summary> Indicating the display info value.<summary>
internal int DisplayInfoValue;
}
/// <summary> ADLAdapterInfo Array</summary>
[StructLayout(LayoutKind.Sequential)]
internal class ADLDisplayInfoArray
{
/// <summary> ADLAdapterInfo Array </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_DISPLAYS)]
internal ADLDisplayInfo[] ADLAdapterInfo;
}
#endregion ADLDisplayInfo
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayEDIDData
{
internal int iSize;
internal int iFlag;
internal int iEDIDSize;
internal int iBlockIndex;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_EDIDDATA_SIZE)]
internal byte[] cEDIDData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
internal int[] iReserved;
};
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayEDIDDataX2
{
internal int iSize;
internal int iFlag;
internal int iEDIDSize;
internal int iIgnored;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)2 * ADL.ADL_MAX_EDIDDATA_SIZE)]
internal byte[] cEDIDData;
};
#region Radeon Image Sharpening
[StructLayout(LayoutKind.Sequential)]
internal struct ADL_RIS_SETTINGS
{
internal int GlobalEnable; //Global enable value
internal int GlobalSharpeningDegree; //Global sharpening value
internal int GlobalSharpeningDegree_MinLimit; //Gloabl sharpening slider min limit value
internal int GlobalSharpeningDegree_MaxLimit; //Gloabl sharpening slider max limit value
internal int GlobalSharpeningDegree_Step; //Gloabl sharpening step value
};
[StructLayout(LayoutKind.Sequential)]
internal struct ADL_RIS_NOTFICATION_REASON
{
internal int GlobalEnableChanged; //Set when Global enable value is changed
internal int GlobalSharpeningDegreeChanged; //Set when Global sharpening Degree value is changed
};
#endregion ADLDisplayInfo
#endregion Export Struct
#region ADL Class
/// <summary> ADL Class</summary>
internal static class ADL
{
internal const int ADL_DEFAULT_ADAPTER = 0;
internal const int ADL_DEFAULT_DISPLAY = 0;
#region Internal Constant
/// <summary> Define the maximum path</summary>
internal const int ADL_MAX_PATH = 256;
/// <summary> Define the maximum adapters</summary>
internal const int ADL_MAX_ADAPTERS = 40 /* 150 */;
/// <summary> Define the maximum displays</summary>
internal const int ADL_MAX_DISPLAYS = 40 /* 150 */;
/// <summary> Define the maximum device name length</summary>
internal const int ADL_MAX_DEVICENAME = 32;
/// <summary> Define the successful</summary>
internal const int ADL_SUCCESS = 0;
/// <summary> Define the failure</summary>
internal const int ADL_FAIL = -1;
/// <summary> Define the driver ok</summary>
internal const int ADL_DRIVER_OK = 0;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORTS = 8;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8;
/// <summary> Maximum number of ADLMOdes for the adapter </summary>
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
internal const int ADL_MAX_EDIDDATA_SIZE = 256;
internal const int ADL_MAX_EDID_EXTENSION_BLOCKS = 3;
internal const int ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED = 0x00000001;
#endregion Internal Constant
#region Internal Constant
/// <summary> Atiadlxx_FileName </summary>
internal const string Atiadlxx_FileName = "atiadlxx.dll";
/// <summary> Kernel32_FileName </summary>
internal const string Kernel32_FileName = "kernel32.dll";
#endregion Internal Constant
#region Export Delegates
/// <summary> ADL Memory allocation function allows ADL to callback for memory allocation</summary>
/// <param name="size">input size</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate IntPtr ADL_Main_Memory_Alloc(int size);
#endregion
#region DLLImport
[DllImport(Kernel32_FileName)]
internal static extern HMODULE GetModuleHandle(string moduleName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters, out IntPtr context);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_Destroy(IntPtr context);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_IsFunctionValid(IntPtr context, HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_NumberOfAdapters_Get(IntPtr context, out int numAdapters);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_AdapterInfo_Get(IntPtr context, out ADLAdapterInfoArray info, int inputSize);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_Active_Get(IntPtr context, int adapterIndex, out int status);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_DisplayInfo_Get(IntPtr context, int adapterIndex, out int numDisplays, out IntPtr displayInfoArray, int forceDetect);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_DFP_GPUScalingEnable_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_DFP_GPUScalingEnable_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_PreservedAspectRatio_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_PreservedAspectRatio_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_ImageExpansion_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_ImageExpansion_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_SCE_State_Get(IntPtr context, int adapterIndex, int displayIndex, out int current, out int support, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_SCE_State_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_RIS_Settings_Get(IntPtr context, int adapterIndex, out ADL_RIS_SETTINGS settings);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_RIS_Settings_Set(IntPtr context, int adapterIndex, ADL_RIS_SETTINGS settings, ADL_RIS_NOTFICATION_REASON reason);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_EdidData_Get(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDData edidData);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_EdidData_Set(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDDataX2 edidData);
#endregion DLLImport
#region ADL_Main_Memory_Alloc
/// <summary> Build in memory allocation function</summary>
/// <param name="size">input size</param>
/// <returns>return the memory buffer</returns>
internal static IntPtr ADL_Main_Memory_Alloc_(int size)
{
IntPtr result = Marshal.AllocCoTaskMem(size);
return result;
}
#endregion ADL_Main_Memory_Alloc
#region ADL_Main_Memory_Free
/// <summary> Build in memory free function</summary>
/// <param name="buffer">input buffer</param>
internal static void ADL_Main_Memory_Free(IntPtr buffer)
{
if (IntPtr.Zero != buffer)
{
Marshal.FreeCoTaskMem(buffer);
}
}
#endregion ADL_Main_Memory_Free
}
#endregion ADL Class
}
#endregion ATI.ADL

View file

@ -1,146 +0,0 @@
using System.Runtime.InteropServices;
using CommonHelpers;
namespace PowerControl.Helpers.AMD
{
internal class ADLContext : IDisposable
{
public IntPtr Context { get; private set; }
public ADLContext()
{
IntPtr context = IntPtr.Zero;
WithSafe(() => ADL.ADL2_Main_Control_Create(ADL.ADL_Main_Memory_Alloc_, 1, out context), true);
Context = context;
}
public bool IsValid
{
get { return Context != IntPtr.Zero; }
}
public ADLAdapterInfo[]? AdapterInfos
{
get
{
int res = ADL.ADL2_Adapter_NumberOfAdapters_Get(Context, out var numAdapters);
if (res != 0)
return null;
res = ADL.ADL2_Adapter_AdapterInfo_Get(Context,
out var adapters,
Marshal.SizeOf<ADLAdapterInfoArray>());
if (res != 0)
return null;
return adapters.ADLAdapterInfo.Take(numAdapters).ToArray();
}
}
public IEnumerable<ADLDisplayInfo> DisplayInfos
{
get
{
foreach (var adapter in AdapterInfos)
{
if (adapter.Present == 0)
continue;
foreach (var display in GetDisplayInfos(adapter.AdapterIndex))
{
if ((display.DisplayInfoValue & ADL.ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED) != ADL.ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED)
continue;
yield return display;
}
}
}
}
public IEnumerable<ADLDisplayInfo> GetDisplayInfos(int adapterIndex)
{
int res = ADL.ADL2_Display_DisplayInfo_Get(Context, adapterIndex, out var numDisplays, out var displays, 0);
if (res != 0)
yield break;
try
{
int sizeOf = Marshal.SizeOf<ADLDisplayInfo>();
for (int i = 0; i < numDisplays; i++)
{
var display = Marshal.PtrToStructure<ADLDisplayInfo>(IntPtr.Add(displays, i * sizeOf));
// TODO: why even though we pass adapterIndex?
if (display.DisplayID.DisplayLogicalAdapterIndex != adapterIndex)
continue;
yield return display;
}
}
finally
{
ADL.ADL_Main_Memory_Free(displays);
}
}
~ADLContext()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
if (Context != IntPtr.Zero)
{
WithSafe(() => ADL.ADL2_Main_Control_Destroy(Context));
Context = IntPtr.Zero;
}
}
private static ADLContext? instance;
private static readonly Mutex mutex = new Mutex();
public static T? WithSafe<T>(Func<ADLContext, T?> func, bool useInstance = false)
{
if (useInstance)
{
if (!mutex.WaitOne(200))
return default;
try
{
if (instance == null)
instance = new ADLContext();
return instance.WithSafe(() => func(instance));
}
finally
{
mutex.ReleaseMutex();
}
}
else
{
using (var context = new ADLContext())
{
return context.WithSafe(() => func(context));
}
}
}
public T? WithSafe<T>(Func<T?> func, bool force = false)
{
if (!IsValid && !force)
return default;
try
{
return func();
}
catch (DllNotFoundException e) { Log.TraceException("ADL: Method not found", e); }
catch (EntryPointNotFoundException e) { Log.TraceException("ADL: Entry point not found", e); }
catch (Exception e) { Log.TraceException("ADL: Generic Exception", e); }
return default;
}
}
}

View file

@ -1,56 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class DCE
{
internal enum Mode
{
Normal = 1,
Vivid
}
internal static Mode? Current
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_Display_SCE_State_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var current, out var support, out _);
if (res == 0 && support == 1)
return current == 2 ? Mode.Vivid : Mode.Normal;
return (DCE.Mode?)null;
});
}
set
{
ADLContext.WithSafe((context) =>
{
if (value is null)
return false;
ADL.ADL2_Display_SCE_State_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
(int)value
);
return true;
});
}
}
}
}

View file

@ -1,57 +0,0 @@
using System.Linq;
using System.Runtime.InteropServices;
namespace PowerControl.Helpers.AMD
{
internal class EDID
{
internal static byte[]? GetEDID(int displayIndex = ADL.ADL_DEFAULT_DISPLAY)
{
return ADLContext.WithSafe((context) =>
{
byte[] edid = new byte[0];
for (int block = 0; block < ADL.ADL_MAX_EDID_EXTENSION_BLOCKS; ++block)
{
ADLDisplayEDIDData displayEdidData = new ADLDisplayEDIDData()
{
iSize = Marshal.SizeOf<ADLDisplayEDIDData>(),
iBlockIndex = block,
};
int res = ADL.ADL2_Display_EdidData_Get(context.Context, ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY, ref displayEdidData);
if (res != 0)
break;
var blockBytes = displayEdidData.cEDIDData.Take(displayEdidData.iEDIDSize);
edid = edid.Concat(blockBytes).ToArray();
}
return edid;
});
}
internal static bool? SetEDID(byte[] value, int displayIndex = ADL.ADL_DEFAULT_DISPLAY)
{
return ADLContext.WithSafe((context) =>
{
var bytes = new byte[ADL.ADL_MAX_EDIDDATA_SIZE * 2];
value.CopyTo(bytes, 0);
var blockData = new ADLDisplayEDIDDataX2()
{
// TODO: Hack to send a full EDID at once
iSize = Marshal.SizeOf<ADLDisplayEDIDData>(),
cEDIDData = bytes,
iEDIDSize = value.Length,
};
int res = ADL.ADL2_Display_EdidData_Set(context.Context,
ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY,
ref blockData);
return res == 0;
});
}
}
}

View file

@ -1,184 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class GPUScaling
{
public enum ScalingMode
{
AspectRatio,
FullPanel,
Centered
}
internal static bool SafeResolutionChange
{
get { return Enabled; }
}
internal static bool IsSupported
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var support, out _, out _);
if (res == 0 && support == 1)
return true;
return false;
});
}
}
internal static bool Enabled
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var support, out var current, out _);
if (res == 0 && support == 1 && current == 1)
return true;
return false;
});
}
set
{
ADLContext.WithSafe((context) =>
{
ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var _, out var current, out _);
if (current == (value ? 1 : 0))
return true;
ADL.ADL2_DFP_GPUScalingEnable_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
value ? 1 : 0
);
return true;
});
}
}
internal static ScalingMode? Mode
{
get
{
return ADLContext.WithSafe((context) =>
{
int resAR = ADL.ADL2_Display_PreservedAspectRatio_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var supportAR, out var ar, out _);
int resIE = ADL.ADL2_Display_ImageExpansion_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var supportIE, out var ie, out _);
if (resAR != 0 || resIE != 0)
return default(ScalingMode?);
TraceLine("GPUScaling: ar={0}, ie={1}",
supportAR > 0 ? ar : -1, supportIE > 0 ? ie : -1);
if (ar == 1)
return ScalingMode.AspectRatio;
else if (ie == 1)
return ScalingMode.FullPanel;
else if (ie == 0 && supportIE == 1)
return ScalingMode.Centered;
return default(ScalingMode?);
});
}
set
{
ADLContext.WithSafe((context) =>
{
int resGS = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var _, out var current, out _);
if (current == 0)
{
resGS = ADL.ADL2_DFP_GPUScalingEnable_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
}
int resAR = -1;
int resIE = -1;
switch (value)
{
case ScalingMode.FullPanel:
resIE = ADL.ADL2_Display_ImageExpansion_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
break;
case ScalingMode.AspectRatio:
resAR = ADL.ADL2_Display_PreservedAspectRatio_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
break;
case ScalingMode.Centered:
resIE = ADL.ADL2_Display_ImageExpansion_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
0
);
break;
}
TraceLine("GPUScaling: mode={0} => resAR={1}, resIE={2}, resGS={3}",
value, resAR, resIE, resGS);
return true;
});
}
}
private static void TraceLine(string format, params object?[]? arg)
{
Trace.WriteLine(string.Format(format, arg));
}
}
}

View file

@ -1,62 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class ImageSharpening
{
internal static bool? Enabled
{
get
{
return GetSettings(out var settings) ? settings.GlobalEnable != 0 : null;
}
set
{
if (!GetSettings(out var settings))
return;
var enabled = value.GetValueOrDefault(false) ? 1 : 0;
if (settings.GlobalEnable == enabled)
return;
settings.GlobalEnable = enabled;
SetSettings(settings, new ADL_RIS_NOTFICATION_REASON() { GlobalEnableChanged = 1 });
}
}
private static bool GetSettings(out ADL_RIS_SETTINGS settings)
{
ADL_RIS_SETTINGS settings2 = default;
var result = ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_RIS_Settings_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
out settings2);
return res == 0;
});
settings = settings2;
return result;
}
private static bool SetSettings(ADL_RIS_SETTINGS settings, ADL_RIS_NOTFICATION_REASON reason)
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_RIS_Settings_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
settings, reason);
return res == 0;
});
}
}
}

View file

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
public class RadeonSoftware
{
public static void Kill()
{
foreach (var process in Process.GetProcessesByName("RadeonSoftware.exe"))
{
try { process.Kill(); }
catch { }
}
}
}
}

View file

@ -1,207 +0,0 @@
using CommonHelpers;
using System.Runtime.InteropServices;
namespace PowerControl.Helpers.AMD
{
public class RyzenSMU : IDisposable
{
public IntPtr MMIO_ADDR;
public uint MMIO_SIZE;
public uint RES_ADDR, MSG_ADDR, PARAM_ADDR;
public uint INDEX_ADDR, DATA_ADDR;
IntPtr mappedAddress;
IntPtr physicalHandle;
InpOut? inpOut;
public RyzenSMU()
{
}
~RyzenSMU()
{
Dispose();
}
public bool Opened
{
get { return physicalHandle != IntPtr.Zero && mappedAddress != IntPtr.Zero; }
}
public bool Open()
{
if (physicalHandle != IntPtr.Zero)
return true;
if (MMIO_ADDR == IntPtr.Zero || MMIO_SIZE == 0)
return false;
try
{
inpOut = new InpOut();
mappedAddress = inpOut.MapPhysToLin(MMIO_ADDR, MMIO_SIZE, out physicalHandle);
}
catch (Exception e)
{
Log.TraceException("RyzenSMU", e);
return false;
}
return Opened;
}
public void Dispose()
{
if (physicalHandle == IntPtr.Zero)
return;
GC.SuppressFinalize(this);
inpOut?.UnmapPhysicalMemory(physicalHandle, mappedAddress);
mappedAddress = IntPtr.Zero;
physicalHandle = IntPtr.Zero;
using (inpOut) { }
inpOut = null;
}
private uint RregRaw(uint reg)
{
return (uint)Marshal.ReadInt32(mappedAddress, (int)reg);
}
private void WregRaw(uint reg, uint value)
{
Marshal.WriteInt32(mappedAddress, (int)reg, (int)value);
}
private bool WregCheckedRaw(uint reg, uint value)
{
WregRaw(reg, value);
return RregRaw(reg) == value;
}
private bool Wreg(uint reg, uint value)
{
if (!Opened)
return false;
bool success = false;
try
{
if (reg < MMIO_SIZE)
{
return success = WregCheckedRaw(reg, value);
}
else
{
if (!WregCheckedRaw(INDEX_ADDR, reg))
return false;
if (!WregCheckedRaw(DATA_ADDR, value))
return false;
}
return success = true;
}
finally
{
Log.TraceLine("Wreg: reg={0:X}, value={1:X} => success={2}",
reg, value, success);
}
}
private bool Rreg(uint reg, out uint value)
{
value = default;
if (!Opened)
return false;
bool success = false;
try
{
if (reg < MMIO_SIZE)
{
value = RregRaw(reg);
}
else
{
if (!WregCheckedRaw(INDEX_ADDR, reg))
return false;
value = RregRaw(DATA_ADDR);
}
return success = true;
}
finally
{
Log.TraceLine("Rreg: reg={0:X} => read={1}/{1:X}, success={2}",
reg, value, success);
}
}
private uint WaitForResponse()
{
const int timeout = 20;
for (int i = 0; i < timeout; i++)
{
uint value;
if (!Rreg(RES_ADDR, out value))
return 0;
if (value != 0)
return value;
Thread.SpinWait(100);
}
return 0;
}
public bool SendMsg(ushort msg, uint param)
{
return SendMsg(msg, param, out _);
}
public bool SendMsg(ushort msg, uint param, out uint arg)
{
bool success = false;
arg = 0;
try
{
var res = WaitForResponse();
if (res != 0x1)
{
// Reset SMU state
if (res != 0)
Wreg(RES_ADDR, 1);
return false;
}
Wreg(RES_ADDR, 0);
Wreg(PARAM_ADDR, param);
Wreg(MSG_ADDR, msg);
res = WaitForResponse();
if (res != 0x1)
return false;
success = Rreg(PARAM_ADDR, out arg);
return success;
}
finally
{
Log.TraceLine(">> SendMsg: msg={0:X}, param={1:X} => arg={2}/{2:X}, success={3}",
msg, param, arg, success);
}
}
public bool SendMsg<T>(T msg, uint param)
{
return SendMsg((ushort)(object)msg, param, out _);
}
public bool SendMsg<T>(T msg, uint param, out uint arg) where T : unmanaged
{
return SendMsg((ushort)(object)msg, param, out arg);
}
}
}

View file

@ -1,474 +0,0 @@
using CommonHelpers;
using System.Diagnostics;
using static CommonHelpers.Log;
using Device = System.Tuple<string, ulong, ulong, uint[]>;
namespace PowerControl.Helpers.AMD
{
internal class VangoghGPU : IDisposable
{
public static readonly Device[] SupportedDevices =
{
// SteamDeck LCD
// F7A0131 = 0x063F0F00
new Device("AMD Custom GPU 0405", 0x80300000, 0x8037ffff, new uint[] { 0x43F3900, 0x43F3C05, 0x43F3E00, 0x063F0F00 }),
// SteamDeck OLED
// BIOS 105
// new Device("AMD Custom GPU 0932", 0x80600000, 0x8067ffff, new uint[] { 0x063F0E00 }),
// BIOS 107
new Device("AMD Custom GPU 0932", 0x80500000, 0x8057ffff, new uint[] { 0x063F0F00 }),
// SteamDeck unofficial APU drivers
// https://sourceforge.net/projects/amernimezone/files/Release%20Polaris-Vega-Navi/AMD%20SOC%20Driver%20Variant/
new Device("AMD Radeon 670M", 0x80300000, 0x8037ffff, new uint[] { 0x43F3900, 0x43F3C05, 0x43F3E00, 0x063F0F00 }),
new Device("AMD Radeon RX 670 Graphics", 0x80300000, 0x8037ffff, new uint[] { 0x43F3900, 0x43F3C05, 0x43F3E00, 0x063F0F00 }),
};
private static Device? DetectedDevice;
public static bool IsSupported
{
get { return DetectedDevice != null; }
}
public static VangoghGPU? Open()
{
if (DetectedDevice is null)
return null;
return Open(DetectedDevice);
}
public static VangoghGPU? Open(Device device)
{
if (device is null)
return null;
return OpenMMIO(new IntPtr((long)device.Item2), (uint)(device.Item3 - device.Item2 + 1));
}
public enum DetectionStatus
{
Detected,
Retryable,
NotDetected
}
public static DetectionStatus Detect()
{
var discoveredDevices = new Dictionary<string, string>();
foreach (var pnp in DeviceManager.GetDevices(DeviceManager.GUID_DISPLAY) ?? new string[0])
{
// Properly support many devices with the same name (pick the first one)
var name = DeviceManager.GetDeviceDesc(pnp);
if (name is not null && !discoveredDevices.ContainsKey(name))
discoveredDevices[name] = pnp;
}
foreach (var device in SupportedDevices)
{
var deviceName = device.Item1;
if (!discoveredDevices.ContainsKey(deviceName))
{
TraceLine("GPU: {0}: Not matched.", deviceName);
continue;
}
var devicePNP = discoveredDevices[deviceName];
var ranges = DeviceManager.GetDeviceMemResources(devicePNP);
if (ranges is null)
{
TraceError("GPU: {0}: {1}: No memory ranges", deviceName, devicePNP);
continue;
}
var expectedRange = new Tuple<UIntPtr, UIntPtr>(new UIntPtr(device.Item2), new UIntPtr(device.Item3));
if (!ranges.Contains(expectedRange))
{
TraceError("GPU: {0}: {1}: Memory range not found: {2}",
deviceName,
devicePNP,
String.Join(",", ranges.Select((item) => item.ToString()))
);
continue;
}
using (var gpu = Open(device))
{
if (gpu is null)
{
TraceError("GPU: {0}: {1}: Failed to open.", deviceName, devicePNP);
continue;
}
var smuVersion = gpu.SMUVersion;
if (!device.Item4.Contains(smuVersion))
{
// Silence SMU_Version = 0 since it happens fairly often
if (smuVersion != 0)
{
TraceError("GPU: {0}: {1}: SMU not supported: {2:X8} (IO: {3})", deviceName, devicePNP, smuVersion, expectedRange);
}
return DetectionStatus.Retryable;
}
TraceLine("GPU: {0}: Matched!", deviceName);
DetectedDevice = device;
return DetectionStatus.Detected;
}
}
DetectedDevice = null;
return DetectionStatus.Detected;
}
// Addresses:
// drivers/gpu/drm/amd/include/vangogh_ip_offset.h => MP1_BASE => 0x00016000
// drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c => mmMP1_SMN_C2PMSG_* => 0x0282/0x0292/0x029a
// drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_4_offset.h => mmPCIE_INDEX2/mmPCIE_DATA2 => 0x000e/0x000f
//
// Messages:
// drivers/gpu/drm/amd/pm/inc/smu_v11_5_ppsmc.h
private RyzenSMU smu;
~VangoghGPU()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
if (smu != null)
smu.Dispose();
}
private static VangoghGPU? OpenMMIO(IntPtr mmioAddress, uint mmioSize)
{
var gpu = new VangoghGPU
{
smu = new RyzenSMU()
{
MMIO_ADDR = mmioAddress,
MMIO_SIZE = mmioSize,
RES_ADDR = 0x0001629A * 4,
MSG_ADDR = 0x00016282 * 4,
PARAM_ADDR = 0x00016292 * 4,
INDEX_ADDR = 0xE * 4,
DATA_ADDR = 0xF * 4
}
};
if (!gpu.smu.Open())
return null;
return gpu;
}
public UInt32 SMUVersion
{
get { return getValue(Message.PPSMC_MSG_GetSmuVersion); }
}
public UInt32 IfVersion
{
get { return getValue(Message.PPSMC_MSG_GetDriverIfVersion); }
}
public Features SmuFeatures
{
get
{
UInt64 low = getValue(Message.PPSMC_MSG_GetEnabledSmuFeatures, 0);
UInt64 high = getValue(Message.PPSMC_MSG_GetEnabledSmuFeatures, 1);
return (Features)((high << 32) | low);
}
}
const uint MIN_TDP = 3000;
const uint MAX_TDP = 21000;
public uint SlowTDP
{
get { return getValue(Message.PPSMC_MSG_GetSlowPPTLimit); }
set { setValue(Message.PPSMC_MSG_SetSlowPPTLimit, value, MIN_TDP, MAX_TDP); }
}
public uint FastTDP
{
get { return getValue(Message.PPSMC_MSG_GetFastPPTLimit); }
set { setValue(Message.PPSMC_MSG_SetFastPPTLimit, value, MIN_TDP, MAX_TDP); }
}
public uint GfxClock
{
get { return getValue(Message.PPSMC_MSG_GetGfxclkFrequency); }
}
public uint FClock
{
get { return getValue(Message.PPSMC_MSG_GetFclkFrequency); }
}
const uint MIN_CPU_CLOCK = 1400;
const uint MAX_CPU_CLOCK = 4000;
public uint MinCPUClock
{
set { setCPUValue(Message.PPSMC_MSG_SetSoftMinCclk, value, MIN_CPU_CLOCK, MAX_CPU_CLOCK); }
}
public uint MaxCPUClock
{
set { setCPUValue(Message.PPSMC_MSG_SetSoftMaxCclk, value, MIN_CPU_CLOCK, MAX_CPU_CLOCK); }
}
const uint MIN_GFX_CLOCK = 200;
const uint MAX_GFX_CLOCK = 1900;
public uint HardMinGfxClock
{
set { setValue(Message.PPSMC_MSG_SetHardMinGfxClk, value, MIN_GFX_CLOCK, MAX_GFX_CLOCK); }
}
public uint SoftMinGfxClock
{
set { setValue(Message.PPSMC_MSG_SetSoftMinGfxclk, value, MIN_GFX_CLOCK, MAX_GFX_CLOCK); }
}
public uint SoftMaxGfxClock
{
set { setValue(Message.PPSMC_MSG_SetSoftMaxGfxClk, value, MIN_GFX_CLOCK, MAX_GFX_CLOCK); }
}
public Dictionary<string, uint> All
{
get
{
var dict = new Dictionary<string, uint>();
foreach (var key in ValuesGetters)
{
if (!this.smu.SendMsg(key, 0, out var value))
continue;
var keyString = key.ToString().Replace("PPSMC_MSG_Get", "");
dict[keyString] = value;
}
return dict;
}
}
private void setCPUValue(Message msg, uint value, uint min = UInt32.MinValue, uint max = UInt32.MaxValue)
{
// TODO: Hardcode CPUs
for (uint i = 0; i < 4; i++)
{
setValue(msg, value | (i << 20), min, max, (1 << 20) - 1);
}
}
private uint getValue(Message msg, UInt32 param = 0)
{
this.smu.SendMsg(msg, param, out var value);
return value;
}
private void setValue(Message msg, uint value, uint min = UInt32.MinValue, uint max = UInt32.MaxValue, uint clampMask = uint.MaxValue)
{
this.smu.SendMsg(msg, Math.Clamp(value & clampMask, min, max) | (value & ~clampMask));
}
private readonly Message[] ValuesGetters = new Message[]
{
Message.PPSMC_MSG_GetGfxclkFrequency,
Message.PPSMC_MSG_GetFclkFrequency,
Message.PPSMC_MSG_GetPptLimit,
Message.PPSMC_MSG_GetThermalLimit,
Message.PPSMC_MSG_GetFastPPTLimit,
Message.PPSMC_MSG_GetSlowPPTLimit,
// Those values return PPSMC_Result_CmdRejectedPrereq
Message.PPSMC_MSG_GetCurrentTemperature,
Message.PPSMC_MSG_GetCurrentPower,
Message.PPSMC_MSG_GetCurrentCurrent,
Message.PPSMC_MSG_GetCurrentFreq,
Message.PPSMC_MSG_GetCurrentVoltage,
Message.PPSMC_MSG_GetAverageCpuActivity,
Message.PPSMC_MSG_GetAverageGfxActivity,
Message.PPSMC_MSG_GetAveragePower,
Message.PPSMC_MSG_GetAverageTemperature
};
enum Result : byte
{
PPSMC_Result_OK = 0x1,
PPSMC_Result_Failed = 0xFF,
PPSMC_Result_UnknownCmd = 0xFE,
PPSMC_Result_CmdRejectedPrereq = 0xFD,
PPSMC_Result_CmdRejectedBusy = 0xFC
}
enum Tables : byte
{
TABLE_BIOS_IF = 0, // Called by BIOS
TABLE_WATERMARKS = 1, // Called by DAL through VBIOS
TABLE_CUSTOM_DPM = 2, // Called by Driver
TABLE_SPARE1 = 3,
TABLE_DPMCLOCKS = 4, // Called by Driver
TABLE_SPARE2 = 5, // Called by Tools
TABLE_MODERN_STDBY = 6, // Called by Tools for Modern Standby Log
TABLE_SMU_METRICS = 7, // Called by Driver
TABLE_COUNT = 8
}
enum Message : ushort
{
PPSMC_MSG_TestMessage = 0x1,
PPSMC_MSG_GetSmuVersion = 0x2,
PPSMC_MSG_GetDriverIfVersion = 0x3,
PPSMC_MSG_EnableGfxOff = 0x4,
PPSMC_MSG_DisableGfxOff = 0x5,
PPSMC_MSG_PowerDownIspByTile = 0x6, // ISP is power gated by default
PPSMC_MSG_PowerUpIspByTile = 0x7,
PPSMC_MSG_PowerDownVcn = 0x8, // VCN is power gated by default
PPSMC_MSG_PowerUpVcn = 0x9,
PPSMC_MSG_RlcPowerNotify = 0xA,
PPSMC_MSG_SetHardMinVcn = 0xB, // For wireless display
PPSMC_MSG_SetSoftMinGfxclk = 0xC, //Sets SoftMin for GFXCLK. Arg is in MHz
PPSMC_MSG_ActiveProcessNotify = 0xD,
PPSMC_MSG_SetHardMinIspiclkByFreq = 0xE,
PPSMC_MSG_SetHardMinIspxclkByFreq = 0xF,
PPSMC_MSG_SetDriverDramAddrHigh = 0x10,
PPSMC_MSG_SetDriverDramAddrLow = 0x11,
PPSMC_MSG_TransferTableSmu2Dram = 0x12,
PPSMC_MSG_TransferTableDram2Smu = 0x13,
PPSMC_MSG_GfxDeviceDriverReset = 0x14, //mode 2 reset during TDR
PPSMC_MSG_GetEnabledSmuFeatures = 0x15,
PPSMC_MSG_spare1 = 0x16,
PPSMC_MSG_SetHardMinSocclkByFreq = 0x17,
PPSMC_MSG_SetSoftMinFclk = 0x18, //Used to be PPSMC_MSG_SetMinVideoFclkFreq
PPSMC_MSG_SetSoftMinVcn = 0x19,
PPSMC_MSG_EnablePostCode = 0x1A,
PPSMC_MSG_GetGfxclkFrequency = 0x1B,
PPSMC_MSG_GetFclkFrequency = 0x1C,
PPSMC_MSG_AllowGfxOff = 0x1D,
PPSMC_MSG_DisallowGfxOff = 0x1E,
PPSMC_MSG_SetSoftMaxGfxClk = 0x1F,
PPSMC_MSG_SetHardMinGfxClk = 0x20,
PPSMC_MSG_SetSoftMaxSocclkByFreq = 0x21,
PPSMC_MSG_SetSoftMaxFclkByFreq = 0x22,
PPSMC_MSG_SetSoftMaxVcn = 0x23,
PPSMC_MSG_spare2 = 0x24,
PPSMC_MSG_SetPowerLimitPercentage = 0x25,
PPSMC_MSG_PowerDownJpeg = 0x26,
PPSMC_MSG_PowerUpJpeg = 0x27,
PPSMC_MSG_SetHardMinFclkByFreq = 0x28,
PPSMC_MSG_SetSoftMinSocclkByFreq = 0x29,
PPSMC_MSG_PowerUpCvip = 0x2A,
PPSMC_MSG_PowerDownCvip = 0x2B,
PPSMC_MSG_GetPptLimit = 0x2C,
PPSMC_MSG_GetThermalLimit = 0x2D,
PPSMC_MSG_GetCurrentTemperature = 0x2E,
PPSMC_MSG_GetCurrentPower = 0x2F,
PPSMC_MSG_GetCurrentVoltage = 0x30,
PPSMC_MSG_GetCurrentCurrent = 0x31,
PPSMC_MSG_GetAverageCpuActivity = 0x32,
PPSMC_MSG_GetAverageGfxActivity = 0x33,
PPSMC_MSG_GetAveragePower = 0x34,
PPSMC_MSG_GetAverageTemperature = 0x35,
PPSMC_MSG_SetAveragePowerTimeConstant = 0x36,
PPSMC_MSG_SetAverageActivityTimeConstant = 0x37,
PPSMC_MSG_SetAverageTemperatureTimeConstant = 0x38,
PPSMC_MSG_SetMitigationEndHysteresis = 0x39,
PPSMC_MSG_GetCurrentFreq = 0x3A,
PPSMC_MSG_SetReducedPptLimit = 0x3B,
PPSMC_MSG_SetReducedThermalLimit = 0x3C,
PPSMC_MSG_DramLogSetDramAddr = 0x3D,
PPSMC_MSG_StartDramLogging = 0x3E,
PPSMC_MSG_StopDramLogging = 0x3F,
PPSMC_MSG_SetSoftMinCclk = 0x40,
PPSMC_MSG_SetSoftMaxCclk = 0x41,
PPSMC_MSG_SetDfPstateActiveLevel = 0x42,
PPSMC_MSG_SetDfPstateSoftMinLevel = 0x43,
PPSMC_MSG_SetCclkPolicy = 0x44,
PPSMC_MSG_DramLogSetDramAddrHigh = 0x45,
PPSMC_MSG_DramLogSetDramBufferSize = 0x46,
PPSMC_MSG_RequestActiveWgp = 0x47,
PPSMC_MSG_QueryActiveWgp = 0x48,
PPSMC_MSG_SetFastPPTLimit = 0x49,
PPSMC_MSG_SetSlowPPTLimit = 0x4A,
PPSMC_MSG_GetFastPPTLimit = 0x4B,
PPSMC_MSG_GetSlowPPTLimit = 0x4C,
PPSMC_Message_Count = 0x4D,
}
[Flags]
public enum Features : UInt64
{
CCLK_DPM_BIT = 0,
FAN_CONTROLLER_BIT = 1,
DATA_CALCULATION_BIT = 2,
PPT_BIT = 3,
TDC_BIT = 4,
THERMAL_BIT = 5,
FIT_BIT = 6,
EDC_BIT = 7,
PLL_POWER_DOWN_BIT = 8,
ULV_BIT = 9,
VDDOFF_BIT = 10,
VCN_DPM_BIT = 11,
CSTATE_BOOST_BIT = 12,
FCLK_DPM_BIT = 13,
SOCCLK_DPM_BIT = 14,
MP0CLK_DPM_BIT = 15,
LCLK_DPM_BIT = 16,
SHUBCLK_DPM_BIT = 17,
DCFCLK_DPM_BIT = 18,
GFX_DPM_BIT = 19,
DS_GFXCLK_BIT = 20,
DS_SOCCLK_BIT = 21,
DS_LCLK_BIT = 22,
DS_DCFCLK_BIT = 23,
DS_SHUBCLK_BIT = 24,
GFX_TEMP_VMIN_BIT = 25,
S0I2_BIT = 26,
WHISPER_MODE_BIT = 27,
DS_FCLK_BIT = 28,
DS_SMNCLK_BIT = 29,
DS_MP1CLK_BIT = 30,
DS_MP0CLK_BIT = 31,
SMU_LOW_POWER_BIT = 32,
FUSE_PG_BIT = 33,
GFX_DEM_BIT = 34,
PSI_BIT = 35,
PROCHOT_BIT = 36,
CPUOFF_BIT = 37,
STAPM_BIT = 38,
S0I3_BIT = 39,
DF_CSTATES_BIT = 40,
PERF_LIMIT_BIT = 41,
CORE_DLDO_BIT = 42,
RSMU_LOW_POWER_BIT = 43,
SMN_LOW_POWER_BIT = 44,
THM_LOW_POWER_BIT = 45,
SMUIO_LOW_POWER_BIT = 46,
MP1_LOW_POWER_BIT = 47,
DS_VCN_BIT = 48,
CPPC_BIT = 49,
OS_CSTATES_BIT = 50,
ISP_DPM_BIT = 51,
A55_DPM_BIT = 52,
CVIP_DSP_DPM_BIT = 53,
MSMU_LOW_POWER_BIT = 54,
SOC_VOLTAGE_MON_BIT = 55,
ATHUB_PG_BIT = 56,
ECO_DEEPCSTATE_BIT = 57,
CC6_BIT = 58,
GFX_EDC_BIT = 59
}
}
}

View file

@ -1,214 +0,0 @@
using PowerControl.External;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers
{
internal class DeviceManager
{
public static string[]? GetDevices(Guid? classGuid)
{
string? filter = null;
int flags = CM_GETIDLIST_FILTER_PRESENT;
if (classGuid is not null)
{
filter = classGuid?.ToString("B").ToUpper();
flags |= CM_GETIDLIST_FILTER_CLASS;
}
var res = CM_Get_Device_ID_List_Size(out var size, filter, flags);
if (res != CR_SUCCESS)
return null;
char[] data = new char[size];
res = CM_Get_Device_ID_List(filter, data, size, flags);
if (res != CR_SUCCESS)
return null;
var result = new string(data);
var devices = result.Split('\0', StringSplitOptions.RemoveEmptyEntries);
return devices.ToArray();
}
public static string? GetDeviceDesc(String PNPString)
{
if (CM_Locate_DevNode(out var devInst, PNPString, 0) != 0)
return null;
if (!CM_Get_DevNode_Property(devInst, DEVPKEY_Device_DeviceDesc, out var deviceDesc, 0))
return null;
return deviceDesc;
}
public static IList<Tuple<UIntPtr, UIntPtr>>? GetDeviceMemResources(string PNPString)
{
int res = CM_Locate_DevNode(out var devInst, PNPString, 0);
if (res != CR_SUCCESS)
return null;
res = CM_Get_First_Log_Conf(out var logConf, devInst, ALLOC_LOG_CONF);
if (res != CR_SUCCESS)
res = CM_Get_First_Log_Conf(out logConf, devInst, BOOT_LOG_CONF);
if (res != CR_SUCCESS)
return null;
var ranges = new List<Tuple<UIntPtr, UIntPtr>>();
while (CM_Get_Next_Res_Des(out var newResDes, logConf, ResType_Mem, out _, 0) == 0)
{
CM_Free_Res_Des_Handle(logConf);
logConf = newResDes;
if (!CM_Get_Res_Des_Data<MEM_RESOURCE>(logConf, out var memResource, 0))
continue;
ranges.Add(new Tuple<UIntPtr, UIntPtr>(
memResource.MEM_Header.MD_Alloc_Base, memResource.MEM_Header.MD_Alloc_End));
}
CM_Free_Res_Des_Handle(logConf);
return ranges;
}
static bool CM_Get_DevNode_Property(IntPtr devInst, DEVPROPKEY propertyKey, out string result, int flags)
{
result = default;
// int length = 0;
// int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, null, ref length, flags);
// if (res != CR_SUCCESS && res != CR_BUFFER_TOO_SMALL)
// return false;
char[] buffer = new char[2048];
int length = buffer.Length;
int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, buffer, ref length, flags);
if (res != CR_SUCCESS)
return false;
if (propertyType != DEVPROP_TYPE_STRING)
return false;
result = new String(buffer, 0, length).Split('\0').First();
return true;
}
static bool CM_Get_Res_Des_Data<T>(IntPtr rdResDes, out T buffer, int ulFlags) where T : struct
{
buffer = default;
int res = CM_Get_Res_Des_Data_Size(out var size, rdResDes, ulFlags);
if (res != CR_SUCCESS)
return false;
int sizeOf = Marshal.SizeOf<T>();
if (sizeOf < size)
return false;
var addr = Marshal.AllocHGlobal(sizeOf);
try
{
res = CM_Get_Res_Des_Data(rdResDes, addr, size, 0);
if (res != CR_SUCCESS)
return false;
buffer = Marshal.PtrToStructure<T>(addr);
return true;
}
finally
{
Marshal.FreeHGlobal(addr);
}
}
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
static extern int CM_Locate_DevNode(out IntPtr pdnDevInst, string pDeviceID, int ulFlags);
[DllImport("setupapi.dll", CharSet = CharSet.Unicode)]
static extern int CM_Get_Device_ID_List_Size(out int idListlen, string? filter, int ulFlags);
[DllImport("setupapi.dll", CharSet = CharSet.Unicode)]
static extern int CM_Get_Device_ID_List(string? filter, char[] bffr, int bffrLen, int ulFlags);
[DllImport("CfgMgr32.dll", CharSet = CharSet.Unicode)]
static extern int CM_Get_DevNode_Property(IntPtr devInst, ref DEVPROPKEY propertyKey, out int propertyType, char[]? bffr, ref int bffrLen, int flags);
[DllImport("setupapi.dll")]
static extern int CM_Free_Res_Des_Handle(IntPtr rdResDes);
[DllImport("setupapi.dll")]
static extern int CM_Get_First_Log_Conf(out IntPtr rdResDes, IntPtr pdnDevInst, int ulFlags);
[DllImport("setupapi.dll")]
static extern int CM_Get_Next_Res_Des(out IntPtr newResDes, IntPtr rdResDes, int resType, out int resourceID, int ulFlags);
[DllImport("setupapi.dll")]
static extern int CM_Get_Res_Des_Data_Size(out int size, IntPtr rdResDes, int ulFlags);
[DllImport("setupapi.dll")]
static extern int CM_Get_Res_Des_Data(IntPtr rdResDes, IntPtr buffer, int size, int ulFlags);
[StructLayout(LayoutKind.Sequential)]
struct MEM_DES
{
internal uint MD_Count;
internal uint MD_Type;
internal UIntPtr MD_Alloc_Base;
internal UIntPtr MD_Alloc_End;
internal uint MD_Flags;
internal uint MD_Reserved;
};
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct MEM_RANGE
{
internal UIntPtr MR_Align; // specifies mask for base alignment
internal uint MR_nBytes; // specifies number of bytes required
internal UIntPtr MR_Min; // specifies minimum address of the range
internal UIntPtr MR_Max; // specifies maximum address of the range
internal uint MR_Flags; // specifies flags describing range (fMD flags)
internal uint MR_Reserved;
};
[StructLayout(LayoutKind.Sequential)]
struct MEM_RESOURCE
{
internal MEM_DES MEM_Header;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
internal MEM_RANGE[] MEM_Data;
};
[StructLayout(LayoutKind.Sequential)]
struct DEVPROPKEY
{
public Guid Guid;
public uint Pid;
public DEVPROPKEY(String guid, uint pid)
{
this.Guid = new Guid(guid);
this.Pid = pid;
}
};
const int ALLOC_LOG_CONF = 0x00000002; // Specifies the Alloc Element.
const int BOOT_LOG_CONF = 0x00000003; // Specifies the RM Alloc Element.
const int ResType_Mem = (0x00000001); // Physical address resource
const int CM_GETIDLIST_FILTER_PRESENT = 0x00000100;
const int CM_GETIDLIST_FILTER_CLASS = 0x00000200;
const int CR_SUCCESS = 0x0;
const int CR_BUFFER_TOO_SMALL = 0x1A;
const int DEVPROP_TYPE_STRING = 0x00000012;
static readonly DEVPROPKEY DEVPKEY_Device_DeviceDesc = new DEVPROPKEY("a45c254e-df1c-4efd-8020-67d146a850e0", 2);
internal static readonly Guid GUID_DISPLAY = new Guid("{4d36e968-e325-11ce-bfc1-08002be10318}");
}
}

View file

@ -1,377 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using CommonHelpers;
using static PowerControl.Helpers.PhysicalMonitorBrightnessController;
namespace PowerControl.Helpers
{
internal class DisplayResolutionController
{
public struct DisplayResolution : IComparable<DisplayResolution>
{
public int Width { get; set; }
public int Height { get; set; }
public DisplayResolution() { Width = 0; Height = 0; }
public DisplayResolution(int width, int height) { Width = width; Height = height; }
public DisplayResolution(String text)
{
var options = text.Split("x", 2);
Width = int.Parse(options[0]);
Height = int.Parse(options[1]);
}
public static bool operator ==(DisplayResolution sz1, DisplayResolution sz2) => sz1.Width == sz2.Width && sz1.Height == sz2.Height;
public static bool operator !=(DisplayResolution sz1, DisplayResolution sz2) => !(sz1 == sz2);
public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is DisplayResolution && Equals((DisplayResolution)obj);
public readonly bool Equals(DisplayResolution other) => this == other;
public override readonly int GetHashCode() => HashCode.Combine(Width, Height);
public int CompareTo(DisplayResolution other)
{
var index = Width.CompareTo(other.Width);
if (index == 0) index = Height.CompareTo(other.Height);
return index;
}
public override string ToString()
{
return String.Format("{0}x{1}", Width, Height);
}
}
private static IEnumerable<DEVMODE> FindAllDisplaySettings()
{
DEVMODE dm = new DEVMODE();
for (int i = 0; EnumDisplaySettings(null, i, ref dm); i++)
{
if (dm.dmFields.HasFlag(DM.PelsWidth) && dm.dmFields.HasFlag(DM.PelsHeight) && dm.dmFields.HasFlag(DM.PelsHeight))
yield return dm;
dm = new DEVMODE();
}
}
internal static DEVMODE? CurrentDisplaySettings()
{
DEVMODE dm = new DEVMODE();
if (EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref dm))
return dm;
return null;
}
private static bool SetDisplaySettings(String type, DEVMODE? best)
{
if (best == null)
return false;
DEVMODE oldDm = new DEVMODE();
if (!EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref oldDm))
return false;
Log.TraceObject("SetDisplaySettings:" + type + ":IN", oldDm);
DEVMODE dm = best.Value;
if (dm.dmPelsWidth == oldDm.dmPelsWidth &&
dm.dmPelsHeight == oldDm.dmPelsHeight &&
dm.dmDisplayFrequency == oldDm.dmDisplayFrequency)
{
Log.TraceLine(
"DispChange: {0}, already set: {1}x{2}@{3}",
type,
oldDm.dmPelsWidth, oldDm.dmPelsHeight, oldDm.dmDisplayFrequency);
return true;
}
var testChange = ChangeDisplaySettingsEx(
null, ref dm, IntPtr.Zero,
ChangeDisplaySettingsFlags.CDS_TEST, IntPtr.Zero);
var applyChange = DISP_CHANGE.NotUpdated;
Log.TraceObject("SetDisplaySettings:" + type + ":REQ", dm);
if (testChange == DISP_CHANGE.Successful)
{
applyChange = ChangeDisplaySettingsEx(
null, ref dm, IntPtr.Zero,
ChangeDisplaySettingsFlags.CDS_RESET, IntPtr.Zero);
}
DEVMODE newDm = new DEVMODE();
if (!EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref newDm))
return false;
Log.TraceObject("SetDisplaySettings:" + type + ":OUT", newDm);
Log.TraceLine(
"DispChange: {0}, Test: {1}, Set: {8}, from: {2}x{3}@{4}, to: {5}x{6}@{7}",
type, testChange,
oldDm.dmPelsWidth, oldDm.dmPelsHeight, oldDm.dmDisplayFrequency,
newDm.dmPelsWidth, newDm.dmPelsHeight, newDm.dmDisplayFrequency,
applyChange
);
return applyChange == DISP_CHANGE.Successful;
}
public static bool SetDisplaySettings(DisplayResolution size, int hz, String type = "DisplaySettings")
{
DEVMODE? best = FindAllDisplaySettings()
.Where((dm) => dm.dmPelsWidth == size.Width && dm.dmPelsHeight == size.Height)
.Where((dm) => dm.dmDisplayFrequency == hz)
.First();
if (best is null)
return false;
return SetDisplaySettings(type, best);
}
public static bool ResetCurrentResolution()
{
try
{
var dm = CurrentDisplaySettings();
// Reset to best default
var bestResolution = GetAllResolutions().Last();
var bestRefreshRate = GetRefreshRates(bestResolution).Max();
SetDisplaySettings(bestResolution, bestRefreshRate, "ResetToDefault");
return SetDisplaySettings("Reset", dm);
}
catch (Exception e)
{
Log.TraceException("ResetResolution", e);
return false;
}
}
public static DisplayResolution[] GetAllResolutions()
{
return FindAllDisplaySettings()
.Select((dm) => new DisplayResolution(dm.dmPelsWidth, dm.dmPelsHeight))
.ToImmutableSortedSet()
.ToArray();
}
public static DisplayResolution? GetResolution()
{
var dm = CurrentDisplaySettings();
if (dm is not null)
return new DisplayResolution(dm.Value.dmPelsWidth, dm.Value.dmPelsHeight);
return null;
}
public static bool SetResolution(DisplayResolution size)
{
DEVMODE? best = FindAllDisplaySettings()
.Where((dm) => dm.dmPelsWidth == size.Width && dm.dmPelsHeight == size.Height)
.MaxBy((dm) => dm.dmDisplayFrequency);
if (best == null)
return false;
return SetDisplaySettings("Resolution", best);
}
public static int[] GetRefreshRates(DisplayResolution? size = null)
{
if (size is null)
size = GetResolution();
if (size is null)
return new int[0];
return FindAllDisplaySettings()
.Where((dm) => dm.dmPelsWidth == size?.Width && dm.dmPelsHeight == size?.Height)
.Select((dm) => dm.dmDisplayFrequency)
.ToHashSet()
.ToArray();
}
public static int GetRefreshRate()
{
var dm = CurrentDisplaySettings();
if (dm is not null)
return dm.Value.dmDisplayFrequency;
return -1;
}
public static bool SetRefreshRate(int hz)
{
var current = GetResolution();
if (current is null)
return false;
return SetDisplaySettings(current.Value, hz, "SetRefreshRate");
}
enum DISP_CHANGE : int
{
Successful = 0,
Restart = 1,
Failed = -1,
BadMode = -2,
NotUpdated = -3,
BadFlags = -4,
BadParam = -5,
BadDualView = -6
}
[Flags()]
internal enum DM : int
{
Orientation = 0x1,
PaperSize = 0x2,
PaperLength = 0x4,
PaperWidth = 0x8,
Scale = 0x10,
Position = 0x20,
NUP = 0x40,
DisplayOrientation = 0x80,
Copies = 0x100,
DefaultSource = 0x200,
PrintQuality = 0x400,
Color = 0x800,
Duplex = 0x1000,
YResolution = 0x2000,
TTOption = 0x4000,
Collate = 0x8000,
FormName = 0x10000,
LogPixels = 0x20000,
BitsPerPixel = 0x40000,
PelsWidth = 0x80000,
PelsHeight = 0x100000,
DisplayFlags = 0x200000,
DisplayFrequency = 0x400000,
ICMMethod = 0x800000,
ICMIntent = 0x1000000,
MeduaType = 0x2000000,
DitherType = 0x4000000,
PanningWidth = 0x8000000,
PanningHeight = 0x10000000,
DisplayFixedOutput = 0x20000000
}
internal struct POINTL
{
public Int32 x;
public Int32 y;
};
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
internal struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[System.Runtime.InteropServices.FieldOffset(0)]
public string dmDeviceName;
[System.Runtime.InteropServices.FieldOffset(32)]
public Int16 dmSpecVersion;
[System.Runtime.InteropServices.FieldOffset(34)]
public Int16 dmDriverVersion;
[System.Runtime.InteropServices.FieldOffset(36)]
public Int16 dmSize;
[System.Runtime.InteropServices.FieldOffset(38)]
public Int16 dmDriverExtra;
[System.Runtime.InteropServices.FieldOffset(40)]
public DM dmFields;
[System.Runtime.InteropServices.FieldOffset(44)]
Int16 dmOrientation;
[System.Runtime.InteropServices.FieldOffset(46)]
Int16 dmPaperSize;
[System.Runtime.InteropServices.FieldOffset(48)]
Int16 dmPaperLength;
[System.Runtime.InteropServices.FieldOffset(50)]
Int16 dmPaperWidth;
[System.Runtime.InteropServices.FieldOffset(52)]
Int16 dmScale;
[System.Runtime.InteropServices.FieldOffset(54)]
Int16 dmCopies;
[System.Runtime.InteropServices.FieldOffset(56)]
Int16 dmDefaultSource;
[System.Runtime.InteropServices.FieldOffset(58)]
Int16 dmPrintQuality;
[System.Runtime.InteropServices.FieldOffset(44)]
public POINTL dmPosition;
[System.Runtime.InteropServices.FieldOffset(52)]
public Int32 dmDisplayOrientation;
[System.Runtime.InteropServices.FieldOffset(56)]
public Int32 dmDisplayFixedOutput;
[System.Runtime.InteropServices.FieldOffset(60)]
public short dmColor; // See note below!
[System.Runtime.InteropServices.FieldOffset(62)]
public short dmDuplex; // See note below!
[System.Runtime.InteropServices.FieldOffset(64)]
public short dmYResolution;
[System.Runtime.InteropServices.FieldOffset(66)]
public short dmTTOption;
[System.Runtime.InteropServices.FieldOffset(68)]
public short dmCollate; // See note below!
//[System.Runtime.InteropServices.FieldOffset(70)]
//[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
//public string dmFormName;
[System.Runtime.InteropServices.FieldOffset(102)]
public Int16 dmLogPixels;
[System.Runtime.InteropServices.FieldOffset(104)]
public Int32 dmBitsPerPel;
[System.Runtime.InteropServices.FieldOffset(108)]
public Int32 dmPelsWidth;
[System.Runtime.InteropServices.FieldOffset(112)]
public Int32 dmPelsHeight;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmDisplayFlags;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmNup;
[System.Runtime.InteropServices.FieldOffset(120)]
public Int32 dmDisplayFrequency;
}
[Flags()]
public enum ChangeDisplaySettingsFlags : uint
{
CDS_NONE = 0,
CDS_UPDATEREGISTRY = 0x00000001,
CDS_TEST = 0x00000002,
CDS_FULLSCREEN = 0x00000004,
CDS_GLOBAL = 0x00000008,
CDS_SET_PRIMARY = 0x00000010,
CDS_VIDEOPARAMETERS = 0x00000020,
CDS_ENABLE_UNSAFE_MODES = 0x00000100,
CDS_DISABLE_UNSAFE_MODES = 0x00000200,
CDS_RESET = 0x40000000,
CDS_RESET_EX = 0x20000000,
CDS_NORESET = 0x10000000
}
const int ENUM_CURRENT_SETTINGS = -1;
const int ENUM_REGISTRY_SETTINGS = -2;
[DllImport("user32.dll")]
static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll")]
static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
}
}

View file

@ -1,190 +0,0 @@
using PowerControl.External;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers
{
internal class ProcessorCores
{
public static int GetProcessorCoreCount()
{
return GetProcessorCores().Count();
}
public static IntPtr GetProcessorMask(bool firstThreadOnly = false)
{
Int64 mask = 0;
foreach (var process in GetProcessorCores())
{
// This works only up-to 63 CPUs
Int64 processorMask = (Int64)process.ProcessorMask.ToUInt64();
if (firstThreadOnly)
processorMask = LSB(processorMask);
mask |= processorMask;
}
return new IntPtr(mask);
}
public static bool HasSMTThreads()
{
foreach (var processorMask in GetProcessorMasks())
{
if (processorMask != LSB(processorMask))
return true;
}
return false;
}
public static bool IsUsingSMT(int processId)
{
try
{
var p = Process.GetProcessById(processId);
UInt64 mask = (UInt64)p.ProcessorAffinity.ToInt64();
foreach (var processorMask in GetProcessorMasks())
{
// look for CPU that has more than 1 thread
// and has both assigned to process
var filtered = mask & processorMask;
if (filtered != LSB(filtered))
return true;
}
return false;
}
catch(ArgumentException)
{
return false;
}
}
public static bool SetProcessSMT(int processId, bool allThreads)
{
try
{
var p = Process.GetProcessById(processId);
UInt64 mask = (UInt64)p.ProcessorAffinity.ToInt64();
foreach (var processorMask in GetProcessorMasks())
{
var selectedMask = mask & processorMask;
if (selectedMask == 0)
continue; // ignore not assigned processors
else if (allThreads)
mask |= processorMask; // assign all threads
else
mask = LSB(selectedMask) | (mask & ~processorMask); // assign only first thread
}
p.ProcessorAffinity = new IntPtr((Int64)mask);
return true;
}
catch (ArgumentException)
{
return false;
}
}
private static UInt64 LSB(UInt64 value)
{
return (UInt64)LSB((Int64)value);
}
private static Int64 LSB(Int64 value)
{
return (value & -value);
}
static IEnumerable<UInt64> GetProcessorMasks()
{
return GetProcessorCores().Select((p) => p.ProcessorMask.ToUInt64());
}
static IEnumerable<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> GetProcessorCores()
{
return GetLogicalProcessorInformation().Where((p) => p.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore);
}
static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] GetLogicalProcessorInformation()
{
int bufferSize = 0;
GetLogicalProcessorInformation(IntPtr.Zero, ref bufferSize);
if (bufferSize == 0)
return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0];
int sizeOfEntry = Marshal.SizeOf<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>();
int numEntries = bufferSize / sizeOfEntry;
var processors = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[numEntries];
var handle = Marshal.AllocHGlobal(bufferSize);
try
{
if (!GetLogicalProcessorInformation(handle, ref bufferSize))
return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0];
for (int i = 0; i < processors.Length; i++)
processors[i] = Marshal.PtrToStructure<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>(IntPtr.Add(handle, sizeOfEntry * i));
return processors;
}
finally
{
Marshal.FreeHGlobal(handle);
}
}
// Taken from: https://stackoverflow.com/a/63744912
[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
public byte Level;
public byte Associativity;
public ushort LineSize;
public uint Size;
public uint Type;
};
[StructLayout(LayoutKind.Explicit)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
[FieldOffset(0)] public byte ProcessorCore;
[FieldOffset(0)] public uint NumaNode;
[FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
[FieldOffset(0)] private UInt64 Reserved1;
[FieldOffset(8)] private UInt64 Reserved2;
};
public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
[StructLayout(LayoutKind.Sequential)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
public UIntPtr ProcessorMask;
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}
[DllImport("kernel32.dll")]
static extern bool GetLogicalProcessorInformation(IntPtr buffer, ref int bufferSize);
}
}

View file

@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using CommonHelpers;
namespace PowerControl.Helper
{
public class ProfileSettings : BaseSettings
{
public static String UserProfilesPath
{
get
{
var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
var exeFolder = Path.GetDirectoryName(exePath) ?? Directory.GetCurrentDirectory();
var exeGameProfiles = Path.Combine(exeFolder, "GameProfiles");
if (!Directory.Exists(exeGameProfiles))
Directory.CreateDirectory(exeGameProfiles);
return exeGameProfiles;
}
}
public String ProfileName { get; }
public ProfileSettings(string profileName) : base("PersistentSettings")
{
this.ProfileName = profileName;
this.ConfigFile = Path.Combine(UserProfilesPath, String.Format("PowerControl.Process.{0}.ini", profileName));
this.SettingChanging += delegate { };
this.SettingChanged += delegate { };
}
public String? GetValue(string key)
{
var result = base.Get(key, String.Empty);
if (result == String.Empty)
return null;
return result;
}
public int GetInt(string key, int defaultValue)
{
return base.Get(key, defaultValue);
}
public void SetValue(string key, string value)
{
base.Set(key, value);
}
}
}

View file

@ -1,37 +0,0 @@
namespace PowerControl.Menu
{
public abstract class MenuItem
{
public static readonly String[] OSDHelpers =
{
"<C0=008040><C1=0080C0><C2=C08080><C3=FF0000><C4=FFFFFF><C250=FF8000>",
"<A0=-4><A1=5><A2=-2><A5=-5><S0=-50><S1=50>",
};
public enum Colors : int
{
Green,
Blue,
Redish,
Red,
White
}
public String Name { get; set; } = "";
public bool Visible { get; set; } = true;
public bool Selectable { get; set; }
protected string Color(String text, Colors index)
{
return String.Format("<C{1}>{0}<C>", text, (int)index);
}
public abstract string Render(MenuItem? selected);
public abstract void CreateMenu(System.Windows.Forms.ContextMenuStrip contextMenu);
public abstract void Update();
public abstract void Reset();
public abstract void SelectNext(int change);
}
}

View file

@ -1,268 +0,0 @@
namespace PowerControl.Menu
{
public class MenuItemWithOptions : MenuItem
{
public IList<string> Options { get; set; } = new List<string>();
public string? SelectedOption { get; private set; }
public string? ActiveOption { get; set; }
public string? ProfileOption { get; set; }
public int ApplyDelay { get; set; }
public bool CycleOptions { get; set; } = true;
public string? PersistentKey { get; set; }
public bool PersistOnCreate { get; set; } = true;
public IList<MenuItemWithOptions> Impacts { get; set; } = new List<MenuItemWithOptions>();
public Func<string?>? CurrentValue { get; set; }
public Func<string[]?>? OptionsValues { get; set; }
public Func<string, string?>? ApplyValue { get; set; }
public Action<MenuItemWithOptions, string?, string>? ImpactedBy { get; set; }
public Action? AfterApply { get; set; }
public Func<string?>? ResetValue { get; set; }
public event Action<MenuItemWithOptions, String?, String> ValueChanged;
private System.Windows.Forms.Timer delayTimer = new System.Windows.Forms.Timer();
private ToolStripMenuItem toolStripItem = new ToolStripMenuItem();
private bool runAfterApply = false;
public MenuItemWithOptions()
{
this.Selectable = true;
ValueChanged += delegate { };
delayTimer.Tick += delegate (object? sender, EventArgs e)
{
if (delayTimer != null)
delayTimer.Stop();
FinalizeSet();
};
}
public override void Reset()
{
if (ResetValue == null)
return;
var resetOption = ResetValue();
if (resetOption == null || resetOption == ActiveOption)
return;
Set(resetOption, true, false);
}
public override void Update()
{
if (CurrentValue != null)
{
var result = CurrentValue();
if (result != null)
{
ActiveOption = result;
Visible = true;
}
else
{
Visible = false;
}
}
if (OptionsValues != null)
{
var result = OptionsValues();
if (result != null)
Options = result.ToList();
else
Visible = false;
}
if (ActiveOption == null && Options.Count > 0)
ActiveOption = Options.First();
}
public void Set(String value, bool immediate, bool refresh)
{
if (delayTimer != null)
delayTimer.Stop();
SelectedOption = value;
runAfterApply = refresh;
if (ApplyDelay == 0 || immediate)
{
FinalizeSet();
return;
}
delayTimer.Interval = ApplyDelay > 0 ? ApplyDelay : 1;
delayTimer.Enabled = true;
}
private void FinalizeSet()
{
var wasOption = ActiveOption;
if (ApplyValue != null && SelectedOption != null)
{
try
{
ActiveOption = ApplyValue(SelectedOption);
}
catch (Exception e)
{
CommonHelpers.Log.TraceException("FinalizeSet", Name, e);
Update();
}
}
else
ActiveOption = SelectedOption;
SelectedOption = null;
if (wasOption != ActiveOption && ActiveOption != null)
{
if (AfterApply != null)
AfterApply();
foreach (var impact in Impacts)
{
if (impact.ImpactedBy is not null)
impact.ImpactedBy(this, wasOption, ActiveOption);
impact.Update();
}
ValueChanged(this, wasOption, ActiveOption);
}
}
public override void CreateMenu(System.Windows.Forms.ContextMenuStrip contextMenu)
{
toolStripItem.Text = Name;
contextMenu.Items.Add(toolStripItem);
contextMenu.Opening += delegate
{
Update();
toolStripItem.DropDownItems.Clear();
foreach (var option in Options)
{
var item = new ToolStripMenuItem(option);
item.Checked = option == (SelectedOption ?? ActiveOption);
item.Click += delegate { Set(option, true, true); };
toolStripItem.DropDownItems.Add(item);
}
toolStripItem.Visible = Visible && Options.Count > 0;
};
}
private void SelectIndex(int index)
{
if (Options.Count == 0)
return;
Set(Options[Math.Clamp(index, 0, Options.Count - 1)], false, true);
}
public override void SelectNext(int change)
{
int index = Options.IndexOf(SelectedOption ?? ActiveOption ?? "");
if (index < 0)
{
if (change > 0)
SelectIndex(0); // select first
else
SelectIndex(Options.Count); // select last
return;
}
if (CycleOptions)
SelectIndex((index + change + Options.Count) % Options.Count);
else
SelectIndex(index + change);
}
public override string Render(MenuItem? selected)
{
string output = "";
if (selected == this)
output += Color(Name + ":", Colors.White).PadRight(30);
else
output += Color(Name + ":", Colors.Blue).PadRight(30);
output += optionText(SelectedOption ?? ActiveOption);
if (SelectedOption != null && ActiveOption != SelectedOption)
output += " (active: " + optionText(ActiveOption) + ")";
if (ProfileOption != null)
{
if (ProfileOption != ActiveOption && ProfileOption != SelectedOption)
output += " (profile: " + optionText(ProfileOption) + ")";
else
output += " [P]";
}
return output;
}
private String optionText(String? option)
{
String text;
if (option is null)
text = Color("?", Colors.White);
else if (option == (SelectedOption ?? ActiveOption))
text = Color(option, Colors.Red);
else if (option == ActiveOption)
text = Color(option, Colors.White);
else
text = Color(option, Colors.Green);
return text;
}
public static IEnumerable<MenuItemWithOptions> Order(IEnumerable<MenuItemWithOptions> items)
{
HashSet<MenuItemWithOptions> processed = new HashSet<MenuItemWithOptions>();
// Try to run iteratively up to 10 times
for (int i = 0; i < 10; i++)
{
List<MenuItemWithOptions> leftItems = new List<MenuItemWithOptions>();
foreach (var item in items)
{
bool valid = item.Impacts.All((impactsItem) => processed.Contains(impactsItem));
if (valid)
{
processed.Add(item);
yield return item;
}
else
{
leftItems.Add(item);
}
}
if (leftItems.Count() == 0)
yield break;
items = leftItems;
}
CommonHelpers.Log.TraceLine("PowerControl: Failed to order items: {0}",
string.Join(", ", items.Select((item) => item.Name)));
foreach (var item in items)
{
yield return item;
}
}
}
}

View file

@ -1,156 +0,0 @@
using System.Text;
namespace PowerControl.Menu
{
public class MenuRoot : MenuItem
{
public IList<MenuItem> Items { get; } = new List<MenuItem>();
public MenuItem? Selected;
public event Action VisibleChanged;
public event Action<MenuItemWithOptions, String?, String> ValueChanged;
public MenuRoot()
{
VisibleChanged += delegate { };
ValueChanged += delegate { };
}
public MenuItem? this[String name]
{
get
{
foreach (var item in Items)
{
if (item.Name == name)
return item;
}
return null;
}
}
public IEnumerable<MenuItemWithOptions> AllMenuItemOptions()
{
foreach (var item in Items)
{
if (item is MenuItemWithOptions)
yield return (MenuItemWithOptions)item;
}
}
public override void CreateMenu(System.Windows.Forms.ContextMenuStrip contextMenu)
{
foreach (var item in Items)
item.CreateMenu(contextMenu);
}
public void Init()
{
foreach (var item in AllMenuItemOptions())
{
item.ValueChanged += MenuItem_ValueChanged;
}
}
private void MenuItem_ValueChanged(MenuItemWithOptions item, String? was, String isNow)
{
ValueChanged(item, was, isNow);
}
public override void Update()
{
foreach (var item in Items)
item.Update();
}
public override void Reset()
{
foreach (var item in Items)
item.Reset();
VisibleChanged();
}
public override string Render(MenuItem? parentSelected)
{
var sb = new StringBuilder();
sb.AppendJoin("", OSDHelpers);
if (Name != "")
sb.AppendLine(Color(Name, Colors.Blue));
bool lastSelectable = false;
foreach (var item in Items)
{
if (!item.Visible)
continue;
if (!item.Selectable && !lastSelectable)
continue;
var lines = item.Render(Selected).Split("\r\n").Select(line => " " + line);
foreach (var line in lines)
sb.AppendLine(line);
lastSelectable = item.Selectable;
}
return sb.ToString();
}
public bool Show()
{
if (Visible)
return false;
Visible = true;
Update();
VisibleChanged();
return true;
}
public void Next(int change)
{
if (Show())
return;
int index = -1;
if (Selected is not null)
index = Items.IndexOf(Selected);
if (index < 0 && change < 0)
index = Items.Count; // Select last item if want to iterate down
for (int i = 0; i < Items.Count; i++)
{
index = (index + change + Items.Count) % Items.Count;
var item = Items[index];
if (item.Visible && item.Selectable)
{
Selected = item;
VisibleChanged();
return;
}
}
}
public override void SelectNext(int change)
{
if (Show())
return;
if (Selected != null)
{
Selected.SelectNext(change);
VisibleChanged();
}
}
public MenuItem? Select(String name)
{
Selected = this[name];
if (Selected is null)
return null;
Show();
VisibleChanged();
return Selected;
}
}
}

View file

@ -1,34 +0,0 @@
namespace PowerControl.Menu
{
public class MenuItemSeparator : MenuItem
{
private ToolStripItem toolStripItem = new ToolStripSeparator();
public MenuItemSeparator()
{
Selectable = false;
}
public override void CreateMenu(System.Windows.Forms.ContextMenuStrip contextMenu)
{
contextMenu.Items.Add(toolStripItem);
}
public override string Render(MenuItem? selected)
{
return Color("---", Colors.Blue);
}
public override void SelectNext(int change)
{
}
public override void Update()
{
}
public override void Reset()
{
}
}
}

View file

@ -1,49 +0,0 @@
using System.Globalization;
namespace PowerControl
{
internal class MenuStack
{
public static Menu.MenuRoot Root = new Menu.MenuRoot()
{
Name = String.Format(
"\r\n\r\nPower Control v{0} <C4>-<C> <TIME={1}>\r\n",
Application.ProductVersion.ToString(),
Is24hClock ? "%H:%M:%S" : "%I:%M:%S %p"
),
Items =
{
Options.Profiles.Instance,
new Menu.MenuItemSeparator(),
Options.Brightness.Instance,
Options.Volume.Instance,
new Menu.MenuItemSeparator(),
Options.Resolution.Instance,
Options.RefreshRate.Instance,
Options.FPSLimit.Instance,
Options.GPUScalingItem.Instance,
#if DEBUG
Options.Sharpening.Instance,
#endif
Options.GPUColors.Instance,
new Menu.MenuItemSeparator(),
Options.TDP.Instance,
Options.GPUFrequency.Instance,
Options.CPUFrequency.Instance,
Options.SMT.Instance,
new Menu.MenuItemSeparator(),
Options.PerformanceOverlay.EnabledInstance,
Options.PerformanceOverlay.ModeInstance,
Options.PerformanceOverlay.KernelDriversInstance,
Options.FanControl.Instance,
Options.SteamController.Instance,
Options.BatteryChargeLimit.Instance
}
};
private static bool Is24hClock
{
get => DateTimeFormatInfo.CurrentInfo.ShortTimePattern.Contains("HH");
}
}
}

View file

@ -1,32 +0,0 @@
using CommonHelpers;
namespace PowerControl.Options
{
public static class BatteryChargeLimit
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Charge Limit",
ApplyDelay = 1000,
Options = { "70%", "80%", "90%", "100%" },
ActiveOption = "?",
ApplyValue = (selected) =>
{
var value = int.Parse(selected.ToString().TrimEnd('%'));
using (var vlv0100 = new Vlv0100())
{
if (!vlv0100.Open())
return null;
vlv0100.SetMaxBatteryCharge(value);
var newValue = vlv0100.GetMaxBatteryCharge();
if (newValue is null)
return null;
return newValue.ToString() + "%";
}
}
};
}
}

View file

@ -1,21 +0,0 @@
namespace PowerControl.Options
{
public static class Brightness
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Brightness",
Options = { "0", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", "60", "65", "70", "75", "80", "85", "90", "95", "100" },
CycleOptions = false,
CurrentValue = delegate ()
{
return Helpers.WindowsSettingsBrightnessController.Get(5.0).ToString();
},
ApplyValue = (selected) =>
{
Helpers.WindowsSettingsBrightnessController.Set(int.Parse(selected));
return Helpers.WindowsSettingsBrightnessController.Get(5.0).ToString();
}
};
}
}

View file

@ -1,72 +0,0 @@
using CommonHelpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class CPUFrequency
{
public const string SoftMin = "SoftMin";
public const string SoftMax = "SoftMax";
public const int DefaultMin = 1400;
public const int DefaultMax = 3500;
public static PersistedOptions UserOptions()
{
var options = new PersistedOptions("CPUFrequency");
if (options.GetOptions().Count() == 0)
{
options.SetOptions(new PersistedOptions.Option[]
{
options.ForOption("Default").Set(SoftMin, DefaultMin).Set(SoftMax, DefaultMax),
options.ForOption("Power-Save").Set(SoftMin, 1400).Set(SoftMax, 1800),
options.ForOption("Balanced").Set(SoftMin, 2200).Set(SoftMax, 2800),
options.ForOption("Max").Set(SoftMin, 3000).Set(SoftMax, 3500),
});
}
return options;
}
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "CPU",
PersistentKey = "CPUFrequency",
PersistOnCreate = false,
OptionsValues = () => { return UserOptions().GetOptions(); },
ApplyDelay = 1000,
ActiveOption = "?",
Visible = VangoghGPU.IsSupported,
ResetValue = () => { return UserOptions().GetOptions().FirstOrDefault("Default"); },
ApplyValue = (selected) =>
{
if (!AntiCheatSettings.Default.AckAntiCheat(
Controller.TitleWithVersion,
"Changing CPU frequency requires kernel access for a short period.",
"Leave the game if it uses anti-cheat protection."))
return null;
var selectedOption = UserOptions().ForOption(selected);
if (!selectedOption.Exist)
return null;
var softMin = selectedOption.Get(SoftMin, DefaultMin);
var softMax = selectedOption.Get(SoftMax, DefaultMax);
return CommonHelpers.Instance.WithGlobalMutex<string>(200, () =>
{
using (var sd = VangoghGPU.Open())
{
if (sd is null)
return null;
sd.MinCPUClock = (uint)softMin;
sd.MaxCPUClock = (uint)softMax;
return selected;
}
});
}
};
}
}

View file

@ -1,117 +0,0 @@
using CommonHelpers;
using PowerControl.Helpers;
namespace PowerControl.Options
{
public static class FPSLimit
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "FPS Limit",
PersistentKey = "FPSLimit",
ApplyDelay = 500,
ResetValue = () => { return "Off"; },
OptionsValues = delegate ()
{
var refreshRate = DisplayResolutionController.GetRefreshRate();
return new string[]
{
(refreshRate / 4).ToString(),
(refreshRate / 2).ToString(),
refreshRate.ToString(),
"Off"
};
},
CurrentValue = delegate ()
{
try
{
if (!Dependencies.EnsureRTSS(null))
return "?";
RTSS.LoadProfile();
if (RTSS.GetProfileProperty("FramerateLimit", out int framerate))
return (framerate == 0) ? "Off" : framerate.ToString();
return null;
}
catch (Exception e)
{
#if DEBUG
CommonHelpers.Log.TraceException("RTSS", e);
#endif
return "?";
}
},
ApplyValue = (selected) =>
{
try
{
if (!Dependencies.EnsureRTSS(Controller.TitleWithVersion))
return null;
int framerate = 0;
if (selected != "Off")
framerate = int.Parse(selected);
RTSS.LoadProfile();
if (!RTSS.SetProfileProperty("FramerateLimit", framerate))
return null;
if (!RTSS.GetProfileProperty("FramerateLimit", out framerate))
return null;
RTSS.SaveProfile();
RTSS.UpdateProfiles();
return (framerate == 0) ? "Off" : framerate.ToString();
}
catch (Exception e)
{
CommonHelpers.Log.TraceException("RTSS", e);
}
return null;
},
ImpactedBy = (option, was, isNow) =>
{
if (Instance is null)
return;
try
{
if (!Dependencies.EnsureRTSS(null))
return;
var refreshRate = DisplayResolutionController.GetRefreshRate();
if (refreshRate <= 0)
return;
RTSS.LoadProfile();
RTSS.GetProfileProperty("FramerateLimit", out int fpsLimit);
if (fpsLimit == 0)
return;
// FPSLimit, RR => outcome
// 50 + 60 => 60 (div 1)
// 25 + 60 => 30 (div 2)
// 10 + 60 => 15 (div 6)
// 60 + 50 => 50 (div 0)
// 50 + 40 => 40 (div 0)
// 60 + 30 => 30 (div 0)
int div = refreshRate / fpsLimit;
if (div >= 4)
fpsLimit = refreshRate / 4;
else if (div >= 2)
fpsLimit = refreshRate / 2;
else
fpsLimit = refreshRate;
RTSS.SetProfileProperty("FramerateLimit", fpsLimit);
RTSS.SaveProfile();
RTSS.UpdateProfiles();
}
catch (Exception e)
{
#if DEBUG
CommonHelpers.Log.TraceException("RTSS", e);
#endif
}
}
};
}
}

View file

@ -1,34 +0,0 @@
using CommonHelpers;
namespace PowerControl.Options
{
public static class FanControl
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "FAN",
PersistentKey = "FANMode",
PersistOnCreate = false,
ApplyDelay = 500,
OptionsValues = delegate ()
{
return Enum.GetNames<FanMode>();
},
CurrentValue = delegate ()
{
if (SharedData<FanModeSetting>.GetExistingValue(out var value))
return value.Current.ToString();
return null;
},
ApplyValue = (selected) =>
{
if (!SharedData<FanModeSetting>.GetExistingValue(out var value))
return null;
value.Desired = Enum.Parse<FanMode>(selected);
if (!SharedData<FanModeSetting>.SetExistingValue(value))
return null;
return selected;
}
};
}
}

View file

@ -1,28 +0,0 @@
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class GPUColors
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Colors",
PersistentKey = "GPUColors",
ApplyDelay = 1000,
Options = Enum.GetNames<DCE.Mode>(),
CurrentValue = delegate ()
{
return DCE.Current?.ToString();
},
ApplyValue = (selected) =>
{
if (DCE.Current is null)
return null;
DCE.Current = Enum.Parse<DCE.Mode>(selected);
RadeonSoftware.Kill();
return DCE.Current.ToString();
}
};
}
}

View file

@ -1,73 +0,0 @@
using CommonHelpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class GPUFrequency
{
public const string HardMin = "HardMin";
public const string SoftMax = "SoftMax";
public const int DefaultMin = 200;
public const int DefaultMax = 1600;
public static PersistedOptions UserOptions()
{
var options = new PersistedOptions("GPUFrequency");
if (options.GetOptions().Count() == 0)
{
options.SetOptions(new PersistedOptions.Option[]
{
options.ForOption("Default").Set(HardMin, DefaultMin).Set(SoftMax, DefaultMax),
options.ForOption("400MHz").Set(HardMin, 400).Set(SoftMax, DefaultMax),
options.ForOption("800MHz").Set(HardMin, 800).Set(SoftMax, DefaultMax),
options.ForOption("1200MHz").Set(HardMin, 1200).Set(SoftMax, DefaultMax),
options.ForOption("1600MHz").Set(HardMin, 1600).Set(SoftMax, DefaultMax),
});
}
return options;
}
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "GPU",
PersistentKey = "GPUFrequency",
PersistOnCreate = false,
OptionsValues = () => { return UserOptions().GetOptions(); },
ApplyDelay = 1000,
Visible = VangoghGPU.IsSupported,
ActiveOption = "?",
ResetValue = () => { return UserOptions().GetOptions().FirstOrDefault("Default"); },
ApplyValue = (selected) =>
{
if (!AntiCheatSettings.Default.AckAntiCheat(
Controller.TitleWithVersion,
"Changing GPU frequency requires kernel access for a short period.",
"Leave the game if it uses anti-cheat protection."))
return null;
var selectedOption = UserOptions().ForOption(selected);
if (!selectedOption.Exist)
return null;
var hardMin = selectedOption.Get(HardMin, DefaultMin);
var softMax = selectedOption.Get(SoftMax, DefaultMax);
return CommonHelpers.Instance.WithGlobalMutex<string>(200, () =>
{
using (var sd = VangoghGPU.Open())
{
if (sd is null)
return null;
sd.HardMinGfxClock = (uint)hardMin;
sd.SoftMaxGfxClock = (uint)softMax;
return selected;
}
});
}
};
}
}

View file

@ -1,46 +0,0 @@
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class GPUScalingItem
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "GPU Scaling",
PersistentKey = "GPUScaling",
ApplyDelay = 1000,
Options = Enum.GetNames<GPUScaling.ScalingMode>().Prepend("Off").ToArray(),
CurrentValue = delegate ()
{
if (!GPUScaling.IsSupported)
return null;
if (!GPUScaling.Enabled)
return "Off";
return GPUScaling.Mode.ToString();
},
ApplyValue = (selected) =>
{
if (!GPUScaling.IsSupported)
return null;
if (selected == "Off")
GPUScaling.Enabled = false;
else
GPUScaling.Mode = Enum.Parse<GPUScaling.ScalingMode>(selected);
// Since the RadeonSoftware will try to revert values
RadeonSoftware.Kill();
if (!GPUScaling.Enabled)
return "Off";
return GPUScaling.Mode.ToString();
},
Impacts =
{
Resolution.Instance,
RefreshRate.Instance,
FPSLimit.Instance
}
};
}
}

View file

@ -1,84 +0,0 @@
using CommonHelpers;
namespace PowerControl.Options
{
public static class PerformanceOverlay
{
public static Menu.MenuItemWithOptions EnabledInstance = new Menu.MenuItemWithOptions()
{
Name = "OSD",
PersistentKey = "PerformanceOverlay",
ApplyDelay = 500,
OptionsValues = delegate ()
{
return Enum.GetNames<OverlayEnabled>();
},
CurrentValue = delegate ()
{
if (SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return value.CurrentEnabled.ToString();
return null;
},
ApplyValue = (selected) =>
{
if (!SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return null;
value.DesiredEnabled = Enum.Parse<OverlayEnabled>(selected);
if (!SharedData<OverlayModeSetting>.SetExistingValue(value))
return null;
return selected;
}
};
public static Menu.MenuItemWithOptions ModeInstance = new Menu.MenuItemWithOptions()
{
Name = "OSD Mode",
PersistentKey = "PerformanceOverlayMode",
ApplyDelay = 500,
OptionsValues = delegate ()
{
return Enum.GetNames<OverlayMode>();
},
CurrentValue = delegate ()
{
if (SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return value.Current.ToString();
return null;
},
ApplyValue = (selected) =>
{
if (!SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return null;
value.Desired = Enum.Parse<OverlayMode>(selected);
if (!SharedData<OverlayModeSetting>.SetExistingValue(value))
return null;
return selected;
}
};
public static Menu.MenuItemWithOptions KernelDriversInstance = new Menu.MenuItemWithOptions()
{
Name = "OSD Kernel Drivers",
ApplyDelay = 500,
OptionsValues = delegate ()
{
return Enum.GetNames<KernelDriversLoaded>();
},
CurrentValue = delegate ()
{
if (SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return value.KernelDriversLoaded.ToString();
return null;
},
ApplyValue = (selected) =>
{
if (!SharedData<OverlayModeSetting>.GetExistingValue(out var value))
return null;
value.DesiredKernelDriversLoaded = Enum.Parse<KernelDriversLoaded>(selected);
if (!SharedData<OverlayModeSetting>.SetExistingValue(value))
return null;
return selected;
}
};
}
}

View file

@ -1,72 +0,0 @@
namespace PowerControl.Options
{
public static class Profiles
{
public static ProfilesController? Controller;
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Profiles",
ApplyDelay = 500,
OptionsValues = delegate ()
{
var currentProfileSettings = Controller?.CurrentProfileSettings;
if (currentProfileSettings == null)
return null;
if (currentProfileSettings.Exists)
{
return new string[] {
currentProfileSettings.ProfileName,
"Save All",
"Delete"
};
}
else
{
return new string[] {
"None",
"Create New",
"Save All"
};
}
},
CycleOptions = false,
CurrentValue = delegate ()
{
var currentProfileSettings = Controller?.CurrentProfileSettings;
if (currentProfileSettings == null)
return null;
if (currentProfileSettings.Exists)
return currentProfileSettings.ProfileName;
else
return "None";
},
ApplyValue = (selected) =>
{
switch (selected)
{
case "Delete":
Controller?.DeleteProfile();
return "None";
case "Create New":
Controller?.CreateProfile(false);
return Controller?.CurrentProfileSettings?.ProfileName;
case "Save All":
Controller?.CreateProfile(true);
return Controller?.CurrentProfileSettings?.ProfileName;
default:
return selected;
}
},
AfterApply = () =>
{
Instance?.Update();
}
};
}
}

View file

@ -1,37 +0,0 @@
using PowerControl.Helpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class RefreshRate
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Refresh Rate",
PersistentKey = "RefreshRate",
ApplyDelay = 1000,
ResetValue = () => { return DisplayResolutionController.GetRefreshRates().Max().ToString(); },
OptionsValues = delegate ()
{
var refreshRates = DisplayResolutionController.GetRefreshRates();
if (refreshRates.Count() > 1)
return refreshRates.Select(item => item.ToString()).ToArray();
return null;
},
CurrentValue = delegate ()
{
return DisplayResolutionController.GetRefreshRate().ToString();
},
ApplyValue = (selected) =>
{
DisplayResolutionController.SetRefreshRate(int.Parse(selected));
return DisplayResolutionController.GetRefreshRate().ToString();
},
Impacts =
{
FPSLimit.Instance
}
};
}
}

View file

@ -1,45 +0,0 @@
using PowerControl.Helpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class Resolution
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Resolution",
PersistentKey = "Resolution",
ApplyDelay = 1000,
ResetValue = () =>
{
if (!GPUScaling.SafeResolutionChange && !Settings.Default.EnableExperimentalFeatures)
return null;
return DisplayResolutionController.GetAllResolutions().Last().ToString();
},
OptionsValues = delegate ()
{
var resolutions = DisplayResolutionController.GetAllResolutions();
if (resolutions.Count() > 1)
return resolutions.Select(item => item.ToString()).ToArray();
return null;
},
CurrentValue = delegate ()
{
if (!GPUScaling.SafeResolutionChange && !Settings.Default.EnableExperimentalFeatures)
return null;
return DisplayResolutionController.GetResolution().ToString();
},
ApplyValue = (selected) =>
{
var selectedResolution = new DisplayResolutionController.DisplayResolution(selected);
DisplayResolutionController.SetResolution(selectedResolution);
return DisplayResolutionController.GetResolution().ToString();
},
Impacts =
{
RefreshRate.Instance,
FPSLimit.Instance
}
};
}
}

View file

@ -1,37 +0,0 @@
using CommonHelpers;
using PowerControl.Helpers;
namespace PowerControl.Options
{
public static class SMT
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "SMT",
PersistentKey = "SMT",
ApplyDelay = 500,
Options = { "No", "Yes" },
ResetValue = () => { return "Yes"; },
CurrentValue = delegate ()
{
if (!OSDHelpers.IsOSDForeground(out var processId))
return null;
if (!ProcessorCores.HasSMTThreads())
return null;
return ProcessorCores.IsUsingSMT(processId) ? "Yes" : "No";
},
ApplyValue = (selected) =>
{
if (!OSDHelpers.IsOSDForeground(out var processId))
return null;
if (!ProcessorCores.HasSMTThreads())
return null;
ProcessorCores.SetProcessSMT(processId, selected.ToString() == "Yes");
return ProcessorCores.IsUsingSMT(processId) ? "Yes" : "No";
}
};
}
}

View file

@ -1,31 +0,0 @@
using PowerControl.Helpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class Sharpening
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Sharpening",
ApplyDelay = 500,
Options = { "Off", "On" },
CurrentValue = delegate ()
{
var value = ImageSharpening.Enabled;
if (value is null)
return null;
return value.Value ? "On" : "Off";
},
ApplyValue = (selected) =>
{
ImageSharpening.Enabled = (string)selected == "On";
var value = ImageSharpening.Enabled;
if (value is null)
return null;
return value.Value ? "On" : "Off";
}
};
}
}

View file

@ -1,36 +0,0 @@
using CommonHelpers;
namespace PowerControl.Options
{
public static class SteamController
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Controller",
PersistentKey = "SteamController",
PersistOnCreate = false,
ApplyDelay = 500,
OptionsValues = delegate ()
{
if (SharedData<SteamControllerSetting>.GetExistingValue(out var value))
return value.SelectableProfiles.SplitWithN();
return null;
},
CurrentValue = delegate ()
{
if (SharedData<SteamControllerSetting>.GetExistingValue(out var value))
return value.CurrentProfile.Length > 0 ? value.CurrentProfile : null;
return null;
},
ApplyValue = (selected) =>
{
if (!SharedData<SteamControllerSetting>.GetExistingValue(out var value))
return null;
value.DesiredProfile = selected;
if (!SharedData<SteamControllerSetting>.SetExistingValue(value))
return null;
return selected;
}
};
}
}

View file

@ -1,80 +0,0 @@
using System.Diagnostics;
using CommonHelpers;
using PowerControl.Helpers.AMD;
namespace PowerControl.Options
{
public static class TDP
{
public const string SlowTDP = "SlowTDP";
public const string FastTDP = "FastTDP";
public const int DefaultSlowTDP = 15000;
public const int DefaultFastTDP = 15000;
public static PersistedOptions UserOptions()
{
var options = new PersistedOptions("TDP");
if (options.GetOptions().Count() == 0)
{
options.SetOptions(new PersistedOptions.Option[]
{
options.ForOption("3W").Set(SlowTDP, 3000).Set(FastTDP, 3000),
options.ForOption("4W").Set(SlowTDP, 4000).Set(FastTDP, 4000),
options.ForOption("5W").Set(SlowTDP, 5000).Set(FastTDP, 5000),
options.ForOption("6W").Set(SlowTDP, 6000).Set(FastTDP, 6000),
options.ForOption("7W").Set(SlowTDP, 7000).Set(FastTDP, 7000),
options.ForOption("8W").Set(SlowTDP, 8000).Set(FastTDP, 8000),
options.ForOption("9W").Set(SlowTDP, 9000).Set(FastTDP, 9000),
options.ForOption("10W").Set(SlowTDP, 10000).Set(FastTDP, 10000),
options.ForOption("12W").Set(SlowTDP, 12000).Set(FastTDP, 12000),
options.ForOption("15W").Set(SlowTDP, 15000).Set(FastTDP, 15000),
});
}
return options;
}
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "TDP",
PersistentKey = "TDP",
PersistOnCreate = false,
OptionsValues = () => { return UserOptions().GetOptions(); },
ApplyDelay = 1000,
Visible = VangoghGPU.IsSupported,
ResetValue = () => { return "15W"; },
ActiveOption = "?",
ApplyValue = (selected) =>
{
if (!AntiCheatSettings.Default.AckAntiCheat(
Controller.TitleWithVersion,
"Changing TDP requires kernel access for a short period.",
"Leave the game if it uses anti-cheat protection."))
return null;
var selectedOption = UserOptions().ForOption(selected);
if (!selectedOption.Exist)
return null;
var slowTDP = selectedOption.Get(SlowTDP, DefaultSlowTDP);
var fastTDP = selectedOption.Get(FastTDP, DefaultFastTDP);
return CommonHelpers.Instance.WithGlobalMutex<string>(200, () =>
{
using (var sd = VangoghGPU.Open())
{
if (sd is null)
return null;
sd.SlowTDP = (uint)slowTDP;
sd.FastTDP = (uint)fastTDP;
}
return selected;
});
}
};
}
}

View file

@ -1,32 +0,0 @@
namespace PowerControl.Options
{
public static class Volume
{
public static Menu.MenuItemWithOptions Instance = new Menu.MenuItemWithOptions()
{
Name = "Volume",
Options = { "0", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", "60", "65", "70", "75", "80", "85", "90", "95", "100" },
CycleOptions = false,
CurrentValue = delegate ()
{
try { return Helpers.AudioManager.GetMasterVolume(5.0).ToString(); }
catch (Exception) { return null; }
},
ApplyValue = (selected) =>
{
try
{
Helpers.AudioManager.SetMasterVolumeMute(false);
Helpers.AudioManager.SetMasterVolume(int.Parse(selected));
return Helpers.AudioManager.GetMasterVolume(5.0).ToString();
}
catch (Exception)
{
// In some cases MasterVolume device is missing
return null;
}
}
};
}
}

View file

@ -1,56 +0,0 @@
namespace PowerControl
{
public class PersistedOptions : CommonHelpers.BaseSettings
{
public const string OptionsKey = "Options";
public PersistedOptions(string name) : base(name + "Options")
{
}
public struct Option
{
public PersistedOptions Options { get; set; }
public string Key { get; set; }
public string FullKey(string setting)
{
return String.Format("{0}_{1}", Key, setting);
}
public bool Exist
{
get => Options.GetOptions().Contains(Key);
}
public Option Set<T>(string setting, T value)
{
// Get and persist value on first access
Options.Get(FullKey(setting), value, true);
return this;
}
public T Get<T>(string setting, T defaultValue)
{
// Get and do not persist value
return Options.Get(FullKey(setting), defaultValue, false);
}
}
public Option ForOption(string option)
{
return new Option() { Options = this, Key = option };
}
public void SetOptions(IEnumerable<Option> options)
{
Set<string>(OptionsKey, String.Join(",", options.Select((option) => option.Key)));
}
public string[] GetOptions()
{
var options = Get<string>(OptionsKey, "");
return options.Split(',', StringSplitOptions.RemoveEmptyEntries);
}
}
}

View file

@ -1,73 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>True</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Resources\traffic-light-outline.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="app.manifest" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="app.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Management" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommonHelpers\CommonHelpers.csproj" />
<ProjectReference Include="..\ExternalHelpers\ExternalHelpers.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="hidapi.net">
<HintPath>..\ExternalHelpers\hidapi.net.dll</HintPath>
</Reference>
<Reference Include="RTSSSharedMemoryNET">
<HintPath>..\ExternalHelpers\RTSSSharedMemoryNET.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Resources\RyzenAdj\inpoutx64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\RyzenAdj\libryzenadj.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\RyzenAdj\ryzenadj.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\RyzenAdj\WinRing0x64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\RyzenAdj\WinRing0x64.sys">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -1,278 +0,0 @@
using System.Diagnostics;
using CommonHelpers;
using ExternalHelpers;
using PowerControl.Helper;
using PowerControl.Menu;
namespace PowerControl
{
public class ProfilesController : IDisposable
{
public const bool AutoCreateProfiles = true;
public const int ApplyProfileDelayMs = 500;
public const int ResetProfileDelayMs = 500;
private Dictionary<int, PowerControl.Helper.ProfileSettings> watchedProcesses = new Dictionary<int, PowerControl.Helper.ProfileSettings>();
private Dictionary<MenuItemWithOptions, String>? changedSettings;
private CancellationTokenSource? changeTask;
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer()
{
Interval = 1000
};
public IEnumerable<String> WatchedProfiles
{
get
{
foreach (var process in watchedProcesses)
yield return process.Value.ProfileName;
}
}
public ProfileSettings? CurrentProfileSettings { get; private set; }
public ProfilesController()
{
PowerControl.Options.Profiles.Controller = this;
MenuStack.Root.ValueChanged += Root_OnOptionValueChange;
timer.Start();
timer.Tick += Timer_Tick;
}
~ProfilesController()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
PowerControl.Options.Profiles.Controller = null;
MenuStack.Root.ValueChanged -= Root_OnOptionValueChange;
timer.Stop();
}
private void Timer_Tick(object? sender, EventArgs e)
{
timer.Enabled = false;
try { RefreshProfiles(); }
finally { timer.Enabled = true; }
}
private void RefreshProfiles()
{
if (DisplayConfig.IsInternalConnected != true)
{
foreach (var process in watchedProcesses)
RemoveProcess(process.Key);
return;
}
OSDHelpers.Applications.Instance.Refresh();
if (OSDHelpers.Applications.Instance.FindForeground(out var processId, out var processName))
{
if (!BringUpProcess(processId))
AddProcess(processId, processName);
}
foreach (var process in watchedProcesses)
{
if (OSDHelpers.Applications.Instance.IsRunning(process.Key))
continue;
RemoveProcess(process.Key);
}
}
private bool BringUpProcess(int processId)
{
if (!watchedProcesses.TryGetValue(processId, out var profileSettings))
return false;
if (CurrentProfileSettings != profileSettings)
{
Log.TraceLine("ProfilesController: Foreground changed: {0} => {1}",
CurrentProfileSettings?.ProfileName, profileSettings.ProfileName);
CurrentProfileSettings = profileSettings;
ProfileChanged();
}
return true;
}
private void AddProcess(int processId, string processName)
{
Log.TraceLine("ProfilesController: New Process: {0}/{1}", processId, processName);
if (changedSettings == null)
changedSettings = new Dictionary<MenuItemWithOptions, string>();
var profileSettings = new ProfileSettings(processName);
watchedProcesses.Add(processId, profileSettings);
ApplyProfile(profileSettings);
}
private void RemoveProcess(int processId)
{
if (!watchedProcesses.Remove(processId, out var profileSettings))
return;
if (CurrentProfileSettings == profileSettings)
CurrentProfileSettings = null;
Log.TraceLine("ProfilesController: Removed Process: {0}", processId);
if (watchedProcesses.Any())
return;
ResetProfile();
}
private void Root_OnOptionValueChange(MenuItemWithOptions options, string? oldValue, string newValue)
{
if (options.PersistentKey is null)
return;
if (oldValue is not null)
{
if (changedSettings?.TryAdd(options, oldValue) == true)
{
Log.TraceLine("ProfilesController: Saved change: {0} from {1}", options.PersistentKey, oldValue);
}
}
// If profile exists persist value
if (CurrentProfileSettings != null && (CurrentProfileSettings.Exists || AutoCreateProfiles))
{
CurrentProfileSettings.SetValue(options.PersistentKey, newValue);
options.ProfileOption = newValue;
Log.TraceLine("ProfilesController: Stored: {0} {1} = {2}",
CurrentProfileSettings.ProfileName, options.PersistentKey, newValue);
}
}
private void ProfileChanged()
{
foreach (var menuItem in PersistableOptions())
{
menuItem.ProfileOption = CurrentProfileSettings?.GetValue(menuItem.PersistentKey ?? "");
}
}
public void CreateProfile(bool saveAll = true)
{
var profileSettings = CurrentProfileSettings;
profileSettings?.TouchFile();
Log.TraceLine("ProfilesController: Created Profile: {0}, SaveAll={1}",
profileSettings?.ProfileName, saveAll);
if (!saveAll)
return;
foreach (var menuItem in PersistableOptions())
{
if (menuItem.ActiveOption is null || !menuItem.PersistOnCreate)
continue;
profileSettings?.SetValue(menuItem.PersistentKey ?? "", menuItem.ActiveOption);
}
ProfileChanged();
}
public void DeleteProfile()
{
CurrentProfileSettings?.DeleteFile();
ProfileChanged();
Log.TraceLine("ProfilesController: Deleted Profile: {0}", CurrentProfileSettings?.ProfileName);
}
private void ApplyProfile(ProfileSettings profileSettings)
{
CurrentProfileSettings = profileSettings;
ProfileChanged();
if (CurrentProfileSettings is null || CurrentProfileSettings?.Exists != true)
return;
int delay = CurrentProfileSettings.GetInt("ApplyDelay", ApplyProfileDelayMs);
changeTask?.Cancel();
changeTask = Dispatcher.RunWithDelay(delay, () =>
{
foreach (var menuItem in PersistableOptions())
{
var persistedValue = CurrentProfileSettings.GetValue(menuItem.PersistentKey ?? "");
if (persistedValue is null)
continue;
try
{
menuItem.Set(persistedValue, true, false);
Log.TraceLine("ProfilesController: Applied from Profile: {0}: {1} = {2}",
CurrentProfileSettings.ProfileName, menuItem.PersistentKey, persistedValue);
}
catch (Exception e)
{
Log.TraceLine("ProfilesController: Exception Profile: {0}: {1} = {2} => {3}",
CurrentProfileSettings.ProfileName, menuItem.PersistentKey, persistedValue, e);
CurrentProfileSettings.DeleteKey(menuItem.PersistentKey ?? "");
menuItem.ProfileOption = null;
}
}
});
}
private void ResetProfile()
{
CurrentProfileSettings = null;
ProfileChanged();
if (changedSettings is null)
return;
// Revert all changes made to original value
var appliedSettings = changedSettings;
changedSettings = null;
changeTask?.Cancel();
changeTask = Dispatcher.RunWithDelay(ResetProfileDelayMs, () =>
{
foreach (var menuItem in PersistableOptions())
{
if (!appliedSettings.TryGetValue(menuItem, out var setting))
continue;
try
{
menuItem.Set(setting, true, true);
Log.TraceLine("ProfilesController: Reset: {0} = {1}",
menuItem.PersistentKey, setting);
}
catch (Exception e)
{
Log.TraceLine("ProfilesController: Reset Exception: {0} = {1} => {2}",
menuItem.PersistentKey, setting, e);
}
}
});
}
private IEnumerable<MenuItemWithOptions> PersistableOptions()
{
return MenuItemWithOptions.
Order(MenuStack.Root.AllMenuItemOptions()).
Where((item) => item.PersistentKey is not null).
Reverse();
}
}
}

View file

@ -1,49 +0,0 @@
using CommonHelpers;
using PowerControl.Helpers;
using PowerControl.Helpers.AMD;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PowerControl
{
internal static class Program
{
const int MAX_GPU_RETRIES = 3;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Instance.WithSentry(() =>
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
if (Settings.Default.EnableExperimentalFeatures)
{
if (!AntiCheatSettings.Default.AckAntiCheat(
Controller.TitleWithVersion,
"You are running EXPERIMENTAL build.", null, false))
return;
}
for (int i = 0; !VangoghGPU.IsSupported && i < MAX_GPU_RETRIES; i++)
{
var status = Instance.WithGlobalMutex(1000, () => VangoghGPU.Detect());
if (status != VangoghGPU.DetectionStatus.Retryable)
break;
Thread.Sleep(300);
}
using (var controller = new Controller())
{
Application.Run();
}
});
}
}
}

View file

@ -1,103 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace PowerControl {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PowerControl.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] CRU_SteamDeck {
get {
object obj = ResourceManager.GetObject("CRU_SteamDeck", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon traffic_light_outline {
get {
object obj = ResourceManager.GetObject("traffic_light_outline", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon traffic_light_outline_light {
get {
object obj = ResourceManager.GetObject("traffic-light-outline_light", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon traffic_light_outline_red {
get {
object obj = ResourceManager.GetObject("traffic_light_outline_red", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View file

@ -1,133 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="CRU_SteamDeck" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\CRU_SteamDeck.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="traffic-light-outline_light" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\traffic-light-outline_light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="traffic_light_outline" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\traffic-light-outline.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="traffic_light_outline_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\traffic-light-outline-red.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

View file

@ -1,62 +0,0 @@
using CommonHelpers;
namespace PowerControl
{
internal sealed class Settings : BaseSettings
{
public static readonly Settings Default = new Settings();
public Settings() : base("Settings")
{
TouchSettings = true;
}
public string MenuUpKey
{
get { return Get("MenuUpKey", "Ctrl+Win+Numpad8"); }
set { Set("MenuUpKey", value); }
}
public string MenuDownKey
{
get { return Get("MenuDownKey", "Ctrl+Win+Numpad2"); }
set { Set("MenuDownKey", value); }
}
public string MenuLeftKey
{
get { return Get("MenuLeftKey", "Ctrl+Win+Numpad4"); }
set { Set("MenuLeftKey", value); }
}
public string MenuRightKey
{
get { return Get("MenuRightKey", "Ctrl+Win+Numpad6"); }
set { Set("MenuRightKey", value); }
}
public string MenuToggle
{
get { return Get("MenuToggle", "Alt+F11"); }
set { Set("MenuToggle", value); }
}
public bool EnableNeptuneController
{
get { return Get<bool>("EnableNeptuneController", true); }
set { Set("EnableNeptuneController", value); }
}
public bool EnableVolumeControls
{
get { return Get<bool>("EnableVolumeControls", true); }
set { Set("EnableVolumeControls", value); }
}
public bool EnableExperimentalFeatures
{
get { return Instance.IsDEBUG; }
}
}
}

View file

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="0.1.0.0" name="PerformanceOverlay.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View file

@ -1,3 +1,46 @@
# ROG Ally X Performance Overlay (Fork)
This repository is a personal fork of Steam Deck Tools, customized for my ROG Ally X setup and workflow.
## Scope
This fork is focused on `PerformanceOverlay` only.
Included projects:
- `PerformanceOverlay`
- `CommonHelpers`
- `ExternalHelpers`
The other original applications are intentionally not part of active development in this fork.
## Build
```powershell
dotnet restore PerformanceOverlay/PerformanceOverlay.csproj
dotnet build PerformanceOverlay/PerformanceOverlay.csproj --configuration Release
```
## CI
GitHub Actions is configured to build only `PerformanceOverlay` and publish a ZIP artifact.
## Credits
Based on the original Steam Deck Tools project by Kamil Trzciński.
## License
This fork remains under the original project license:
[Creative Commons Attribution-NonCommercial-ShareAlike (CC-BY-NC-SA)](http://creativecommons.org/licenses/by-nc-sa/4.0/).
-------------------------------------------
# (Windows) Steam Deck Tools
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/ayufan/steam-deck-tools?label=stable&style=flat-square)](https://github.com/ayufan/steam-deck-tools/releases/latest)
@ -55,4 +98,4 @@ Steam Deck Tools is not affiliated with Valve, Steam, or any of their partners.
[Creative Commons Attribution-NonCommercial-ShareAlike (CC-BY-NC-SA)](http://creativecommons.org/licenses/by-nc-sa/4.0/).
Free for personal use. Contact me in other cases (`ayufan@ayufan.eu`).
Free for personal use. Contact me in other cases (`ayufan@ayufan.eu`).

View file

@ -1,43 +0,0 @@
**Consider donating if you are happy with this project:**
<a href='https://ko-fi.com/ayufan' target='_blank'><img height='35' style='border:0px;height:50px;' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' alt='Buy Me a Coffee at ko-fi.com' /></a> <a href="https://www.paypal.com/donate/?hosted_button_id=DHNBE2YR9D5Y2" target='_blank'><img height='35' src="https://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png" alt="Donate with PayPal" style='border:0px;height:55px;'/></a>
[**READ IF PLAYING ONLINE GAMES AND/OR GAMES THAT HAVE ANTI-CHEAT ENABLED**](https://steam-deck-tools.ayufan.dev/#anti-cheat-and-antivirus-software)
## #{GIT_TAG_NAME}
- SteamController: Persist and expose in release Lizard{Buttons,Mouse}
- PerformanceOverlay: Battery-only setting in Performance Overlay (#193)
## 0.7.3
- SteamDeck LCD: Support BIOS F7A0131
## 0.7.2
- PowerControl: Add Charge Limit (70%, 80%, 90%, 100%)
## 0.7.1
- SteamDeck OLED: Support BIOS 107 with temperature readings
- SteamDeck OLED: Remove BIOS 105 support as it is buggy
## 0.7.0
- FanControl: Support for SteamDeck OLED
- PerformanceOverlay: Support the `AMD Custom GPU 0932` found in SteamDeck OLED
- PowerControl: Support `AMD Custom GPU 0932` with a SMU at `0x80600000-0x8067ffff` ver.: `0x063F0E00`
## 0.6.22
- SteamController: Fix broken scroll on left pad introduced by 0.6.21
## 0.6.21
- SteamController: Add support for circular deadzone on left/right sticks
- FanControl: Add Silent fan profile. Configure `Silent4000RPMTemp` threshold in `FanControl.dll.ini`
- SteamController: Added `Win+D` shortcut under `Steam+RightStickPress`
## 0.6.20
- PerformanceOverlay/PowerControl: Add support for `AMD Radeon RX 670 Graphics`

View file

@ -1,237 +0,0 @@
using static CommonHelpers.Log;
namespace SteamController
{
public partial class Context : IDisposable
{
public const double JoystickToMouseSensitivity = 1200;
public const double PadToMouseSensitivity = 150;
public const double PadToWhellSensitivity = 4;
public const double ThumbToWhellSensitivity = 20;
public Devices.SteamController Steam { get; private set; }
public Devices.Xbox360Controller X360 { get; private set; }
public Devices.DS4Controller DS4 { get; private set; }
public Devices.KeyboardController Keyboard { get; private set; }
public Devices.MouseController Mouse { get; private set; }
public List<Profiles.Profile> Profiles { get; } = new List<Profiles.Profile>();
public List<Managers.Manager> Managers { get; } = new List<Managers.Manager>();
private int selectedProfile;
private int controllerProfile;
public struct ContextState
{
public bool GameProcessRunning { get; set; }
public bool RTSSInForeground { get; set; }
public bool SteamUsesX360Controller { get; set; }
public bool SteamUsesDS4Controller { get; set; }
public bool SteamUsesSteamInput { get; set; }
public bool IsActive
{
get { return RTSSInForeground || GameProcessRunning || SteamUsesX360Controller || SteamUsesDS4Controller || SteamUsesSteamInput; }
}
public override string ToString()
{
string reason = "state";
if (GameProcessRunning) reason += " game";
if (SteamUsesX360Controller) reason += " steamX360";
if (SteamUsesDS4Controller) reason += " steamDS4";
if (SteamUsesSteamInput) reason += " steamInput";
if (RTSSInForeground) reason += " rtss";
return reason;
}
}
public bool RequestEnable { get; set; } = true;
public ContextState State;
public event Action<Profiles.Profile> ProfileChanged;
public Action? SelectDefault;
public bool Enabled
{
get { return RequestEnable; }
}
public bool KeyboardMouseValid
{
get { return SteamController.Managers.SASManager.Valid; }
}
public Profiles.Profile? CurrentProfile
{
get
{
for (int i = 0; i < Profiles.Count; i++)
{
var profile = Profiles[(selectedProfile + i) % Profiles.Count];
if (profile.Selected(this))
return profile;
}
return null;
}
}
public Context()
{
Steam = new Devices.SteamController();
X360 = new Devices.Xbox360Controller();
DS4 = new Devices.DS4Controller();
Keyboard = new Devices.KeyboardController();
Mouse = new Devices.MouseController();
ProfileChanged += (_) => X360.Beep();
ProfileChanged += (profile) => TraceLine("Context: Selected Profile: {0}", profile.Name);
}
public void Dispose()
{
foreach (var manager in Managers)
manager.Dispose();
using (Steam) { }
using (X360) { }
using (DS4) { }
using (Keyboard) { }
using (Mouse) { }
}
public void Tick()
{
X360.Tick();
DS4.Tick();
foreach (var manager in Managers)
{
try { manager.Tick(this); }
catch (Exception e) { TraceException("Controller", manager, e); }
}
}
public bool Update()
{
Steam.BeforeUpdate();
X360.BeforeUpdate();
DS4.BeforeUpdate();
Keyboard.BeforeUpdate();
Mouse.BeforeUpdate();
try
{
var profile = CurrentProfile;
if (profile is not null)
profile.Run(this);
return true;
}
catch (Exception e)
{
TraceException("Context", "Update", e);
return false;
}
finally
{
Steam.Update();
X360.Update();
DS4.Update();
Keyboard.Update();
Mouse.Update();
}
}
public bool SelectProfile(String name, bool userDefault = false)
{
lock (this)
{
for (int i = 0; i < Profiles.Count; i++)
{
var profile = Profiles[i];
if (profile.Name != name)
continue;
if (!profile.Selected(this) && !userDefault)
continue;
if (i != selectedProfile)
{
selectedProfile = i;
if (!profile.IsDesktop && !userDefault)
controllerProfile = i;
OnProfileChanged(profile);
}
return true;
}
}
return false;
}
public void SelectController()
{
lock (this)
{
var current = CurrentProfile;
if (current is null)
return;
if (!current.IsDesktop)
return;
// Use last selected controller profile
selectedProfile = controllerProfile;
var currentController = CurrentProfile;
if (current != currentController && currentController?.IsDesktop != true)
return;
// Otherwise use next one
TraceLine("Context: SelectController. State={0}", State);
SelectNext();
}
}
public bool SelectNext()
{
lock (this)
{
// Update selectedProfile index
var current = CurrentProfile;
if (current is null)
return false;
selectedProfile = Profiles.IndexOf(current);
for (int i = 1; i < Profiles.Count; i++)
{
var idx = (selectedProfile + i) % Profiles.Count;
var profile = Profiles[idx];
if (profile.IsDesktop)
continue;
if (!profile.Selected(this))
continue;
selectedProfile = idx;
controllerProfile = idx;
OnProfileChanged(profile);
return true;
}
}
return false;
}
public void BackToDefault()
{
TraceLine("Context: Back To Default.");
if (SelectDefault is not null)
SelectDefault();
}
private void OnProfileChanged(Profiles.Profile profile)
{
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(() => ProfileChanged(profile)));
}
}
}

View file

@ -1,66 +0,0 @@
using CommonHelpers;
namespace SteamController
{
public partial class Context
{
private List<string> debugLastItems = new List<string>();
private Profiles.Profile? debugLastProfile = null;
public void Debug()
{
var items = new List<string>();
var profile = CurrentProfile;
if (profile?.IsDesktop ?? false)
items.Add("[DESKTOP]");
else
items.Add("[CONTROLLER]");
if (profile != debugLastProfile)
{
Log.TraceLine("ProfileChanged: {0} to {1}", debugLastProfile?.Name ?? "null", profile?.Name ?? "null");
debugLastProfile = profile;
}
if (Steam.LizardButtons)
items.Add("[LB]");
if (Steam.LizardMouse)
items.Add("[LM]");
items.Add(X360.Connected ? "[X360]" : X360.Valid ? "[no-X360]" : "[inv-X360]");
items.Add(DS4.Connected ? "[DS4]" : DS4.Valid ? "[no-DS4]" : "[inv-DS4]");
items.Add(KeyboardMouseValid ? "[KM]" : "[inv-KM]");
foreach (var button in Steam.AllButtons)
{
if (button is null || !button.LastValue)
continue;
String text = button.Name;
if (button.Consumed is not null)
text += String.Format("[{0}]", button.Consumed);
if (button.Value)
text += "[P]";
items.Add(text);
}
foreach (var key in Keyboard.DownKeys)
{
items.Add(String.Format("Key{0}", key));
}
foreach (var mouse in Mouse.DownButtons)
{
items.Add(String.Format("Mouse{0}", mouse));
}
if (!items.SequenceEqual(debugLastItems))
{
Log.TraceDebug("DEBUG: {0}", String.Join(" ", items));
debugLastItems = items;
}
}
}
}

View file

@ -1,90 +0,0 @@
using System.Diagnostics;
using static CommonHelpers.Log;
namespace SteamController
{
public partial class Context : IDisposable
{
public readonly TimeSpan UpdateResetInterval = TimeSpan.FromSeconds(1);
Thread? thread;
private bool threadRunning;
public int UpdatesPerSec { get; private set; }
public bool Start(int? startDelayMs = null)
{
if (thread is not null)
return false;
UpdatesPerSec = 0;
threadRunning = true;
thread = new Thread(ThreadLoop);
thread.Start(startDelayMs);
return true;
}
private void ThreadLoop(object? startDelayMs)
{
if (startDelayMs is int)
{
ThreadSleep((int)startDelayMs);
}
var stopwatch = new Stopwatch();
stopwatch.Start();
int updates = 0;
var nextReset = stopwatch.Elapsed.Add(UpdateResetInterval);
X360.Start();
DS4.Start();
while (threadRunning)
{
if (nextReset < stopwatch.Elapsed)
{
UpdatesPerSec = updates;
nextReset = stopwatch.Elapsed.Add(UpdateResetInterval);
updates = 0;
}
updates++;
Update();
Debug();
if (!Enabled || !Steam.Updated)
{
ThreadSleep(100);
}
}
X360.Stop();
DS4.Stop();
}
public void Stop()
{
threadRunning = false;
if (thread != null)
{
thread.Interrupt();
thread.Join();
thread = null;
}
}
private bool ThreadSleep(int delayMs)
{
try
{
Thread.Sleep(delayMs);
return true;
}
catch (ThreadInterruptedException)
{
return false;
}
}
}
}

View file

@ -1,442 +0,0 @@
using CommonHelpers;
using ExternalHelpers;
using Microsoft.Win32;
using System.ComponentModel;
using System.Diagnostics;
namespace SteamController
{
internal class Controller : IDisposable
{
public const String Title = "Steam Controller";
public static readonly String TitleWithVersion = Title + " v" + Application.ProductVersion.ToString();
public const int ControllerDelayAfterResumeMs = 1000;
public static readonly Dictionary<String, Profiles.Profile> PreconfiguredUserProfiles = new Dictionary<String, Profiles.Profile>()
{
{ "*.desktop.cs", new Profiles.Predefined.DesktopProfile() { Name = "Desktop" } },
{ "*.x360.cs", new Profiles.Predefined.X360HapticProfile() { Name = "X360" } }
};
Container components = new Container();
NotifyIcon notifyIcon;
StartupManager startupManager = new StartupManager(Title);
Context context = new Context()
{
Profiles = {
new Profiles.Predefined.DesktopProfile() { Name = "Desktop" },
new Profiles.Predefined.SteamProfile() { Name = "Steam", Visible = false },
new Profiles.Predefined.SteamWithShorcutsProfile() { Name = "Steam with Shortcuts", Visible = false },
new Profiles.Predefined.X360HapticProfile() { Name = "X360", EmulateTouchPads = true },
new Profiles.Predefined.DS4HapticProfile() { Name = "DS4" },
},
Managers = {
new Managers.ProcessManager(),
new Managers.SteamManager(),
new Managers.RTSSManager(),
new Managers.ProfileSwitcher(),
new Managers.SteamConfigsManager(),
new Managers.SharedDataManager(),
new Managers.SASManager()
}
};
static Controller()
{
Dependencies.ValidateHidapi(TitleWithVersion);
}
public Controller()
{
Instance.OnUninstall(() =>
{
Helpers.SteamConfiguration.KillSteam();
Helpers.SteamConfiguration.WaitForSteamClose(5000);
Helpers.SteamConfiguration.BackupSteamConfig();
var steamControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
Devices.SteamController.VendorID, Devices.SteamController.ProductID, false
);
var x360ControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
Devices.Xbox360Controller.VendorID, Devices.Xbox360Controller.ProductID, false
);
Settings.Default.EnableSteamDetection = false;
startupManager.Startup = false;
});
Log.CleanupLogFiles(DateTime.UtcNow.AddDays(-7));
Log.LogToFile = true;
Log.LogToFileDebug = Settings.Default.EnableDebugLogging;
Instance.RunOnce(TitleWithVersion, "Global\\SteamController");
Instance.RunUpdater(TitleWithVersion);
if (Instance.WantsRunOnStartup)
startupManager.Startup = true;
notifyIcon = new NotifyIcon(components);
notifyIcon.Icon = WindowsDarkMode.IsDarkModeEnabled ? Resources.microsoft_xbox_controller_off_white : Resources.microsoft_xbox_controller_off;
notifyIcon.Text = TitleWithVersion;
notifyIcon.Visible = true;
#if DEBUG
foreach (var profile in Profiles.Dynamic.RoslynDynamicProfile.GetUserProfiles(PreconfiguredUserProfiles))
{
profile.ErrorsChanged += (errors) =>
{
notifyIcon.ShowBalloonTip(
3000, profile.Name,
String.Join("\n", errors),
ToolTipIcon.Error
);
};
profile.Compile();
profile.Watch();
context.Profiles.Add(profile);
}
#endif
// Set available profiles
ProfilesSettings.Helpers.ProfileStringConverter.Profiles = context.Profiles;
var contextMenu = new ContextMenuStrip(components);
var enabledItem = new ToolStripMenuItem("&Enabled");
enabledItem.Click += delegate { context.RequestEnable = !context.RequestEnable; };
contextMenu.Opening += delegate { enabledItem.Checked = context.RequestEnable; };
contextMenu.Items.Add(enabledItem);
contextMenu.Items.Add(new ToolStripSeparator());
foreach (var profile in context.Profiles)
{
if (profile.Name == "")
continue;
var profileItem = new ToolStripMenuItem(profile.Name);
profileItem.Click += delegate { context.SelectProfile(profile.Name); };
contextMenu.Opening += delegate
{
profileItem.Checked = context.CurrentProfile == profile;
profileItem.ToolTipText = String.Join("\n", profile.Errors ?? new string[0]);
profileItem.Enabled = profile.Errors is null;
profileItem.Visible = profile.Visible;
};
contextMenu.Items.Add(profileItem);
}
contextMenu.Items.Add(new ToolStripSeparator());
var setupSteamItem = new ToolStripMenuItem("Setup &Steam");
setupSteamItem.Click += delegate { SetupSteam(true); };
contextMenu.Items.Add(setupSteamItem);
contextMenu.Items.Add(new ToolStripSeparator());
var settingsItem = contextMenu.Items.Add("&Settings");
settingsItem.Click += Settings_Click;
var shortcutsItem = contextMenu.Items.Add("&Shortcuts");
shortcutsItem.Click += delegate { Dependencies.OpenLink(Dependencies.SDTURL + "/shortcuts.html"); };
contextMenu.Items.Add(new ToolStripSeparator());
if (startupManager.IsAvailable)
{
var startupItem = new ToolStripMenuItem("Run On Startup");
startupItem.Checked = startupManager.Startup;
startupItem.Click += delegate { startupItem.Checked = startupManager.Startup = !startupManager.Startup; };
contextMenu.Items.Add(startupItem);
}
var checkForUpdatesItem = contextMenu.Items.Add("&Check for Updates");
checkForUpdatesItem.Click += delegate { Instance.RunUpdater(TitleWithVersion, true); };
var helpItem = contextMenu.Items.Add("&Help");
helpItem.Click += delegate { Dependencies.OpenLink(Dependencies.SDTURL); };
contextMenu.Items.Add(new ToolStripSeparator());
var exitItem = contextMenu.Items.Add("&Exit");
exitItem.Click += delegate { Application.Exit(); };
notifyIcon.ContextMenuStrip = contextMenu;
var contextStateUpdate = new System.Windows.Forms.Timer(components);
contextStateUpdate.Interval = 250;
contextStateUpdate.Enabled = true;
contextStateUpdate.Tick += ContextStateUpdate_Tick;
context.SelectDefault = () =>
{
if (!context.SelectProfile(Settings.Default.DefaultProfile, true))
context.SelectProfile(context.Profiles.First().Name, true);
};
context.BackToDefault();
context.ProfileChanged += (profile) =>
{
#if false
notifyIcon.ShowBalloonTip(
1000,
TitleWithVersion,
String.Format("Selected profile: {0}", profile.Name),
ToolTipIcon.Info
);
#endif
};
SetupSteam(false);
context.Start();
Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
Log.TraceLine("SystemEvents_PowerModeChanged: {0}", e.Mode);
switch (e.Mode)
{
case PowerModes.Suspend:
context.Stop();
break;
case PowerModes.Resume:
context.Start(ControllerDelayAfterResumeMs);
break;
}
}
private void ContextStateUpdate_Tick(object? sender, EventArgs e)
{
context.Tick();
var profile = context.CurrentProfile;
if (!context.KeyboardMouseValid)
{
notifyIcon.Text = TitleWithVersion + ". Cannot send input.";
if (WindowsDarkMode.IsDarkModeEnabled)
notifyIcon.Icon = Resources.monitor_off_white;
else
notifyIcon.Icon = Resources.monitor_off;
}
else if (!context.X360.Valid || !context.DS4.Valid)
{
notifyIcon.Text = TitleWithVersion + ". Missing ViGEm?";
notifyIcon.Icon = Resources.microsoft_xbox_controller_red;
}
else if (profile is not null)
{
notifyIcon.Text = TitleWithVersion + ". Profile: " + profile.FullName;
notifyIcon.Icon = profile.Icon;
}
else
{
notifyIcon.Text = TitleWithVersion + ". Disabled";
if (WindowsDarkMode.IsDarkModeEnabled)
notifyIcon.Icon = Resources.microsoft_xbox_controller_off_white;
else
notifyIcon.Icon = Resources.microsoft_xbox_controller_off;
}
notifyIcon.Text += String.Format(". Updates: {0}/s", context.UpdatesPerSec);
}
public void Dispose()
{
Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
notifyIcon.Visible = false;
context.Stop();
using (context) { }
}
public void SetupSteam(bool always)
{
var blacklistedSteamController = Helpers.SteamConfiguration.IsControllerBlacklisted(
Devices.SteamController.VendorID,
Devices.SteamController.ProductID
);
var blacklistedX360Controller = Helpers.SteamConfiguration.IsControllerBlacklisted(
Devices.Xbox360Controller.VendorID,
Devices.Xbox360Controller.ProductID
);
var blacklistedDS4Controller = Helpers.SteamConfiguration.IsControllerBlacklisted(
Devices.DS4Controller.VendorID,
Devices.DS4Controller.ProductID
);
if (blacklistedSteamController is null || blacklistedX360Controller is null || blacklistedDS4Controller is null)
{
// Appears that Steam is not installed
if (always)
{
MessageBox.Show("Steam appears not to be installed.", TitleWithVersion, MessageBoxButtons.OK);
}
return;
}
Application.DoEvents();
var page = new TaskDialogPage();
page.Caption = TitleWithVersion;
page.AllowCancel = true;
var useXInputController = page.RadioButtons.Add("Use &X360/DS4 Controller with Steam (preferred)");
useXInputController.Text += "\n- Will always use X360 or DS4 controller.";
useXInputController.Checked = Settings.Default.EnableSteamDetection == true &&
blacklistedSteamController == true &&
blacklistedX360Controller == false &&
blacklistedDS4Controller == false;
var useSteamInput = page.RadioButtons.Add("Use &Steam Input with Steam (requires configuration)");
useSteamInput.Text += "\n- Will try to use Steam controls.";
useSteamInput.Text += "\n- Does REQUIRE disabling DESKTOP MODE shortcuts in Steam.";
useSteamInput.Text += "\n- Click Help for more information.";
useSteamInput.Checked = Settings.Default.EnableSteamDetection == true &&
blacklistedSteamController == false &&
blacklistedX360Controller == true &&
blacklistedDS4Controller == true;
var ignoreSteam = page.RadioButtons.Add("&Ignore Steam (only if you know why you need it)");
ignoreSteam.Text += "\n- Will revert all previously made changes.";
ignoreSteam.Checked = Settings.Default.EnableSteamDetection == false;
bool valid = ignoreSteam.Checked || useXInputController.Checked || useSteamInput.Checked;
// If everything is OK, on subsequent runs nothing to configure
if (valid && !always)
return;
if (valid || Settings.Default.EnableSteamDetection == null)
{
page.Heading = "Steam Controller Setup";
page.Text = "To use Steam Controller with Steam you need to configure it first.";
page.Icon = TaskDialogIcon.ShieldBlueBar;
}
else
{
page.Heading = "Steam Controller Setup - Configuration Lost";
page.Text = "Configure your Steam Controller again.";
page.Icon = TaskDialogIcon.ShieldWarningYellowBar;
}
var continueButton = new TaskDialogButton("Continue") { ShowShieldIcon = true };
page.Buttons.Add(continueButton);
page.Buttons.Add(TaskDialogButton.Cancel);
page.Buttons.Add(TaskDialogButton.Help);
page.Footnote = new TaskDialogFootnote();
page.Footnote.Text = "This will change Steam configuration. ";
page.Footnote.Text += "Close Steam before confirming as otherwise Steam will be forcefully closed.";
page.Footnote.Icon = TaskDialogIcon.Warning;
page.HelpRequest += delegate { Dependencies.OpenLink(Dependencies.SDTURL + "/steam-controller"); };
var result = TaskDialog.ShowDialog(new Form { TopMost = true }, page, TaskDialogStartupLocation.CenterScreen);
if (result != continueButton)
return;
Helpers.SteamConfiguration.KillSteam();
Helpers.SteamConfiguration.WaitForSteamClose(5000);
Helpers.SteamConfiguration.BackupSteamConfig();
var steamControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
Devices.SteamController.VendorID,
Devices.SteamController.ProductID,
useXInputController.Checked
);
var x360ControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
Devices.Xbox360Controller.VendorID,
Devices.Xbox360Controller.ProductID,
useSteamInput.Checked
);
var ds4ControllerUpdate = Helpers.SteamConfiguration.UpdateControllerBlacklist(
Devices.DS4Controller.VendorID,
Devices.DS4Controller.ProductID,
useSteamInput.Checked
);
Settings.Default.EnableSteamDetection = useSteamInput.Checked || useXInputController.Checked;
if (steamControllerUpdate && x360ControllerUpdate && ds4ControllerUpdate)
{
notifyIcon.ShowBalloonTip(
3000, TitleWithVersion,
"Steam Configuration changed. You can start Steam now.",
ToolTipIcon.Info
);
}
else
{
notifyIcon.ShowBalloonTip(
3000, TitleWithVersion,
"Steam Configuration was not updated. Maybe Steam is open?",
ToolTipIcon.Warning
);
}
}
private void Settings_Click(object? sender, EventArgs e)
{
var form = new Form()
{
Text = TitleWithVersion + " Settings",
StartPosition = FormStartPosition.CenterScreen,
Size = new Size(400, 500),
AutoScaleMode = AutoScaleMode.Font,
AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F)
};
var propertyGrid = new PropertyGrid()
{
Dock = DockStyle.Fill,
ToolbarVisible = false,
LargeButtons = true,
SelectedObject = new
{
DesktopShortcuts = ProfilesSettings.DesktopPanelSettings.Default,
X360Shortcuts = ProfilesSettings.X360BackPanelSettings.Default,
X360Haptic = ProfilesSettings.HapticSettings.X360,
DS4Shortcuts = ProfilesSettings.DS4BackPanelSettings.Default,
DS4Haptic = ProfilesSettings.HapticSettings.DS4,
Application = Settings.Default,
DEBUG = SettingsDebug.Default
}
};
var helpLabel = new Label()
{
Cursor = Cursors.Hand,
Dock = DockStyle.Top,
Font = new Font("Segoe UI", 9F, FontStyle.Bold | FontStyle.Underline),
ForeColor = SystemColors.HotTrack,
Text = Dependencies.SDTURL,
TextAlign = ContentAlignment.MiddleCenter
};
var donateLabel = new Label()
{
Cursor = Cursors.Hand,
Dock = DockStyle.Top,
Font = new Font("Segoe UI", 9F, FontStyle.Bold),
Text = String.Join("\n",
"Consider donating if you are happy with this project."
),
TextAlign = ContentAlignment.MiddleLeft,
Height = 100
};
helpLabel.Click += delegate { Dependencies.OpenLink(Dependencies.SDTURL); };
donateLabel.Click += delegate { Dependencies.OpenLink(Dependencies.SDTURL + "/#help-this-project"); };
propertyGrid.ExpandAllGridItems();
form.Controls.Add(propertyGrid);
form.Controls.Add(donateLabel);
form.Controls.Add(helpLabel);
form.ShowDialog();
}
}
}

View file

@ -1,9 +0,0 @@
// If L5 is hold, the A, B, X, Y is turbo: 10x per second
if (Steam.BtnL5)
{
X360.BtnA = Turbo(Steam.BtnA, 10);
X360.BtnB = Turbo(Steam.BtnB, 10);
X360.BtnX = Turbo(Steam.BtnX, 10);
X360.BtnY = Turbo(Steam.BtnY, 10);
}

View file

@ -1,304 +0,0 @@
using System.Diagnostics;
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Exceptions;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.DualShock4;
using Nefarius.ViGEm.Client.Targets.Xbox360;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class DS4Controller : IDisposable
{
public readonly TimeSpan FeedbackTimeout = TimeSpan.FromMilliseconds(1000);
public const ushort VendorID = 0x054C;
public const ushort ProductID = 0x05C4;
private const int REPORT_SIZE = 63;
private const int TIMESTAMP_HZ = 800;
private const int TIMESTAMP_INCREMENT = 188;
private ViGEmClient? client;
private IDualShock4Controller? device;
private bool isConnected;
private byte[] reportBytes = new byte[REPORT_SIZE];
private bool submitReport;
private int submittedReports = 0;
private Stopwatch stopwatch = new Stopwatch();
public DS4Controller()
{
}
public void Dispose()
{
using (client) { }
}
public void Start()
{
}
public void Stop()
{
lock (this) { Fail(); }
}
internal bool Tick()
{
if (this.device is not null)
return true;
try
{
var client = new ViGEmClient();
var device = client.CreateDualShock4Controller();
device.AutoSubmitReport = false;
device.FeedbackReceived += DS4_FeedbackReceived;
this.device = device;
this.client = client;
return true;
}
catch (VigemBusNotFoundException)
{
// ViGem is not installed
return false;
}
}
private void Fail()
{
var client = this.client;
// unset current device
this.isConnected = false;
this.client = null;
this.device = null;
this.stopwatch.Stop();
try { using (client) { } }
catch (Exception) { }
}
private void PrepareReport()
{
var oldReportBytes = reportBytes;
reportBytes = new byte[63];
submitReport = false;
Counter.Set(reportBytes, (byte)((submittedReports << 2) & 0xFF));
BatteryLevel.Set(reportBytes, 255);
Timestamp.Set(reportBytes, (ushort)(stopwatch.ElapsedMilliseconds * TIMESTAMP_HZ * TIMESTAMP_INCREMENT / 1000));
DPadReleased.Set(reportBytes);
LeftThumbX.SetScaled(reportBytes, 0);
LeftThumbY.SetScaled(reportBytes, 0);
RightThumbX.SetScaled(reportBytes, 0);
RightThumbY.SetScaled(reportBytes, 0);
GyroX.Set(reportBytes, 0);
GyroY.Set(reportBytes, 0);
GyroZ.Set(reportBytes, 0);
AccelX.Set(reportBytes, 0);
AccelY.Set(reportBytes, 0);
AccelZ.Set(reportBytes, 0);
// Copy trackpad packets
reportBytes[32] = (byte)((reportBytes[32] + 1) & 0x3);
Array.Copy(oldReportBytes, 33, reportBytes, 33, 9);
Array.Copy(oldReportBytes, 33, reportBytes, 42, 9 * 2);
}
internal void BeforeUpdate()
{
device?.ResetReport();
if (!isConnected)
{
FeedbackLargeMotor = null;
FeedbackSmallMotor = null;
FeedbackReceived = null;
LightbarColor = null;
}
PrepareReport();
Connected = false;
}
private void SetConnected(bool wantsConnected)
{
if (wantsConnected == isConnected)
return;
if (wantsConnected)
{
try
{
device?.Connect();
stopwatch.Restart();
TraceLine("Connected DS4 Controller.");
}
catch (System.ComponentModel.Win32Exception e)
{
// This is expected exception (as sometimes device will fail to connect)
// ERROR_SUCCESS, which likely means COM did not succeed
if (e.NativeErrorCode == 0)
DebugException("DS4", "ConnectExpected", e);
else
TraceException("DS4", "ConnectExpected", e);
Fail();
return;
}
catch (Exception e)
{
TraceException("DS4", "Connect", e);
Fail();
return;
}
}
else
{
try
{
device?.Disconnect();
stopwatch.Stop();
TraceLine("Disconnected DS4 Controller.");
}
catch (VigemTargetNotPluggedInException)
{
// everything fine
}
catch (Exception e)
{
TraceException("DS4", "Disconnect", e);
Fail();
return;
}
}
isConnected = wantsConnected;
}
internal void Update()
{
if (device is not null && Connected != isConnected)
{
lock (this)
{
SetConnected(Connected);
}
}
if (isConnected && submitReport)
{
try
{
device?.SubmitRawReport(reportBytes);
submittedReports++;
}
catch (VigemInvalidTargetException)
{
// Device was lost
lock (this) { Fail(); }
}
catch (Exception e)
{
TraceException("DS4", "SubmitReport", e);
}
}
if (FeedbackReceived is not null && FeedbackReceived.Value.Add(FeedbackTimeout) < DateTime.Now)
{
FeedbackLargeMotor = null;
FeedbackSmallMotor = null;
FeedbackReceived = null;
}
}
public bool Valid
{
get { return device is not null; }
}
public bool Connected { get; set; }
public byte? FeedbackLargeMotor { get; private set; }
public byte? FeedbackSmallMotor { get; private set; }
public LightbarColor? LightbarColor { get; private set; }
public DateTime? FeedbackReceived { get; private set; }
public bool this[DualShock4Button button]
{
set
{
if (value)
button.Set(reportBytes, value);
submitReport = true;
}
}
public bool this[DualShock4DPadDirection button]
{
set
{
if (value)
button.Set(reportBytes);
submitReport = true;
}
}
public short this[DualShock4Axis axis]
{
set
{
axis.SetScaled(reportBytes, value);
submitReport = true;
}
}
public short this[DualShock4Slider slider]
{
set
{
slider.Set(reportBytes, value);
submitReport = true;
}
}
public short this[DualShock4Sensor sensor]
{
set
{
sensor.Set(reportBytes, value);
submitReport = true;
}
}
public Point? this[DualShock4Finger finger]
{
set
{
finger.Set(reportBytes, value);
submitReport = true;
}
}
public void Overwrite(DualShock4Button button, bool value, int minPressedTime = 0)
{
button.Set(reportBytes, value);
submitReport = true;
}
public void ResetFeedback()
{
FeedbackReceived = null;
}
private void DS4_FeedbackReceived(object sender, DualShock4FeedbackReceivedEventArgs e)
{
FeedbackLargeMotor = e.LargeMotor;
FeedbackSmallMotor = e.SmallMotor;
LightbarColor = e.LightbarColor;
FeedbackReceived = DateTime.Now;
}
}
}

View file

@ -1,208 +0,0 @@
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Exceptions;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.DualShock4;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class DS4Controller
{
public struct DualShock4Axis
{
public int Offset { get; }
public bool Invert { get; }
public DualShock4Axis(int offset, bool invert)
{
Offset = offset;
Invert = invert;
}
internal void Set(byte[] report, byte value)
{
report[Offset] = value;
}
internal void SetScaled(byte[] report, short value)
{
var valuef = value / 256;
if (Invert)
valuef = -valuef;
Set(report, (byte)(valuef + sbyte.MinValue));
}
}
public struct DualShock4Button
{
public int Offset { get; }
public int Bit { get; }
public byte Mask { get => (byte)(1 << Bit); }
public DualShock4Button(int offset, int bit)
{
Offset = offset;
Bit = bit;
}
internal void Set(byte[] report, bool value)
{
report[Offset] = (byte)((report[Offset] & ~Mask) | (value ? Mask : 0));
}
}
public struct DualShock4DPadDirection
{
public int Offset { get; }
public int Value { get; }
public int Mask { get; }
public DualShock4DPadDirection(int offset, int value, int mask)
{
Offset = offset;
Value = value;
Mask = mask;
}
internal void Set(byte[] report)
{
report[Offset] = (byte)((report[Offset] & ~Mask) | Value);
}
}
public struct DualShock4Slider
{
public int Offset { get; }
public DualShock4Slider(int offset)
{
Offset = offset;
}
internal void Set(byte[] report, byte value)
{
report[Offset] = value;
}
internal void Set(byte[] report, short value)
{
int result = Math.Clamp(value, (short)0, short.MaxValue) * byte.MaxValue / short.MaxValue;
Set(report, (byte)result);
}
}
public struct DualShock4Sensor
{
public int Offset { get; }
public DualShock4Sensor(int offset)
{
Offset = offset;
}
internal void Set(byte[] report, short value)
{
BitConverter.GetBytes(value).CopyTo(report, Offset);
}
internal void Set(byte[] report, ushort value)
{
BitConverter.GetBytes(value).CopyTo(report, Offset);
}
}
public struct DualShock4Finger
{
public const int MaxX = 1920;
public const int MaxY = 942;
public int Index { get; }
private int Offset { get => 34 + Index * 4; }
public DualShock4Finger(int index)
{
Index = index;
}
internal void Set(byte[] report, Point? data)
{
uint currentValue = BitConverter.ToUInt32(report, Offset);
// copy report ID
uint calculatedValue = (byte)(((currentValue & 0x7F) + 1) & 0x7F);
if (data.HasValue)
{
// store coordinates into report
int x = Math.Clamp(data.Value.X, 0, MaxX);
int y = Math.Clamp(data.Value.Y, 0, MaxY);
calculatedValue |= (uint)((x & 0x7FF) << 8);
calculatedValue |= (uint)((y & 0x7FF) << 20);
}
else
{
// copy existing coordinates
calculatedValue |= 0x80;
calculatedValue |= (uint)(currentValue & 0xFFFFFF00);
}
// compare position and key status
if ((currentValue & 0xFFFFFF80) == (calculatedValue & 0xFFFFFF80))
return;
// increment packet number (if it changed since the last packet)
if (report[33] == report[42])
report[33] = (byte)(report[33] + 1);
BitConverter.GetBytes(calculatedValue).CopyTo(report, Offset);
}
}
public readonly static DualShock4Axis LeftThumbX = new DualShock4Axis(0, false);
public readonly static DualShock4Axis LeftThumbY = new DualShock4Axis(1, true);
public readonly static DualShock4Axis RightThumbX = new DualShock4Axis(2, false);
public readonly static DualShock4Axis RightThumbY = new DualShock4Axis(3, true);
public readonly static DualShock4Slider LeftTrigger = new DualShock4Slider(7);
public readonly static DualShock4Slider RightTrigger = new DualShock4Slider(8);
public readonly static DualShock4Button ThumbRight = new DualShock4Button(5, 7);
public readonly static DualShock4Button ThumbLeft = new DualShock4Button(5, 6);
public readonly static DualShock4Button Options = new DualShock4Button(5, 5);
public readonly static DualShock4Button Share = new DualShock4Button(5, 4);
public readonly static DualShock4Button TriggerRight = new DualShock4Button(5, 3);
public readonly static DualShock4Button TriggerLeft = new DualShock4Button(5, 2);
public readonly static DualShock4Button ShoulderRight = new DualShock4Button(5, 1);
public readonly static DualShock4Button ShoulderLeft = new DualShock4Button(5, 0);
public readonly static DualShock4Button Triangle = new DualShock4Button(4, 7);
public readonly static DualShock4Button Circle = new DualShock4Button(4, 6);
public readonly static DualShock4Button Cross = new DualShock4Button(4, 5);
public readonly static DualShock4Button Square = new DualShock4Button(4, 4);
public readonly static DualShock4Button TPadClick = new DualShock4Button(6, 1);
public readonly static DualShock4Button PS = new DualShock4Button(6, 0);
private readonly static DualShock4Sensor Timestamp = new DualShock4Sensor(9);
private readonly static DualShock4Slider BatteryLevel = new DualShock4Slider(11);
private readonly static DualShock4Slider Counter = new DualShock4Slider(6);
public readonly static DualShock4Sensor GyroX = new DualShock4Sensor(12);
public readonly static DualShock4Sensor GyroY = new DualShock4Sensor(14);
public readonly static DualShock4Sensor GyroZ = new DualShock4Sensor(16);
public readonly static DualShock4Sensor AccelX = new DualShock4Sensor(18);
public readonly static DualShock4Sensor AccelY = new DualShock4Sensor(20);
public readonly static DualShock4Sensor AccelZ = new DualShock4Sensor(22);
public readonly static DualShock4DPadDirection DPadReleased = new DualShock4DPadDirection(4, 8, 15);
public readonly static DualShock4DPadDirection DPadNorthwest = new DualShock4DPadDirection(4, 7, 15);
public readonly static DualShock4DPadDirection DPadWest = new DualShock4DPadDirection(4, 6, 15);
public readonly static DualShock4DPadDirection DPadSouthwest = new DualShock4DPadDirection(4, 5, 15);
public readonly static DualShock4DPadDirection DPadSouth = new DualShock4DPadDirection(4, 4, 15);
public readonly static DualShock4DPadDirection DPadSoutheast = new DualShock4DPadDirection(4, 3, 15);
public readonly static DualShock4DPadDirection DPadEast = new DualShock4DPadDirection(4, 2, 15);
public readonly static DualShock4DPadDirection DPadNortheast = new DualShock4DPadDirection(4, 1, 15);
public readonly static DualShock4DPadDirection DPadNorth = new DualShock4DPadDirection(4, 0, 15);
public readonly static DualShock4Finger LeftFinger = new DualShock4Finger(0);
public readonly static DualShock4Finger RightFinger = new DualShock4Finger(1);
}
}

View file

@ -1,140 +0,0 @@
using WindowsInput;
namespace SteamController.Devices
{
public class KeyboardController : IDisposable
{
public static readonly TimeSpan FirstRepeat = TimeSpan.FromMilliseconds(400);
public static readonly TimeSpan NextRepeats = TimeSpan.FromMilliseconds(45);
InputSimulator simulator = new InputSimulator();
Dictionary<VirtualKeyCode, DateTime> keyCodes = new Dictionary<VirtualKeyCode, DateTime>();
Dictionary<VirtualKeyCode, DateTime> lastKeyCodes = new Dictionary<VirtualKeyCode, DateTime>();
public KeyboardController()
{
}
public void Dispose()
{
}
public bool this[System.Windows.Forms.Keys key]
{
get
{
if (key.HasFlag(System.Windows.Forms.Keys.Shift) && !this[VirtualKeyCode.SHIFT])
return false;
if (key.HasFlag(System.Windows.Forms.Keys.Alt) && !this[VirtualKeyCode.MENU])
return false;
if (key.HasFlag(System.Windows.Forms.Keys.Control) && !this[VirtualKeyCode.CONTROL])
return false;
return this[(VirtualKeyCode)(key & System.Windows.Forms.Keys.KeyCode)];
}
set
{
if (value)
{
this[VirtualKeyCode.SHIFT] = key.HasFlag(System.Windows.Forms.Keys.Shift);
this[VirtualKeyCode.MENU] = key.HasFlag(System.Windows.Forms.Keys.Alt);
this[VirtualKeyCode.CONTROL] = key.HasFlag(System.Windows.Forms.Keys.Control);
this[(VirtualKeyCode)(key & System.Windows.Forms.Keys.KeyCode)] = true;
}
}
}
public bool this[VirtualKeyCode key]
{
get { return keyCodes.ContainsKey(key); }
set
{
if (key == VirtualKeyCode.None)
return;
if (value)
{
if (keyCodes.ContainsKey(key))
return;
if (!lastKeyCodes.TryGetValue(key, out var keyRepeat))
keyRepeat = DateTime.Now.Add(FirstRepeat);
keyCodes.Add(key, keyRepeat);
}
}
}
public VirtualKeyCode[] DownKeys
{
get { return keyCodes.Keys.ToArray(); }
}
internal void BeforeUpdate()
{
lastKeyCodes = keyCodes;
keyCodes = new Dictionary<VirtualKeyCode, DateTime>();
}
private void Safe(Action action)
{
try
{
action();
Managers.SASManager.Valid = true;
}
catch (InvalidOperationException)
{
Managers.SASManager.Valid = false;
}
}
internal void Update()
{
// Key Up: it is missing now
foreach (var keyUp in lastKeyCodes.Except(keyCodes))
{
Safe(() => simulator.Keyboard.KeyUp(keyUp.Key));
}
// Key Down: new keys being down
foreach (var keyDown in keyCodes.Except(lastKeyCodes))
{
Safe(() => simulator.Keyboard.KeyDown(keyDown.Key));
}
// Key Repeats
var now = DateTime.Now;
foreach (var keyPress in keyCodes)
{
if (keyPress.Value > now)
continue;
Safe(() => simulator.Keyboard.KeyPress(keyPress.Key));
keyCodes[keyPress.Key] = DateTime.Now.Add(NextRepeats);
}
}
public void Overwrite(VirtualKeyCode key, bool value)
{
if (value)
this[key] = true;
else
keyCodes.Remove(key);
}
public void KeyPress(params VirtualKeyCode[] keyCodes)
{
Safe(() => simulator.Keyboard.KeyPress(keyCodes));
}
public void KeyPress(VirtualKeyCode modifierKey, params VirtualKeyCode[] keyCodes)
{
Safe(() => simulator.Keyboard.ModifiedKeyStroke(modifierKey, keyCodes));
}
public void KeyPress(IEnumerable<VirtualKeyCode> modifierKeys, params VirtualKeyCode[] keyCodes)
{
Safe(() => simulator.Keyboard.ModifiedKeyStroke(modifierKeys, keyCodes));
}
}
}

View file

@ -1,271 +0,0 @@
using WindowsInput;
namespace SteamController.Devices
{
public class MouseController : IDisposable
{
private struct Accum
{
double? last, now;
public bool Used
{
get { return now is not null; }
}
public void Tick()
{
last = now;
now = null;
}
public void Add(double delta)
{
now = (now ?? 0.0) + delta;
}
public int Consume()
{
double accum = ((now ?? 0.0) + (last ?? 0.0));
now = accum - (int)accum;
last = null;
return (int)accum;
}
}
// TODO: Unsure what it is
public const int XButtonID = 0;
public const int YButtonID = 1;
InputSimulator simulator = new InputSimulator();
HashSet<Button> mouseButtons = new HashSet<Button>();
HashSet<Button> lastMouseButtons = new HashSet<Button>();
Accum movedX, movedY, verticalScroll, horizontalScroll;
public enum Button
{
Left,
Right,
Middle,
X,
Y
}
public bool this[Button button]
{
get { return mouseButtons.Contains(button); }
set
{
if (value)
mouseButtons.Add(button);
}
}
public Button[] DownButtons
{
get { return mouseButtons.ToArray(); }
}
internal MouseController()
{
}
public void Dispose()
{
}
private void Safe(Action action)
{
try
{
action();
Managers.SASManager.Valid = true;
}
catch (InvalidOperationException)
{
Managers.SASManager.Valid = false;
}
}
internal void BeforeUpdate()
{
lastMouseButtons = mouseButtons;
mouseButtons = new HashSet<Button>();
movedX.Tick();
movedY.Tick();
verticalScroll.Tick();
horizontalScroll.Tick();
}
internal void Update()
{
// Mouse Up: it is missing now
foreach (var button in lastMouseButtons.Except(mouseButtons))
{
switch (button)
{
case Button.Left:
Safe(() => simulator.Mouse.LeftButtonUp());
break;
case Button.Right:
Safe(() => simulator.Mouse.RightButtonUp());
break;
case Button.Middle:
Safe(() => simulator.Mouse.MiddleButtonUp());
break;
case Button.X:
Safe(() => simulator.Mouse.XButtonUp(XButtonID));
break;
case Button.Y:
Safe(() => simulator.Mouse.XButtonUp(YButtonID));
break;
}
}
// Key Down: new keys being down
foreach (var button in mouseButtons.Except(lastMouseButtons))
{
switch (button)
{
case Button.Left:
Safe(() => simulator.Mouse.LeftButtonDown());
break;
case Button.Right:
Safe(() => simulator.Mouse.RightButtonDown());
break;
case Button.Middle:
Safe(() => simulator.Mouse.MiddleButtonDown());
break;
case Button.X:
Safe(() => simulator.Mouse.XButtonDown(XButtonID));
break;
case Button.Y:
Safe(() => simulator.Mouse.XButtonDown(YButtonID));
break;
}
}
// Move cursor
if (movedX.Used || movedY.Used)
{
int x = movedX.Consume();
int y = movedY.Consume();
if (x != 0 || y != 0)
{
Safe(() => simulator.Mouse.MoveMouseBy(x, y));
}
}
// Scroll
if (verticalScroll.Used)
{
int value = verticalScroll.Consume();
if (value != 0)
{
Safe(() => simulator.Mouse.VerticalScroll(value));
}
}
if (horizontalScroll.Used)
{
int value = horizontalScroll.Consume();
if (value != 0)
{
Safe(() => simulator.Mouse.HorizontalScroll(value));
}
}
}
public void MouseClick(Button button)
{
switch (button)
{
case Button.Left:
Safe(() => simulator.Mouse.LeftButtonClick());
break;
case Button.Right:
Safe(() => simulator.Mouse.RightButtonClick());
break;
case Button.Middle:
Safe(() => simulator.Mouse.MiddleButtonClick());
break;
case Button.X:
Safe(() => simulator.Mouse.XButtonClick(XButtonID));
break;
case Button.Y:
Safe(() => simulator.Mouse.XButtonClick(YButtonID));
break;
}
}
public void MouseDoubleClick(Button button)
{
switch (button)
{
case Button.Left:
Safe(() => simulator.Mouse.LeftButtonDoubleClick());
break;
case Button.Right:
Safe(() => simulator.Mouse.RightButtonDoubleClick());
break;
case Button.Middle:
Safe(() => simulator.Mouse.MiddleButtonDoubleClick());
break;
case Button.X:
Safe(() => simulator.Mouse.XButtonDoubleClick(XButtonID));
break;
case Button.Y:
Safe(() => simulator.Mouse.XButtonDoubleClick(YButtonID));
break;
}
}
public void Overwrite(Button button, bool value)
{
if (value)
mouseButtons.Add(button);
else
mouseButtons.Remove(button);
}
public void MoveBy(double pixelDeltaX, double pixelDeltaY)
{
movedX.Add(pixelDeltaX);
movedY.Add(pixelDeltaY);
}
public void MoveTo(double absoluteX, double absoluteY)
{
Safe(() => simulator.Mouse.MoveMouseTo(absoluteX, absoluteY));
}
public void VerticalScroll(double scrollAmountInClicks)
{
verticalScroll.Add(scrollAmountInClicks);
}
public void HorizontalScroll(double scrollAmountInClicks)
{
horizontalScroll.Add(scrollAmountInClicks);
}
}
}

View file

@ -1,405 +0,0 @@
using hidapi;
using PowerControl.External;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public abstract class SteamAction
{
public SteamController? Controller { get; internal set; }
public String Name { get; internal set; } = "";
/// This is action controlled by Lizard mode
public bool LizardButton { get; internal set; }
public bool LizardMouse { get; internal set; }
internal abstract void Reset();
internal abstract bool BeforeUpdate(byte[] buffer);
internal abstract void Update();
internal SteamAction()
{
}
protected bool ValueCanBeUsed
{
get
{
if (LizardButton && Controller?.LizardButtons == true)
return false;
if (LizardMouse && Controller?.LizardMouse == true)
return false;
return true;
}
}
}
public class SteamButton : SteamAction
{
public static readonly TimeSpan DefaultHoldDuration = TimeSpan.FromMilliseconds(10);
public static readonly TimeSpan DefaultFirstHold = TimeSpan.FromMilliseconds(400);
public static readonly TimeSpan DefaultRepeatHold = TimeSpan.FromMilliseconds(45);
private bool rawValue, rawLastValue;
public bool Value
{
get { return ValueCanBeUsed ? rawValue : false; }
}
public bool LastValue
{
get { return ValueCanBeUsed ? rawLastValue : false; }
}
/// Last press was already consumed by other
internal string? Consumed { get; private set; }
/// Set on raising edge
private DateTime? HoldSince { get; set; }
private DateTime? HoldRepeated { get; set; }
internal SteamButton()
{
}
public static implicit operator bool(SteamButton button) => button.Hold(DefaultHoldDuration, null);
/// Generated when button is pressed for the first time
public bool JustPressed()
{
if (!LastValue && Value)
return true;
return false;
}
/// Generated on failing edge of key press
public bool Pressed(TimeSpan? duration = null)
{
// We expect Last to be true, and now to be false (failing edge)
if (!(LastValue && !Value))
return false;
if (Consumed is not null)
return false;
if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now)
return false;
return true;
}
private bool Consume(string consume)
{
if (Consumed is not null && Consumed != consume)
return false;
Consumed = consume;
return true;
}
public bool Hold(string? consume)
{
return Hold(null, consume);
}
/// Generated when button was hold for a given period
public bool Hold(TimeSpan? duration, string? consume)
{
if (!Value)
return false;
if (Consumed is not null && Consumed != consume)
return false;
if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now)
return false;
if (consume is not null)
Consumed = consume;
return true;
}
public bool HoldOnce(string consume)
{
return HoldOnce(null, consume);
}
/// Generated when button was hold for a given period
/// but triggered exactly once
public bool HoldOnce(TimeSpan? duration, string consume)
{
if (!Hold(duration, null))
return false;
Consumed = consume;
return true;
}
/// Generated when button was hold for a given period
/// but triggered exactly after previously being hold
public bool HoldChain(TimeSpan? duration, string previousConsume, string replaceConsme)
{
if (!Hold(duration, previousConsume))
return false;
Consumed = replaceConsme;
return true;
}
/// Generated when button was repeated for a given period
/// but triggered exactly once
public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, string? consume)
{
// always generate at least one keypress
if (Pressed(duration))
return true;
if (!Hold(duration, consume))
return false;
// first keypress
if (!HoldRepeated.HasValue)
{
HoldRepeated = DateTime.Now;
return true;
}
// repeated keypress
if (HoldRepeated.Value.Add(repeatEvery) <= DateTime.Now)
{
HoldRepeated = DateTime.Now;
return true;
}
return false;
}
public bool HoldRepeat(string consume)
{
return HoldRepeat(DefaultFirstHold, DefaultRepeatHold, consume);
}
internal override void Reset()
{
rawLastValue = rawValue;
rawValue = false;
HoldSince = null;
HoldRepeated = null;
Consumed = null;
}
internal void SetValue(bool newValue)
{
rawLastValue = rawValue;
rawValue = newValue;
if (!rawLastValue && rawValue)
{
HoldSince = DateTime.Now;
HoldRepeated = null;
}
}
internal override bool BeforeUpdate(byte[] buffer)
{
return true;
}
internal override void Update()
{
if (!Value)
Consumed = null;
}
public override string? ToString()
{
if (Name != "")
return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue);
return base.ToString();
}
}
public class SteamButton2 : SteamButton
{
private int offset;
private uint mask;
internal SteamButton2(int offset, object mask)
{
this.offset = offset;
this.mask = (uint)mask.GetHashCode();
while (this.mask > 0xFF)
{
this.mask >>= 8;
this.offset++;
}
}
internal override bool BeforeUpdate(byte[] buffer)
{
if (offset < buffer.Length)
{
SetValue((buffer[offset] & mask) != 0);
return true;
}
else
{
SetValue((buffer[offset] & mask) != 0);
return false;
}
}
}
public enum DeltaValueMode
{
Absolute,
AbsoluteTime,
Delta,
DeltaTime
}
public class SteamAxis : SteamAction
{
public const short VirtualLeftThreshold = short.MinValue / 2;
public const short VirtualRightThreshold = short.MaxValue / 2;
private int offset;
private short rawValue, rawLastValue;
public SteamButton? ActiveButton { get; internal set; }
public SteamButton? VirtualLeft { get; internal set; }
public SteamButton? VirtualRight { get; internal set; }
public SteamAxis[] DeadzoneAxis { get; internal set; } = new SteamAxis[0];
public short Value
{
get { return ValueCanBeUsed ? rawValue : (short)0; }
}
public short LastValue
{
get { return ValueCanBeUsed ? rawLastValue : (short)0; }
}
public SteamAxis(int offset)
{
this.offset = offset;
}
public static implicit operator bool(SteamAxis button) => button.Active;
public static implicit operator short(SteamAxis button)
{
return button.Value;
}
public bool Active
{
get { return ActiveButton?.Value ?? true; }
}
private bool CheckDeadzone(short deadzone)
{
if (deadzone == 0)
return true;
int sum = Value * Value;
foreach (SteamAxis otherAxis in this.DeadzoneAxis)
sum += otherAxis.Value * otherAxis.Value;
return Math.Sqrt(sum) > deadzone;
}
public short GetValue(short deadzone)
{
if (CheckDeadzone(deadzone))
return Value;
return 0;
}
public double GetDeltaValue(double range, DeltaValueMode mode, short deadzone)
{
return GetDeltaValue(-range, range, mode, deadzone);
}
public double GetDeltaValue(double min, double max, DeltaValueMode mode, short deadzone)
{
if (!CheckDeadzone(deadzone))
return 0.0;
int value = 0;
switch (mode)
{
case DeltaValueMode.Absolute:
value = Value;
break;
case DeltaValueMode.AbsoluteTime:
value = (int)(Value * (Controller?.DeltaTime ?? 0.0));
break;
case DeltaValueMode.Delta:
value = Value - LastValue;
break;
case DeltaValueMode.DeltaTime:
value = Value - LastValue;
value = (int)(value * (Controller?.DeltaTime ?? 0.0));
break;
}
if (value == 0)
return 0.0;
double factor = (double)(value - short.MinValue) / (short.MaxValue - short.MinValue);
return factor * (max - min) + min;
}
internal override void Reset()
{
rawLastValue = rawValue;
rawValue = 0;
}
internal void SetValue(short newValue)
{
rawLastValue = rawValue;
rawValue = newValue;
// first time pressed, reset value as this is a Pad
if (ActiveButton is not null && ActiveButton.JustPressed())
rawLastValue = newValue;
if (VirtualRight is not null)
VirtualRight.SetValue(newValue > VirtualRightThreshold);
if (VirtualLeft is not null)
VirtualLeft.SetValue(newValue < VirtualLeftThreshold);
}
internal override bool BeforeUpdate(byte[] buffer)
{
if (offset + 1 < buffer.Length)
{
SetValue(BitConverter.ToInt16(buffer, offset));
return true;
}
else
{
SetValue(0);
return false;
}
}
internal override void Update()
{
}
public override string? ToString()
{
if (Name != "")
return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue);
return base.ToString();
}
}
}

View file

@ -1,82 +0,0 @@
using hidapi;
using PowerControl.External;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class SteamController
{
public readonly SteamButton BtnL5 = new SteamButton2(0x08, SDCButton0.BTN_L5);
public readonly SteamButton BtnOptions = new SteamButton2(0x08, SDCButton0.BTN_OPTIONS);
public readonly SteamButton BtnSteam = new SteamButton2(0x08, SDCButton0.BTN_STEAM);
public readonly SteamButton BtnMenu = new SteamButton2(0x08, SDCButton0.BTN_MENU);
public readonly SteamButton BtnDpadDown = new SteamButton2(0x08, SDCButton0.BTN_DPAD_DOWN) { LizardButton = true };
public readonly SteamButton BtnDpadLeft = new SteamButton2(0x08, SDCButton0.BTN_DPAD_LEFT) { LizardButton = true };
public readonly SteamButton BtnDpadRight = new SteamButton2(0x08, SDCButton0.BTN_DPAD_RIGHT) { LizardButton = true };
public readonly SteamButton BtnDpadUp = new SteamButton2(0x08, SDCButton0.BTN_DPAD_UP) { LizardButton = true };
public readonly SteamButton BtnA = new SteamButton2(0x08, SDCButton0.BTN_A) { LizardButton = true };
public readonly SteamButton BtnX = new SteamButton2(0x08, SDCButton0.BTN_X);
public readonly SteamButton BtnB = new SteamButton2(0x08, SDCButton0.BTN_B) { LizardButton = true };
public readonly SteamButton BtnY = new SteamButton2(0x08, SDCButton0.BTN_Y);
public readonly SteamButton BtnL1 = new SteamButton2(0x08, SDCButton0.BTN_L1);
public readonly SteamButton BtnL2 = new SteamButton2(0x08, SDCButton0.BTN_L2) { LizardButton = true };
public readonly SteamButton BtnR1 = new SteamButton2(0x08, SDCButton0.BTN_R1);
public readonly SteamButton BtnR2 = new SteamButton2(0x08, SDCButton0.BTN_R2) { LizardButton = true };
public readonly SteamButton BtnLeftStickPress = new SteamButton2(0x0a, SDCButton1.BTN_LSTICK_PRESS);
public readonly SteamButton BtnLPadTouch = new SteamButton2(0x0a, SDCButton1.BTN_LPAD_TOUCH);
public readonly SteamButton BtnLPadPress = new SteamButton2(0x0a, SDCButton1.BTN_LPAD_PRESS);
public readonly SteamButton BtnRPadPress = new SteamButton2(0x0a, SDCButton1.BTN_RPAD_PRESS);
public readonly SteamButton BtnRPadTouch = new SteamButton2(0x0a, SDCButton1.BTN_RPAD_TOUCH);
public readonly SteamButton BtnR5 = new SteamButton2(0x0a, SDCButton1.BTN_R5);
public readonly SteamButton BtnRightStickPress = new SteamButton2(0x0B, SDCButton2.BTN_RSTICK_PRESS);
public readonly SteamButton BtnLStickTouch = new SteamButton2(0x0D, SDCButton4.BTN_LSTICK_TOUCH);
public readonly SteamButton BtnRStickTouch = new SteamButton2(0x0D, SDCButton4.BTN_RSTICK_TOUCH);
public readonly SteamButton BtnR4 = new SteamButton2(0x0D, SDCButton4.BTN_R4);
public readonly SteamButton BtnL4 = new SteamButton2(0x0D, SDCButton4.BTN_L4);
public readonly SteamButton BtnQuickAccess = new SteamButton2(0x0E, SDCButton5.BTN_QUICK_ACCESS);
public readonly SteamButton BtnVirtualLeftThumbUp = new SteamButton();
public readonly SteamButton BtnVirtualLeftThumbDown = new SteamButton();
public readonly SteamButton BtnVirtualLeftThumbLeft = new SteamButton();
public readonly SteamButton BtnVirtualLeftThumbRight = new SteamButton();
public readonly SteamAxis LPadX = new SteamAxis(0x10);
public readonly SteamAxis LPadY = new SteamAxis(0x12);
public readonly SteamAxis RPadX = new SteamAxis(0x14) { LizardMouse = true };
public readonly SteamAxis RPadY = new SteamAxis(0x16) { LizardMouse = true };
public readonly SteamAxis AccelX = new SteamAxis(0x18);
public readonly SteamAxis AccelY = new SteamAxis(0x1A);
public readonly SteamAxis AccelZ = new SteamAxis(0x1C);
public readonly SteamAxis GyroPitch = new SteamAxis(0x1E);
public readonly SteamAxis GyroRoll = new SteamAxis(0x20);
public readonly SteamAxis GyroYaw = new SteamAxis(0x22);
public readonly SteamAxis LeftTrigger = new SteamAxis(0x2C);
public readonly SteamAxis RightTrigger = new SteamAxis(0x2E);
public readonly SteamAxis LeftThumbX = new SteamAxis(0x30);
public readonly SteamAxis LeftThumbY = new SteamAxis(0x32);
public readonly SteamAxis RightThumbX = new SteamAxis(0x34);
public readonly SteamAxis RightThumbY = new SteamAxis(0x36);
public readonly SteamAxis LPadPressure = new SteamAxis(0x38);
public readonly SteamAxis RPadPressure = new SteamAxis(0x38);
private void InitializeButtons()
{
LPadX.ActiveButton = BtnLPadTouch;
LPadY.ActiveButton = BtnLPadTouch;
RPadX.ActiveButton = BtnRPadTouch;
RPadY.ActiveButton = BtnRPadTouch;
// map virtual key presses
LeftThumbX.VirtualLeft = BtnVirtualLeftThumbLeft;
LeftThumbX.VirtualRight = BtnVirtualLeftThumbRight;
LeftThumbY.VirtualLeft = BtnVirtualLeftThumbDown;
LeftThumbY.VirtualRight = BtnVirtualLeftThumbUp;
// Include circular deadzone in calculation
LeftThumbX.DeadzoneAxis = new SteamAxis[] { LeftThumbY };
LeftThumbY.DeadzoneAxis = new SteamAxis[] { LeftThumbX };
RightThumbX.DeadzoneAxis = new SteamAxis[] { RightThumbY };
RightThumbY.DeadzoneAxis = new SteamAxis[] { RightThumbX };
}
}
}

View file

@ -1,133 +0,0 @@
using System.Diagnostics;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class SteamController : IDisposable
{
public const ushort VendorID = 0x28DE;
public const ushort ProductID = 0x1205;
private const int ReadTimeout = 50;
public const int MaxFailures = 10;
private hidapi.HidDevice neptuneDevice;
private Stopwatch stopwatch = new Stopwatch();
private TimeSpan? lastUpdate;
private int failures;
public long ElapsedMilliseconds { get => stopwatch.ElapsedMilliseconds; }
public double DeltaTime { get; private set; }
internal SteamController()
{
InitializeButtons();
InitializeActions();
OpenDevice();
stopwatch.Start();
}
~SteamController()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
using (neptuneDevice) { }
}
public bool Updated { get; private set; }
private void OpenDevice()
{
using (neptuneDevice) { }
neptuneDevice = new hidapi.HidDevice(VendorID, ProductID, 64);
neptuneDevice.OpenDevice();
}
internal void Fail(bool immediately = false)
{
foreach (var action in AllActions)
action.Reset();
// Try to re-open every MaxFailures
failures++;
if (failures % MaxFailures == 0 || immediately)
{
OpenDevice();
failures = 0;
}
}
private void BeforeUpdate(byte[] buffer)
{
foreach (var action in AllActions)
action.BeforeUpdate(buffer);
}
internal void BeforeUpdate()
{
var ts = stopwatch.Elapsed;
DeltaTime = lastUpdate is not null ? (ts - lastUpdate.Value).TotalSeconds : 0.0;
DeltaTime = Math.Min(DeltaTime, 0.1); // max update is 100ms
lastUpdate = ts;
LizardButtons = true;
LizardMouse = true;
try
{
byte[] data = neptuneDevice.Read(ReadTimeout);
if (data == null)
{
Fail();
Updated = false;
return;
}
BeforeUpdate(data);
Updated = true;
}
catch (hidapi.HidDeviceInvalidException)
{
// Steam might disconnect device
Fail();
Updated = false;
}
catch (Exception e)
{
TraceException("STEAM", "BeforeUpdate", e);
Fail();
Updated = false;
}
}
internal void Update()
{
foreach (var action in AllActions)
action.Update();
try
{
UpdateLizardButtons();
UpdateLizardMouse();
}
catch (hidapi.HidDeviceInvalidException)
{
// Steam might disconnect device
Fail();
}
catch (Exception e)
{
// Steam have disconnected device, which triggered exception
if (e.Message == "Could not send report to hid device. Error: -1")
DebugException("STEAM", "Update", e);
else
TraceException("STEAM", "Update", e);
Fail();
}
}
}
}

View file

@ -1,57 +0,0 @@
using hidapi;
using PowerControl.External;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class SteamController
{
public SteamAction?[] AllActions { get; private set; }
public SteamButton?[] AllButtons { get; private set; }
public SteamAxis?[] AllAxises { get; private set; }
public IEnumerable<SteamButton> HoldButtons
{
get
{
foreach (var action in AllButtons)
{
if (action.Value)
yield return action;
}
}
}
private void InitializeActions()
{
var allActions = GetType().
GetFields().
Where((field) => field.FieldType.IsSubclassOf(typeof(SteamAction))).
Select((field) => Tuple.Create(field, field.GetValue(this) as SteamAction)).
ToList();
allActions.ForEach((tuple) => tuple.Item2.Controller = this);
allActions.ForEach((tuple) => tuple.Item2.Name = tuple.Item1.Name);
AllActions = allActions.Select((tuple) => tuple.Item2).ToArray();
AllAxises = allActions.Where((tuple) => tuple.Item2 is SteamAxis).Select((tuple) => tuple.Item2 as SteamAxis).ToArray();
AllButtons = allActions.Where((tuple) => tuple.Item2 is SteamButton).Select((tuple) => tuple.Item2 as SteamButton).ToArray();
}
public IEnumerable<string> GetReport()
{
List<string> report = new List<string>();
var buttons = AllButtons.Where((button) => button.Value).Select((button) => button.Name);
if (buttons.Any())
yield return String.Format("Buttons: {0}", String.Join(",", buttons));
foreach (var axis in AllAxises)
{
if (!axis.Active)
continue;
yield return String.Format("Axis: {0} = {1} [Delta: {2}]", axis.Name, axis.Value, axis.Value - axis.LastValue);
}
}
}
}

View file

@ -1,124 +0,0 @@
using System.Runtime.InteropServices;
using CommonHelpers;
using PowerControl.External;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class SteamController
{
private bool[] feedbackEnabled = new bool[byte.MaxValue];
public bool SetFeedback(byte position, ushort amplitude, ushort period, ushort count = 0)
{
// do not send repeated haptic queries if was disabled
bool enabled = amplitude != 0 && period != 0;
if (!feedbackEnabled[position] && !enabled)
return false;
feedbackEnabled[position] = enabled;
var haptic = new SDCHapticPacket()
{
packet_type = (byte)SDCPacketType.PT_FEEDBACK,
len = (byte)SDCPacketLength.PL_FEEDBACK,
position = position,
amplitude = amplitude,
period = period,
count = count
};
Log.TraceLine("STEAM: Feedback: pos={0}, amplitude={1}, period={2}, count={3}",
position, amplitude, period, count);
var bytes = new byte[Marshal.SizeOf<SDCHapticPacket>()];
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
Marshal.StructureToPtr(haptic, handle.AddrOfPinnedObject(), false);
neptuneDevice.RequestFeatureReportAsync(bytes);
return true;
}
catch (Exception e)
{
TraceException("STEAM", "Feedback", e);
return false;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct SDCHapticPacket2
{
public byte packet_type = 0xea;
public byte len = 0xd;
public HapticPad position = HapticPad.Left;
public HapticStyle style = HapticStyle.Strong; //
public byte unsure2 = 0x0;
public sbyte intensity = 0x00; // -7..5 => -2dB..10dB
public byte unsure3 = 0x4;
public int tsA = 0; // timestamp?
public int tsB = 0;
public SDCHapticPacket2()
{
var ts = Random.Shared.Next();
this.tsA = ts;
this.tsB = ts;
}
public SDCHapticPacket2(HapticPad position, HapticStyle style, sbyte intensityDB) : this()
{
this.position = position;
this.style = style;
this.intensity = (sbyte)(intensityDB - 5); // convert from dB to values
}
}
private Dictionary<HapticPad, Task?> hapticTasks = new Dictionary<HapticPad, Task?>();
public enum HapticPad : byte
{
Left,
Right
};
public enum HapticStyle : byte
{
Disabled = 0,
Weak = 1,
Strong = 2
};
public bool SendHaptic(HapticPad position, HapticStyle style, sbyte intensityDB)
{
if (hapticTasks.GetValueOrDefault(position)?.IsCompleted == false)
return false;
if (style == HapticStyle.Disabled)
return true;
var haptic = new SDCHapticPacket2(position, style, intensityDB);
Log.TraceLine("STEAM: Haptic: position={0}, style={1}, intensity={2}",
position, style, intensityDB);
var bytes = new byte[Marshal.SizeOf<SDCHapticPacket2>()];
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
Marshal.StructureToPtr(haptic, handle.AddrOfPinnedObject(), false);
hapticTasks[position] = neptuneDevice.RequestFeatureReportAsync(bytes);
return true;
}
catch (hidapi.HidDeviceInvalidException)
{
// Steam might disconnect device
Fail();
return false;
}
catch (Exception e)
{
TraceException("STEAM", "Haptic", e);
return false;
}
}
}
}

View file

@ -1,73 +0,0 @@
using hidapi;
using PowerControl.External;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public partial class SteamController
{
private const int LizardModeUpdateInterval = 250;
public bool LizardMouse { get; set; } = true;
public bool LizardButtons { get; set; } = true;
private bool? savedLizardMouse;
private bool? savedLizardButtons;
private DateTime lizardMouseUpdated = DateTime.Now;
private DateTime lizardButtonUpdated = DateTime.Now;
private void UpdateLizardMouse()
{
if (savedLizardMouse == LizardMouse)
{
// We need to explicitly disable lizard every some time
// but don't fight enabling it, as someone else might be taking control (Steam?)
if (lizardMouseUpdated.AddMilliseconds(LizardModeUpdateInterval) > DateTime.Now)
return;
}
savedLizardMouse = LizardMouse;
lizardMouseUpdated = DateTime.Now;
if (LizardMouse)
{
//Enable mouse emulation
byte[] data = new byte[] { 0x8e, 0x00 };
neptuneDevice.RequestFeatureReport(data);
}
else
{
//Disable mouse emulation
byte[] data = new byte[] { 0x87, 0x03, 0x08, 0x07 };
neptuneDevice.RequestFeatureReport(data);
}
}
private void UpdateLizardButtons()
{
if (savedLizardButtons == LizardButtons)
{
// We need to explicitly disable lizard every some time
// but don't fight enabling it, as someone else might be taking control (Steam?)
if (lizardButtonUpdated.AddMilliseconds(LizardModeUpdateInterval) > DateTime.Now)
return;
}
savedLizardButtons = LizardButtons;
lizardButtonUpdated = DateTime.Now;
if (LizardButtons)
{
//Enable keyboard/mouse button emulation
byte[] data = new byte[] { 0x85, 0x00 };
neptuneDevice.RequestFeatureReport(data);
}
else
{
//Disable keyboard/mouse button emulation
byte[] data = new byte[] { 0x81, 0x00 };
neptuneDevice.RequestFeatureReport(data);
}
}
}
}

View file

@ -1,284 +0,0 @@
using Nefarius.ViGEm.Client;
using Nefarius.ViGEm.Client.Exceptions;
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.Xbox360;
using static CommonHelpers.Log;
namespace SteamController.Devices
{
public class Xbox360Controller : IDisposable
{
public readonly TimeSpan FeedbackTimeout = TimeSpan.FromMilliseconds(1000);
public const ushort VendorID = 0x045E;
public const ushort ProductID = 0x028E;
private ViGEmClient? client;
private IXbox360Controller? device;
private bool isConnected;
private bool submitReport;
private Dictionary<Xbox360Button, DateTime> lastPressed = new Dictionary<Xbox360Button, DateTime>();
private Dictionary<Xbox360Button, DateTime> pressed = new Dictionary<Xbox360Button, DateTime>();
public Xbox360Controller()
{
}
public void Dispose()
{
using (client) { }
}
public void Start()
{
}
public void Stop()
{
lock (this) { Fail(); }
}
internal bool Tick()
{
if (this.device is not null)
return true;
try
{
var client = new ViGEmClient();
var device = client.CreateXbox360Controller();
device.AutoSubmitReport = false;
device.FeedbackReceived += X360Device_FeedbackReceived;
this.device = device;
this.client = client;
return true;
}
catch (VigemBusNotFoundException)
{
// ViGem is not installed
return false;
}
}
private void Fail()
{
var client = this.client;
// unset current device
this.isConnected = false;
this.client = null;
this.device = null;
try { using (client) { } }
catch (Exception) { }
}
internal void BeforeUpdate()
{
device?.ResetReport();
if (!isConnected)
{
FeedbackLargeMotor = null;
FeedbackSmallMotor = null;
FeedbackReceived = null;
LedNumber = 0;
}
lastPressed = pressed;
pressed = new Dictionary<Xbox360Button, DateTime>();
submitReport = false;
Connected = Settings.Default.KeepX360AlwaysConnected;
}
private void SetConnected(bool wantsConnected)
{
if (wantsConnected == isConnected)
return;
if (wantsConnected)
{
try
{
device?.Connect();
TraceLine("Connected X360 Controller.");
}
catch (System.ComponentModel.Win32Exception e)
{
// This is expected exception (as sometimes device will fail to connect)
// ERROR_SUCCESS, which likely means COM did not succeed
if (e.NativeErrorCode == 0)
DebugException("X360", "ConnectExpected", e);
else
TraceException("X360", "ConnectExpected", e);
Fail();
return;
}
catch (Exception e)
{
TraceException("X360", "Connect", e);
Fail();
return;
}
}
else
{
try
{
device?.Disconnect();
TraceLine("Disconnected X360 Controller.");
}
catch (VigemTargetNotPluggedInException)
{
// everything fine
}
catch (Exception e)
{
TraceException("X360", "Disconnect", e);
Fail();
return;
}
}
isConnected = wantsConnected;
}
internal void Beep()
{
if (Settings.Default.KeepX360AlwaysConnected)
return;
if (device is null)
return;
lock (this)
{
// cycle currently connected device
SetConnected(!isConnected);
Thread.Sleep(100);
}
}
internal void Update()
{
if (device is not null && Connected != isConnected)
{
lock (this)
{
SetConnected(Connected);
}
}
UpdateMinimumPressedTime();
if (isConnected && submitReport)
{
try
{
device?.SubmitReport();
}
catch (VigemInvalidTargetException)
{
// Device was lost
lock (this) { Fail(); }
}
catch (Exception e)
{
TraceException("X360", "SubmitReport", e);
}
}
if (FeedbackReceived is not null && FeedbackReceived.Value.Add(FeedbackTimeout) < DateTime.Now)
{
FeedbackLargeMotor = null;
FeedbackSmallMotor = null;
FeedbackReceived = null;
}
}
private void UpdateMinimumPressedTime()
{
var now = DateTime.Now;
foreach (var key in lastPressed)
{
if (pressed.ContainsKey(key.Key))
continue;
// until time elapsed, keep setting button state
if (key.Value < DateTime.Now)
continue;
device?.SetButtonState(key.Key, true);
pressed.Add(key.Key, key.Value);
submitReport = true;
}
}
public bool Valid
{
get { return device is not null; }
}
public bool Connected { get; set; }
public byte? FeedbackLargeMotor { get; private set; }
public byte? FeedbackSmallMotor { get; private set; }
public byte LedNumber { get; private set; }
public DateTime? FeedbackReceived { get; private set; }
public bool this[Xbox360Button? button]
{
set
{
if (value && button is not null)
device?.SetButtonState(button, value);
submitReport = true;
}
}
public short this[Xbox360Axis axis]
{
set
{
device?.SetAxisValue(axis, value);
submitReport = true;
}
}
public short this[Xbox360Slider slider]
{
set
{
// rescale from 0..32767 to 0..255
int result = Math.Clamp(value, (short)0, short.MaxValue) * byte.MaxValue / short.MaxValue;
device?.SetSliderValue(slider, (byte)result);
submitReport = true;
}
}
public void Overwrite(Xbox360Button button, bool value, int minPresTimeMs = 0)
{
device?.SetButtonState(button, value);
submitReport = true;
if (value && minPresTimeMs > 0)
{
if (!lastPressed.TryGetValue(button, out var firstPressed))
firstPressed = DateTime.Now.AddMilliseconds(minPresTimeMs);
pressed.Add(button, firstPressed);
}
}
public void ResetFeedback()
{
FeedbackReceived = null;
}
private void X360Device_FeedbackReceived(object sender, Xbox360FeedbackReceivedEventArgs e)
{
FeedbackLargeMotor = e.LargeMotor;
FeedbackSmallMotor = e.SmallMotor;
LedNumber = e.LedNumber;
FeedbackReceived = DateTime.Now;
}
}
}

View file

@ -1,74 +0,0 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SteamController.Helpers
{
internal static class ForegroundProcess
{
private static int? lastForegroundProcess;
public static void Store()
{
lastForegroundProcess = GetTopLevelProcessId();
}
public static bool Kill(bool onlyTheSame = false)
{
var process = Find();
if (process is null)
return true;
if (onlyTheSame && process.Id != lastForegroundProcess)
return false;
try
{
using (process) { process.Kill(); }
return true;
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
}
public static Process? Find()
{
return Find(GetForegroundWindow());
}
public static Process? Find(IntPtr hWnd)
{
try
{
var id = GetProcessId(hWnd);
if (id is null)
return null;
var process = Process.GetProcessById(id.Value);
if (!process.HasExited)
return process;
}
catch { }
return null;
}
private static int? GetTopLevelProcessId()
{
return GetProcessId(GetForegroundWindow());
}
private static int? GetProcessId(IntPtr hWnd)
{
var result = GetWindowThreadProcessId(hWnd, out var processId);
if (result != 0)
return processId;
return null;
}
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
}
}

View file

@ -1,420 +0,0 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using CommonHelpers;
using Microsoft.Win32;
namespace SteamController.Helpers
{
internal static class SteamConfiguration
{
public const String SteamKey = @"Software\Valve\Steam";
public const String RunningAppIDValue = @"RunningAppID";
public const String SteamExeValue = @"SteamExe";
public const String SteamPathValue = @"SteamPath";
public const String BigPictureInForegroundValue = @"BigPictureInForeground";
public const String ActiveProcessKey = @"Software\Valve\Steam\ActiveProcess";
public const String PIDValue = @"pid";
public const String RelativeConfigPath = @"config/config.vdf";
public const String CrashReportConfigPath = @"bin/cef/cef.win7x64/crash_reporter.cfg";
private static readonly Regex ControllerBlacklistRegex = new Regex("^(\\s*\"controller_blacklist\"\\s*\")([^\"]*)(\"\\s*)$");
public static bool IsRunning
{
get
{
using (var process = SteamProcess)
{
return process is not null;
}
}
}
public static bool? IsBigPictureMode
{
get
{
var value = GetValue<int>(SteamKey, BigPictureInForegroundValue);
return value.HasValue ? value != 0 : null;
}
}
public static bool? IsRunningGame
{
get
{
var value = GetValue<int>(SteamKey, RunningAppIDValue);
return value.HasValue ? value != 0 : null;
}
}
public static uint SteamVersion
{
get
{
var path = GetConfigPath(CrashReportConfigPath);
if (path is null)
return 0;
return GetPrivateProfileInt("Config", "ProductVersion", 0, path);
}
}
public static String? SteamExe
{
get { return GetValue2<string>(SteamKey, SteamExeValue); }
}
public static String? SteamPath
{
get { return GetValue2<string>(SteamKey, SteamPathValue); }
}
private static Process? SteamProcess
{
get
{
var value = GetValue<int>(ActiveProcessKey, PIDValue);
if (value is null)
return null;
try
{
var process = Process.GetProcessById(value.Value);
if (!process.ProcessName.Equals("Steam", StringComparison.CurrentCultureIgnoreCase))
return null;
if (process.HasExited)
return null;
return process;
}
catch { return null; }
}
}
public static String? GetConfigPath(String configPath)
{
var path = SteamPath;
if (path is null)
return null;
return Path.Join(SteamPath, configPath);
}
public static bool ShutdownSteam()
{
var steamExe = SteamExe;
if (steamExe is null)
return false;
var process = Process.Start(new ProcessStartInfo()
{
FileName = steamExe,
ArgumentList = { "-shutdown" },
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
CreateNoWindow = true
});
return process is not null;
}
public static bool KillSteam()
{
var process = SteamProcess;
if (process is null)
return true;
try
{
using (process) { process.Kill(); }
return true;
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
}
public static bool WaitForSteamClose(int timeout)
{
var waitTill = DateTimeOffset.Now.AddMilliseconds(timeout);
while (DateTimeOffset.Now < waitTill)
{
if (!IsRunning)
return true;
Application.DoEvents();
Thread.Sleep(50);
}
return false;
}
public static HashSet<String>? GetControllerBlacklist()
{
try
{
var configPath = GetConfigPath(RelativeConfigPath);
if (configPath is null)
return null;
foreach (var line in File.ReadLines(configPath).Reverse())
{
var match = ControllerBlacklistRegex.Match(line);
if (!match.Success)
continue;
// matches `"controller_blacklist" "<value>"`
var value = match.Groups[2].Captures[0].Value;
return value.Split(',', StringSplitOptions.RemoveEmptyEntries).ToHashSet();
}
return new HashSet<String>();
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return null;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return null;
}
}
public static bool? IsControllerBlacklisted(ushort vendorId, ushort productId)
{
var controllers = GetControllerBlacklist();
if (controllers is null)
return null;
var id = String.Format("{0:x}/{1:x}", vendorId, productId);
return controllers.Contains(id);
}
public static bool BackupSteamConfig(String path)
{
var configPath = GetConfigPath(path);
if (configPath is null)
return true;
try
{
var suffix = DateTime.Now.ToString("yyyyMMddHHmmss");
File.Copy(configPath, String.Format("{0}.{1}.bak", configPath, suffix));
return true;
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return false;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return false;
}
}
public static bool BackupSteamConfig()
{
return BackupSteamConfig(RelativeConfigPath);
}
public static bool UpdateControllerBlacklist(ushort vendorId, ushort productId, bool add)
{
if (IsRunning)
return false;
var configPath = GetConfigPath(RelativeConfigPath);
if (configPath is null)
return false;
try
{
var lines = File.ReadLines(configPath).ToList();
var id = String.Format("{0:x}/{1:x}", vendorId, productId);
for (int i = 0; i < lines.Count; i++)
{
if (lines[i] == "}")
{
if (add)
{
// append controller_blacklist
lines.Insert(i, String.Format("\t\"controller_blacklist\"\t\t\"{0}\"", id));
break;
}
}
var match = ControllerBlacklistRegex.Match(lines[i]);
if (!match.Success)
continue;
var value = match.Groups[2].Captures[0].Value;
var controllers = value.Split(',', StringSplitOptions.RemoveEmptyEntries).ToHashSet();
if (add)
controllers.Add(id);
else
controllers.Remove(id);
lines[i] = String.Format("{0}{1}{2}",
match.Groups[1].Captures[0].Value,
String.Join(',', controllers),
match.Groups[3].Captures[0].Value
);
break;
}
File.WriteAllLines(configPath, lines);
return true;
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return false;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return false;
}
}
public static bool? IsConfigFileOverwritten(String path, byte[] content)
{
try
{
var configPath = GetConfigPath(path);
if (configPath is null)
return null;
byte[] diskContent = File.ReadAllBytes(configPath);
return content.SequenceEqual(diskContent);
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return null;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return null;
}
}
public static bool? ResetConfigFile(String path)
{
try
{
var configPath = GetConfigPath(path);
if (configPath is null)
return null;
File.Copy(configPath + ".orig", configPath, true);
return true;
}
catch (FileNotFoundException e)
{
// File was not found (which is valid as it might be before first start of the application)
Log.DebugException("STEAM", e);
return false;
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return null;
}
catch (UnauthorizedAccessException)
{
return false;
}
catch (System.Security.SecurityException)
{
return false;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return null;
}
}
public static bool? OverwriteConfigFile(String path, byte[] content, bool backup)
{
try
{
var configPath = GetConfigPath(path);
if (configPath is null)
return null;
try
{
byte[] diskContent = File.ReadAllBytes(configPath);
if (content.Equals(diskContent))
return false;
}
catch (IOException) { }
if (backup)
File.Copy(configPath, configPath + ".orig", true);
File.WriteAllBytes(configPath, content);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
catch (System.Security.SecurityException)
{
return false;
}
catch (DirectoryNotFoundException)
{
// Steam was installed, but got removed
return false;
}
catch (IOException e)
{
Log.TraceException("STEAM", "Config", e);
return null;
}
}
private static T? GetValue<T>(string key, string value) where T : struct
{
try
{
using (var registryKey = Registry.CurrentUser.OpenSubKey(key))
{
return registryKey?.GetValue(value) as T?;
}
}
catch (Exception)
{
return null;
}
}
private static T? GetValue2<T>(string key, string value) where T : class
{
try
{
using (var registryKey = Registry.CurrentUser.OpenSubKey(key))
{
return registryKey?.GetValue(value) as T;
}
}
catch (Exception)
{
return null;
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern uint GetPrivateProfileInt(string lpAppName, string lpKeyName, int nDefault, string lpFileName);
}
}

View file

@ -1,13 +0,0 @@
using SteamController.Profiles;
namespace SteamController.Managers
{
public abstract class Manager : IDisposable
{
public abstract void Tick(Context context);
public virtual void Dispose()
{
}
}
}

Some files were not shown because too many files have changed in this diff Show more