mirror of https://github.com/fairyglade/ly.git
Remember last session for each user (closes #619)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
parent
aef1dd9c1a
commit
b3f1e91cf6
|
@ -0,0 +1,98 @@
|
|||
#!/bin/sh
|
||||
# Styling options
|
||||
local BOLD = 0x01000000
|
||||
local UNDERLINE = 0x02000000
|
||||
local REVERSE = 0x04000000
|
||||
local ITALIC = 0x08000000
|
||||
local BLINK = 0x10000000
|
||||
local HI_BLACK = 0x20000000
|
||||
local BRIGHT = 0x40000000
|
||||
local DIM = 0x80000000
|
||||
|
||||
# Common colors
|
||||
local DEFAULT = 0x00000000
|
||||
local RED = 0x00FF0000
|
||||
local GREEN = 0x0000FF00
|
||||
local YELLOW = 0x00FFFF00
|
||||
local BLUE = 0x000000FF
|
||||
local MAGENTA = 0x00FF00FF
|
||||
local CYAN = 0x0000FFFF
|
||||
local WHITE = 0x00FFFFFF
|
||||
|
||||
source lang/en.sh # From lang
|
||||
|
||||
# It'd be a good idea to condense multiple options into 1 command (e.g.
|
||||
# animation settings)
|
||||
ly set-config allow_empty_password true
|
||||
ly set-config animation none
|
||||
ly set-config animation_timeout_sec 0
|
||||
ly set-config asterisk "*"
|
||||
ly set-config auth_fails 10
|
||||
ly set-config bg $DEFAULT
|
||||
ly set-config bigclock_12hr false
|
||||
ly set-config bigclock_seconds false
|
||||
ly set-config blank_box true
|
||||
ly set-config border_fg $WHITE
|
||||
ly set-config box_title null
|
||||
ly set-config clear_password false
|
||||
ly set-config clock null
|
||||
ly set-config cmatrix_fg $GREEN
|
||||
ly set-config cmatrix_head_col $(($WHITE | $BOLD))
|
||||
ly set-config cmatrix_min_codepoint 0x21
|
||||
ly set-config cmatrix_max_codepoint = 0x7B
|
||||
ly set-config colormix_col1 $RED
|
||||
ly set-config colormix_col2 $BLUE
|
||||
ly set-config colormix_col3 $HI_BLACK
|
||||
ly set-config default_input login
|
||||
ly set-config doom_fire_height 6
|
||||
ly set-config doom_fire_spread 2
|
||||
ly set-config doom_top_color 0x009F2707
|
||||
ly set-config doom_middle_color 0x00C78F17
|
||||
ly set-config doom_bottom_color $WHITE
|
||||
ly set-config error_bg $DEFAULT
|
||||
ly set-config error_fg $(($RED | $BOLD))
|
||||
ly set-config fg $WHITE
|
||||
ly set-config full_color true
|
||||
ly set-config gameoflife_entropy_interval 10
|
||||
ly set-config gameoflife_fg $GREEN
|
||||
ly set-config gameoflife_frame_delay 6
|
||||
ly set-config gameoflife_initial_density 0.4
|
||||
ly set-config hide_borders false
|
||||
ly set-config initial_info_text null
|
||||
ly set-config input_len 34
|
||||
ly set-config login_cmd null
|
||||
ly set-config login_defs_path "/etc/login.defs"
|
||||
ly set-config logout_cmd null
|
||||
ly set-config ly_log "/var/log/ly.log"
|
||||
ly set-config margin_box_h 2
|
||||
ly set-config margin_box_v 1
|
||||
ly set-config min_refresh_delta 5
|
||||
ly set-config numlock false
|
||||
ly set-config path "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
ly set-config save true
|
||||
ly set-config service_name ly
|
||||
ly set-config session_log "ly-session.log"
|
||||
ly set-config setup_cmd "$CONFIG_DIRECTORY/ly/setup.sh"
|
||||
ly set-config text_in_center false
|
||||
ly set-config vi_default_mode normal
|
||||
ly set-config vi_mode false
|
||||
ly set-config x_cmd "$PREFIX_DIRECTORY/bin/X"
|
||||
ly set-config xauth_cmd "$PREFIX_DIRECTORY/bin/xauth"
|
||||
|
||||
# Replaces respective options
|
||||
# X11 requires special support from the display manager, which is why a separate
|
||||
# command is required
|
||||
ly add-session "System shell" shell "/bin/sh"
|
||||
ly add-x11-session xinitrc x11 "~/.xinitrc"
|
||||
|
||||
# ly add-session-dir custom "$CONFIG_DIRECTORY/ly/custom-sessions"
|
||||
ly add-session-dir wayland "$PREFIX_DIRECTORY/share/wayland-sessions"
|
||||
ly add-x11-session-dir x11 "$PREFIX_DIRECTORY/share/xsessions"
|
||||
|
||||
ly add-hud 0 0 "Ly version %VERSION" null null top left
|
||||
ly add-hud 1 0 "F1 shutdown" F1 "/sbin/shutdown -a now" top left
|
||||
ly add-hud 2 0 "F2 reboot" F2 "/sbin/shutdown -r now" top left
|
||||
#ly add-hud 0 0 "F3 sleep" F3 null top left
|
||||
ly add-hud 3 0 "F5 decrease brightness" F5 "$PREFIX_DIRECTORY/bin/brightnessctl -q s 10%-" top left
|
||||
ly add-hud 4 0 "F6 increase brightness" F6 "$PREFIX_DIRECTORY/bin/brightnessctl -q s +10%" top left
|
||||
ly add-hud 5 0 "%CLOCK" null null top right
|
|
@ -0,0 +1,22 @@
|
|||
const std = @import("std");
|
||||
|
||||
const SavedUsers = @This();
|
||||
|
||||
const User = struct {
|
||||
username: []const u8,
|
||||
session_index: usize,
|
||||
};
|
||||
|
||||
user_list: std.ArrayList(User),
|
||||
last_username_index: ?usize,
|
||||
|
||||
pub fn init() SavedUsers {
|
||||
return .{
|
||||
.user_list = .empty,
|
||||
.last_username_index = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SavedUsers, allocator: std.mem.Allocator) void {
|
||||
self.user_list.deinit(allocator);
|
||||
}
|
|
@ -5,7 +5,8 @@
|
|||
const std = @import("std");
|
||||
const ini = @import("zigini");
|
||||
const Config = @import("Config.zig");
|
||||
const Save = @import("Save.zig");
|
||||
const OldSave = @import("OldSave.zig");
|
||||
const SavedUsers = @import("SavedUsers.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
|
||||
const Color = TerminalBuffer.Color;
|
||||
|
@ -186,8 +187,8 @@ pub fn lateConfigFieldHandler(config: *Config) void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn tryMigrateSaveFile(user_buf: *[32]u8) Save {
|
||||
var save = Save{};
|
||||
pub fn tryMigrateFirstSaveFile(user_buf: *[32]u8) OldSave {
|
||||
var save = OldSave{};
|
||||
|
||||
if (maybe_save_file) |path| {
|
||||
defer temporary_allocator.free(path);
|
||||
|
@ -216,3 +217,29 @@ pub fn tryMigrateSaveFile(user_buf: *[32]u8) Save {
|
|||
|
||||
return save;
|
||||
}
|
||||
|
||||
pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, save_ini: *ini.Ini(OldSave), path: []const u8, saved_users: *SavedUsers, usernames: [][]const u8) !bool {
|
||||
var old_save_file_exists = true;
|
||||
|
||||
var user_buf: [32]u8 = undefined;
|
||||
const save = save_ini.readFileToStruct(path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = "#",
|
||||
}) catch no_save_file: {
|
||||
old_save_file_exists = false;
|
||||
break :no_save_file tryMigrateFirstSaveFile(&user_buf);
|
||||
};
|
||||
|
||||
if (!old_save_file_exists) return false;
|
||||
|
||||
// Add all other users to the list
|
||||
for (usernames, 0..) |username, i| {
|
||||
if (save.user) |user| {
|
||||
if (std.mem.eql(u8, user, username)) saved_users.last_username_index = i;
|
||||
}
|
||||
|
||||
try saved_users.user_list.append(allocator, .{ .username = username, .session_index = save.session_index orelse 0 });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
134
src/main.zig
134
src/main.zig
|
@ -21,7 +21,8 @@ const InfoLine = @import("tui/components/InfoLine.zig");
|
|||
const UserList = @import("tui/components/UserList.zig");
|
||||
const Config = @import("config/Config.zig");
|
||||
const Lang = @import("config/Lang.zig");
|
||||
const Save = @import("config/Save.zig");
|
||||
const OldSave = @import("config/OldSave.zig");
|
||||
const SavedUsers = @import("config/SavedUsers.zig");
|
||||
const migrator = @import("config/migrator.zig");
|
||||
const SharedError = @import("SharedError.zig");
|
||||
const UidRange = @import("UidRange.zig");
|
||||
|
@ -118,11 +119,14 @@ pub fn main() !void {
|
|||
|
||||
var config: Config = undefined;
|
||||
var lang: Lang = undefined;
|
||||
var save: Save = undefined;
|
||||
var old_save_file_exists = false;
|
||||
var maybe_config_load_error: ?anyerror = null;
|
||||
var can_get_lock_state = true;
|
||||
var can_draw_clock = true;
|
||||
|
||||
var saved_users = SavedUsers.init();
|
||||
defer saved_users.deinit(allocator);
|
||||
|
||||
if (res.args.help != 0) {
|
||||
try clap.help(stderr, clap.Help, ¶ms, .{});
|
||||
|
||||
|
@ -143,13 +147,15 @@ pub fn main() !void {
|
|||
var lang_ini = Ini(Lang).init(allocator);
|
||||
defer lang_ini.deinit();
|
||||
|
||||
var save_ini = Ini(Save).init(allocator);
|
||||
defer save_ini.deinit();
|
||||
var old_save_ini = ini.Ini(OldSave).init(allocator);
|
||||
defer old_save_ini.deinit();
|
||||
|
||||
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini";
|
||||
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.txt";
|
||||
var old_save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini";
|
||||
var save_path_alloc = false;
|
||||
defer {
|
||||
if (save_path_alloc) allocator.free(save_path);
|
||||
if (save_path_alloc) allocator.free(old_save_path);
|
||||
}
|
||||
|
||||
const comment_characters = "#";
|
||||
|
@ -178,18 +184,9 @@ pub fn main() !void {
|
|||
}) catch Lang{};
|
||||
|
||||
if (config.save) {
|
||||
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
|
||||
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.txt", .{ s, trailing_slash });
|
||||
old_save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
|
||||
save_path_alloc = true;
|
||||
|
||||
var user_buf: [32]u8 = undefined;
|
||||
save = save_ini.readFileToStruct(save_path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch migrator.tryMigrateSaveFile(&user_buf);
|
||||
}
|
||||
|
||||
if (maybe_config_load_error == null) {
|
||||
migrator.lateConfigFieldHandler(&config);
|
||||
}
|
||||
} else {
|
||||
const config_path = build_options.config_directory ++ "/ly/config.ini";
|
||||
|
@ -210,17 +207,47 @@ pub fn main() !void {
|
|||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch Lang{};
|
||||
}
|
||||
|
||||
if (config.save) {
|
||||
var user_buf: [32]u8 = undefined;
|
||||
save = save_ini.readFileToStruct(save_path, .{
|
||||
.fieldHandler = null,
|
||||
.comment_characters = comment_characters,
|
||||
}) catch migrator.tryMigrateSaveFile(&user_buf);
|
||||
}
|
||||
if (maybe_config_load_error == null) {
|
||||
migrator.lateConfigFieldHandler(&config);
|
||||
}
|
||||
|
||||
if (maybe_config_load_error == null) {
|
||||
migrator.lateConfigFieldHandler(&config);
|
||||
var usernames = try getAllUsernames(allocator, config.login_defs_path);
|
||||
defer {
|
||||
for (usernames.items) |username| allocator.free(username);
|
||||
usernames.deinit(allocator);
|
||||
}
|
||||
|
||||
if (config.save) read_save_file: {
|
||||
old_save_file_exists = migrator.tryMigrateIniSaveFile(allocator, &old_save_ini, old_save_path, &saved_users, usernames.items) catch break :read_save_file;
|
||||
|
||||
// Don't read the new save file if the old one still exists
|
||||
if (old_save_file_exists) break :read_save_file;
|
||||
|
||||
var save_file = std.fs.cwd().openFile(save_path, .{}) catch break :read_save_file;
|
||||
defer save_file.close();
|
||||
|
||||
var file_buffer: [256]u8 = undefined;
|
||||
var file_reader = save_file.reader(&file_buffer);
|
||||
var reader = &file_reader.interface;
|
||||
|
||||
const last_username_index_str = reader.takeDelimiterInclusive('\n') catch break :read_save_file;
|
||||
saved_users.last_username_index = std.fmt.parseInt(usize, last_username_index_str[0..(last_username_index_str.len - 1)], 10) catch break :read_save_file;
|
||||
|
||||
while (reader.seek < reader.buffer.len) {
|
||||
const line = reader.takeDelimiterInclusive('\n') catch break;
|
||||
|
||||
var user = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':');
|
||||
const username = user.next() orelse continue;
|
||||
const session_index_str = user.next() orelse continue;
|
||||
|
||||
const session_index = std.fmt.parseInt(usize, session_index_str, 10) catch continue;
|
||||
|
||||
try saved_users.user_list.append(allocator, .{
|
||||
.username = username,
|
||||
.session_index = session_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,7 +372,9 @@ pub fn main() !void {
|
|||
try log_writer.print("failed to set numlock: {s}\n", .{@errorName(err)});
|
||||
};
|
||||
|
||||
var session = Session.init(allocator, &buffer);
|
||||
var login: UserList = undefined;
|
||||
|
||||
var session = Session.init(allocator, &buffer, &login);
|
||||
defer session.deinit();
|
||||
|
||||
addOtherEnvironment(&session, lang, .shell, null) catch |err| {
|
||||
|
@ -396,12 +425,6 @@ pub fn main() !void {
|
|||
try crawl(&session, lang, dir, .custom);
|
||||
}
|
||||
|
||||
var usernames = try getAllUsernames(allocator, config.login_defs_path);
|
||||
defer {
|
||||
for (usernames.items) |username| allocator.free(username);
|
||||
usernames.deinit(allocator);
|
||||
}
|
||||
|
||||
if (usernames.items.len == 0) {
|
||||
// If we have no usernames, simply add an error to the info line.
|
||||
// This effectively means you can't login, since there would be no local
|
||||
|
@ -411,7 +434,7 @@ pub fn main() !void {
|
|||
try log_writer.writeAll("no users found\n");
|
||||
}
|
||||
|
||||
var login = try UserList.init(allocator, &buffer, usernames);
|
||||
login = try UserList.init(allocator, &buffer, usernames, &saved_users, &session);
|
||||
defer login.deinit();
|
||||
|
||||
var password = Text.init(allocator, &buffer, true, config.asterisk);
|
||||
|
@ -422,23 +445,21 @@ pub fn main() !void {
|
|||
|
||||
// Load last saved username and desktop selection, if any
|
||||
if (config.save) {
|
||||
if (save.user) |user| {
|
||||
if (saved_users.last_username_index) |index| {
|
||||
const user = saved_users.user_list.items[index];
|
||||
|
||||
// Find user with saved name, and switch over to it
|
||||
// If it doesn't exist (anymore), we don't change the value
|
||||
// Note that we could instead save the username index, but migrating
|
||||
// from the raw username to an index is non-trivial and I'm lazy :P
|
||||
for (usernames.items, 0..) |username, i| {
|
||||
if (std.mem.eql(u8, username, user)) {
|
||||
if (std.mem.eql(u8, username, user.username)) {
|
||||
login.label.current = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
active_input = .password;
|
||||
}
|
||||
|
||||
if (save.session_index) |session_index| {
|
||||
if (session_index < session.label.list.items.len) session.label.current = session_index;
|
||||
if (user.session_index < session.label.list.items.len) session.label.current = user.session_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -853,22 +874,33 @@ pub fn main() !void {
|
|||
_ = termbox.tb_present();
|
||||
|
||||
if (config.save) save_last_settings: {
|
||||
var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
|
||||
// It isn't worth cluttering the code with precise error
|
||||
// handling, so let's just report a generic error message,
|
||||
// that should be good enough for debugging anyway.
|
||||
errdefer log_writer.writeAll("failed to save current user data\n") catch {};
|
||||
|
||||
var file = std.fs.cwd().createFile(save_path, .{}) catch |err| {
|
||||
log_writer.print("failed to create save file: {s}\n", .{@errorName(err)}) catch break :save_last_settings;
|
||||
break :save_last_settings;
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
var file_buffer: [64]u8 = undefined;
|
||||
var file_buffer: [256]u8 = undefined;
|
||||
var file_writer = file.writer(&file_buffer);
|
||||
var writer = &file_writer.interface;
|
||||
|
||||
const save_data = Save{
|
||||
.user = login.getCurrentUser(),
|
||||
.session_index = session.label.current,
|
||||
};
|
||||
ini.writeFromStruct(save_data, writer, null, .{}) catch break :save_last_settings;
|
||||
try writer.print("{d}\n", .{login.label.current});
|
||||
for (saved_users.user_list.items) |user| {
|
||||
try writer.print("{s}:{d}\n", .{ user.username, user.session_index });
|
||||
}
|
||||
try writer.flush();
|
||||
|
||||
// Delete previous save file if it exists
|
||||
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
|
||||
if (migrator.maybe_save_file) |path| {
|
||||
std.fs.cwd().deleteFile(path) catch {};
|
||||
} else if (old_save_file_exists) {
|
||||
std.fs.cwd().deleteFile(old_save_path) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
var shared_err = try SharedError.init();
|
||||
|
@ -877,7 +909,7 @@ pub fn main() !void {
|
|||
{
|
||||
session_pid = try std.posix.fork();
|
||||
if (session_pid == 0) {
|
||||
const current_environment = session.label.list.items[session.label.current];
|
||||
const current_environment = session.label.list.items[session.label.current].environment;
|
||||
const auth_options = auth.AuthOptions{
|
||||
.tty = active_tty,
|
||||
.service_name = config.service_name,
|
||||
|
@ -898,7 +930,7 @@ pub fn main() !void {
|
|||
};
|
||||
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
|
||||
|
||||
auth.authenticate(allocator, log_writer, auth_options, current_environment, login.getCurrentUser(), password.text.items) catch |err| {
|
||||
auth.authenticate(allocator, log_writer, auth_options, current_environment, login.getCurrentUsername(), password.text.items) catch |err| {
|
||||
shared_err.writeError(err);
|
||||
std.process.exit(1);
|
||||
};
|
||||
|
@ -1026,7 +1058,7 @@ fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplaySer
|
|||
.name = name,
|
||||
.xdg_session_desktop = null,
|
||||
.xdg_desktop_names = null,
|
||||
.cmd = exec orelse "",
|
||||
.cmd = exec,
|
||||
.specifier = lang.other,
|
||||
.display_server = display_server,
|
||||
.is_terminal = display_server == .shell,
|
||||
|
|
|
@ -4,7 +4,7 @@ const generic = @import("generic.zig");
|
|||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const MessageLabel = generic.CyclableLabel(Message);
|
||||
const MessageLabel = generic.CyclableLabel(Message, Message);
|
||||
|
||||
const InfoLine = @This();
|
||||
|
||||
|
@ -19,7 +19,7 @@ label: MessageLabel,
|
|||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine {
|
||||
return .{
|
||||
.label = MessageLabel.init(allocator, buffer, drawItem),
|
||||
.label = MessageLabel.init(allocator, buffer, drawItem, null, null),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,41 +3,53 @@ const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|||
const enums = @import("../../enums.zig");
|
||||
const Environment = @import("../../Environment.zig");
|
||||
const generic = @import("generic.zig");
|
||||
const UserList = @import("UserList.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const DisplayServer = enums.DisplayServer;
|
||||
const EnvironmentLabel = generic.CyclableLabel(Environment);
|
||||
|
||||
const Env = struct {
|
||||
environment: Environment,
|
||||
index: usize,
|
||||
};
|
||||
const EnvironmentLabel = generic.CyclableLabel(Env, *UserList);
|
||||
|
||||
const Session = @This();
|
||||
|
||||
label: EnvironmentLabel,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) Session {
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, user_list: *UserList) Session {
|
||||
return .{
|
||||
.label = EnvironmentLabel.init(allocator, buffer, drawItem),
|
||||
.label = EnvironmentLabel.init(allocator, buffer, drawItem, sessionChanged, user_list),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Session) void {
|
||||
for (self.label.list.items) |*environment| {
|
||||
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||
for (self.label.list.items) |*env| {
|
||||
if (env.environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||
}
|
||||
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
||||
try self.label.addItem(environment);
|
||||
try self.label.addItem(.{ .environment = environment, .index = self.label.list.items.len });
|
||||
}
|
||||
|
||||
fn drawItem(label: *EnvironmentLabel, environment: Environment, x: usize, y: usize) bool {
|
||||
const length = @min(environment.name.len, label.visible_length - 3);
|
||||
fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
|
||||
if (maybe_user_list) |user_list| {
|
||||
user_list.label.list.items[user_list.label.current].session_index.* = env.index;
|
||||
}
|
||||
}
|
||||
|
||||
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize) bool {
|
||||
const length = @min(env.environment.name.len, label.visible_length - 3);
|
||||
if (length == 0) return false;
|
||||
|
||||
const nx = if (label.text_in_center) (label.x + (label.visible_length - environment.name.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = nx + environment.name.len;
|
||||
const nx = if (label.text_in_center) (label.x + (label.visible_length - env.environment.name.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = nx + env.environment.name.len;
|
||||
|
||||
label.buffer.drawLabel(environment.specifier, x, y);
|
||||
label.buffer.drawLabel(environment.name, nx, label.y);
|
||||
label.buffer.drawLabel(env.environment.specifier, x, y);
|
||||
label.buffer.drawLabel(env.environment.name, nx, label.y);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,83 @@
|
|||
const std = @import("std");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const generic = @import("generic.zig");
|
||||
const Session = @import("Session.zig");
|
||||
const SavedUsers = @import("../../config/SavedUsers.zig");
|
||||
|
||||
const StringList = std.ArrayListUnmanaged([]const u8);
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const UsernameText = generic.CyclableLabel([]const u8);
|
||||
pub const User = struct {
|
||||
name: []const u8,
|
||||
session_index: *usize,
|
||||
allocated_index: bool,
|
||||
};
|
||||
const UserLabel = generic.CyclableLabel(User, *Session);
|
||||
|
||||
const UserList = @This();
|
||||
|
||||
label: UsernameText,
|
||||
label: UserLabel,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList) !UserList {
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList, saved_users: *SavedUsers, session: *Session) !UserList {
|
||||
var userList = UserList{
|
||||
.label = UsernameText.init(allocator, buffer, drawItem),
|
||||
.label = UserLabel.init(allocator, buffer, drawItem, usernameChanged, session),
|
||||
};
|
||||
|
||||
for (usernames.items) |username| {
|
||||
if (username.len == 0) continue;
|
||||
|
||||
try userList.label.addItem(username);
|
||||
var maybe_session_index: ?*usize = null;
|
||||
for (saved_users.user_list.items) |*saved_user| {
|
||||
if (std.mem.eql(u8, username, saved_user.username)) {
|
||||
maybe_session_index = &saved_user.session_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var allocated_index = false;
|
||||
if (maybe_session_index == null) {
|
||||
maybe_session_index = try allocator.create(usize);
|
||||
maybe_session_index.?.* = 0;
|
||||
allocated_index = true;
|
||||
}
|
||||
|
||||
try userList.label.addItem(.{
|
||||
.name = username,
|
||||
.session_index = maybe_session_index.?,
|
||||
.allocated_index = allocated_index,
|
||||
});
|
||||
}
|
||||
|
||||
return userList;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *UserList) void {
|
||||
for (self.label.list.items) |user| {
|
||||
if (user.allocated_index) {
|
||||
self.label.allocator.destroy(user.session_index);
|
||||
}
|
||||
}
|
||||
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn getCurrentUser(self: UserList) []const u8 {
|
||||
return self.label.list.items[self.label.current];
|
||||
pub fn getCurrentUsername(self: UserList) []const u8 {
|
||||
return self.label.list.items[self.label.current].name;
|
||||
}
|
||||
|
||||
fn drawItem(label: *UsernameText, username: []const u8, _: usize, _: usize) bool {
|
||||
const length = @min(username.len, label.visible_length - 3);
|
||||
fn usernameChanged(user: User, maybe_session: ?*Session) void {
|
||||
if (maybe_session) |session| {
|
||||
session.label.current = user.session_index.*;
|
||||
}
|
||||
}
|
||||
|
||||
fn drawItem(label: *UserLabel, user: User, _: usize, _: usize) bool {
|
||||
const length = @min(user.name.len, label.visible_length - 3);
|
||||
if (length == 0) return false;
|
||||
|
||||
const x = if (label.text_in_center) (label.x + (label.visible_length - username.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = x + username.len;
|
||||
const x = if (label.text_in_center) (label.x + (label.visible_length - user.name.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = x + user.name.len;
|
||||
|
||||
label.buffer.drawLabel(username, x, label.y);
|
||||
label.buffer.drawLabel(user.name, x, label.y);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ const std = @import("std");
|
|||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
|
||||
pub fn CyclableLabel(comptime ItemType: type) type {
|
||||
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
|
||||
return struct {
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ItemList = std.ArrayListUnmanaged(ItemType);
|
||||
const DrawItemFn = *const fn (*Self, ItemType, usize, usize) bool;
|
||||
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
|
@ -22,8 +23,10 @@ pub fn CyclableLabel(comptime ItemType: type) type {
|
|||
first_char_x: usize,
|
||||
text_in_center: bool,
|
||||
draw_item_fn: DrawItemFn,
|
||||
change_item_fn: ?ChangeItemFn,
|
||||
change_item_arg: ?ChangeItemType,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, draw_item_fn: DrawItemFn) Self {
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, draw_item_fn: DrawItemFn, change_item_fn: ?ChangeItemFn, change_item_arg: ?ChangeItemType) Self {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
|
@ -35,6 +38,8 @@ pub fn CyclableLabel(comptime ItemType: type) type {
|
|||
.first_char_x = 0,
|
||||
.text_in_center = false,
|
||||
.draw_item_fn = draw_item_fn,
|
||||
.change_item_fn = change_item_fn,
|
||||
.change_item_arg = change_item_arg,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -94,21 +99,19 @@ pub fn CyclableLabel(comptime ItemType: type) type {
|
|||
}
|
||||
|
||||
fn goLeft(self: *Self) void {
|
||||
if (self.current == 0) {
|
||||
self.current = self.list.items.len - 1;
|
||||
return;
|
||||
}
|
||||
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
|
||||
|
||||
self.current -= 1;
|
||||
if (self.change_item_fn) |change_item_fn| {
|
||||
@call(.auto, change_item_fn, .{ self.list.items[self.current], self.change_item_arg });
|
||||
}
|
||||
}
|
||||
|
||||
fn goRight(self: *Self) void {
|
||||
if (self.current == self.list.items.len - 1) {
|
||||
self.current = 0;
|
||||
return;
|
||||
}
|
||||
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
|
||||
|
||||
self.current += 1;
|
||||
if (self.change_item_fn) |change_item_fn| {
|
||||
@call(.auto, change_item_fn, .{ self.list.items[self.current], self.change_item_arg });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue