Add ASCII Background Animation

This commit is contained in:
Frederick Ziola 2025-07-29 20:02:13 -05:00
parent 933e5bdd2d
commit 0380b8d32b
5 changed files with 113 additions and 0 deletions

View File

@ -24,6 +24,7 @@ allow_empty_password = true
# matrix -> CMatrix
# colormix -> Color mixing shader
# gameoflife -> John Conway's Game of Life
# ascii -> Custom ASCII art background loaded from a file
animation = none
# Stop the animation after some time
@ -155,6 +156,17 @@ gameoflife_frame_delay = 6
# 0.7+ -> Dense, chaotic patterns
gameoflife_initial_density = 0.4
# ASCII background file path (absolute)
ascii_filename = /etc/ly/config.ini
# ASCII background text foreground color id
ascii_fg = 0x00FFFFFF
# ASCII background offset (top left = 0,0; specified in characters)
# These must be >= 0
ascii_x = 0
ascii_y = 0
# Remove main box borders
hide_borders = false

91
src/animations/Ascii.zig Normal file
View File

@ -0,0 +1,91 @@
const std = @import("std");
const Animation = @import("../tui/Animation.zig");
const Cell = @import("../tui/Cell.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const Ascii = @This();
const MAX_WIDTH = 512;
const MAX_HEIGHT = 256;
terminal_buffer: *TerminalBuffer,
ascii_art: [MAX_HEIGHT][MAX_WIDTH]u8 = undefined,
line_len: [MAX_HEIGHT]usize = undefined,
line_count: usize = undefined,
fg: u32 = undefined,
x: u32 = undefined,
y: u32 = undefined,
pub fn init(terminal_buffer: *TerminalBuffer, filename: []const u8, fg: u32, x: u32, y: u32) !Ascii {
var ascii_art: [MAX_HEIGHT][MAX_WIDTH]u8 = undefined;
var line_len: [MAX_HEIGHT]usize = [_]usize{0} ** MAX_HEIGHT;
var line_count: usize = 0;
var file = std.fs.openFileAbsolute(filename, .{}) catch {
std.log.err("ASCII background file not found: {s}", .{filename});
return .{
.terminal_buffer = terminal_buffer,
.ascii_art = undefined, // Undefined buffers here are safe, as line_count
.line_len = undefined, // is set to 0 so nothing will be drawn
.line_count = 0,
.fg = 0x00000000,
.x = 0, .y = 0,
};
};
var reader = file.reader();
defer file.close();
while (line_count < MAX_HEIGHT) {
const line = reader.readUntilDelimiterOrEof(&ascii_art[line_count], '\n') catch |err| switch (err) {
error.StreamTooLong => {
_ = try reader.skipUntilDelimiterOrEof('\n'); // consume remainder of line
line_len[line_count] = MAX_WIDTH;
line_count += 1;
continue;
},
else => return err,
} orelse break;
line_len[line_count] = line.len;
line_count += 1;
}
return .{
.terminal_buffer = terminal_buffer,
.ascii_art = ascii_art,
.line_len = line_len,
.line_count = line_count,
.fg = fg, .x = x, .y = y,
};
}
pub fn animation(self: *Ascii) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(_: *Ascii) void {}
fn realloc(_: *Ascii) anyerror!void {}
fn min(a: usize, b: usize) usize {
if (a < b) return a;
return b;
}
fn draw(self: *Ascii) void {
const buf_width = self.terminal_buffer.width;
const buf_height = self.terminal_buffer.height;
var y: usize = 0;
while (y < min(buf_height, self.line_count)) : (y += 1) {
const line_width = self.line_len[y];
var x: usize = 0;
while (x < min(buf_width, line_width)) : (x += 1) {
const cell = Cell {
.ch = self.ascii_art[y][x],
.fg = self.fg,
.bg = self.terminal_buffer.bg,
};
cell.put(x + self.x, y + self.y);
}
}
}

View File

@ -42,6 +42,10 @@ gameoflife_fg: u32 = 0x0000FF00,
gameoflife_entropy_interval: usize = 10,
gameoflife_frame_delay: usize = 6,
gameoflife_initial_density: f32 = 0.4,
ascii_filename: []const u8 = "/etc/ly/config.ini",
ascii_fg: u32 = 0x00FF0000,
ascii_x: u32 = 0,
ascii_y: u32 = 0,
hide_borders: bool = false,
hide_version_string: bool = false,
hide_key_hints: bool = false,

View File

@ -4,6 +4,7 @@ pub const Animation = enum {
matrix,
colormix,
gameoflife,
ascii,
};
pub const DisplayServer = enum {

View File

@ -13,6 +13,7 @@ const Doom = @import("animations/Doom.zig");
const Dummy = @import("animations/Dummy.zig");
const Matrix = @import("animations/Matrix.zig");
const GameOfLife = @import("animations/GameOfLife.zig");
const Ascii = @import("animations/Ascii.zig");
const Animation = @import("tui/Animation.zig");
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Session = @import("tui/components/Session.zig");
@ -400,6 +401,10 @@ pub fn main() !void {
var game_of_life = try GameOfLife.init(allocator, &buffer, config.gameoflife_fg, config.gameoflife_entropy_interval, config.gameoflife_frame_delay, config.gameoflife_initial_density);
animation = game_of_life.animation();
},
.ascii => {
var ascii = try Ascii.init(&buffer, config.ascii_filename, config.ascii_fg, config.ascii_x, config.ascii_y);
animation = ascii.animation();
},
}
defer animation.deinit();