From e648c4ed5c8e67a67b53089995c1611f965fcf45 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sat, 27 Dec 2025 15:54:32 -0600 Subject: [PATCH 1/5] Make pixel shifting smarter Closes #85 --- DesktopClock.Tests/PixelShifterTests.cs | 242 ++++-------------------- DesktopClock/MainWindow.xaml.cs | 19 +- DesktopClock/Utilities/PixelShifter.cs | 171 ++++++++++++++--- 3 files changed, 206 insertions(+), 226 deletions(-) diff --git a/DesktopClock.Tests/PixelShifterTests.cs b/DesktopClock.Tests/PixelShifterTests.cs index d8ae5d2..770adb4 100644 --- a/DesktopClock.Tests/PixelShifterTests.cs +++ b/DesktopClock.Tests/PixelShifterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using DesktopClock.Utilities; namespace DesktopClock.Tests; @@ -7,249 +6,90 @@ 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) + [InlineData(50, 0.1, 100, 5)] + [InlineData(20, 0.5, 4, 4)] + public void GetEffectiveMaxOffset_ShouldUseWindowSizeRatio(double windowSize, double ratio, int maxOffset, double expected) { var shifter = new PixelShifter { - PixelsPerShift = shiftAmount, - MaxPixelOffset = maxTotalShift, + MaxPixelOffsetRatio = ratio, + MaxPixelOffset = maxOffset, }; - double totalShiftX = 0; - - // Test 100 times because it's random. - for (var i = 0; i < 100; i++) - { - var shift = shifter.ShiftX(); - totalShiftX += shift; + var effective = shifter.GetEffectiveMaxOffset(windowSize); - Assert.InRange(Math.Abs(totalShiftX), 0, maxTotalShift); - } + Assert.Equal(expected, effective); } - [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) + [Fact] + public void ShiftX_ShouldBounceDeterministicallyWithinBounds() { var shifter = new PixelShifter { - PixelsPerShift = shiftAmount, - MaxPixelOffset = maxTotalShift, + PixelsPerShift = 2, + MaxPixelOffset = 100, + MaxPixelOffsetRatio = 0.1, }; - double totalShiftY = 0; + const double windowSize = 50; + Assert.Equal(5d, shifter.GetEffectiveMaxOffset(windowSize)); - // Test 100 times because it's random. - for (var i = 0; i < 100; i++) + var expectedShifts = new double[] { 2, 2, 1, -2, -2, -2, -2, -2, 2, 2 }; + foreach (var expected in expectedShifts) { - var shift = shifter.ShiftY(); - totalShiftY += shift; - - Assert.InRange(Math.Abs(totalShiftY), 0, maxTotalShift); + var shift = shifter.ShiftX(windowSize); + Assert.Equal(expected, shift); + Assert.InRange(Math.Abs(shifter.TotalShiftX), 0, 5); } } [Fact] - public void ShiftX_WithZeroPixelsPerShift_ShouldReturnZero() + public void ShiftY_ShouldBounceDeterministicallyWithinBounds() { - // Arrange var shifter = new PixelShifter { - PixelsPerShift = 0, - MaxPixelOffset = 10, + PixelsPerShift = 2, + MaxPixelOffset = 100, + MaxPixelOffsetRatio = 0.1, }; - // Act - var shift = shifter.ShiftX(); - - // Assert - Assert.Equal(0, shift); - } + const double windowSize = 50; + Assert.Equal(5d, shifter.GetEffectiveMaxOffset(windowSize)); - [Fact] - public void ShiftY_WithZeroPixelsPerShift_ShouldReturnZero() - { - // Arrange - var shifter = new PixelShifter - { - PixelsPerShift = 0, - MaxPixelOffset = 10, - }; - - // Act - var shift = shifter.ShiftY(); - - // Assert - Assert.Equal(0, shift); - } - - [Fact] - public void ShiftX_WithZeroMaxOffset_ShouldReturnZero() - { - // Arrange - var shifter = new PixelShifter + var expectedShifts = new double[] { 2, 2, 1, -2, -2, -2, -2, -2, 2, 2 }; + foreach (var expected in expectedShifts) { - PixelsPerShift = 5, - MaxPixelOffset = 0, - }; - - // Act - var shift = shifter.ShiftX(); - - // Assert - Assert.Equal(0, shift); + var shift = shifter.ShiftY(windowSize); + Assert.Equal(expected, shift); + Assert.InRange(Math.Abs(shifter.TotalShiftY), 0, 5); + } } - [Fact] - public void ShiftY_WithZeroMaxOffset_ShouldReturnZero() + [Theory] + [InlineData(0, 50)] + [InlineData(2, 0)] + public void ShiftX_WhenDisabled_ReturnsZero(int pixelsPerShift, double windowSize) { - // Arrange var shifter = new PixelShifter { - PixelsPerShift = 5, - MaxPixelOffset = 0, + PixelsPerShift = pixelsPerShift, + MaxPixelOffset = 10, + MaxPixelOffsetRatio = 0.1, }; - // Act - var shift = shifter.ShiftY(); + var shift = shifter.ShiftX(windowSize); - // Assert Assert.Equal(0, shift); + Assert.Equal(0, shifter.TotalShiftX); } [Fact] public void DefaultValues_ShouldBeExpected() { - // Arrange var shifter = new PixelShifter(); - // Assert Assert.Equal(1, shifter.PixelsPerShift); Assert.Equal(4, shifter.MaxPixelOffset); - } - - [Fact] - public void ShiftX_ShouldReverseDirectionAtBoundary() - { - // Arrange - set up to hit boundary quickly - var shifter = new PixelShifter - { - PixelsPerShift = 10, - MaxPixelOffset = 10, - }; - - // Act - call multiple times to force direction reversal - double total = 0; - var shifts = new List(); - for (int i = 0; i < 10; i++) - { - var shift = shifter.ShiftX(); - shifts.Add(shift); - total += shift; - } - - // Assert - total should stay within bounds - Assert.InRange(Math.Abs(total), 0, 10); - - // There should be both positive and negative shifts (direction reversal) - // OR the total stayed within bounds - Assert.True(Math.Abs(total) <= 10); - } - - [Fact] - public void ShiftY_ShouldReverseDirectionAtBoundary() - { - // Arrange - set up to hit boundary quickly - var shifter = new PixelShifter - { - PixelsPerShift = 10, - MaxPixelOffset = 10, - }; - - // Act - call multiple times to force direction reversal - double total = 0; - var shifts = new List(); - for (int i = 0; i < 10; i++) - { - var shift = shifter.ShiftY(); - shifts.Add(shift); - total += shift; - } - - // Assert - total should stay within bounds - Assert.InRange(Math.Abs(total), 0, 10); - } - - [Fact] - public void ShiftX_And_ShiftY_ShouldBeIndependent() - { - // Arrange - var shifter = new PixelShifter - { - PixelsPerShift = 5, - MaxPixelOffset = 20, - }; - - // Act - double totalX = 0, totalY = 0; - for (int i = 0; i < 50; i++) - { - totalX += shifter.ShiftX(); - totalY += shifter.ShiftY(); - } - - // Assert - both should be within bounds independently - Assert.InRange(Math.Abs(totalX), 0, 20); - Assert.InRange(Math.Abs(totalY), 0, 20); - } - - [Fact] - public void ShiftX_MultipleShiftersWithSameConfig_ShouldBeIndependent() - { - // Arrange - var shifter1 = new PixelShifter { PixelsPerShift = 5, MaxPixelOffset = 10 }; - var shifter2 = new PixelShifter { PixelsPerShift = 5, MaxPixelOffset = 10 }; - - // Act - double total1 = 0, total2 = 0; - for (int i = 0; i < 20; i++) - { - total1 += shifter1.ShiftX(); - total2 += shifter2.ShiftX(); - } - - // Assert - both should be within their own bounds - Assert.InRange(Math.Abs(total1), 0, 10); - Assert.InRange(Math.Abs(total2), 0, 10); - } - - [Theory] - [InlineData(1, 5)] - [InlineData(2, 8)] - [InlineData(3, 15)] - public void Shift_ShouldReturnValueWithinPixelsPerShiftRange(int pixelsPerShift, int maxOffset) - { - // Arrange - var shifter = new PixelShifter - { - PixelsPerShift = pixelsPerShift, - MaxPixelOffset = maxOffset, - }; - - // Act & Assert - for (int i = 0; i < 50; i++) - { - var shiftX = shifter.ShiftX(); - var shiftY = shifter.ShiftY(); - - // Shift should be within the range [-pixelsPerShift, +pixelsPerShift] - Assert.InRange(shiftX, -pixelsPerShift, pixelsPerShift); - Assert.InRange(shiftY, -pixelsPerShift, pixelsPerShift); - } + Assert.Equal(0.1, shifter.MaxPixelOffsetRatio, 5); } } diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 769fcaf..abab888 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -243,8 +243,9 @@ private void TryShiftPixels() return; _pixelShifter ??= new(); - Left += _pixelShifter.ShiftX(); - Top += _pixelShifter.ShiftY(); + var (left, top) = _pixelShifter.ApplyShift(ActualWidth, ActualHeight, Left, Top); + Left = left; + Top = top; }); } @@ -270,9 +271,14 @@ private void Window_MouseDown(object sender, MouseButtonEventArgs e) if (e.ChangedButton == MouseButton.Left && Settings.Default.DragToMove) { // Pause time updates to maintain placement. + _pixelShifter ??= new(); + var (left, top) = _pixelShifter.ClearShift(Left, Top); + Left = left; + Top = top; _systemClockTimer.Stop(); DragMove(); + _pixelShifter.UpdateBasePosition(Left, Top); UpdateTimeString(); _systemClockTimer.Start(); @@ -298,6 +304,8 @@ private void Window_MouseWheel(object sender, MouseWheelEventArgs e) private void Window_SourceInitialized(object sender, EventArgs e) { this.SetPlacement(Settings.Default.Placement); + _pixelShifter ??= new(); + _pixelShifter.UpdateBasePosition(Left, Top); // Apply click-through setting. this.SetClickThrough(Settings.Default.ClickThrough); @@ -337,6 +345,12 @@ private void Window_ContentRendered(object sender, EventArgs e) private void Window_Closing(object sender, CancelEventArgs e) { // Save the last text and the placement to preserve dimensions and position of the clock. + if (_pixelShifter != null) + { + var (left, top) = _pixelShifter.RestoreBasePosition(Left, Top); + Left = left; + Top = top; + } Settings.Default.LastDisplay = CurrentTimeOrCountdownString; Settings.Default.Placement = this.GetPlacement(); @@ -358,6 +372,7 @@ private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { var widthChange = e.NewSize.Width - e.PreviousSize.Width; Left -= widthChange; + _pixelShifter?.AdjustForRightAlignedWidthChange(widthChange); } } diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs index 73f78aa..f5a3d15 100644 --- a/DesktopClock/Utilities/PixelShifter.cs +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -4,9 +4,13 @@ namespace DesktopClock.Utilities; public class PixelShifter { - private readonly Random _random = new(); private double _totalShiftX; private double _totalShiftY; + private int _directionX = 1; + private int _directionY = 1; + private double _baseLeft; + private double _baseTop; + private bool _hasBasePosition; /// /// The number of pixels that will be shifted each time. @@ -18,54 +22,175 @@ public class PixelShifter /// public int MaxPixelOffset { get; set; } = 4; + /// + /// The ratio of the window size that determines the effective max drift. + /// + public double MaxPixelOffsetRatio { get; set; } = 0.1; + + /// + /// The total amount shifted horizontally. + /// + public double TotalShiftX => _totalShiftX; + + /// + /// The total amount shifted vertically. + /// + public double TotalShiftY => _totalShiftY; + /// /// Returns an amount to shift horizontally by while staying within the specified bounds. /// - public double ShiftX() + public double ShiftX(double windowWidth) { - double pixelsToMoveBy = GetRandomShift(); - pixelsToMoveBy = GetFinalShiftAmount(_totalShiftX, pixelsToMoveBy, MaxPixelOffset); - _totalShiftX += pixelsToMoveBy; - return pixelsToMoveBy; + var maxOffset = GetEffectiveMaxOffset(windowWidth); + return ShiftAxis(ref _totalShiftX, ref _directionX, maxOffset); } /// /// Returns an amount to shift vertically by while staying within the specified bounds. /// - public double ShiftY() + public double ShiftY(double windowHeight) + { + var maxOffset = GetEffectiveMaxOffset(windowHeight); + return ShiftAxis(ref _totalShiftY, ref _directionY, maxOffset); + } + + /// + /// Applies the current shift to the window's position. + /// + public (double Left, double Top) ApplyShift(double windowWidth, double windowHeight, double currentLeft, double currentTop) + { + EnsureBasePosition(currentLeft, currentTop); + ShiftX(windowWidth); + ShiftY(windowHeight); + return (_baseLeft + _totalShiftX, _baseTop + _totalShiftY); + } + + /// + /// Clears any shift and returns the base position. + /// + public (double Left, double Top) ClearShift(double currentLeft, double currentTop) + { + EnsureBasePosition(currentLeft, currentTop); + Reset(); + return (_baseLeft, _baseTop); + } + + /// + /// Returns the base position to persist the unshifted placement. + /// + public (double Left, double Top) RestoreBasePosition(double currentLeft, double currentTop) + { + if (!_hasBasePosition) + { + UpdateBasePosition(currentLeft, currentTop); + return (currentLeft, currentTop); + } + + return (_baseLeft, _baseTop); + } + + /// + /// Updates the base position after a user move. + /// + public void UpdateBasePosition(double currentLeft, double currentTop) + { + _baseLeft = currentLeft; + _baseTop = currentTop; + _hasBasePosition = true; + } + + /// + /// Adjusts the base position when right alignment nudges the left edge. + /// + public void AdjustForRightAlignedWidthChange(double widthChange) + { + if (!_hasBasePosition) + return; + + _baseLeft -= widthChange; + } + + /// + /// Returns the effective max offset based on the window size ratio. + /// + public double GetEffectiveMaxOffset(double windowSize) + { + if (windowSize <= 0 || MaxPixelOffset <= 0) + return 0; + + if (MaxPixelOffsetRatio <= 0) + return MaxPixelOffset; + + var ratioOffset = (int)Math.Floor(windowSize * MaxPixelOffsetRatio); + return Math.Min(MaxPixelOffset, Math.Max(0, ratioOffset)); + } + + /// + /// Resets the shifter state back to zero. + /// + public void Reset() { - double pixelsToMoveBy = GetRandomShift(); - pixelsToMoveBy = GetFinalShiftAmount(_totalShiftY, pixelsToMoveBy, MaxPixelOffset); - _totalShiftY += pixelsToMoveBy; - return pixelsToMoveBy; + _totalShiftX = 0; + _totalShiftY = 0; + _directionX = 1; + _directionY = 1; } /// - /// Returns a random amount to shift by within the specified amount. + /// Ensures the base position is captured once. /// - private int GetRandomShift() => _random.Next(-PixelsPerShift, PixelsPerShift + 1); + private void EnsureBasePosition(double currentLeft, double currentTop) + { + if (_hasBasePosition) + return; + + _baseLeft = currentLeft; + _baseTop = currentTop; + _hasBasePosition = true; + } /// /// 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 current total amount of shift that has occurred. + /// The direction of the next shift. /// The bounds to stay within in respect to the total shift. - private double GetFinalShiftAmount(double current, double offset, double max) + private double ShiftAxis(ref double total, ref int direction, double max) { - var newTotal = current + offset; + if (PixelsPerShift <= 0 || max <= 0) + { + total = 0; + direction = 1; + return 0; + } - if (newTotal > max) + if (total >= max) { - return max - current; + total = max; + direction = -1; } - else if (newTotal < -max) + else if (total <= -max) { - return -max - current; + total = -max; + direction = 1; } - else + + var step = direction * PixelsPerShift; + var proposed = total + step; + + if (proposed > max) { - return offset; + step = max - total; + direction = -1; } + else if (proposed < -max) + { + step = -max - total; + direction = 1; + } + + total += step; + return step; } } From 19725f611b9b457484a61cedc804505cd09b493f Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sun, 28 Dec 2025 00:36:29 -0600 Subject: [PATCH 2/5] refactor --- DesktopClock/MainWindow.xaml.cs | 32 +++++++++------------ DesktopClock/Utilities/PixelShifter.cs | 40 +++++++++++++++----------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index abab888..d1a5541 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -42,6 +42,14 @@ public partial class MainWindow : Window [ObservableProperty] private Thickness _pixelShift; + private PixelShifter GetPixelShifter() + { + if (!Settings.Default.BurnInMitigation) + return null; + + return _pixelShifter ??= new(); + } + public MainWindow() { InitializeComponent(); @@ -242,10 +250,7 @@ private void TryShiftPixels() if (!IsVisible || WindowState == WindowState.Minimized) return; - _pixelShifter ??= new(); - var (left, top) = _pixelShifter.ApplyShift(ActualWidth, ActualHeight, Left, Top); - Left = left; - Top = top; + GetPixelShifter()?.ApplyShift(this); }); } @@ -271,14 +276,11 @@ private void Window_MouseDown(object sender, MouseButtonEventArgs e) if (e.ChangedButton == MouseButton.Left && Settings.Default.DragToMove) { // Pause time updates to maintain placement. - _pixelShifter ??= new(); - var (left, top) = _pixelShifter.ClearShift(Left, Top); - Left = left; - Top = top; + GetPixelShifter()?.ClearShift(this); _systemClockTimer.Stop(); DragMove(); - _pixelShifter.UpdateBasePosition(Left, Top); + GetPixelShifter()?.UpdateBasePosition(this); UpdateTimeString(); _systemClockTimer.Start(); @@ -304,8 +306,7 @@ private void Window_MouseWheel(object sender, MouseWheelEventArgs e) private void Window_SourceInitialized(object sender, EventArgs e) { this.SetPlacement(Settings.Default.Placement); - _pixelShifter ??= new(); - _pixelShifter.UpdateBasePosition(Left, Top); + GetPixelShifter()?.UpdateBasePosition(this); // Apply click-through setting. this.SetClickThrough(Settings.Default.ClickThrough); @@ -345,12 +346,7 @@ private void Window_ContentRendered(object sender, EventArgs e) private void Window_Closing(object sender, CancelEventArgs e) { // Save the last text and the placement to preserve dimensions and position of the clock. - if (_pixelShifter != null) - { - var (left, top) = _pixelShifter.RestoreBasePosition(Left, Top); - Left = left; - Top = top; - } + GetPixelShifter()?.RestoreBasePosition(this); Settings.Default.LastDisplay = CurrentTimeOrCountdownString; Settings.Default.Placement = this.GetPlacement(); @@ -372,7 +368,7 @@ private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { var widthChange = e.NewSize.Width - e.PreviousSize.Width; Left -= widthChange; - _pixelShifter?.AdjustForRightAlignedWidthChange(widthChange); + GetPixelShifter()?.AdjustForRightAlignedWidthChange(widthChange); } } diff --git a/DesktopClock/Utilities/PixelShifter.cs b/DesktopClock/Utilities/PixelShifter.cs index f5a3d15..c600621 100644 --- a/DesktopClock/Utilities/PixelShifter.cs +++ b/DesktopClock/Utilities/PixelShifter.cs @@ -1,4 +1,5 @@ using System; +using System.Windows; namespace DesktopClock.Utilities; @@ -58,42 +59,47 @@ public double ShiftY(double windowHeight) /// /// Applies the current shift to the window's position. /// - public (double Left, double Top) ApplyShift(double windowWidth, double windowHeight, double currentLeft, double currentTop) + public void ApplyShift(Window window) { - EnsureBasePosition(currentLeft, currentTop); - ShiftX(windowWidth); - ShiftY(windowHeight); - return (_baseLeft + _totalShiftX, _baseTop + _totalShiftY); + EnsureBasePosition(window.Left, window.Top); + ShiftX(window.ActualWidth); + ShiftY(window.ActualHeight); + window.Left = _baseLeft + _totalShiftX; + window.Top = _baseTop + _totalShiftY; } /// - /// Clears any shift and returns the base position. + /// Clears any shift and restores the base position. /// - public (double Left, double Top) ClearShift(double currentLeft, double currentTop) + public void ClearShift(Window window) { - EnsureBasePosition(currentLeft, currentTop); + EnsureBasePosition(window.Left, window.Top); Reset(); - return (_baseLeft, _baseTop); + window.Left = _baseLeft; + window.Top = _baseTop; } /// - /// Returns the base position to persist the unshifted placement. + /// Restores the base position to persist the unshifted placement. /// - public (double Left, double Top) RestoreBasePosition(double currentLeft, double currentTop) + public void RestoreBasePosition(Window window) { if (!_hasBasePosition) { - UpdateBasePosition(currentLeft, currentTop); - return (currentLeft, currentTop); + UpdateBasePosition(window.Left, window.Top); + return; } - return (_baseLeft, _baseTop); + window.Left = _baseLeft; + window.Top = _baseTop; } /// /// Updates the base position after a user move. /// - public void UpdateBasePosition(double currentLeft, double currentTop) + public void UpdateBasePosition(Window window) => UpdateBasePosition(window.Left, window.Top); + + private void UpdateBasePosition(double currentLeft, double currentTop) { _baseLeft = currentLeft; _baseTop = currentTop; @@ -181,12 +187,12 @@ private double ShiftAxis(ref double total, ref int direction, double max) if (proposed > max) { - step = max - total; + step = (int)(max - total); direction = -1; } else if (proposed < -max) { - step = -max - total; + step = (int)(-max - total); direction = 1; } From d1043953fbd8f2fc95d6d30996614b4526b35fea Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sun, 28 Dec 2025 00:45:37 -0600 Subject: [PATCH 3/5] refactor --- DesktopClock/MainWindow.xaml.cs | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index d1a5541..21ebe27 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Media; @@ -36,19 +35,7 @@ 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; - - private PixelShifter GetPixelShifter() - { - if (!Settings.Default.BurnInMitigation) - return null; - - return _pixelShifter ??= new(); - } + private PixelShifter PixelShifter => Settings.Default.BurnInMitigation ? (_pixelShifter ??= new()) : null; public MainWindow() { @@ -242,7 +229,7 @@ private void TryPlaySound() private void TryShiftPixels() { - if (!Settings.Default.BurnInMitigation || DateTimeOffset.Now.Second != 0) + if (!Settings.Default.BurnInMitigation) return; Dispatcher.Invoke(() => @@ -250,7 +237,7 @@ private void TryShiftPixels() if (!IsVisible || WindowState == WindowState.Minimized) return; - GetPixelShifter()?.ApplyShift(this); + PixelShifter?.ApplyShift(this); }); } @@ -276,11 +263,11 @@ private void Window_MouseDown(object sender, MouseButtonEventArgs e) if (e.ChangedButton == MouseButton.Left && Settings.Default.DragToMove) { // Pause time updates to maintain placement. - GetPixelShifter()?.ClearShift(this); + PixelShifter?.ClearShift(this); _systemClockTimer.Stop(); DragMove(); - GetPixelShifter()?.UpdateBasePosition(this); + PixelShifter?.UpdateBasePosition(this); UpdateTimeString(); _systemClockTimer.Start(); @@ -306,7 +293,7 @@ private void Window_MouseWheel(object sender, MouseWheelEventArgs e) private void Window_SourceInitialized(object sender, EventArgs e) { this.SetPlacement(Settings.Default.Placement); - GetPixelShifter()?.UpdateBasePosition(this); + PixelShifter?.UpdateBasePosition(this); // Apply click-through setting. this.SetClickThrough(Settings.Default.ClickThrough); @@ -346,7 +333,7 @@ private void Window_ContentRendered(object sender, EventArgs e) private void Window_Closing(object sender, CancelEventArgs e) { // Save the last text and the placement to preserve dimensions and position of the clock. - GetPixelShifter()?.RestoreBasePosition(this); + PixelShifter?.RestoreBasePosition(this); Settings.Default.LastDisplay = CurrentTimeOrCountdownString; Settings.Default.Placement = this.GetPlacement(); @@ -368,7 +355,7 @@ private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { var widthChange = e.NewSize.Width - e.PreviousSize.Width; Left -= widthChange; - GetPixelShifter()?.AdjustForRightAlignedWidthChange(widthChange); + PixelShifter?.AdjustForRightAlignedWidthChange(widthChange); } } From a240c74b270fc85626aa40e275d224c79dcd2243 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sun, 28 Dec 2025 00:46:07 -0600 Subject: [PATCH 4/5] refactor --- DesktopClock/MainWindow.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 21ebe27..3c31673 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -35,8 +35,6 @@ public partial class MainWindow : Window [ObservableProperty] private string _currentTimeOrCountdownString; - private PixelShifter PixelShifter => Settings.Default.BurnInMitigation ? (_pixelShifter ??= new()) : null; - public MainWindow() { InitializeComponent(); @@ -106,6 +104,8 @@ public void Exit() Application.Current.Shutdown(); } + public PixelShifter PixelShifter => Settings.Default.BurnInMitigation ? (_pixelShifter ??= new()) : null; + protected override void OnClosed(EventArgs e) { Settings.Default.PropertyChanged -= _settingsPropertyChanged; From 919da9478e4384a59a11a8ebb48b29493a84e32c Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sun, 28 Dec 2025 00:47:18 -0600 Subject: [PATCH 5/5] oops --- DesktopClock/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 3c31673..c8ecf9a 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -229,7 +229,7 @@ private void TryPlaySound() private void TryShiftPixels() { - if (!Settings.Default.BurnInMitigation) + if (!Settings.Default.BurnInMitigation || DateTimeOffset.Now.Second != 0) return; Dispatcher.Invoke(() =>