Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
danielchalmers committed Jul 18, 2024
2 parents ec20847 + 95cbc0c commit 6631dad
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
55 changes: 55 additions & 0 deletions DesktopClock.Tests/PixelShifterTests.cs
Original file line number Diff line number Diff line change
@@ -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
{
PixelsPerShift = shiftAmount,
MaxPixelOffset = 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
{
PixelsPerShift = shiftAmount,
MaxPixelOffset = 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);
}
}
}
24 changes: 24 additions & 0 deletions DesktopClock/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,7 @@ public partial class MainWindow : Window
private TaskbarIcon _trayIcon;
private TimeZoneInfo _timeZone;
private SoundPlayer _soundPlayer;
private PixelShifter _pixelShifter;

/// <summary>
/// The date and time to countdown to, or <c>null</c> if regular clock is desired.
Expand All @@ -40,6 +42,12 @@ public partial class MainWindow : Window
[ObservableProperty]
private string _currentTimeOrCountdownString;

/// <summary>
/// The amount of margin applied in order to shift the clock's pixels and help prevent burn-in.
/// </summary>
[ObservableProperty]
private Thickness _pixelShift;

public MainWindow()
{
InitializeComponent();
Expand Down Expand Up @@ -206,6 +214,8 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
{
UpdateTimeString();

TryShiftPixels();

TryPlaySound();
}

Expand Down Expand Up @@ -262,6 +272,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()
Expand Down
5 changes: 5 additions & 0 deletions DesktopClock/Properties/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ private Settings()
/// </remarks>
public bool RightAligned { get; set; } = false;

/// <summary>
/// Experimental: Shifts the clock periodically in order to reduce screen burn-in.
/// </summary>
public bool BurnInMitigation { get; set; } = false;

/// <summary>
/// Path to a WAV file to be played on a specified interval.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions DesktopClock/SettingsWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@
FontSize="10"
Margin="0,0,0,12" />

<CheckBox Content="Burn-in Mitigation" IsChecked="{Binding Settings.BurnInMitigation, Mode=TwoWay}" />
<TextBlock Text="Experimental: Shifts the clock periodically in order to reduce screen burn-in."
FontStyle="Italic"
FontSize="10"
Margin="0,0,0,12" />

<TextBlock Text="WAV File Path:" />
<Grid>
<Grid.ColumnDefinitions>
Expand Down
71 changes: 71 additions & 0 deletions DesktopClock/Utilities/PixelShifter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;

namespace DesktopClock.Utilities;

public class PixelShifter
{
private readonly Random _random = new();
private double _totalShiftX;
private double _totalShiftY;

/// <summary>
/// The number of pixels that will be shifted each time.
/// </summary>
public int PixelsPerShift { get; set; } = 1;

/// <summary>
/// The maximum amount of drift that can occur in each direction.
/// </summary>
public int MaxPixelOffset { get; set; } = 4;

/// <summary>
/// Returns an amount to shift horizontally by while staying within the specified bounds.
/// </summary>
public double ShiftX()
{
double pixelsToMoveBy = GetRandomShift();
pixelsToMoveBy = GetFinalShiftAmount(_totalShiftX, pixelsToMoveBy, MaxPixelOffset);
_totalShiftX += pixelsToMoveBy;
return pixelsToMoveBy;
}

/// <summary>
/// Returns an amount to shift vertically by while staying within the specified bounds.
/// </summary>
public double ShiftY()
{
double pixelsToMoveBy = GetRandomShift();
pixelsToMoveBy = GetFinalShiftAmount(_totalShiftY, pixelsToMoveBy, MaxPixelOffset);
_totalShiftY += pixelsToMoveBy;
return pixelsToMoveBy;
}

/// <summary>
/// Returns a random amount to shift by within the specified amount.
/// </summary>
private int GetRandomShift() => _random.Next(-PixelsPerShift, PixelsPerShift + 1);

/// <summary>
/// Returns a capped amount to shift by.
/// </summary>
/// <param name="current">The current total amount of shift that has occurred.</param>
/// <param name="offset">The proposed amount to shift by this time.</param>
/// <param name="max">The bounds to stay within in respect to the total shift.</param>
private double GetFinalShiftAmount(double current, double offset, double max)
{
var newTotal = current + offset;

if (newTotal > max)
{
return max - current;
}
else if (newTotal < -max)
{
return -max - current;
}
else
{
return offset;
}
}
}

0 comments on commit 6631dad

Please sign in to comment.