Skip to content

Commit 901b64c

Browse files
carterscodeclaude
andcommitted
fix: persist preference toggles + log them to changes.log
Two issues the user reported on Fullscreen Optimizations: (1) had to re-enable Monitor and AutoApply (they didn't stick), (2) the change log only recorded registry writes, not the user's UI toggles. Persistence fix: - Each GlobalToggleRow setter (Monitor / DesiredOn / AutoApply) now invokes an onPrefChanged callback that immediately calls _store.Save(_config). So toggling a checkbox writes to disk on the spot — no need to remember to click Save. - SettingsWindow.OnWindowClosing also persists the form (covers any edge cases where a control wasn't routed through a row callback, and any path that closes via the X button). - Cancel button explicitly suppresses save-on-close so its discard semantics still work for users who want to bail on a change. Logging fix: - ChangeLogger.LogPreferenceChange writes a single line: [2026-05-06 22:42:15] [ui ] PREF Fullscreen optimizations (global) | Monitor: False -> True Lives in the same %APPDATA%\GamerGuardian\changes.log alongside the multi-line registry-write entries; sources are distinguishable by the [ui] / [manual] / [auto] tag. - Every Monitor / Want / AutoApply toggle on the 11 global toggle rows fires a log entry. Same source name as shown in Settings (so the entry says "Fullscreen optimizations (global)" not "fso"). Net effect for the user: toggling Monitor=true on FSO is persisted on the click, regardless of how Settings is closed; the log shows the toggle clearly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3b0d771 commit 901b64c

3 files changed

Lines changed: 102 additions & 17 deletions

File tree

src/GamerGuardian/Services/ChangeLogger.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ public static void LogApplyResults(IEnumerable<ApplyResult> results, string sour
3737
catch { /* logging is best-effort */ }
3838
}
3939

40+
/// <summary>
41+
/// Records a preference toggle (Monitor / AutoApply / Want) made in the Settings UI.
42+
/// Single line per change so the log stays scannable.
43+
/// </summary>
44+
public static void LogPreferenceChange(string settingName, string field, string before, string after)
45+
{
46+
try
47+
{
48+
var dir = Path.GetDirectoryName(LogPath);
49+
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
50+
RotateIfNeeded();
51+
var line = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ui ] PREF {settingName} | {field}: {before} -> {after}";
52+
File.AppendAllText(LogPath, line + Environment.NewLine, Encoding.UTF8);
53+
}
54+
catch { }
55+
}
56+
4057
private static string Format(ApplyResult r, string source)
4158
{
4259
var status = r.Verified ? "OK" : "FAILED";

src/GamerGuardian/UI/SettingsWindow.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
55
Title="GamerGuardian - Settings"
6+
Closing="OnWindowClosing"
67
Width="780" Height="760"
78
MinWidth="640" MinHeight="500"
89
WindowStartupLocation="CenterScreen"

src/GamerGuardian/UI/SettingsWindow.xaml.cs

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,17 @@ private void LoadGlobals()
8989
currentText: $"Current: {OnOffText(SafeRead(GameModeMonitor.ReadCurrent))}",
9090
defaultText: "Default: Enabled",
9191
onLabel: "Enabled", offLabel: "Disabled",
92-
pref: g.GameMode, groupName: "gm"));
92+
pref: g.GameMode, groupName: "gm",
93+
onPrefChanged: OnRowPrefChanged));
9394

9495
GlobalToggleRows.Add(new GlobalToggleRow(
9596
name: "Game DVR background recording",
9697
description: "Always-on game capture. Costs CPU/GPU during gameplay; off is gaming-recommended.",
9798
currentText: $"Current: {OnOffText(SafeRead(GameDvrMonitor.ReadCurrent))}",
9899
defaultText: "Default: Enabled",
99100
onLabel: "Enabled", offLabel: "Disabled",
100-
pref: g.GameDvr, groupName: "dvr"));
101+
pref: g.GameDvr, groupName: "dvr",
102+
onPrefChanged: OnRowPrefChanged));
101103

102104
GlobalToggleRows.Add(new GlobalToggleRow(
103105
name: "Hardware-accelerated GPU Scheduling (HAGS)",
@@ -106,7 +108,8 @@ private void LoadGlobals()
106108
defaultText: "Default: Enabled (Win11)",
107109
onLabel: "Enabled", offLabel: "Disabled",
108110
requiresReboot: true,
109-
pref: g.Hags, groupName: "hags"));
111+
pref: g.Hags, groupName: "hags",
112+
onPrefChanged: OnRowPrefChanged));
110113

111114
GlobalToggleRows.Add(new GlobalToggleRow(
112115
name: "Memory Integrity / VBS (Core Isolation)",
@@ -115,7 +118,8 @@ private void LoadGlobals()
115118
defaultText: "Default: Enabled (Win11)",
116119
onLabel: "Enabled", offLabel: "Disabled",
117120
requiresReboot: true,
118-
pref: g.MemoryIntegrity, groupName: "memint"));
121+
pref: g.MemoryIntegrity, groupName: "memint",
122+
onPrefChanged: OnRowPrefChanged));
119123

120124
GlobalToggleRows.Add(new GlobalToggleRow(
121125
name: "System Responsiveness",
@@ -124,15 +128,17 @@ private void LoadGlobals()
124128
defaultText: "Default: 20 Gaming: 10",
125129
onLabel: "Gaming", offLabel: "Default",
126130
requiresReboot: true,
127-
pref: g.SystemResponsiveness, groupName: "sysresp"));
131+
pref: g.SystemResponsiveness, groupName: "sysresp",
132+
onPrefChanged: OnRowPrefChanged));
128133

129134
GlobalToggleRows.Add(new GlobalToggleRow(
130135
name: "Network Throttling",
131136
description: "Multimedia packet pacing. Disabling reduces network jitter for online games.",
132137
currentText: $"Current: {GamingDefaultText(SafeRead(NetworkThrottlingMonitor.ReadCurrent))}",
133138
defaultText: "Default: 10 Gaming: Disabled",
134139
onLabel: "Gaming", offLabel: "Default",
135-
pref: g.NetworkThrottling, groupName: "netthr"));
140+
pref: g.NetworkThrottling, groupName: "netthr",
141+
onPrefChanged: OnRowPrefChanged));
136142

137143
GlobalToggleRows.Add(new GlobalToggleRow(
138144
name: "USB Selective Suspend (global)",
@@ -141,39 +147,44 @@ private void LoadGlobals()
141147
defaultText: "Default: Enabled Gaming: Disabled",
142148
onLabel: "Gaming", offLabel: "Default",
143149
requiresReboot: true,
144-
pref: g.UsbSelectiveSuspend, groupName: "usbsus"));
150+
pref: g.UsbSelectiveSuspend, groupName: "usbsus",
151+
onPrefChanged: OnRowPrefChanged));
145152

146153
GlobalToggleRows.Add(new GlobalToggleRow(
147154
name: "Games multimedia task profile",
148155
description: "Priority + scheduling values for processes registered with the Games multimedia class.",
149156
currentText: $"Current: {GamingDefaultText(SafeRead(GamesTaskProfileMonitor.ReadCurrent))}",
150157
defaultText: "Default: standard Gaming: boosted",
151158
onLabel: "Gaming", offLabel: "Default",
152-
pref: g.GamesTaskProfile, groupName: "gtask"));
159+
pref: g.GamesTaskProfile, groupName: "gtask",
160+
onPrefChanged: OnRowPrefChanged));
153161

154162
GlobalToggleRows.Add(new GlobalToggleRow(
155163
name: "Mouse \"Enhance pointer precision\"",
156164
description: "Acceleration curve applied to mouse movement. Most gamers want this off for consistent aim.",
157165
currentText: $"Current: {OnOffText(SafeRead(MousePrecisionMonitor.ReadCurrent))}",
158166
defaultText: "Default: Enabled",
159167
onLabel: "Enabled", offLabel: "Disabled",
160-
pref: g.MousePrecision, groupName: "mp"));
168+
pref: g.MousePrecision, groupName: "mp",
169+
onPrefChanged: OnRowPrefChanged));
161170

162171
GlobalToggleRows.Add(new GlobalToggleRow(
163172
name: "Fullscreen optimizations (global)",
164173
description: "Borderless-windowed compositing layer. Generally fine; some titles prefer it off.",
165174
currentText: $"Current: {OnOffText(SafeRead(FullscreenOptimizationsMonitor.ReadCurrent))}",
166175
defaultText: "Default: Enabled",
167176
onLabel: "Enabled", offLabel: "Disabled",
168-
pref: g.FullscreenOptimizations, groupName: "fso"));
177+
pref: g.FullscreenOptimizations, groupName: "fso",
178+
onPrefChanged: OnRowPrefChanged));
169179

170180
GlobalToggleRows.Add(new GlobalToggleRow(
171181
name: "Variable Refresh Rate (DirectX)",
172182
description: "G-Sync / FreeSync compatibility flag (Settings → Display → Graphics). Not the same as Dynamic Refresh Rate (DRR) in Advanced Display.",
173183
currentText: $"Current: {OnOffText(SafeRead(VrrMonitor.ReadCurrent))}",
174184
defaultText: "Default: not set",
175185
onLabel: "Enabled", offLabel: "Disabled",
176-
pref: g.Vrr, groupName: "vrr"));
186+
pref: g.Vrr, groupName: "vrr",
187+
onPrefChanged: OnRowPrefChanged));
177188

178189
var planNames = PowerPlanMonitor.ListAvailablePlans();
179190
var active = SafeRunGuid(PowerPlanMonitor.GetActivePlan);
@@ -393,12 +404,36 @@ private void PersistFormToConfig()
393404
foreach (var row in DisplayRows) row.WriteTo(_config);
394405
}
395406

396-
private void CancelButton_Click(object sender, RoutedEventArgs e) => Close();
407+
private bool _suppressSaveOnClose = false;
408+
409+
private void OnWindowClosing(object? sender, CancelEventArgs e)
410+
{
411+
if (_suppressSaveOnClose) return;
412+
try
413+
{
414+
PersistFormToConfig();
415+
_store.Save(_config);
416+
}
417+
catch { }
418+
}
419+
420+
private void OnRowPrefChanged(string settingName, string field, string before, string after)
421+
{
422+
try { _store.Save(_config); } catch { }
423+
ChangeLogger.LogPreferenceChange(settingName, field, before, after);
424+
}
425+
426+
private void CancelButton_Click(object sender, RoutedEventArgs e)
427+
{
428+
_suppressSaveOnClose = true;
429+
Close();
430+
}
397431
}
398432

399433
public sealed class GlobalToggleRow : INotifyPropertyChanged
400434
{
401435
private readonly ToggleSettingPref _pref;
436+
private readonly Action<string, string, string, string>? _onPrefChanged;
402437
public string Name { get; }
403438
public string Description { get; }
404439
public string CurrentText { get; }
@@ -409,23 +444,54 @@ public sealed class GlobalToggleRow : INotifyPropertyChanged
409444
public bool RequiresReboot { get; }
410445
public Visibility RebootBadgeVisibility => RequiresReboot ? Visibility.Visible : Visibility.Collapsed;
411446

412-
public bool Monitor { get => _pref.Monitor; set { _pref.Monitor = value; OnPropertyChanged(); } }
447+
public bool Monitor
448+
{
449+
get => _pref.Monitor;
450+
set
451+
{
452+
if (_pref.Monitor == value) return;
453+
var before = _pref.Monitor;
454+
_pref.Monitor = value;
455+
OnPropertyChanged();
456+
_onPrefChanged?.Invoke(Name, "Monitor", before.ToString(), value.ToString());
457+
}
458+
}
413459
public bool DesiredOn
414460
{
415461
get => _pref.DesiredOn;
416-
set { if (_pref.DesiredOn != value) { _pref.DesiredOn = value; OnPropertyChanged(); OnPropertyChanged(nameof(DesiredOff)); } }
462+
set
463+
{
464+
if (_pref.DesiredOn == value) return;
465+
var before = _pref.DesiredOn;
466+
_pref.DesiredOn = value;
467+
OnPropertyChanged();
468+
OnPropertyChanged(nameof(DesiredOff));
469+
_onPrefChanged?.Invoke(Name, "Want", before ? OnLabel : OffLabel, value ? OnLabel : OffLabel);
470+
}
417471
}
418472
public bool DesiredOff
419473
{
420474
get => !_pref.DesiredOn;
421-
set { if (value && _pref.DesiredOn) { _pref.DesiredOn = false; OnPropertyChanged(); OnPropertyChanged(nameof(DesiredOn)); } }
475+
set { if (value && _pref.DesiredOn) DesiredOn = false; }
476+
}
477+
public bool AutoApply
478+
{
479+
get => _pref.AutoApply;
480+
set
481+
{
482+
if (_pref.AutoApply == value) return;
483+
var before = _pref.AutoApply;
484+
_pref.AutoApply = value;
485+
OnPropertyChanged();
486+
_onPrefChanged?.Invoke(Name, "AutoApply", before.ToString(), value.ToString());
487+
}
422488
}
423-
public bool AutoApply { get => _pref.AutoApply; set { _pref.AutoApply = value; OnPropertyChanged(); } }
424489

425490
public GlobalToggleRow(string name, string description, string currentText, string defaultText,
426491
string onLabel, string offLabel,
427492
ToggleSettingPref pref, string groupName,
428-
bool requiresReboot = false)
493+
bool requiresReboot = false,
494+
Action<string, string, string, string>? onPrefChanged = null)
429495
{
430496
Name = name;
431497
Description = description;
@@ -436,6 +502,7 @@ public GlobalToggleRow(string name, string description, string currentText, stri
436502
_pref = pref;
437503
GroupName = groupName;
438504
RequiresReboot = requiresReboot;
505+
_onPrefChanged = onPrefChanged;
439506
}
440507

441508
public void WriteBack() { /* mutations are direct; nothing to do */ }

0 commit comments

Comments
 (0)