Skip to content

Commit f660c9e

Browse files
committed
Feature: Add ClockCanvas screensaver with bouncing digital clock display.
1 parent ac88eaf commit f660c9e

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

G33kShell.Desktop/Console/HighResScreen.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ public HighResScreen(ScreenData screen, DrawMode drawMode = DrawMode.Overwrite)
3434
m_screen = screen;
3535
m_drawMode = drawMode;
3636
}
37-
37+
38+
public int Width => m_screen.Width;
39+
public int Height => m_screen.Height * 2;
40+
3841
public void Plot(int x, int y, Rgb color)
3942
{
40-
if (x < 0 || x >= m_screen.Width || y < 0 || y >= m_screen.Height * 2)
43+
if (x < 0 || x >= Width || y < 0 || y >= Height)
4144
return; // Off screen.
4245

4346
var isTopPixel = (y & 1) == 0;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Code authored by Dean Edis (DeanTheCoder).
2+
// Anyone is free to copy, modify, use, compile, or distribute this software,
3+
// either in source code form or as a compiled binary, for any non-commercial
4+
// purpose.
5+
//
6+
// If you modify the code, please retain this copyright header,
7+
// and consider contributing back to the repository or letting us know
8+
// about your modifications. Your contributions are valued!
9+
//
10+
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND.
11+
12+
using System;
13+
using System.Diagnostics;
14+
using System.Linq;
15+
using System.Reflection;
16+
using DTC.Core.Extensions;
17+
using G33kShell.Desktop.Console.Controls;
18+
using JetBrains.Annotations;
19+
using WenceyWang.FIGlet;
20+
21+
namespace G33kShell.Desktop.Console.Screensavers;
22+
23+
/// <summary>
24+
/// Digital clock that bounces around the screen.
25+
/// </summary>
26+
[DebuggerDisplay("ClockCanvas:{X},{Y} {Width}x{Height}")]
27+
[UsedImplicitly]
28+
public class ClockCanvas : ScreensaverBase
29+
{
30+
private readonly FIGletFont m_font;
31+
private int m_posX;
32+
private int m_posY;
33+
private int m_dX = 1;
34+
private int m_dY = 1;
35+
36+
public ClockCanvas(int screenWidth, int screenHeight) : base(screenWidth, screenHeight, 5)
37+
{
38+
Name = "clock";
39+
40+
m_font = LoadFont();
41+
}
42+
43+
public override void UpdateFrame(ScreenData screen)
44+
{
45+
screen.Clear(Foreground, Background);
46+
var highResScreen = new HighResScreen(screen);
47+
48+
var now = DateTime.Now.ToString("HH:mm");
49+
50+
// Calculate width and height of the text.
51+
var textWidth = 0;
52+
var textHeight = 0;
53+
foreach (var ch in now)
54+
{
55+
var charLines = Enumerable.Range(0, m_font.Height)
56+
.Select(y => m_font.GetCharacter(ch, y))
57+
.ToArray();
58+
if (charLines.Length > 0)
59+
textWidth += charLines[0].Length;
60+
61+
var blankLines = charLines
62+
.Reverse()
63+
.TakeWhile(string.IsNullOrWhiteSpace)
64+
.Count();
65+
textHeight = Math.Max(textHeight, m_font.Height - blankLines);
66+
}
67+
68+
// Update position.
69+
m_posX += m_dX;
70+
m_posY += m_dY;
71+
72+
// Bounce off walls.
73+
if (m_posX <= 0 || m_posX + textWidth >= highResScreen.Width)
74+
{
75+
m_dX = -m_dX;
76+
m_posX = Math.Max(0, Math.Min(highResScreen.Width - textWidth, m_posX));
77+
}
78+
79+
if (m_posY <= 0 || m_posY + textHeight >= highResScreen.Height)
80+
{
81+
m_dY = -m_dY;
82+
m_posY = Math.Max(0, Math.Min(highResScreen.Height - textHeight, m_posY));
83+
}
84+
85+
// Draw the time at the bouncing position
86+
for (var y = 0; y < m_font.Height; y++)
87+
{
88+
var offset = 0;
89+
foreach (var textRow in now.Select(ch => m_font.GetCharacter(ch, y)))
90+
{
91+
for (var x = 0; x < textRow.Length; x++)
92+
{
93+
var ch = textRow[x];
94+
if (ch == ' ')
95+
continue; // Nothing to draw.
96+
97+
var screenX = m_posX + x + offset;
98+
var screenY = m_posY + y;
99+
highResScreen.Plot(screenX, screenY, ch == '█' ? Foreground : 0.25.Lerp(Background, Foreground));
100+
}
101+
102+
offset += textRow.Length;
103+
}
104+
}
105+
}
106+
107+
private static FIGletFont LoadFont()
108+
{
109+
// Enumerate all Avalonia embedded resources.
110+
var fontFolder = Assembly.GetExecutingAssembly().GetDirectory();
111+
var fontFile = fontFolder.GetFiles("Assets/Fonts/Figlet/*.flf").Single();
112+
113+
// Load the font.
114+
using var fontStream = fontFile.OpenRead();
115+
return new FIGletFont(fontStream);
116+
}
117+
}

0 commit comments

Comments
 (0)