diff --git a/res/config.ini b/res/config.ini index 1d28cb8..3738cce 100644 --- a/res/config.ini +++ b/res/config.ini @@ -35,6 +35,14 @@ # matrix -> CMatrix animation = none +# The minimum time between animation frames +# 32 -> ~30fps +# 16 -> ~60fps +# 8 -> ~120fps +# 6 -> ~144fps +# 4 -> ~240fps +animation_refresh_ms = 16 + # Stop the animation after some time # 0 -> Run forever # 1..2e12 -> Stop the animation after this many seconds diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index 9813122..8d410e2 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -50,35 +50,57 @@ pub fn realloc(self: *Doom) !void { self.buffer = buffer; } -pub fn draw(self: Doom) void { +pub fn drawWithUpdate(self: Doom) void { for (0..self.terminal_buffer.width) |x| { for (1..self.terminal_buffer.height) |y| { + // get source index const source = y * self.terminal_buffer.width + x; + + // random number between 0 and 3 inclusive const random = (self.terminal_buffer.random.int(u16) % 7) & 3; + // adjust destination index based on random value var dest = (source - @min(source, random)) + 1; if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width; + // get source intensity and destination offset const buffer_source = self.buffer[source]; const buffer_dest_offset = random & 1; if (buffer_source < buffer_dest_offset) continue; + // calculate the destination intensity var buffer_dest = buffer_source - buffer_dest_offset; if (buffer_dest > 12) buffer_dest = 0; self.buffer[dest] = @intCast(buffer_dest); + // update terminal self.terminal_buffer.buffer[dest] = toTermboxCell(FIRE[buffer_dest]); self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]); } } } +pub fn draw(self: Doom) void { + for (0..self.terminal_buffer.width) |x| { + for (1..self.terminal_buffer.height) |y| { + // get source index + const source = y * self.terminal_buffer.width + x; + + // get intensity from buffer + const buffer_source = self.buffer[source]; + + // set cell to correct fire char + self.terminal_buffer.buffer[source] = toTermboxCell(FIRE[buffer_source]); + } + } +} + fn initBuffer(buffer: []u8, width: usize) void { - const length = buffer.len - width; - const slice_start = buffer[0..length]; - const slice_end = buffer[length..]; + const slice_start = buffer[0..buffer.len]; + const slice_end = buffer[buffer.len - width .. buffer.len]; + // set cell initial values to 0, set bottom row to be fire sources @memset(slice_start, 0); @memset(slice_end, STEPS - 1); } diff --git a/src/animations/Matrix.zig b/src/animations/Matrix.zig index ba7e6e1..a343e02 100644 --- a/src/animations/Matrix.zig +++ b/src/animations/Matrix.zig @@ -68,80 +68,83 @@ pub fn realloc(self: *Matrix) !void { self.lines = lines; } -pub fn draw(self: *Matrix) void { +pub fn drawWithUpdate(self: *Matrix) void { const buf_height = self.terminal_buffer.height; const buf_width = self.terminal_buffer.width; - self.count += 1; - if (self.count > FRAME_DELAY) { - self.frame += 1; - if (self.frame > 4) self.frame = 1; - self.count = 0; - var x: usize = 0; - while (x < self.terminal_buffer.width) : (x += 2) { - var tail: usize = 0; - var line = &self.lines[x]; - if (self.frame <= line.update) continue; - - if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') { - if (line.space > 0) { - line.space -= 1; - } else { - const randint = self.terminal_buffer.random.int(i16); - const h: isize = @intCast(self.terminal_buffer.height); - line.length = @mod(randint, h - 3) + 3; - self.dots[x].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; - line.space = @mod(randint, h + 1); - } - } + self.frame += 1; + if (self.frame > 4) self.frame = 1; - var y: usize = 0; - var first_col = true; - var seg_len: u64 = 0; - height_it: while (y <= buf_height) : (y += 1) { - var dot = &self.dots[buf_width * y + x]; - // Skip over spaces - while (y <= buf_height and (dot.value == ' ' or dot.value == -1)) { - y += 1; - if (y > buf_height) break :height_it; - dot = &self.dots[buf_width * y + x]; - } + var x: usize = 0; + while (x < self.terminal_buffer.width) : (x += 2) { + var tail: usize = 0; + var line = &self.lines[x]; + if (self.frame <= line.update) continue; + + if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') { + if (line.space > 0) { + line.space -= 1; + } else { + const randint = self.terminal_buffer.random.int(i16); + const h: isize = @intCast(self.terminal_buffer.height); + line.length = @mod(randint, h - 3) + 3; + self.dots[x].value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; + line.space = @mod(randint, h + 1); + } + } - // Find the head of this column - tail = y; - seg_len = 0; - while (y <= buf_height and dot.value != ' ' and dot.value != -1) { - dot.is_head = false; - if (MID_SCROLL_CHANGE) { - const randint = self.terminal_buffer.random.int(i16); - if (@mod(randint, 8) == 0) { - dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; - } - } + var y: usize = 0; + var first_col = true; + var seg_len: u64 = 0; + height_it: while (y <= buf_height) : (y += 1) { + var dot = &self.dots[buf_width * y + x]; + // Skip over spaces + while (y <= buf_height and (dot.value == ' ' or dot.value == -1)) { + y += 1; + if (y > buf_height) break :height_it; + dot = &self.dots[buf_width * y + x]; + } - y += 1; - seg_len += 1; - // Head's down offscreen - if (y > buf_height) { - self.dots[buf_width * tail + x].value = ' '; - break :height_it; + // Find the head of this column + tail = y; + seg_len = 0; + while (y <= buf_height and dot.value != ' ' and dot.value != -1) { + dot.is_head = false; + if (MID_SCROLL_CHANGE) { + const randint = self.terminal_buffer.random.int(i16); + if (@mod(randint, 8) == 0) { + dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; } - dot = &self.dots[buf_width * y + x]; } - const randint = self.terminal_buffer.random.int(i16); - dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; - dot.is_head = true; - - if (seg_len > line.length or !first_col) { + y += 1; + seg_len += 1; + // Head's down offscreen + if (y > buf_height) { self.dots[buf_width * tail + x].value = ' '; - self.dots[x].value = -1; + break :height_it; } - first_col = false; + dot = &self.dots[buf_width * y + x]; + } + + const randint = self.terminal_buffer.random.int(i16); + dot.value = @mod(randint, MAX_CODEPOINT) + MIN_CODEPOINT; + dot.is_head = true; + + if (seg_len > line.length or !first_col) { + self.dots[buf_width * tail + x].value = ' '; + self.dots[x].value = -1; } + first_col = false; } } + self.draw(); +} + +pub fn draw(self: *Matrix) void { + const buf_width = self.terminal_buffer.width; + var x: usize = 0; while (x < buf_width) : (x += 2) { var y: usize = 1; diff --git a/src/config/Config.zig b/src/config/Config.zig index 38c8b4c..f35c2d9 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -8,6 +8,7 @@ const Bigclock = enums.Bigclock; animation: Animation = .none, animation_timeout_sec: u12 = 0, +animation_refresh_ms: u16 = 16, asterisk: ?u8 = '*', auth_fails: u64 = 10, bg: u16 = 0, diff --git a/src/main.zig b/src/main.zig index 4afefad..04ac604 100644 --- a/src/main.zig +++ b/src/main.zig @@ -317,6 +317,11 @@ pub fn main() !void { } } + var animation_timer = switch (config.animation) { + .none => undefined, + else => try std.time.Timer.start(), + }; + const animate = config.animation != .none; const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10); const shutdown_len = try utils.strWidth(lang.shutdown); @@ -380,8 +385,22 @@ pub fn main() !void { if (!animation_timed_out) { switch (config.animation) { .none => {}, - .doom => doom.draw(), - .matrix => matrix.draw(), + .doom => { + if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) { + animation_timer.reset(); + doom.drawWithUpdate(); + } else { + doom.draw(); + } + }, + .matrix => { + if (animation_timer.read() / std.time.ns_per_ms > config.animation_refresh_ms) { + animation_timer.reset(); + matrix.drawWithUpdate(); + } else { + matrix.draw(); + } + }, } } @@ -685,7 +704,7 @@ pub fn main() !void { const password_text = try allocator.dupeZ(u8, password.text.items); defer allocator.free(password_text); - // Give up control on the TTY + // Give up TTY _ = termbox.tb_shutdown(); session_pid = try std.posix.fork(); @@ -702,10 +721,13 @@ pub fn main() !void { session_pid = -1; } - // Take back control of the TTY + // Take back TTY _ = termbox.tb_init(); _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL); + // Reinitialise buffer to avoid use after free + buffer = TerminalBuffer.init(config, labels_max_length, random); + const auth_err = shared_err.readError(); if (auth_err) |err| { auth_fails += 1;