From 3a04955e70a09b3903f2473e9279d67c9f002a65 Mon Sep 17 00:00:00 2001 From: urly3 Date: Thu, 20 Aug 2026 00:53:37 +0100 Subject: [PATCH] feat: per tty last user caching re #1040 also fix using the saved_users list in places it shouldn't be now this is only used on load and properly saved using the latest valid state, overwriting aged values. --- src/main.zig | 107 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/src/main.zig b/src/main.zig index 1d0db99..e40311d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -126,6 +126,8 @@ const UiState = struct { bigclock_buf: [32:0]u8, custom_binds: std.ArrayList(CustomBindLabel), custom_info: std.ArrayList(CustomInfoLabel), + tty_cache_path: []const u8, + tty_cache: [256]?usize, }; var shutdown = false; @@ -230,6 +232,7 @@ pub fn main(init: std.process.Init) !void { defer if (state.config.save_file_dir != null) { state.allocator.free(state.save_path); state.allocator.free(state.old_save_path); + state.allocator.free(state.tty_cache_path); }; const config_path = try std.Io.Dir.path.join(state.allocator, &[_][]const u8{ config_parent_path, "config.ini" }); @@ -272,6 +275,8 @@ pub fn main(init: std.process.Init) !void { if (state.config.save_file_dir) |dir| { state.save_path = try std.Io.Dir.path.join(state.allocator, &[_][]const u8{ dir, "save.txt" }); state.old_save_path = try std.Io.Dir.path.join(state.allocator, &[_][]const u8{ config_parent_path, "save.ini" }); + state.tty_cache_path = try std.Io.Dir.path.join(state.allocator, &[_][]const u8{ dir, "ttyuc.txt" }); + state.tty_cache = @splat(null); } if (config_parser.maybe_load_error == null) { @@ -336,16 +341,30 @@ pub fn main(init: std.process.Init) !void { } } - // If no save file previously existed, fill it up with all usernames - // TODO: Add new username with existing save file - if (state.config.save_file_dir != null and state.saved_users.user_list.items.len == 0) { - for (usernames.items) |user| { - try state.saved_users.user_list.append(state.allocator, .{ - .username = user, - .session_index = 0, - .first_run = true, - .allocated_username = false, - }); + read_tty_cache: { + var tty_cache_file = std.Io.Dir.cwd().openFile(state.io, state.tty_cache_path, .{}) catch break :read_tty_cache; + defer tty_cache_file.close(state.io); + + var file_buffer: [256]u8 = undefined; + var file_reader = tty_cache_file.reader(state.io, &file_buffer); + var reader = &file_reader.interface; + + while (reader.seek < reader.buffer.len) { + const line = reader.takeDelimiterInclusive('\n') catch break; + + var entry = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); + const tty_num_str = entry.next() orelse continue; + const username = entry.next() orelse continue; + + const tty_num = std.fmt.parseInt(usize, tty_num_str, 10) catch continue; + + // Cache tty users only if they exist + for (usernames.items, 0..) |u, u_index| { + if (std.mem.eql(u8, u, username)) { + if (tty_num < std.math.maxInt(@TypeOf(state.active_tty))) + state.tty_cache[tty_num] = u_index; + } + } } } @@ -1137,11 +1156,14 @@ pub fn main(init: std.process.Init) !void { } } } - } else if (state.saved_users.last_username_index) |index| load_last_user: { + } else if (state.tty_cache[state.active_tty]) |tty_index| { + state.login.?.label.current = tty_index; + state.session.label.current = @min(state.login.?.label.list.items[tty_index].session_index.*, state.session.label.list.items.len - 1); + } else if (state.saved_users.last_username_index) |last_index| load_last_user: { // If the saved index isn't valid, bail out - if (index >= state.saved_users.user_list.items.len) break :load_last_user; + if (last_index >= state.saved_users.user_list.items.len) break :load_last_user; - const user = state.saved_users.user_list.items[index]; + const user = state.saved_users.user_list.items[last_index]; // Find user with saved name, and switch over to it // If it doesn't exist (anymore), we don't change the value @@ -1516,6 +1538,8 @@ fn authenticate(ptr: *anyopaque) !bool { .{}, ) catch {}; + const current = state.login.?.label.current; + var file = std.Io.Dir.cwd().createFile(state.io, state.save_path, .{}) catch |err| { state.log_file.err( state.io, @@ -1534,10 +1558,10 @@ fn authenticate(ptr: *anyopaque) !bool { if (state.login_text) |box| { try writer.print("0-{s}\n", .{box.text.items}); } else { - try writer.print("{d}\n", .{state.login.?.label.current}); + try writer.print("{d}\n", .{current}); } - for (state.saved_users.user_list.items) |user| { - try writer.print("{s}:{d}\n", .{ user.username, user.session_index }); + for (state.login.?.label.list.items) |user| { + try writer.print("{s}:{d}\n", .{ user.name, user.session_index.* }); } try writer.flush(); @@ -1548,6 +1572,57 @@ fn authenticate(ptr: *anyopaque) !bool { } else if (state.has_old_save) { std.Io.Dir.cwd().deleteFile(state.io, state.old_save_path) catch {}; } + + // Update the local tty cache before overwriting + // since multiple instances can be running + update_cache: { + var tty_cache_file = std.Io.Dir.cwd().openFile(state.io, state.tty_cache_path, .{}) catch break :update_cache; + defer tty_cache_file.close(state.io); + + var file_reader = tty_cache_file.reader(state.io, &file_buffer); + var reader = &file_reader.interface; + + while (reader.seek < reader.buffer.len) { + const line = reader.takeDelimiterInclusive('\n') catch break; + + var entry = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); + const tty_num_str = entry.next() orelse continue; + const username = entry.next() orelse continue; + + const tty_num = std.fmt.parseInt(usize, tty_num_str, 10) catch continue; + + // Load cached tty users if they exist + for (state.login.?.label.list.items, 0..) |u, u_index| { + if (std.mem.eql(u8, u.name, username)) { + if (tty_num < std.math.maxInt(@TypeOf(state.active_tty))) + state.tty_cache[tty_num] = u_index; + } + } + } + } + + var tty_cache_file = std.Io.Dir.cwd().createFile(state.io, state.tty_cache_path, .{}) catch |err| { + state.log_file.err( + state.io, + "sys", + "failed to create tty cache file: {s}", + .{@errorName(err)}, + ) catch break :save_last_settings; + break :save_last_settings; + }; + defer tty_cache_file.close(state.io); + + file_writer = tty_cache_file.writer(state.io, &file_buffer); + writer = &file_writer.interface; + + state.tty_cache[state.active_tty] = current; + + for (state.tty_cache, 0..) |maybe_user_index, tty_num| { + if (maybe_user_index) |user_index| { + try writer.print("{d}:{s}\n", .{ tty_num, state.login.?.label.list.items[user_index].name }); + } + } + try writer.flush(); } var shared_err = try SharedError.init(null, null);