Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CPUSetSetter.TrayIcon/AppTrayIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,39 @@ namespace CPUSetSetter.TrayIcon
public class AppTrayIcon : IDisposable
{
private readonly NotifyIcon? _trayIcon;
private readonly ToolStripMenuItem _gameModeItem;
private bool _isGameModeEnabled;

public event EventHandler? OpenClicked;
public event EventHandler? CloseClicked;
public event EventHandler<bool>? GameModeToggled;

public bool IsGameModeEnabled
{
get => _isGameModeEnabled;
set
{
_isGameModeEnabled = value;
_gameModeItem.Text = value ? "Disable Game Mode" : "Enable Game Mode";
}
}

public AppTrayIcon(Stream iconStream)
{
ContextMenuStrip trayMenu = new();
trayMenu.Items.Add("Open", null, (_, _) => OpenClicked?.Invoke(this, new()));
trayMenu.Items.Add(new ToolStripSeparator());

_gameModeItem = new ToolStripMenuItem();
IsGameModeEnabled = false; // This triggers the setter and sets the initial text
_gameModeItem.Click += (_, _) =>
{
IsGameModeEnabled = !_isGameModeEnabled;
GameModeToggled?.Invoke(this, _isGameModeEnabled);
};
trayMenu.Items.Add(_gameModeItem);

trayMenu.Items.Add(new ToolStripSeparator());
trayMenu.Items.Add("Close", null, (_, _) => CloseClicked?.Invoke(this, new()));
_trayIcon = new()
{
Expand Down
9 changes: 9 additions & 0 deletions CPUSetSetter/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using CPUSetSetter.Config.Models;
using CPUSetSetter.Platforms;
using CPUSetSetter.Platforms.Windows;
using CPUSetSetter.TrayIcon;
using CPUSetSetter.UI;
using CPUSetSetter.Util;
Expand Down Expand Up @@ -77,6 +78,14 @@ protected override void OnStartup(StartupEventArgs e)
trayIcon.OpenClicked += (_, _) => ShowMainWindow();
trayIcon.CloseClicked += (_, _) => ExitAppGracefully();

// Sync the Game Mode state between the tray icon and the GameMode class
trayIcon.IsGameModeEnabled = GameMode.IsEnabled;
trayIcon.GameModeToggled += (_, isEnabled) => GameMode.IsEnabled = isEnabled;
GameMode.IsEnabledChanged += (_, _) =>
{
Dispatcher.InvokeAsync(() => trayIcon.IsGameModeEnabled = GameMode.IsEnabled);
};

if (AppConfig.Instance.IsFirstRun)
{
// Promote the tray icon directly onto the Taskbar instead of in the "up-arrow" menu
Expand Down
4 changes: 4 additions & 0 deletions CPUSetSetter/Config/AppConfigFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ private static AppConfig JsonToConfig(ConfigJson configJson, bool generateDefaul
configJson.ShowGameModePopup,
configJson.ShowUpdatePopup,
configJson.ClearMasksOnClose,
configJson.DisableGameModeMaskClearing,
Enum.Parse<Theme>(configJson.UiTheme),
generateDefaultMasks,
isFirstRun,
Expand All @@ -196,6 +197,7 @@ private class ConfigJson
public bool ShowGameModePopup { get; init; }
public bool ShowUpdatePopup { get; init; }
public bool ClearMasksOnClose { get; init; }
public bool DisableGameModeMaskClearing { get; init; }
public string UiTheme { get; init; }
public int ConfigVersion { get; init; } // Can be used in the future to migrate config files

Expand All @@ -215,6 +217,7 @@ private ConfigJson()
ShowGameModePopup = true;
ShowUpdatePopup = true;
ClearMasksOnClose = false;
DisableGameModeMaskClearing = false;
UiTheme = Theme.System.ToString();
ConfigVersion = 0;
}
Expand Down Expand Up @@ -255,6 +258,7 @@ public ConfigJson(AppConfig config)
ShowGameModePopup = config.ShowGameModePopup;
ShowUpdatePopup = config.ShowUpdatePopup;
ClearMasksOnClose = config.ClearMasksOnClose;
DisableGameModeMaskClearing = config.DisableGameModeMaskClearing;
UiTheme = config.UiTheme.ToString();
ConfigVersion = AppConfigFile.ConfigVersion;
}
Expand Down
5 changes: 5 additions & 0 deletions CPUSetSetter/Config/Models/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public partial class AppConfig : ObservableConfigObject
[ObservableProperty]
private bool _clearMasksOnClose;

[ObservableProperty]
private bool _disableGameModeMaskClearing;

[ObservableProperty]
private Theme _uiTheme;

Expand All @@ -57,6 +60,7 @@ public AppConfig(List<LogicalProcessorMask> logicalProcessorMasks,
bool showGameModePopup,
bool showUpdatePopup,
bool clearMasksOnClose,
bool disableGameModeMaskClearing,
Theme uiTheme,
bool generateDefaultMasks,
bool isFirstRun,
Expand All @@ -77,6 +81,7 @@ public AppConfig(List<LogicalProcessorMask> logicalProcessorMasks,
_showGameModePopup = showGameModePopup;
_showUpdatePopup = showUpdatePopup;
_clearMasksOnClose = clearMasksOnClose;
_disableGameModeMaskClearing = disableGameModeMaskClearing;
_uiTheme = uiTheme;
IsFirstRun = isFirstRun;

Expand Down
4 changes: 2 additions & 2 deletions CPUSetSetter/Platforms/IProcessHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace CPUSetSetter.Platforms
public interface IProcessHandler : IDisposable
{
/// <summary>
/// Get the average CPU usage of the process of the recent past (~30 seconds)
/// Get the CPU usage of the process
/// </summary>
/// <returns>Between 0 and 1 on success. -1 on fail</returns>
double GetAverageCpuUsage();
double GetCpuUsage();
bool ApplyMask(LogicalProcessorMask mask);
}
}
69 changes: 69 additions & 0 deletions CPUSetSetter/Platforms/Windows/GameMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using CPUSetSetter.UI.Tabs.Processes;
using Microsoft.Win32;
using System;

namespace CPUSetSetter.Platforms.Windows
{
public static class GameMode
{
private const string GameBarKey = @"Software\Microsoft\GameBar";
private const string ValueName = "AutoGameModeEnabled";

public static event EventHandler? IsEnabledChanged;
private static readonly object _stateLock = new();
private static bool? _lastState;

public static void CheckState()
{
bool raiseEvent = false;

lock (_stateLock)
{
bool currentState = IsEnabled;
if (!_lastState.HasValue)
{
_lastState = currentState;
return;
}
if (_lastState != currentState)
{
_lastState = currentState;
raiseEvent = true;
}
}

if (raiseEvent)
{
IsEnabledChanged?.Invoke(null, EventArgs.Empty);
}
}

public static bool IsEnabled
{
get
{
using var key = Registry.CurrentUser.OpenSubKey(GameBarKey);
return key?.GetValue(ValueName, 0) is int intValue && intValue != 0;
}
set
{
try
{
using var key = Registry.CurrentUser.CreateSubKey(GameBarKey);
if (key == null)
{
WindowLogger.Write("Unable to toggle Windows Game Mode: Registry key could not be created or opened.");
return;
}

key.SetValue(ValueName, value ? 1 : 0, RegistryValueKind.DWord);
CheckState();
}
catch (Exception ex)
{
WindowLogger.Write($"Unable to toggle Windows Game Mode: {ex.Message}");
}
}
}
}
}
59 changes: 30 additions & 29 deletions CPUSetSetter/Platforms/Windows/GameModeWarning.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,65 @@
using CPUSetSetter.Config.Models;
using CPUSetSetter.UI.Tabs.Processes;
using Microsoft.Win32;
using System.Diagnostics;
using System.Windows;


namespace CPUSetSetter.Platforms
namespace CPUSetSetter.Platforms.Windows
{
/// <summary>
/// Windows Game Mode is abysmal to gaming performance when combined with CPU Set Setter.
/// Warn the user, and encourage them to turn it off, or experiment with it on.
/// </summary>
public static class WindowsGameModeWarning
{
private static bool _eventHooked = false;
private const string LogWarningMessage = "WARNING: While Windows Game Mode is enabled, no masks are applied. (This behavior can be disabled in the Settings tab)";

public static void ShowIfEnabled()
{
if (!GameModeEnabled())
return;
if (!_eventHooked)
{
_eventHooked = true;
GameMode.IsEnabledChanged += (s, e) =>
{
if (GameMode.IsEnabled && !AppConfig.Instance.DisableGameModeMaskClearing)
{
WindowLogger.Write(LogWarningMessage);
}
};
}

WindowLogger.Write("WARNING: Windows Game Mode is enabled. This might have a severe negative impact on gaming performance/stability " +
"when also using CPU Set Setter on games.");
if (!GameMode.IsEnabled)
return;

if (AppConfig.Instance.ShowGameModePopup)
{
App.Current.Dispatcher.InvokeAsync(() =>
Application.Current?.Dispatcher?.InvokeAsync(() =>
{
MessageBoxResult result = MessageBox.Show(
"Windows Game Mode is currently enabled.\n" +
"On AMD CPUs, Game Mode is known to conflict with CPU Set Setter, leading to lower FPS and game crashes.\n" +
"I am not sure if Intel CPUs are affected too. So if you have one, please share your findings with me on GitHub!\n\n" +
"Would you like to open the Game Mode Settings page?\n\n" +
"Would you like CPU Set Setter to disable Game Mode for you?\n\n" +
"(This popup can be disabled in the Settings tab)",
"CPU Set Setter: Windows Game Mode warning",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);

if (result == MessageBoxResult.Yes)
{
OpenGameModeSettings();
GameMode.IsEnabled = false;
}
else
{
if (!AppConfig.Instance.DisableGameModeMaskClearing)
WindowLogger.Write(LogWarningMessage);
}
});
}
}

private static bool GameModeEnabled()
{
using RegistryKey? gameBarKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\GameBar");
if (gameBarKey is null)
return false;

object? gameModeValue = gameBarKey.GetValue("AutoGameModeEnabled");
if (gameModeValue is int value)
return value != 0;

return false;
}

private static void OpenGameModeSettings()
{
Process.Start(new ProcessStartInfo { FileName = "ms-settings:gaming-gamemode", UseShellExecute = true });
else
{
if (!AppConfig.Instance.DisableGameModeMaskClearing)
WindowLogger.Write(LogWarningMessage);
}
}
}
}
Loading