Add animation framework

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion 2025-03-16 10:54:06 +01:00
parent fa0748ead2
commit e0ed1b4eb1
No known key found for this signature in database
5 changed files with 108 additions and 49 deletions

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const Animation = @import("../tui/Animation.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const utils = @import("../tui/utils.zig"); const utils = @import("../tui/utils.zig");
@ -43,7 +44,15 @@ pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) C
}; };
} }
pub fn draw(self: *ColorMix) void { pub fn animation(self: *ColorMix) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(_: *ColorMix) void {}
fn realloc(_: *ColorMix) anyerror!void {}
fn draw(self: *ColorMix) void {
self.frames +%= 1; self.frames +%= 1;
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale; const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Animation = @import("../tui/Animation.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const utils = @import("../tui/utils.zig"); const utils = @import("../tui/utils.zig");
@ -38,17 +39,21 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
}; };
} }
pub fn deinit(self: Doom) void { pub fn animation(self: *Doom) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(self: *Doom) void {
self.allocator.free(self.buffer); self.allocator.free(self.buffer);
} }
pub fn realloc(self: *Doom) !void { fn realloc(self: *Doom) anyerror!void {
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height); const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
initBuffer(buffer, self.terminal_buffer.width); initBuffer(buffer, self.terminal_buffer.width);
self.buffer = buffer; self.buffer = buffer;
} }
pub fn draw(self: Doom) void { fn draw(self: *Doom) void {
for (0..self.terminal_buffer.width) |x| { for (0..self.terminal_buffer.width) |x| {
for (1..self.terminal_buffer.height) |y| { for (1..self.terminal_buffer.height) |y| {
const source = y * self.terminal_buffer.width + x; const source = y * self.terminal_buffer.width + x;

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Random = std.Random; const Random = std.Random;
const Animation = @import("../tui/Animation.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const utils = @import("../tui/utils.zig"); const utils = @import("../tui/utils.zig");
@ -54,12 +55,16 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_ini: u32,
}; };
} }
pub fn deinit(self: Matrix) void { pub fn animation(self: *Matrix) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(self: *Matrix) void {
self.allocator.free(self.dots); self.allocator.free(self.dots);
self.allocator.free(self.lines); self.allocator.free(self.lines);
} }
pub fn realloc(self: *Matrix) !void { fn realloc(self: *Matrix) anyerror!void {
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1)); const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width); const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
@ -69,7 +74,7 @@ pub fn realloc(self: *Matrix) !void {
self.lines = lines; self.lines = lines;
} }
pub fn draw(self: *Matrix) void { fn draw(self: *Matrix) void {
const buf_height = self.terminal_buffer.height; const buf_height = self.terminal_buffer.height;
const buf_width = self.terminal_buffer.width; const buf_width = self.terminal_buffer.width;
self.count += 1; self.count += 1;

View File

@ -11,6 +11,7 @@ const interop = @import("interop.zig");
const Doom = @import("animations/Doom.zig"); const Doom = @import("animations/Doom.zig");
const Matrix = @import("animations/Matrix.zig"); const Matrix = @import("animations/Matrix.zig");
const ColorMix = @import("animations/ColorMix.zig"); const ColorMix = @import("animations/ColorMix.zig");
const Animation = @import("tui/Animation.zig");
const TerminalBuffer = @import("tui/TerminalBuffer.zig"); const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Session = @import("tui/components/Session.zig"); const Session = @import("tui/components/Session.zig");
const Text = @import("tui/components/Text.zig"); const Text = @import("tui/components/Text.zig");
@ -336,24 +337,24 @@ pub fn main() !void {
} }
// Initialize the animation, if any // Initialize the animation, if any
var doom: Doom = undefined; var animation: Animation = undefined;
var matrix: Matrix = undefined;
var color_mix: ColorMix = undefined;
switch (config.animation) { switch (config.animation) {
.none => {}, .none => {},
.doom => doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color), .doom => {
.matrix => matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg, config.cmatrix_min_codepoint, config.cmatrix_max_codepoint), var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color);
.colormix => color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3), animation = doom.animation();
} },
defer { .matrix => {
switch (config.animation) { var matrix = try Matrix.init(allocator, &buffer, config.cmatrix_fg, config.cmatrix_min_codepoint, config.cmatrix_max_codepoint);
.none => {}, animation = matrix.animation();
.doom => doom.deinit(), },
.matrix => matrix.deinit(), .colormix => {
.colormix => {}, var color_mix = ColorMix.init(&buffer, config.colormix_col1, config.colormix_col2, config.colormix_col3);
} animation = color_mix.animation();
},
} }
defer animation.deinit();
const animate = config.animation != .none; const animate = config.animation != .none;
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10); const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
@ -382,7 +383,7 @@ pub fn main() !void {
while (run) { while (run) {
// If there's no input or there's an animation, a resolution change needs to be checked // If there's no input or there's an animation, a resolution change needs to be checked
if (!update or config.animation != .none) { if (!update or animate) {
if (!update) std.time.sleep(std.time.ns_per_ms * 100); if (!update) std.time.sleep(std.time.ns_per_ms * 100);
_ = termbox.tb_present(); // Required to update tb_width() and tb_height() _ = termbox.tb_present(); // Required to update tb_width() and tb_height()
@ -396,16 +397,9 @@ pub fn main() !void {
buffer.width = width; buffer.width = width;
buffer.height = height; buffer.height = height;
switch (config.animation) { animation.realloc() catch {
.none => {}, try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
.doom => doom.realloc() catch { };
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
.matrix => matrix.realloc() catch {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
},
.colormix => {},
}
update = true; update = true;
resolution_changed = true; resolution_changed = true;
@ -417,14 +411,7 @@ pub fn main() !void {
if (auth_fails < config.auth_fails) { if (auth_fails < config.auth_fails) {
_ = termbox.tb_clear(); _ = termbox.tb_clear();
if (!animation_timed_out) { if (!animation_timed_out) animation.draw();
switch (config.animation) {
.none => {},
.doom => doom.draw(),
.matrix => matrix.draw(),
.colormix => color_mix.draw(),
}
}
buffer.drawLabel(ly_top_str, 0, 0); buffer.drawLabel(ly_top_str, 0, 0);
@ -585,12 +572,7 @@ pub fn main() !void {
if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) { if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) {
animation_timed_out = true; animation_timed_out = true;
switch (config.animation) { animation.deinit();
.none => {},
.doom => doom.deinit(),
.matrix => matrix.deinit(),
.colormix => {},
}
} }
} else if (config.bigclock != .none and config.clock == null) { } else if (config.bigclock != .none and config.clock == null) {
var tv: interop.system_time.timeval = undefined; var tv: interop.system_time.timeval = undefined;
@ -736,9 +718,6 @@ pub fn main() !void {
const password_text = try allocator.dupeZ(u8, password.text.items); const password_text = try allocator.dupeZ(u8, password.text.items);
defer allocator.free(password_text); defer allocator.free(password_text);
// Give up control on the TTY
// _ = termbox.tb_shutdown();
session_pid = try std.posix.fork(); session_pid = try std.posix.fork();
if (session_pid == 0) { if (session_pid == 0) {
const current_environment = session.label.list.items[session.label.current]; const current_environment = session.label.list.items[session.label.current];

61
src/tui/Animation.zig Normal file
View File

@ -0,0 +1,61 @@
const Animation = @This();
const VTable = struct {
deinit_fn: *const fn (ptr: *anyopaque) void,
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
draw_fn: *const fn (ptr: *anyopaque) void,
};
pointer: *anyopaque,
vtable: VTable,
pub fn init(
pointer: anytype,
comptime deinit_fn: fn (ptr: @TypeOf(pointer)) void,
comptime realloc_fn: fn (ptr: @TypeOf(pointer)) anyerror!void,
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
) Animation {
const Pointer = @TypeOf(pointer);
const Impl = struct {
pub fn deinitImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, deinit_fn, .{impl});
}
pub fn reallocImpl(ptr: *anyopaque) anyerror!void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, realloc_fn, .{impl});
}
pub fn drawImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(.always_inline, draw_fn, .{impl});
}
const vtable = VTable{
.deinit_fn = deinitImpl,
.realloc_fn = reallocImpl,
.draw_fn = drawImpl,
};
};
return .{
.pointer = pointer,
.vtable = Impl.vtable,
};
}
pub fn deinit(self: *Animation) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.deinit_fn, .{impl});
}
pub fn realloc(self: *Animation) anyerror!void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.realloc_fn, .{impl});
}
pub fn draw(self: *Animation) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
return @call(.auto, self.vtable.draw_fn, .{impl});
}