mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-04-03 11:19:34 +02:00
Removes unused projects
This commit is contained in:
parent
f52cc23504
commit
253346b786
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
CommonHelpers/GlobalUsings.cs
Normal file
1
CommonHelpers/GlobalUsings.cs
Normal file
|
|
@ -0,0 +1 @@
|
|||
global using System.IO;
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
1
DisableCheckForUpdates.txt
Normal file
1
DisableCheckForUpdates.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Disabled by default in this fork.
|
||||
1
DisableSentryTracking.txt
Normal file
1
DisableSentryTracking.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Disabled by default in this fork.
|
||||
|
|
@ -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 |
361
FanControl/FanControlForm.Designer.cs
generated
361
FanControl/FanControlForm.Designer.cs
generated
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
83
FanControl/Resources.Designer.cs
generated
83
FanControl/Resources.Designer.cs
generated
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 |
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() + "%";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
103
PowerControl/Resources.Designer.cs
generated
103
PowerControl/Resources.Designer.cs
generated
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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 |
|
|
@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
45
README.md
45
README.md
|
|
@ -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
|
||||
|
||||
[](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`).
|
||||
43
RELEASE.md
43
RELEASE.md
|
|
@ -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`
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in a new issue