From cd152b287df29067ccb0f152aaebdbcd64c21af3 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 13:40:35 -0500 Subject: [PATCH 1/7] wip --- DesktopClock/MainWindow.xaml.cs | 28 ++++++++++++++++++++++++++++ DesktopClock/Properties/Settings.cs | 5 +++++ DesktopClock/SettingsWindow.xaml | 12 ++++++++++++ 3 files changed, 45 insertions(+) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 4ad443c..30b74e8 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -23,10 +23,13 @@ namespace DesktopClock; [ObservableObject] public partial class MainWindow : Window { + private readonly Random _random = new(); private readonly SystemClockTimer _systemClockTimer; private TaskbarIcon _trayIcon; private TimeZoneInfo _timeZone; private SoundPlayer _soundPlayer; + private double totalShiftX; + private double totalShiftY; /// /// The date and time to countdown to, or null if regular clock is desired. @@ -40,6 +43,12 @@ public partial class MainWindow : Window [ObservableProperty] private string _currentTimeOrCountdownString; + /// + /// The amount of margin applied in order to shift the clock's pixels and help prevent burn-in. + /// + [ObservableProperty] + private Thickness _pixelShift; + public MainWindow() { InitializeComponent(); @@ -195,6 +204,23 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) } } + private void ShiftWindow() + { + const int MaxTotalShift = 10; + const int MaxShiftPerTick = 5; + + double ApplyShift(ref double totalShift) + { + var shift = _random.Next(-MaxShiftPerTick, MaxShiftPerTick + 1); + totalShift += shift; + + return Math.Min(MaxTotalShift, totalShift); + } + + Left += ApplyShift(ref totalShiftX); + Top += ApplyShift(ref totalShiftY); + } + /// /// Handles the event when the system clock timer signals a second change. /// @@ -203,6 +229,8 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e) UpdateTimeString(); TryPlaySound(); + + Dispatcher.Invoke(ShiftWindow); } /// diff --git a/DesktopClock/Properties/Settings.cs b/DesktopClock/Properties/Settings.cs index 11e4af8..802447b 100644 --- a/DesktopClock/Properties/Settings.cs +++ b/DesktopClock/Properties/Settings.cs @@ -179,6 +179,11 @@ private Settings() /// public bool RightAligned { get; set; } = false; + /// + /// Experimental: Shifts the clock's pixels and makes the background more transparent in order to prevent burn-in. + /// + public bool BurnInMitigation { get; set; } = false; + /// /// Path to a WAV file to be played on a specified interval. /// diff --git a/DesktopClock/SettingsWindow.xaml b/DesktopClock/SettingsWindow.xaml index 3862b4f..7a3f15a 100644 --- a/DesktopClock/SettingsWindow.xaml +++ b/DesktopClock/SettingsWindow.xaml @@ -195,6 +195,18 @@ FontSize="10" Margin="0,0,0,12" /> + + + + + + From ca4199101f8aede0a672c6bd11777b107a3ea2a2 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 14:05:02 -0500 Subject: [PATCH 2/7] better algo --- DesktopClock/MainWindow.xaml.cs | 28 ++++---------- DesktopClock/Properties/Settings.cs | 2 +- DesktopClock/Utilities/PixelShifter.cs | 53 ++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 DesktopClock/Utilities/PixelShifter.cs diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 30b74e8..e386390 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DesktopClock.Properties; +using DesktopClock.Utilities; using H.NotifyIcon; using H.NotifyIcon.EfficiencyMode; using Humanizer; @@ -23,13 +24,11 @@ namespace DesktopClock; [ObservableObject] public partial class MainWindow : Window { - private readonly Random _random = new(); private readonly SystemClockTimer _systemClockTimer; private TaskbarIcon _trayIcon; private TimeZoneInfo _timeZone; private SoundPlayer _soundPlayer; - private double totalShiftX; - private double totalShiftY; + private PixelShifter _pixelShifter; /// /// The date and time to countdown to, or null if regular clock is desired. @@ -204,23 +203,6 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) } } - private void ShiftWindow() - { - const int MaxTotalShift = 10; - const int MaxShiftPerTick = 5; - - double ApplyShift(ref double totalShift) - { - var shift = _random.Next(-MaxShiftPerTick, MaxShiftPerTick + 1); - totalShift += shift; - - return Math.Min(MaxTotalShift, totalShift); - } - - Left += ApplyShift(ref totalShiftX); - Top += ApplyShift(ref totalShiftY); - } - /// /// Handles the event when the system clock timer signals a second change. /// @@ -230,7 +212,11 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e) TryPlaySound(); - Dispatcher.Invoke(ShiftWindow); + if (Settings.Default.BurnInMitigation) + { + _pixelShifter ??= new(this); + Dispatcher.Invoke(_pixelShifter.ShiftWindow); + } } /// diff --git a/DesktopClock/Properties/Settings.cs b/DesktopClock/Properties/Settings.cs index 802447b..9ffe2a0 100644 --- a/DesktopClock/Properties/Settings.cs +++ b/DesktopClock/Properties/Settings.cs @@ -180,7 +180,7 @@ private Settings() public bool RightAligned { get; set; } = false; /// - /// Experimental: Shifts the clock's pixels and makes the background more transparent in order to prevent burn-in. + /// Experimental: Shifts the clock around in order to prevent burn-in. /// public bool BurnInMitigation { get; set; } = false; diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs new file mode 100644 index 0000000..fb675c5 --- /dev/null +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -0,0 +1,53 @@ +using System; +using System.Windows; + +namespace DesktopClock.Utilities; + +public class PixelShifter +{ + private readonly Random _random = new(); + private readonly Window _window; + private double _totalShiftX; + private double _totalShiftY; + + /// + /// The number of pixels that will be shifted each time. + /// + public int ShiftAmount { get; set; } = 2; + + /// + /// The maximum amount of drift that can occur in each direction. + /// + public int MaxTotalShift { get; set; } = 4; + + public PixelShifter(Window window) + { + _window = window; + } + + /// + /// Shifts the location of the window randomly to help prevent screen burn-in. + /// + public void ShiftWindow() + { + double CalculateShift(ref double totalShift) + { + var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); + var newTotalShift = totalShift + shift; + + if (Math.Abs(newTotalShift) <= MaxTotalShift) + { + totalShift = newTotalShift; + return shift; + } + + return 0; + } + + var shiftX = CalculateShift(ref _totalShiftX); + var shiftY = CalculateShift(ref _totalShiftY); + + _window.Left += shiftX; + _window.Top += shiftY; + } +} From b7bc628e352261d2a4c1c1d6d21c3d9e91efa3b8 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 14:25:27 -0500 Subject: [PATCH 3/7] simplify class and add tests --- DesktopClock.Tests/PixelShifterTests.cs | 55 +++++++++++++++++++++++++ DesktopClock/MainWindow.xaml.cs | 8 +++- DesktopClock/Utilities/PixelShifter.cs | 43 +++++++++---------- 3 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 DesktopClock.Tests/PixelShifterTests.cs diff --git a/DesktopClock.Tests/PixelShifterTests.cs b/DesktopClock.Tests/PixelShifterTests.cs new file mode 100644 index 0000000..fde752a --- /dev/null +++ b/DesktopClock.Tests/PixelShifterTests.cs @@ -0,0 +1,55 @@ +using System; +using DesktopClock.Utilities; + +namespace DesktopClock.Tests; + +public class PixelShifterTests +{ + [Theory] + [InlineData(5, 10)] // Evenly divisible. + [InlineData(3, 10)] // Not evenly divisible. + [InlineData(10, 5)] // Amount is larger than total. + public void ShiftX_ShouldNotExceedMaxTotalShift(int shiftAmount, int maxTotalShift) + { + var shifter = new PixelShifter + { + ShiftAmount = shiftAmount, + MaxTotalShift = maxTotalShift, + }; + + double totalShiftX = 0; + + // Test 100 times because it's random. + for (var i = 0; i < 100; i++) + { + var shift = shifter.ShiftX(); + totalShiftX += shift; + + Assert.InRange(Math.Abs(totalShiftX), 0, maxTotalShift); + } + } + + [Theory] + [InlineData(5, 10)] // Evenly divisible. + [InlineData(3, 10)] // Not evenly divisible. + [InlineData(10, 5)] // Amount is larger than total. + public void ShiftY_ShouldNotExceedMaxTotalShift(int shiftAmount, int maxTotalShift) + { + var shifter = new PixelShifter + { + ShiftAmount = shiftAmount, + MaxTotalShift = maxTotalShift, + }; + + double totalShiftY = 0; + + // Test 100 times because it's random. + for (var i = 0; i < 100; i++) + { + var shift = shifter.ShiftY(); + totalShiftY += shift; + + Assert.InRange(Math.Abs(totalShiftY), 0, maxTotalShift); + } + } +} diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index e386390..7b4d675 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -214,8 +214,12 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e) if (Settings.Default.BurnInMitigation) { - _pixelShifter ??= new(this); - Dispatcher.Invoke(_pixelShifter.ShiftWindow); + _pixelShifter ??= new(); + Dispatcher.Invoke(() => + { + Left += _pixelShifter.ShiftX(); + Top += _pixelShifter.ShiftY(); + }); } } diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs index fb675c5..a57100a 100644 --- a/DesktopClock/Utilities/PixelShifter.cs +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -1,12 +1,10 @@ using System; -using System.Windows; namespace DesktopClock.Utilities; public class PixelShifter { private readonly Random _random = new(); - private readonly Window _window; private double _totalShiftX; private double _totalShiftY; @@ -20,34 +18,31 @@ public class PixelShifter /// public int MaxTotalShift { get; set; } = 4; - public PixelShifter(Window window) + public double ShiftX() { - _window = window; - } + var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); + var newTotalShiftX = _totalShiftX + shift; - /// - /// Shifts the location of the window randomly to help prevent screen burn-in. - /// - public void ShiftWindow() - { - double CalculateShift(ref double totalShift) + if (Math.Abs(newTotalShiftX) <= MaxTotalShift) { - var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); - var newTotalShift = totalShift + shift; + _totalShiftX = newTotalShiftX; + return shift; + } - if (Math.Abs(newTotalShift) <= MaxTotalShift) - { - totalShift = newTotalShift; - return shift; - } + return 0; + } - return 0; - } + public double ShiftY() + { + var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); + var newTotalShiftY = _totalShiftY + shift; - var shiftX = CalculateShift(ref _totalShiftX); - var shiftY = CalculateShift(ref _totalShiftY); + if (Math.Abs(newTotalShiftY) <= MaxTotalShift) + { + _totalShiftY = newTotalShiftY; + return shift; + } - _window.Left += shiftX; - _window.Top += shiftY; + return 0; } } From bd11b889cc2788be1dd09869c26a8332ee136d0f Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 14:31:02 -0500 Subject: [PATCH 4/7] once every minute --- DesktopClock/MainWindow.xaml.cs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 7b4d675..6c8db0d 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -210,17 +210,9 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e) { UpdateTimeString(); - TryPlaySound(); + TryShiftPixels(); - if (Settings.Default.BurnInMitigation) - { - _pixelShifter ??= new(); - Dispatcher.Invoke(() => - { - Left += _pixelShifter.ShiftX(); - Top += _pixelShifter.ShiftY(); - }); - } + TryPlaySound(); } /// @@ -276,6 +268,20 @@ private void TryPlaySound() } } + private void TryShiftPixels() + { + if (!Settings.Default.BurnInMitigation || DateTimeOffset.Now.Second != 0) + return; + + _pixelShifter ??= new(); + + Dispatcher.Invoke(() => + { + Left += _pixelShifter.ShiftX(); + Top += _pixelShifter.ShiftY(); + }); + } + private void UpdateTimeString() { string GetTimeString() From 530d51dea57b8019a44e624a56a61db00e3939fc Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 16:46:28 -0500 Subject: [PATCH 5/7] update comment --- DesktopClock/Properties/Settings.cs | 2 +- DesktopClock/SettingsWindow.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DesktopClock/Properties/Settings.cs b/DesktopClock/Properties/Settings.cs index 9ffe2a0..206d71c 100644 --- a/DesktopClock/Properties/Settings.cs +++ b/DesktopClock/Properties/Settings.cs @@ -180,7 +180,7 @@ private Settings() public bool RightAligned { get; set; } = false; /// - /// Experimental: Shifts the clock around in order to prevent burn-in. + /// Experimental: Shifts the clock around in order to prevent screen burn-in. /// public bool BurnInMitigation { get; set; } = false; diff --git a/DesktopClock/SettingsWindow.xaml b/DesktopClock/SettingsWindow.xaml index 7a3f15a..a0f8409 100644 --- a/DesktopClock/SettingsWindow.xaml +++ b/DesktopClock/SettingsWindow.xaml @@ -202,7 +202,7 @@ Margin="0,0,0,12" /> - From 63da7bc919386600a8e6fcbb8cf1166e6fef1afc Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 17:16:05 -0500 Subject: [PATCH 6/7] refactor and improve algo --- DesktopClock.Tests/PixelShifterTests.cs | 8 ++--- DesktopClock/Utilities/PixelShifter.cs | 46 +++++++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/DesktopClock.Tests/PixelShifterTests.cs b/DesktopClock.Tests/PixelShifterTests.cs index fde752a..8af6e53 100644 --- a/DesktopClock.Tests/PixelShifterTests.cs +++ b/DesktopClock.Tests/PixelShifterTests.cs @@ -13,8 +13,8 @@ public void ShiftX_ShouldNotExceedMaxTotalShift(int shiftAmount, int maxTotalShi { var shifter = new PixelShifter { - ShiftAmount = shiftAmount, - MaxTotalShift = maxTotalShift, + PixelsPerShift = shiftAmount, + MaxPixelOffset = maxTotalShift, }; double totalShiftX = 0; @@ -37,8 +37,8 @@ public void ShiftY_ShouldNotExceedMaxTotalShift(int shiftAmount, int maxTotalShi { var shifter = new PixelShifter { - ShiftAmount = shiftAmount, - MaxTotalShift = maxTotalShift, + PixelsPerShift = shiftAmount, + MaxPixelOffset = maxTotalShift, }; double totalShiftY = 0; diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs index a57100a..be4de31 100644 --- a/DesktopClock/Utilities/PixelShifter.cs +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -11,38 +11,46 @@ public class PixelShifter /// /// The number of pixels that will be shifted each time. /// - public int ShiftAmount { get; set; } = 2; + public int PixelsPerShift { get; set; } = 1; /// /// The maximum amount of drift that can occur in each direction. /// - public int MaxTotalShift { get; set; } = 4; + public int MaxPixelOffset { get; set; } = 4; public double ShiftX() { - var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); - var newTotalShiftX = _totalShiftX + shift; - - if (Math.Abs(newTotalShiftX) <= MaxTotalShift) - { - _totalShiftX = newTotalShiftX; - return shift; - } - - return 0; + double pixelsToMoveBy = GetRandomShift(); + pixelsToMoveBy = GetFinalShiftAmount(_totalShiftX, pixelsToMoveBy, MaxPixelOffset); + _totalShiftX += pixelsToMoveBy; + return pixelsToMoveBy; } public double ShiftY() { - var shift = _random.Next(-ShiftAmount, ShiftAmount + 1); - var newTotalShiftY = _totalShiftY + shift; + double pixelsToMoveBy = GetRandomShift(); + pixelsToMoveBy = GetFinalShiftAmount(_totalShiftY, pixelsToMoveBy, MaxPixelOffset); + _totalShiftY += pixelsToMoveBy; + return pixelsToMoveBy; + } + + private int GetRandomShift() => _random.Next(-PixelsPerShift, PixelsPerShift + 1); - if (Math.Abs(newTotalShiftY) <= MaxTotalShift) + private double GetFinalShiftAmount(double current, double offset, double max) + { + var newTotal = current + offset; + + if (newTotal > max) { - _totalShiftY = newTotalShiftY; - return shift; + return max - current; + } + else if (newTotal < -max) + { + return -max - current; + } + else + { + return offset; } - - return 0; } } From 011188a95c20ddb8e4b0f120d64b5f627dc15b7e Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Wed, 17 Jul 2024 17:20:59 -0500 Subject: [PATCH 7/7] docs --- DesktopClock/Properties/Settings.cs | 2 +- DesktopClock/SettingsWindow.xaml | 2 +- DesktopClock/Utilities/PixelShifter.cs | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/DesktopClock/Properties/Settings.cs b/DesktopClock/Properties/Settings.cs index 206d71c..95ed4ee 100644 --- a/DesktopClock/Properties/Settings.cs +++ b/DesktopClock/Properties/Settings.cs @@ -180,7 +180,7 @@ private Settings() public bool RightAligned { get; set; } = false; /// - /// Experimental: Shifts the clock around in order to prevent screen burn-in. + /// Experimental: Shifts the clock periodically in order to reduce screen burn-in. /// public bool BurnInMitigation { get; set; } = false; diff --git a/DesktopClock/SettingsWindow.xaml b/DesktopClock/SettingsWindow.xaml index a0f8409..3e52d62 100644 --- a/DesktopClock/SettingsWindow.xaml +++ b/DesktopClock/SettingsWindow.xaml @@ -202,7 +202,7 @@ Margin="0,0,0,12" /> - diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs index be4de31..73f78aa 100644 --- a/DesktopClock/Utilities/PixelShifter.cs +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -18,6 +18,9 @@ public class PixelShifter /// public int MaxPixelOffset { get; set; } = 4; + /// + /// Returns an amount to shift horizontally by while staying within the specified bounds. + /// public double ShiftX() { double pixelsToMoveBy = GetRandomShift(); @@ -26,6 +29,9 @@ public double ShiftX() return pixelsToMoveBy; } + /// + /// Returns an amount to shift vertically by while staying within the specified bounds. + /// public double ShiftY() { double pixelsToMoveBy = GetRandomShift(); @@ -34,8 +40,17 @@ public double ShiftY() return pixelsToMoveBy; } + /// + /// Returns a random amount to shift by within the specified amount. + /// private int GetRandomShift() => _random.Next(-PixelsPerShift, PixelsPerShift + 1); + /// + /// Returns a capped amount to shift by. + /// + /// The current total amount of shift that has occurred. + /// The proposed amount to shift by this time. + /// The bounds to stay within in respect to the total shift. private double GetFinalShiftAmount(double current, double offset, double max) { var newTotal = current + offset;