From 41f4378bfe2a4b7d3f9e2dbd7a24d67088cf6832 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 6 Jul 2025 09:25:06 +0200 Subject: [PATCH 01/15] Fix XDG_RUNTIME_DIR not being set properly (fixes #781) Signed-off-by: AnErrupTion --- src/auth.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/auth.zig b/src/auth.zig index 35eee5e..aa853c9 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -41,8 +41,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty}); // Set the XDG environment variables - setXdgSessionEnv(current_environment.display_server); - try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names); + try setXdgEnv(tty_str, current_environment); // Open the PAM session var credentials = [_:null]?[*:0]const u8{ login, password }; @@ -96,7 +95,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi child_pid = try std.posix.fork(); if (child_pid == 0) { - startSession(options, pwd, handle, current_environment) catch |e| { + startSession(options, tty_str, pwd, handle, current_environment) catch |e| { shared_err.writeError(e); std.process.exit(1); }; @@ -132,6 +131,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi fn startSession( options: AuthOptions, + tty_str: [:0]u8, pwd: *interop.pwd.passwd, handle: ?*interop.pam.pam_handle, current_environment: Environment, @@ -155,6 +155,9 @@ fn startSession( // Set up the environment try initEnv(pwd, options.path); + // Reset the XDG environment variables + try setXdgEnv(tty_str, current_environment); + // Set the PAM variables const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle); if (pam_env_vars == null) return error.GetEnvListFailed; @@ -193,15 +196,13 @@ fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void { } } -fn setXdgSessionEnv(display_server: enums.DisplayServer) void { - _ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (display_server) { +fn setXdgEnv(tty_str: [:0]u8, environment: Environment) !void { + _ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (environment.display_server) { .wayland => "wayland", .shell => "tty", .xinitrc, .x11 => "x11", }, 0); -} -fn setXdgEnv(tty_str: [:0]u8, maybe_desktop_name: ?[:0]const u8, maybe_xdg_desktop_names: ?[:0]const u8) !void { // The "/run/user/%d" directory is not available on FreeBSD. It is much // better to stick to the defaults and let applications using // XDG_RUNTIME_DIR to fall back to directories inside user's home @@ -214,10 +215,10 @@ fn setXdgEnv(tty_str: [:0]u8, maybe_desktop_name: ?[:0]const u8, maybe_xdg_deskt _ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0); } - if (maybe_xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0); + if (environment.xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0); _ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0); _ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0); - if (maybe_desktop_name) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0); + if (environment.xdg_session_desktop) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0); _ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0); _ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0); } From d08b9a916e32357df3c9d559f844edbab36d70b0 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Mon, 7 Jul 2025 21:45:54 +0200 Subject: [PATCH 02/15] Remove config.console_dev option + handle ioctl errors Signed-off-by: AnErrupTion --- res/config.ini | 3 --- res/lang/ar.ini | 2 ++ res/lang/cat.ini | 2 ++ res/lang/cs.ini | 2 ++ res/lang/de.ini | 2 ++ res/lang/en.ini | 2 ++ res/lang/es.ini | 2 ++ res/lang/fr.ini | 2 ++ res/lang/it.ini | 2 ++ res/lang/pl.ini | 2 ++ res/lang/pt.ini | 2 ++ res/lang/pt_BR.ini | 2 ++ res/lang/ro.ini | 2 ++ res/lang/ru.ini | 2 ++ res/lang/sr.ini | 2 ++ res/lang/sv.ini | 2 ++ res/lang/tr.ini | 2 ++ res/lang/uk.ini | 2 ++ res/lang/zh_CN.ini | 2 ++ src/config/Config.zig | 1 - src/config/Lang.zig | 2 ++ src/interop.zig | 25 ++++++++++++------------- src/main.zig | 14 ++++++-------- 23 files changed, 56 insertions(+), 25 deletions(-) diff --git a/res/config.ini b/res/config.ini index a74cd5c..526c6f9 100644 --- a/res/config.ini +++ b/res/config.ini @@ -100,9 +100,6 @@ colormix_col2 = 0x000000FF # Color mixing animation third color id colormix_col3 = 0x20000000 -# Console path -console_dev = /dev/console - # Input box active by default on startup # Available inputs: info_line, session, login, password default_input = login diff --git a/res/lang/ar.ini b/res/lang/ar.ini index 835fd66..95644b7 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -13,6 +13,7 @@ err_domain = اسم نطاق غير صالح err_empty_password = لا يُسمح بكلمة مرور فارغة err_envlist = فشل في جلب قائمة المتغيرات البيئية err_hostname = فشل في جلب اسم المضيف (Hostname) + err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock) err_null = مؤشر فارغ (Null pointer) err_numlock = فشل في ضبط Num Lock @@ -38,6 +39,7 @@ err_perm_group = فشل في تخفيض صلاحيات المجموعة (Group p err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions) err_pwnam = فشل في جلب معلومات المستخدم err_sleep = فشل في تنفيذ أمر sleep + err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY) err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم err_user_init = فشل في تهيئة بيانات المستخدم diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 01d9232..f1adf55 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -13,6 +13,7 @@ err_domain = domini invàlid err_envlist = error en obtenir l'envlist err_hostname = error en obtenir el nom de l'amfitrió + err_mlock = error en bloquejar la memòria de clau err_null = punter nul err_numlock = error en establir el Bloq num @@ -39,6 +40,7 @@ err_perm_user = error en degradar els permisos de l'usuari err_pwnam = error en obtenir la informació de l'usuari + err_user_gid = error en establir el GID de l'usuari err_user_init = error en inicialitzar usuari err_user_uid = error en establir l'UID de l'usuari diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 2adc297..05c2e4d 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -13,6 +13,7 @@ err_domain = neplatná doména err_hostname = nelze získat název hostitele + err_mlock = uzamčení paměti hesel selhalo err_null = nulový ukazatel @@ -39,6 +40,7 @@ err_perm_user = nepodařilo se snížit uživatelská oprávnění err_pwnam = nelze získat informace o uživateli + err_user_gid = nastavení GID uživatele selhalo err_user_init = inicializace uživatele selhala err_user_uid = nastavení UID uživateli selhalo diff --git a/res/lang/de.ini b/res/lang/de.ini index 47c44aa..7277510 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -13,6 +13,7 @@ err_domain = Ungueltige Domain err_empty_password = Leeres Passwort nicht zugelassen err_envlist = Fehler beim Abrufen der Umgebungs-Variablen err_hostname = Abrufen des Hostnames fehlgeschlagen + err_mlock = Sperren des Passwortspeichers fehlgeschlagen err_null = Null Pointer err_numlock = Numlock konnte nicht aktiviert werden @@ -38,6 +39,7 @@ err_perm_group = Fehler beim Heruntersetzen der Gruppenberechtigungen err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen err_sleep = Sleep-Befehl fehlgeschlagen + err_tty_ctrl = Fehler bei der TTY-Uebergabe err_user_gid = Fehler beim Setzen der Gruppen-ID err_user_init = Nutzer-Initialisierung fehlgeschlagen diff --git a/res/lang/en.ini b/res/lang/en.ini index a0eae31..3e1b7d7 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -13,6 +13,7 @@ err_domain = invalid domain err_empty_password = empty password not allowed err_envlist = failed to get envlist err_hostname = failed to get hostname +err_lock_state = failed to get lock state err_mlock = failed to lock password memory err_null = null pointer err_numlock = failed to set numlock @@ -38,6 +39,7 @@ err_perm_group = failed to downgrade group permissions err_perm_user = failed to downgrade user permissions err_pwnam = failed to get user info err_sleep = failed to execute sleep command +err_switch_tty = failed to switch tty err_tty_ctrl = tty control transfer failed err_user_gid = failed to set user GID err_user_init = failed to initialize user diff --git a/res/lang/es.ini b/res/lang/es.ini index 5004d12..f9780ec 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -13,6 +13,7 @@ err_domain = dominio inválido err_hostname = error al obtener el nombre de host + err_mlock = error al bloquear la contraseña de memoria err_null = puntero nulo @@ -39,6 +40,7 @@ err_perm_user = error al degradar los permisos del usuario err_pwnam = error al obtener la información del usuario + err_user_gid = error al establecer el GID del usuario err_user_init = error al inicializar usuario err_user_uid = error al establecer el UID del usuario diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 75c7c6f..3d73ef1 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -13,6 +13,7 @@ err_domain = domaine invalide err_empty_password = mot de passe vide non autorisé err_envlist = échec de lecture de la liste d'environnement err_hostname = échec de lecture du nom d'hôte +err_lock_state = échec de lecture de l'état de verrouillage err_mlock = échec du verrouillage mémoire err_null = pointeur null err_numlock = échec de modification du verr.num @@ -38,6 +39,7 @@ err_perm_group = échec du déclassement des permissions de groupe err_perm_user = échec du déclassement des permissions utilisateur err_pwnam = échec de lecture des infos utilisateur err_sleep = échec de l'exécution de la commande de veille +err_switch_tty = échec du changement de terminal err_tty_ctrl = échec du transfert de contrôle du terminal err_user_gid = échec de modification du GID err_user_init = échec d'initialisation de l'utilisateur diff --git a/res/lang/it.ini b/res/lang/it.ini index 8d749c6..1781c1e 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -13,6 +13,7 @@ err_domain = dominio non valido err_hostname = impossibile ottenere hostname + err_mlock = impossibile ottenere lock per la password in memoria err_null = puntatore nullo @@ -39,6 +40,7 @@ err_perm_user = impossibile ridurre permessi utente err_pwnam = impossibile ottenere dati utente + err_user_gid = impossibile impostare GID utente err_user_init = impossibile inizializzare utente err_user_uid = impossible impostare UID utente diff --git a/res/lang/pl.ini b/res/lang/pl.ini index cdd3f29..4995ef0 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -13,6 +13,7 @@ err_domain = niepoprawna domena err_empty_password = puste hasło jest niedozwolone err_envlist = nie udało się pobrać listy zmiennych środowiskowych err_hostname = nie udało się uzyskać nazwy hosta + err_mlock = nie udało się zablokować pamięci haseł err_null = pusty wskaźnik err_numlock = nie udało się ustawić numlock @@ -38,6 +39,7 @@ err_perm_group = nie udało się obniżyć uprawnień grupy err_perm_user = nie udało się obniżyć uprawnień użytkownika err_pwnam = nie udało się uzyskać informacji o użytkowniku err_sleep = nie udało się wykonać polecenia sleep + err_tty_ctrl = nie udało się przekazać kontroli tty err_user_gid = nie udało się ustawić GID użytkownika err_user_init = nie udało się zainicjalizować użytkownika diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 25361b6..a4f0fa8 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -13,6 +13,7 @@ err_domain = domínio inválido err_hostname = erro ao obter o nome do host + err_mlock = erro de bloqueio de memória err_null = ponteiro nulo @@ -39,6 +40,7 @@ err_perm_user = erro ao reduzir as permissões do utilizador err_pwnam = erro ao obter informação do utilizador + err_user_gid = erro ao definir o GID do utilizador err_user_init = erro ao iniciar o utilizador err_user_uid = erro ao definir o UID do utilizador diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index 8148e27..2d2bb89 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -13,6 +13,7 @@ err_domain = domínio inválido err_hostname = não foi possível obter o nome do host + err_mlock = bloqueio da memória de senha malsucedido err_null = ponteiro nulo @@ -39,6 +40,7 @@ err_perm_user = não foi possível reduzir as permissões de usuário err_pwnam = não foi possível obter informações do usuário + err_user_gid = não foi possível definir o GID do usuário err_user_init = não foi possível iniciar o usuário err_user_uid = não foi possível definir o UID do usuário diff --git a/res/lang/ro.ini b/res/lang/ro.ini index f4928cf..9f50c8f 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -17,6 +17,7 @@ err_console_dev = nu s-a putut accesa consola + err_pam_abort = tranzacţie pam anulată err_pam_acct_expired = cont expirat err_pam_auth = eroare de autentificare @@ -47,6 +48,7 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator + login = utilizator logout = opreşte sesiunea diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 8e9b1a0..fe7315e 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -13,6 +13,7 @@ err_domain = неверный домен err_hostname = не удалось получить имя хоста + err_mlock = сбой блокировки памяти err_null = нулевой указатель @@ -39,6 +40,7 @@ err_perm_user = не удалось понизить права доступа err_pwnam = не удалось получить информацию о пользователе + err_user_gid = не удалось установить GID пользователя err_user_init = не удалось инициализировать пользователя err_user_uid = не удалось установить UID пользователя diff --git a/res/lang/sr.ini b/res/lang/sr.ini index bedc690..9251011 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -13,6 +13,7 @@ err_domain = nevazeci domen err_hostname = neuspijesno trazenje hostname-a + err_mlock = neuspijesno zakljucavanje memorije lozinke err_null = null pokazivac @@ -39,6 +40,7 @@ err_perm_user = neuspijesno snizavanje dozvola korisnika err_pwnam = neuspijesno skupljanje informacija o korisniku + err_user_gid = neuspijesno postavljanje korisničkog GID-a err_user_init = neuspijensa inicijalizacija korisnika err_user_uid = neuspijesno postavljanje UID-a korisnika diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 40b8cbd..aad2244 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -13,6 +13,7 @@ err_domain = okänd domän err_hostname = misslyckades att hämta värdnamn + err_mlock = misslyckades att låsa lösenordsminne err_null = nullpekare @@ -39,6 +40,7 @@ err_perm_user = misslyckades att nergradera användarbehörigheter err_pwnam = misslyckades att hämta användarinfo + err_user_gid = misslyckades att ställa in användar-GID err_user_init = misslyckades att initialisera användaren err_user_uid = misslyckades att ställa in användar-UID diff --git a/res/lang/tr.ini b/res/lang/tr.ini index 77a22ae..6ad0e94 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -13,6 +13,7 @@ err_domain = gecersiz etki alani err_hostname = ana bilgisayar adi alinamadi + err_mlock = parola bellegi kilitlenemedi err_null = bos isaretci hatasi @@ -39,6 +40,7 @@ err_perm_user = kullanici izinleri dusurulemedi err_pwnam = kullanici bilgileri alinamadi + err_user_gid = kullanici icin GID ayarlanamadi err_user_init = kullanici oturumu baslatilamadi err_user_uid = kullanici icin UID ayarlanamadi diff --git a/res/lang/uk.ini b/res/lang/uk.ini index a24731e..df925df 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -13,6 +13,7 @@ err_domain = недійсний домен err_hostname = не вдалося отримати ім'я хосту + err_mlock = збій блокування пам'яті err_null = нульовий вказівник @@ -39,6 +40,7 @@ err_perm_user = не вдалося понизити права доступу err_pwnam = не вдалося отримати дані користувача + err_user_gid = не вдалося змінити GID користувача err_user_init = не вдалося ініціалізувати користувача err_user_uid = не вдалося змінити UID користувача diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 9960d07..d1ccc57 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -13,6 +13,7 @@ err_domain = 无效的域 err_hostname = 获取主机名失败 + err_mlock = 锁定密码存储器失败 err_null = 空指针 @@ -39,6 +40,7 @@ err_perm_user = 用户权限降级失败 err_pwnam = 获取用户信息失败 + err_user_gid = 设置用户GID失败 err_user_init = 初始化用户失败 err_user_uid = 设置用户UID失败 diff --git a/src/config/Config.zig b/src/config/Config.zig index 558668b..874a75e 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -28,7 +28,6 @@ cmatrix_max_codepoint: u16 = 0x7B, colormix_col1: u32 = 0x00FF0000, colormix_col2: u32 = 0x000000FF, colormix_col3: u32 = 0x20000000, -console_dev: []const u8 = "/dev/console", default_input: Input = .login, doom_top_color: u32 = 0x00FF0000, doom_middle_color: u32 = 0x00FFFF00, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index 3360b71..8334d6c 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -18,6 +18,7 @@ err_domain: []const u8 = "invalid domain", err_empty_password: []const u8 = "empty password not allowed", err_envlist: []const u8 = "failed to get envlist", err_hostname: []const u8 = "failed to get hostname", +err_lock_state: []const u8 = "failed to get lock state", err_mlock: []const u8 = "failed to lock password memory", err_null: []const u8 = "null pointer", err_numlock: []const u8 = "failed to set numlock", @@ -43,6 +44,7 @@ err_perm_group: []const u8 = "failed to downgrade group permissions", err_perm_user: []const u8 = "failed to downgrade user permissions", err_pwnam: []const u8 = "failed to get user info", err_sleep: []const u8 = "failed to execute sleep command", +err_switch_tty: []const u8 = "failed to switch tty", err_tty_ctrl: []const u8 = "tty control transfer failed", err_user_gid: []const u8 = "failed to set user GID", err_user_init: []const u8 = "failed to initialize user", diff --git a/src/interop.zig b/src/interop.zig index 9fc5017..8540c21 100644 --- a/src/interop.zig +++ b/src/interop.zig @@ -36,7 +36,7 @@ pub const stdlib = @cImport({ pub const pwd = @cImport({ @cInclude("pwd.h"); // We include a FreeBSD-specific header here since login_cap.h references - // the passwd struct directly, so we can't import it separately' + // the passwd struct directly, so we can't import it separately if (builtin.os.tag == .freebsd) @cInclude("login_cap.h"); }); @@ -75,23 +75,21 @@ pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 { return buf[0..len]; } -pub fn switchTty(console_dev: []const u8, tty: u8) !void { - const fd = try std.posix.open(console_dev, .{ .ACCMODE = .WRONLY }, 0); - defer std.posix.close(fd); +pub fn switchTty(tty: u8) !void { + var status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_ACTIVATE, tty); + if (status != 0) return error.FailedToActivateTty; - _ = std.c.ioctl(fd, vt.VT_ACTIVATE, tty); - _ = std.c.ioctl(fd, vt.VT_WAITACTIVE, tty); + status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_WAITACTIVE, tty); + if (status != 0) return error.FailedToWaitForActiveTty; } -pub fn getLockState(console_dev: []const u8) !struct { +pub fn getLockState() !struct { numlock: bool, capslock: bool, } { - const fd = try std.posix.open(console_dev, .{ .ACCMODE = .RDONLY }, 0); - defer std.posix.close(fd); - var led: LedState = undefined; - _ = std.c.ioctl(fd, get_led_state, &led); + const status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led); + if (status != 0) return error.FailedToGetLockState; return .{ .numlock = (led & numlock_led) != 0, @@ -101,11 +99,12 @@ pub fn getLockState(console_dev: []const u8) !struct { pub fn setNumlock(val: bool) !void { var led: LedState = undefined; - _ = std.c.ioctl(0, get_led_state, &led); + var status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led); + if (status != 0) return error.FailedToGetNumlock; const numlock = (led & numlock_led) != 0; if (numlock != val) { - const status = std.c.ioctl(std.posix.STDIN_FILENO, set_led_state, led ^ numlock_led); + status = std.c.ioctl(std.posix.STDIN_FILENO, set_led_state, led ^ numlock_led); if (status != 0) return error.FailedToSetNumlock; } } diff --git a/src/main.zig b/src/main.zig index 17309df..9e51691 100644 --- a/src/main.zig +++ b/src/main.zig @@ -389,12 +389,10 @@ pub fn main() !void { var update = true; var resolution_changed = false; var auth_fails: u64 = 0; - var can_access_console_dev = true; - // Switch to selected TTY if possible - interop.switchTty(config.console_dev, config.tty) catch { - try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); - can_access_console_dev = false; + // Switch to selected TTY + interop.switchTty(config.tty) catch { + try info_line.addMessage(lang.err_switch_tty, config.error_bg, config.error_fg); }; while (run) { @@ -546,9 +544,9 @@ pub fn main() !void { buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height); } - if (can_access_console_dev) draw_lock_state: { - const lock_state = interop.getLockState(config.console_dev) catch { - try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); + draw_lock_state: { + const lock_state = interop.getLockState() catch { + try info_line.addMessage(lang.err_lock_state, config.error_bg, config.error_fg); break :draw_lock_state; }; From 1bcbb08202da5fcb5f0e3b6d58128e437dc95220 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Mon, 7 Jul 2025 21:46:49 +0200 Subject: [PATCH 03/15] Add config.console_dev as a removed property in migrator Signed-off-by: AnErrupTion --- src/config/migrator.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/migrator.zig b/src/config/migrator.zig index 2d2e34e..5f1269b 100644 --- a/src/config/migrator.zig +++ b/src/config/migrator.zig @@ -26,6 +26,7 @@ const removed_properties = [_][]const u8{ "term_restore_cursor_cmd", "x_cmd_setup", "wayland_cmd", + "console_dev", }; var temporary_allocator = std.heap.page_allocator; From ab23631e66b224976274262b850f2cfa9b102c69 Mon Sep 17 00:00:00 2001 From: Matthew Rothlisberger Date: Wed, 18 Jun 2025 09:59:20 -0400 Subject: [PATCH 04/15] reimplements PSX Doom fire animation; adds flame height control --- res/config.ini | 13 +++++++-- src/animations/Doom.zig | 64 ++++++++++++++++++++++++++++++----------- src/config/Config.zig | 2 ++ src/main.zig | 2 +- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/res/config.ini b/res/config.ini index 526c6f9..cabc070 100644 --- a/res/config.ini +++ b/res/config.ini @@ -104,13 +104,20 @@ colormix_col3 = 0x20000000 # Available inputs: info_line, session, login, password default_input = login -# DOOM animation top color (low intensity flames) +# DOOM animation fire height (1 thru 9) +doom_fire_height = 6 + +# DOOM animation use natural fire colors +# If false, below custom colors used +doom_default_colors = true + +# DOOM animation custom top color (low intensity flames) doom_top_color = 0x00FF0000 -# DOOM animation middle color (medium intensity flames) +# DOOM animation custom middle color (medium intensity flames) doom_middle_color = 0x00FFFF00 -# DOOM animation bottom color (high intensity flames) +# DOOM animation custom bottom color (high intensity flames) doom_bottom_color = 0x00FFFFFF # Error background color id diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index 41de5ee..fe90ad4 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -11,17 +11,31 @@ pub const STEPS = 12; allocator: Allocator, terminal_buffer: *TerminalBuffer, buffer: []u8, +height: u8, fire: [STEPS + 1]Cell, -pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32) !Doom { +pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fire_height: u8, default_colors: bool, top_color: u32, middle_color: u32, bottom_color: u32) !Doom { const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height); initBuffer(buffer, terminal_buffer.width); - return .{ - .allocator = allocator, - .terminal_buffer = terminal_buffer, - .buffer = buffer, - .fire = [_]Cell{ + const levels = if (default_colors) + [_]Cell{ + Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2591, 0x070707, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2592, 0x470F07, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2593, 0x771F07, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2588, 0xAF3F07, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2591, 0xC74707, 0xAF3F07), + Cell.init(0x2592, 0xDF5707, 0xAF3F07), + Cell.init(0x2593, 0xCF6F0F, 0xAF3F07), + Cell.init(0x2588, 0xC78F17, 0xAF3F07), + Cell.init(0x2591, 0xBF9F1F, 0xAF3F07), + Cell.init(0x2592, 0xBFAF2F, 0xAF3F07), + Cell.init(0x2593, 0xCFCF6F, 0xAF3F07), + Cell.init(0x2588, 0xFFFFFF, 0xAF3F07), + } + else + [_]Cell{ Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT), Cell.init(0x2591, top_color, TerminalBuffer.Color.DEFAULT), Cell.init(0x2592, top_color, TerminalBuffer.Color.DEFAULT), @@ -35,7 +49,14 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u Cell.init(0x2592, bottom_color, middle_color), Cell.init(0x2593, bottom_color, middle_color), Cell.init(0x2588, bottom_color, middle_color), - }, + }; + + return .{ + .allocator = allocator, + .terminal_buffer = terminal_buffer, + .buffer = buffer, + .height = @min(9, fire_height), + .fire = levels, }; } @@ -57,20 +78,31 @@ fn draw(self: *Doom) void { for (0..self.terminal_buffer.width) |x| { // We start from 1 so that we always have the topmost line when spreading fire for (1..self.terminal_buffer.height) |y| { - // Get current cell + // Get index of current cell in fire level buffer const from = y * self.terminal_buffer.width + x; - const cell_index = self.buffer[from]; - // Spread fire - const propagate = self.terminal_buffer.random.int(u1); - const to = from - self.terminal_buffer.width; // Get the line above + // Generate random datum for fire propagation + const random = (self.terminal_buffer.random.int(u16) % 10); - self.buffer[to] = if (cell_index > 0) cell_index - propagate else cell_index; + // Select semi-random target cell + const to = from -| self.terminal_buffer.width -| (random & 3) + 1; - // Put the cell - const cell = self.fire[cell_index]; - cell.put(x, y); + // Get fire level of current cell + const level_buf_from = self.buffer[from]; + + // Choose new fire level and store in level buffer + var level_buf_to = level_buf_from; + if (random >= self.height) level_buf_to -|= 1; + self.buffer[to] = @intCast(level_buf_to); + + // Send fire level to terminal buffer + const to_cell = self.fire[level_buf_to]; + to_cell.put(x, y); } + + // Draw bottom line (fire source) + const src_cell = self.fire[STEPS]; + src_cell.put(x, self.terminal_buffer.height - 1); } } diff --git a/src/config/Config.zig b/src/config/Config.zig index 874a75e..613c55a 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -29,6 +29,8 @@ colormix_col1: u32 = 0x00FF0000, colormix_col2: u32 = 0x000000FF, colormix_col3: u32 = 0x20000000, default_input: Input = .login, +doom_fire_height: u8 = 6, +doom_default_colors: bool = true, doom_top_color: u32 = 0x00FF0000, doom_middle_color: u32 = 0x00FFFF00, doom_bottom_color: u32 = 0x00FFFFFF, diff --git a/src/main.zig b/src/main.zig index 9e51691..b14a462 100644 --- a/src/main.zig +++ b/src/main.zig @@ -354,7 +354,7 @@ pub fn main() !void { animation = dummy.animation(); }, .doom => { - var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color); + var doom = try Doom.init(allocator, &buffer, config.doom_fire_height, config.doom_default_colors, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color); animation = doom.animation(); }, .matrix => { From 1c5686ea545fa19ca58a57f672ddcd91eee7ecb3 Mon Sep 17 00:00:00 2001 From: Matthew Rothlisberger Date: Fri, 20 Jun 2025 10:23:17 -0400 Subject: [PATCH 05/15] further improves fire behavior --- src/animations/Doom.zig | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index fe90ad4..f988495 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -24,14 +24,14 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fire_height: Cell.init(0x2591, 0x070707, TerminalBuffer.Color.DEFAULT), Cell.init(0x2592, 0x470F07, TerminalBuffer.Color.DEFAULT), Cell.init(0x2593, 0x771F07, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2588, 0xAF3F07, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2591, 0xC74707, 0xAF3F07), - Cell.init(0x2592, 0xDF5707, 0xAF3F07), - Cell.init(0x2593, 0xCF6F0F, 0xAF3F07), - Cell.init(0x2588, 0xC78F17, 0xAF3F07), - Cell.init(0x2591, 0xBF9F1F, 0xAF3F07), - Cell.init(0x2592, 0xBFAF2F, 0xAF3F07), - Cell.init(0x2593, 0xCFCF6F, 0xAF3F07), + Cell.init(0x2588, 0x9F2F07, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2591, 0xBF4707, 0xAF3F07), + Cell.init(0x2592, 0xC74707, 0xAF3F07), + Cell.init(0x2593, 0xDF5707, 0xAF3F07), + Cell.init(0x2588, 0xCF6F0F, 0xAF3F07), + Cell.init(0x2591, 0xC78F17, 0xAF3F07), + Cell.init(0x2592, 0xBF9F1F, 0xAF3F07), + Cell.init(0x2593, 0xBFAF2F, 0xAF3F07), Cell.init(0x2588, 0xFFFFFF, 0xAF3F07), } else @@ -82,10 +82,12 @@ fn draw(self: *Doom) void { const from = y * self.terminal_buffer.width + x; // Generate random datum for fire propagation - const random = (self.terminal_buffer.random.int(u16) % 10); + const random = (self.terminal_buffer.random.int(u8) % 10); // Select semi-random target cell const to = from -| self.terminal_buffer.width -| (random & 3) + 1; + const to_x = to % self.terminal_buffer.width; + const to_y = to / self.terminal_buffer.width; // Get fire level of current cell const level_buf_from = self.buffer[from]; @@ -95,9 +97,11 @@ fn draw(self: *Doom) void { if (random >= self.height) level_buf_to -|= 1; self.buffer[to] = @intCast(level_buf_to); - // Send fire level to terminal buffer + // Send known fire levels to terminal buffer + const from_cell = self.fire[level_buf_from]; const to_cell = self.fire[level_buf_to]; - to_cell.put(x, y); + from_cell.put(x, y); + to_cell.put(to_x, to_y); } // Draw bottom line (fire source) From 2a8e221e80240c690c7cdbf9b35cf4ca3736e5d7 Mon Sep 17 00:00:00 2001 From: Matthew Rothlisberger Date: Fri, 20 Jun 2025 22:22:29 -0400 Subject: [PATCH 06/15] improves fire gradient in true color mode --- src/animations/Doom.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index f988495..721eead 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -21,18 +21,18 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fire_height: const levels = if (default_colors) [_]Cell{ Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2591, 0x070707, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2592, 0x470F07, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2593, 0x771F07, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2588, 0x9F2F07, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2591, 0xBF4707, 0xAF3F07), - Cell.init(0x2592, 0xC74707, 0xAF3F07), - Cell.init(0x2593, 0xDF5707, 0xAF3F07), - Cell.init(0x2588, 0xCF6F0F, 0xAF3F07), - Cell.init(0x2591, 0xC78F17, 0xAF3F07), - Cell.init(0x2592, 0xBF9F1F, 0xAF3F07), - Cell.init(0x2593, 0xBFAF2F, 0xAF3F07), - Cell.init(0x2588, 0xFFFFFF, 0xAF3F07), + Cell.init(0x2591, 0x009F2707, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2592, 0x009F2707, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2593, 0x009F2707, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2588, 0x009F2707, TerminalBuffer.Color.DEFAULT), + Cell.init(0x2591, 0x00C78F17, 0x009F2707), + Cell.init(0x2592, 0x00C78F17, 0x009F2707), + Cell.init(0x2593, 0x00C78F17, 0x009F2707), + Cell.init(0x2588, 0x00C78F17, 0x009F2707), + Cell.init(0x2591, 0x00FFFFFF, 0x00C78F17), + Cell.init(0x2592, 0x00FFFFFF, 0x00C78F17), + Cell.init(0x2593, 0x00FFFFFF, 0x00C78F17), + Cell.init(0x2588, 0x00FFFFFF, 0x00C78F17), } else [_]Cell{ From 99f3ab96ba04554d300ba5db90d276e2913a3d81 Mon Sep 17 00:00:00 2001 From: Matthew Rothlisberger Date: Mon, 7 Jul 2025 21:01:36 -0400 Subject: [PATCH 07/15] changes fire parameters --- res/config.ini | 9 ++++----- src/animations/Doom.zig | 38 +++++++++++++------------------------- src/config/Config.zig | 2 +- src/main.zig | 2 +- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/res/config.ini b/res/config.ini index cabc070..492319c 100644 --- a/res/config.ini +++ b/res/config.ini @@ -107,15 +107,14 @@ default_input = login # DOOM animation fire height (1 thru 9) doom_fire_height = 6 -# DOOM animation use natural fire colors -# If false, below custom colors used -doom_default_colors = true +# DOOM animation fire spread (0 thru 4) +doom_fire_spread = 2 # DOOM animation custom top color (low intensity flames) -doom_top_color = 0x00FF0000 +doom_top_color = 0x009F2707 # DOOM animation custom middle color (medium intensity flames) -doom_middle_color = 0x00FFFF00 +doom_middle_color = 0x00C78F17 # DOOM animation custom bottom color (high intensity flames) doom_bottom_color = 0x00FFFFFF diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index 721eead..76850fa 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -7,34 +7,21 @@ const TerminalBuffer = @import("../tui/TerminalBuffer.zig"); const Doom = @This(); pub const STEPS = 12; +pub const HEIGHT_MAX = 9; +pub const SPREAD_MAX = 4; allocator: Allocator, terminal_buffer: *TerminalBuffer, buffer: []u8, height: u8, +spread: u8, fire: [STEPS + 1]Cell, -pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fire_height: u8, default_colors: bool, top_color: u32, middle_color: u32, bottom_color: u32) !Doom { +pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32, fire_height: u8, fire_spread: u8) !Doom { const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height); initBuffer(buffer, terminal_buffer.width); - const levels = if (default_colors) - [_]Cell{ - Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2591, 0x009F2707, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2592, 0x009F2707, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2593, 0x009F2707, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2588, 0x009F2707, TerminalBuffer.Color.DEFAULT), - Cell.init(0x2591, 0x00C78F17, 0x009F2707), - Cell.init(0x2592, 0x00C78F17, 0x009F2707), - Cell.init(0x2593, 0x00C78F17, 0x009F2707), - Cell.init(0x2588, 0x00C78F17, 0x009F2707), - Cell.init(0x2591, 0x00FFFFFF, 0x00C78F17), - Cell.init(0x2592, 0x00FFFFFF, 0x00C78F17), - Cell.init(0x2593, 0x00FFFFFF, 0x00C78F17), - Cell.init(0x2588, 0x00FFFFFF, 0x00C78F17), - } - else + const levels = [_]Cell{ Cell.init(' ', TerminalBuffer.Color.DEFAULT, TerminalBuffer.Color.DEFAULT), Cell.init(0x2591, top_color, TerminalBuffer.Color.DEFAULT), @@ -55,7 +42,8 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fire_height: .allocator = allocator, .terminal_buffer = terminal_buffer, .buffer = buffer, - .height = @min(9, fire_height), + .height = @min(HEIGHT_MAX, fire_height), + .spread = @min(SPREAD_MAX, fire_spread), .fire = levels, }; } @@ -81,11 +69,12 @@ fn draw(self: *Doom) void { // Get index of current cell in fire level buffer const from = y * self.terminal_buffer.width + x; - // Generate random datum for fire propagation - const random = (self.terminal_buffer.random.int(u8) % 10); + // Generate random data for fire propagation + const rand_loss = self.terminal_buffer.random.intRangeAtMost(u8, 0, HEIGHT_MAX); + const rand_spread = self.terminal_buffer.random.intRangeAtMost(u8, 0, self.spread * 2); // Select semi-random target cell - const to = from -| self.terminal_buffer.width -| (random & 3) + 1; + const to = from -| self.terminal_buffer.width + self.spread -| rand_spread; const to_x = to % self.terminal_buffer.width; const to_y = to / self.terminal_buffer.width; @@ -93,9 +82,8 @@ fn draw(self: *Doom) void { const level_buf_from = self.buffer[from]; // Choose new fire level and store in level buffer - var level_buf_to = level_buf_from; - if (random >= self.height) level_buf_to -|= 1; - self.buffer[to] = @intCast(level_buf_to); + const level_buf_to = level_buf_from -| @intFromBool(rand_loss >= self.height); + self.buffer[to] = level_buf_to; // Send known fire levels to terminal buffer const from_cell = self.fire[level_buf_from]; diff --git a/src/config/Config.zig b/src/config/Config.zig index 613c55a..8b020f0 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -30,7 +30,7 @@ colormix_col2: u32 = 0x000000FF, colormix_col3: u32 = 0x20000000, default_input: Input = .login, doom_fire_height: u8 = 6, -doom_default_colors: bool = true, +doom_fire_spread: u8 = 2, doom_top_color: u32 = 0x00FF0000, doom_middle_color: u32 = 0x00FFFF00, doom_bottom_color: u32 = 0x00FFFFFF, diff --git a/src/main.zig b/src/main.zig index b14a462..fb42923 100644 --- a/src/main.zig +++ b/src/main.zig @@ -354,7 +354,7 @@ pub fn main() !void { animation = dummy.animation(); }, .doom => { - var doom = try Doom.init(allocator, &buffer, config.doom_fire_height, config.doom_default_colors, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color); + var doom = try Doom.init(allocator, &buffer, config.doom_top_color, config.doom_middle_color, config.doom_bottom_color, config.doom_fire_height, config.doom_fire_spread); animation = doom.animation(); }, .matrix => { From ce17d346e873c67161e2fe3aa418dfb7c967a652 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 8 Jul 2025 12:45:47 +0200 Subject: [PATCH 08/15] Remove lang.err_console_dev Signed-off-by: AnErrupTion --- res/lang/ar.ini | 1 - res/lang/cat.ini | 1 - res/lang/cs.ini | 1 - res/lang/de.ini | 1 - res/lang/en.ini | 1 - res/lang/es.ini | 1 - res/lang/fr.ini | 1 - res/lang/it.ini | 1 - res/lang/pl.ini | 1 - res/lang/pt.ini | 1 - res/lang/pt_BR.ini | 1 - res/lang/ro.ini | 1 - res/lang/ru.ini | 1 - res/lang/sr.ini | 1 - res/lang/sv.ini | 1 - res/lang/tr.ini | 1 - res/lang/uk.ini | 1 - res/lang/zh_CN.ini | 1 - src/config/Lang.zig | 1 - 19 files changed, 19 deletions(-) diff --git a/res/lang/ar.ini b/res/lang/ar.ini index 95644b7..ba1871f 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -7,7 +7,6 @@ err_bounds = out-of-bounds index err_brightness_change = فشل في تغيير سطوع الشاشة err_chdir = فشل في فتح مجلد المنزل err_config = فشل في تفسير ملف الإعدادات -err_console_dev = فشل في الوصول إلى جهاز وحدة التحكم err_dgn_oob = رسالة سجل (Log) err_domain = اسم نطاق غير صالح err_empty_password = لا يُسمح بكلمة مرور فارغة diff --git a/res/lang/cat.ini b/res/lang/cat.ini index f1adf55..40d877f 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -7,7 +7,6 @@ err_bounds = índex fora de límits err_brightness_change = error en canviar la brillantor err_chdir = error en obrir la carpeta home -err_console_dev = error en accedir a la consola err_dgn_oob = missatge de registre err_domain = domini invàlid diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 05c2e4d..9052663 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -7,7 +7,6 @@ err_bounds = index je mimo hranice pole err_chdir = nelze otevřít domovský adresář -err_console_dev = chyba při přístupu do konzole err_dgn_oob = zpráva protokolu err_domain = neplatná doména diff --git a/res/lang/de.ini b/res/lang/de.ini index 7277510..3f76cc6 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -7,7 +7,6 @@ err_bounds = Index ausserhalb des Bereichs err_brightness_change = Helligkeitsänderung fehlgeschlagen err_chdir = Fehler beim Oeffnen des Home-Ordners err_config = Fehler beim Verarbeiten der Konfigurationsdatei -err_console_dev = Zugriff auf die Konsole fehlgeschlagen err_dgn_oob = Diagnose-Nachricht err_domain = Ungueltige Domain err_empty_password = Leeres Passwort nicht zugelassen diff --git a/res/lang/en.ini b/res/lang/en.ini index 3e1b7d7..423020e 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -7,7 +7,6 @@ err_bounds = out-of-bounds index err_brightness_change = failed to change brightness err_chdir = failed to open home folder err_config = unable to parse config file -err_console_dev = failed to access console err_dgn_oob = log message err_domain = invalid domain err_empty_password = empty password not allowed diff --git a/res/lang/es.ini b/res/lang/es.ini index f9780ec..5ec48af 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -7,7 +7,6 @@ err_bounds = índice fuera de límites err_chdir = error al abrir la carpeta home -err_console_dev = error al acceder a la consola err_dgn_oob = mensaje de registro err_domain = dominio inválido diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 3d73ef1..bda901b 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -7,7 +7,6 @@ err_bounds = indice hors-limite err_brightness_change = échec du changement de luminosité err_chdir = échec de l'ouverture du répertoire home err_config = échec de lecture du fichier de configuration -err_console_dev = échec d'accès à la console err_dgn_oob = message err_domain = domaine invalide err_empty_password = mot de passe vide non autorisé diff --git a/res/lang/it.ini b/res/lang/it.ini index 1781c1e..e7e0d17 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -7,7 +7,6 @@ err_bounds = indice fuori limite err_chdir = impossibile aprire home directory -err_console_dev = impossibile aprire console err_dgn_oob = messaggio log err_domain = dominio non valido diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 4995ef0..eeb43e4 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -7,7 +7,6 @@ err_bounds = indeks poza zakresem err_brightness_change = nie udało się zmienić jasności err_chdir = nie udało się otworzyć folderu domowego err_config = nie można przetworzyć pliku konfiguracyjnego -err_console_dev = nie udało się uzyskać dostępu do konsoli err_dgn_oob = wiadomość loga err_domain = niepoprawna domena err_empty_password = puste hasło jest niedozwolone diff --git a/res/lang/pt.ini b/res/lang/pt.ini index a4f0fa8..97afee3 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -7,7 +7,6 @@ err_bounds = índice fora de limites err_chdir = erro ao abrir a pasta home -err_console_dev = erro ao aceder à consola err_dgn_oob = mensagem de registo err_domain = domínio inválido diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index 2d2bb89..c89b8aa 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -7,7 +7,6 @@ err_bounds = índice fora de limites err_chdir = não foi possível abrir o diretório home -err_console_dev = não foi possível acessar o console err_dgn_oob = mensagem de log err_domain = domínio inválido diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 9f50c8f..fe7ee38 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -7,7 +7,6 @@ capslock = capslock -err_console_dev = nu s-a putut accesa consola diff --git a/res/lang/ru.ini b/res/lang/ru.ini index fe7315e..25dc553 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -7,7 +7,6 @@ err_bounds = за пределами индекса err_chdir = не удалось открыть домашнюю папку -err_console_dev = не удалось получить доступ к консоли err_dgn_oob = отладочное сообщение (log) err_domain = неверный домен diff --git a/res/lang/sr.ini b/res/lang/sr.ini index 9251011..a24e4b2 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -7,7 +7,6 @@ err_bounds = izvan granica indeksa err_chdir = neuspijesno otvaranje home foldera -err_console_dev = neuspijesno pristupanje konzoli err_dgn_oob = log poruka err_domain = nevazeci domen diff --git a/res/lang/sv.ini b/res/lang/sv.ini index aad2244..668f431 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -7,7 +7,6 @@ err_bounds = utanför banan index err_chdir = misslyckades att öppna hemkatalog -err_console_dev = misslyckades att komma åt konsol err_dgn_oob = loggmeddelande err_domain = okänd domän diff --git a/res/lang/tr.ini b/res/lang/tr.ini index 6ad0e94..ce69231 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -7,7 +7,6 @@ err_bounds = sinirlarin disinda dizin err_chdir = ev klasoru acilamadi -err_console_dev = konsola erisilemedi err_dgn_oob = log mesaji err_domain = gecersiz etki alani diff --git a/res/lang/uk.ini b/res/lang/uk.ini index df925df..af8f247 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -7,7 +7,6 @@ err_bounds = поза межами індексу err_chdir = не вдалося відкрити домашній каталог -err_console_dev = невдалий доступ до консолі err_dgn_oob = повідомлення журналу (log) err_domain = недійсний домен diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index d1ccc57..ecc19be 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -7,7 +7,6 @@ err_bounds = 索引越界 err_chdir = 无法打开home文件夹 -err_console_dev = 无法访问控制台 err_dgn_oob = 日志消息 err_domain = 无效的域 diff --git a/src/config/Lang.zig b/src/config/Lang.zig index 8334d6c..f673090 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -12,7 +12,6 @@ err_bounds: []const u8 = "out-of-bounds index", err_brightness_change: []const u8 = "failed to change brightness", err_chdir: []const u8 = "failed to open home folder", err_config: []const u8 = "unable to parse config file", -err_console_dev: []const u8 = "failed to access console", err_dgn_oob: []const u8 = "log message", err_domain: []const u8 = "invalid domain", err_empty_password: []const u8 = "empty password not allowed", From 918e9ad5acf13a9dc7df8c3253c99290f8c719e3 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 8 Jul 2025 12:47:24 +0200 Subject: [PATCH 09/15] Only show lang.err_lock_state once Signed-off-by: AnErrupTion --- src/main.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index fb42923..aed67e5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -102,6 +102,7 @@ pub fn main() !void { var lang: Lang = undefined; var save: Save = undefined; var config_load_failed = false; + var can_get_lock_state = true; if (res.args.help != 0) { try clap.help(stderr, clap.Help, ¶ms, .{}); @@ -544,9 +545,10 @@ pub fn main() !void { buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height); } - draw_lock_state: { + if (can_get_lock_state) draw_lock_state: { const lock_state = interop.getLockState() catch { try info_line.addMessage(lang.err_lock_state, config.error_bg, config.error_fg); + can_get_lock_state = false; break :draw_lock_state; }; From 48e5369f564db06ff2eebd6bbf5ceaf8dc55373a Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 8 Jul 2025 16:32:20 +0200 Subject: [PATCH 10/15] Fix character width calculation Signed-off-by: AnErrupTion --- src/tui/TerminalBuffer.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index ca072f6..7d608c1 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -207,9 +207,9 @@ pub fn drawColorLabel(text: []const u8, x: usize, y: usize, fg: u32, bg: u32) vo const utf8view = std.unicode.Utf8View.init(text) catch return; var utf8 = utf8view.iterator(); - var i = x; - while (utf8.nextCodepoint()) |codepoint| : (i += 1) { - _ = termbox.tb_set_cell(@intCast(i), yc, codepoint, fg, bg); + var i: c_int = @intCast(x); + while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) { + _ = termbox.tb_set_cell(i, yc, codepoint, fg, bg); } } @@ -218,10 +218,10 @@ pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: us const utf8view = std.unicode.Utf8View.init(text) catch return; var utf8 = utf8view.iterator(); - var i: usize = 0; - while (utf8.nextCodepoint()) |codepoint| : (i += 1) { + var i: c_int = @intCast(x); + while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) { if (i >= max_length) break; - _ = termbox.tb_set_cell(@intCast(i + x), yc, codepoint, self.fg, self.bg); + _ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg); } } @@ -235,7 +235,8 @@ pub fn drawCharMultiple(self: TerminalBuffer, char: u32, x: usize, y: usize, len pub fn strWidth(str: []const u8) !u8 { const utf8view = try std.unicode.Utf8View.init(str); var utf8 = utf8view.iterator(); - var i: u8 = 0; - while (utf8.nextCodepoint()) |_| i += 1; - return i; + var i: c_int = 0; + while (utf8.nextCodepoint()) |codepoint| i += termbox.tb_wcwidth(codepoint); + + return @intCast(i); } From 04920e1b1ba5c50cbc3f69227b04867dd60ece72 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 8 Jul 2025 22:39:14 +0200 Subject: [PATCH 11/15] Implement custom session support (fixes #757) Signed-off-by: AnErrupTion --- build.zig | 14 ++++++++++++++ res/config.ini | 9 +++++++-- res/custom-sessions/README | 22 ++++++++++++++++++++++ res/lang/ar.ini | 1 + res/lang/cat.ini | 1 + res/lang/cs.ini | 1 + res/lang/de.ini | 1 + res/lang/en.ini | 1 + res/lang/es.ini | 1 + res/lang/fr.ini | 1 + res/lang/it.ini | 1 + res/lang/pl.ini | 1 + res/lang/pt.ini | 1 + res/lang/pt_BR.ini | 1 + res/lang/ro.ini | 1 + res/lang/ru.ini | 1 + res/lang/sr.ini | 1 + res/lang/sv.ini | 1 + res/lang/tr.ini | 1 + res/lang/uk.ini | 1 + res/lang/zh_CN.ini | 1 + src/Environment.zig | 2 ++ src/auth.zig | 18 ++++++++++++++++++ src/config/Config.zig | 1 + src/config/Lang.zig | 1 + src/enums.zig | 1 + src/main.zig | 38 ++++++++++++++++++++++---------------- 27 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 res/custom-sessions/README diff --git a/build.zig b/build.zig index 80de3e8..128357c 100644 --- a/build.zig +++ b/build.zig @@ -135,6 +135,12 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory}); }; + const ly_custom_sessions_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" }); + + std.fs.cwd().makePath(ly_custom_sessions_directory) catch { + std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory}); + }; + const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" }); std.fs.cwd().makePath(ly_lang_path) catch { std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path}); @@ -167,6 +173,14 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 }); } + { + var custom_sessions_dir = std.fs.cwd().openDir(ly_custom_sessions_directory, .{}) catch unreachable; + defer custom_sessions_dir.close(); + + const patched_readme = try patchFile(allocator, "res/custom-sessions/README", patch_map); + try installText(patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{}); + } + { var lang_dir = std.fs.cwd().openDir(ly_lang_path, .{}) catch unreachable; defer lang_dir.close(); diff --git a/res/config.ini b/res/config.ini index 492319c..8b4a4f9 100644 --- a/res/config.ini +++ b/res/config.ini @@ -100,6 +100,11 @@ colormix_col2 = 0x000000FF # Color mixing animation third color id colormix_col3 = 0x20000000 +# Custom sessions directory +# You can specify multiple directories, +# e.g. $CONFIG_DIRECTORY/ly/custom-sessions:$PREFIX_DIRECTORY/share/custom-sessions +waylandsessions = $CONFIG_DIRECTORY/share/custom-sessions + # Input box active by default on startup # Available inputs: info_line, session, login, password default_input = login @@ -251,7 +256,7 @@ vi_mode = false # Wayland desktop environments # You can specify multiple directories, -# e.g. /usr/share/wayland-sessions:/usr/local/share/wayland-sessions +# e.g. $PREFIX_DIRECTORY/share/wayland-sessions:$PREFIX_DIRECTORY/local/share/wayland-sessions waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions # Xorg server command @@ -266,5 +271,5 @@ xinitrc = ~/.xinitrc # Xorg desktop environments # You can specify multiple directories, -# e.g. /usr/share/xsessions:/usr/local/share/xsessions +# e.g. $PREFIX_DIRECTORY/share/xsessions:$PREFIX_DIRECTORY/local/share/xsessions xsessions = $PREFIX_DIRECTORY/share/xsessions diff --git a/res/custom-sessions/README b/res/custom-sessions/README new file mode 100644 index 0000000..23c1ed1 --- /dev/null +++ b/res/custom-sessions/README @@ -0,0 +1,22 @@ +A custom session is just a desktop entry file, like for X11 and Wayland +sessions. For example: + +[Desktop Entry] +Name=Fish shell +Exec=$PREFIX_DIRECTORY/bin/fish +DesktopNames=null +Terminal=true + +The DesktopNames value is optional and sets the XDG_SESSION_DESKTOP and +XDG_CURRENT_DESKTOP environment variables. If equal to null or if not present, +XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP will not be set. Otherwise, the +syntax is the same as described in the Freedesktop Desktop Entry Specification. + +The Terminal value specifies if standard output and standard error should be +redirected to the session log file found in Ly's configuration file. If set to +true, Ly will consider the program is going to run in a TTY, and thus will not +redirect standard output & error. + +Finally, do note that the XDG_SESSION_TYPE environment variable is set to +"unspecified" (without quotes), which is behavior that at least systemd +recognizes (see pam_systemd's man page) diff --git a/res/lang/ar.ini b/res/lang/ar.ini index ba1871f..9c2f9be 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -2,6 +2,7 @@ authenticating = جاري المصادقة... brightness_down = خفض السطوع brightness_up = رفع السطوع capslock = capslock + err_alloc = فشل في تخصيص الذاكرة err_bounds = out-of-bounds index err_brightness_change = فشل في تغيير سطوع الشاشة diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 40d877f..7aa4261 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -2,6 +2,7 @@ authenticating = autenticant... brightness_down = abaixar brillantor brightness_up = apujar brillantor capslock = Bloq Majús + err_alloc = assignació de memòria fallida err_bounds = índex fora de límits err_brightness_change = error en canviar la brillantor diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 9052663..cca457a 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = alokace paměti selhala err_bounds = index je mimo hranice pole diff --git a/res/lang/de.ini b/res/lang/de.ini index 3f76cc6..32e00a6 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -2,6 +2,7 @@ authenticating = authentifizieren... brightness_down = Helligkeit- brightness_up = Helligkeit+ capslock = Feststelltaste + err_alloc = Speicherzuweisung fehlgeschlagen err_bounds = Index ausserhalb des Bereichs err_brightness_change = Helligkeitsänderung fehlgeschlagen diff --git a/res/lang/en.ini b/res/lang/en.ini index 423020e..d7076db 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -2,6 +2,7 @@ authenticating = authenticating... brightness_down = decrease brightness brightness_up = increase brightness capslock = capslock +custom = custom err_alloc = failed memory allocation err_bounds = out-of-bounds index err_brightness_change = failed to change brightness diff --git a/res/lang/es.ini b/res/lang/es.ini index 5ec48af..fe3e6d9 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -2,6 +2,7 @@ authenticating = autenticando... brightness_down = bajar brillo brightness_up = subir brillo capslock = Bloq Mayús + err_alloc = asignación de memoria fallida err_bounds = índice fuera de límites diff --git a/res/lang/fr.ini b/res/lang/fr.ini index bda901b..6d46a1d 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -2,6 +2,7 @@ authenticating = authentification... brightness_down = diminuer la luminosité brightness_up = augmenter la luminosité capslock = verr.maj +custom = customisé err_alloc = échec d'allocation mémoire err_bounds = indice hors-limite err_brightness_change = échec du changement de luminosité diff --git a/res/lang/it.ini b/res/lang/it.ini index e7e0d17..4bff87e 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = impossibile allocare memoria err_bounds = indice fuori limite diff --git a/res/lang/pl.ini b/res/lang/pl.ini index eeb43e4..cfc5722 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -2,6 +2,7 @@ authenticating = uwierzytelnianie... brightness_down = zmniejsz jasność brightness_up = zwiększ jasność capslock = capslock + err_alloc = nieudana alokacja pamięci err_bounds = indeks poza zakresem err_brightness_change = nie udało się zmienić jasności diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 97afee3..df1db13 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = erro na atribuição de memória err_bounds = índice fora de limites diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index c89b8aa..a7cee7b 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -2,6 +2,7 @@ capslock = caixa alta + err_alloc = alocação de memória malsucedida err_bounds = índice fora de limites diff --git a/res/lang/ro.ini b/res/lang/ro.ini index fe7ee38..4289b7f 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -17,6 +17,7 @@ capslock = capslock + err_pam_abort = tranzacţie pam anulată err_pam_acct_expired = cont expirat err_pam_auth = eroare de autentificare diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 25dc553..b9cfb1a 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = не удалось выделить память err_bounds = за пределами индекса diff --git a/res/lang/sr.ini b/res/lang/sr.ini index a24e4b2..61ec4c0 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = neuspijesna alokacija memorije err_bounds = izvan granica indeksa diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 668f431..9ccd0f3 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = misslyckad minnesallokering err_bounds = utanför banan index diff --git a/res/lang/tr.ini b/res/lang/tr.ini index ce69231..d553495 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = basarisiz bellek ayirma err_bounds = sinirlarin disinda dizin diff --git a/res/lang/uk.ini b/res/lang/uk.ini index af8f247..ffae75c 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -2,6 +2,7 @@ capslock = capslock + err_alloc = невдале виділення пам'яті err_bounds = поза межами індексу diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index ecc19be..6f7ba41 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -2,6 +2,7 @@ capslock = 大写锁定 + err_alloc = 内存分配失败 err_bounds = 索引越界 diff --git a/src/Environment.zig b/src/Environment.zig index b21d451..47d4d7f 100644 --- a/src/Environment.zig +++ b/src/Environment.zig @@ -8,6 +8,7 @@ pub const DesktopEntry = struct { Exec: []const u8 = "", Name: [:0]const u8 = "", DesktopNames: ?[:0]u8 = null, + Terminal: ?bool = null, }; pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} }; @@ -19,3 +20,4 @@ xdg_desktop_names: ?[:0]const u8 = null, cmd: []const u8 = "", specifier: []const u8 = "", display_server: DisplayServer = .wayland, +is_terminal: bool = false, diff --git a/src/auth.zig b/src/auth.zig index aa853c9..0b468da 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -180,6 +180,7 @@ fn startSession( const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty}); try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt); }, + .custom => try executeCustomCmd(pwd.pw_shell.?, options, current_environment.is_terminal, current_environment.cmd), } } @@ -201,6 +202,7 @@ fn setXdgEnv(tty_str: [:0]u8, environment: Environment) !void { .wayland => "wayland", .shell => "tty", .xinitrc, .x11 => "x11", + .custom => "unspecified", }, 0); // The "/run/user/%d" directory is not available on FreeBSD. It is much @@ -462,6 +464,22 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio _ = std.c.waitpid(x_pid, &status, 0); } +fn executeCustomCmd(shell: [*:0]const u8, options: AuthOptions, is_terminal: bool, exec_cmd: []const u8) !void { + var maybe_log_file: ?std.fs.File = null; + if (!is_terminal) { + // For custom desktop entries, the "Terminal" value here determines if + // we redirect standard output & error or not. That is, we redirect only + // if it's equal to false (so if it's not running in a TTY). + maybe_log_file = try redirectStandardStreams(options.session_log, true); + } + defer if (maybe_log_file) |log_file| log_file.close(); + + var cmd_buffer: [1024]u8 = undefined; + const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", exec_cmd }); + const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; + return std.posix.execveZ(shell, &args, std.c.environ); +} + fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File { const log_file = if (create) (try std.fs.cwd().createFile(session_log, .{ .mode = 0o666 })) else (try std.fs.cwd().openFile(session_log, .{ .mode = .read_write })); diff --git a/src/config/Config.zig b/src/config/Config.zig index 8b020f0..4a95b40 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -28,6 +28,7 @@ cmatrix_max_codepoint: u16 = 0x7B, colormix_col1: u32 = 0x00FF0000, colormix_col2: u32 = 0x000000FF, colormix_col3: u32 = 0x20000000, +custom_sessions: []const u8 = build_options.config_directory ++ "/ly/custom-sessions", default_input: Input = .login, doom_fire_height: u8 = 6, doom_fire_spread: u8 = 2, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index f673090..3761fa7 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -7,6 +7,7 @@ authenticating: []const u8 = "authenticating...", brightness_down: []const u8 = "decrease brightness", brightness_up: []const u8 = "increase brightness", capslock: []const u8 = "capslock", +custom: []const u8 = "custom", err_alloc: []const u8 = "failed memory allocation", err_bounds: []const u8 = "out-of-bounds index", err_brightness_change: []const u8 = "failed to change brightness", diff --git a/src/enums.zig b/src/enums.zig index a70f17c..82c478d 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -11,6 +11,7 @@ pub const DisplayServer = enum { shell, xinitrc, x11, + custom, }; pub const Input = enum { diff --git a/src/main.zig b/src/main.zig index aed67e5..b52e779 100644 --- a/src/main.zig +++ b/src/main.zig @@ -290,10 +290,12 @@ pub fn main() !void { try info_line.addMessage(hostname, config.bg, config.fg); } + // Crawl session directories (Wayland, X11 and custom respectively) var wayland_session_dirs = std.mem.splitScalar(u8, config.waylandsessions, ':'); while (wayland_session_dirs.next()) |dir| { try crawl(&session, lang, dir, .wayland); } + if (build_options.enable_x11_support) { var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':'); while (x_session_dirs.next()) |dir| { @@ -301,6 +303,11 @@ pub fn main() !void { } } + var custom_session_dirs = std.mem.splitScalar(u8, config.custom_sessions, ':'); + while (custom_session_dirs.next()) |dir| { + try crawl(&session, lang, dir, .custom); + } + var login = Text.init(allocator, &buffer, false, null); defer login.deinit(); @@ -864,11 +871,7 @@ fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplaySer .xdg_session_desktop = null, .xdg_desktop_names = null, .cmd = exec orelse "", - .specifier = switch (display_server) { - .wayland => lang.wayland, - .x11 => lang.x11, - else => lang.other, - }, + .specifier = lang.other, .display_server = display_server, }); } @@ -890,40 +893,43 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa }); errdefer entry_ini.deinit(); - var xdg_session_desktop: []const u8 = undefined; + var maybe_xdg_session_desktop: ?[]const u8 = null; const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames; if (maybe_desktop_names) |desktop_names| { - xdg_session_desktop = std.mem.sliceTo(desktop_names, ';'); - } else { - // if DesktopNames is empty, we'll take the name of the session file - xdg_session_desktop = std.fs.path.stem(item.name); + maybe_xdg_session_desktop = std.mem.sliceTo(desktop_names, ';'); + } else if (display_server != .custom) { + // If DesktopNames is empty, and this isn't a custom session entry, + // we'll take the name of the session file + maybe_xdg_session_desktop = std.fs.path.stem(item.name); } // Prepare the XDG_CURRENT_DESKTOP environment variable here const entry = entry_ini.data.@"Desktop Entry"; - var xdg_desktop_names: ?[:0]const u8 = null; + var maybe_xdg_desktop_names: ?[:0]const u8 = null; if (entry.DesktopNames) |desktop_names| { for (desktop_names) |*c| { if (c.* == ';') c.* = ':'; } - xdg_desktop_names = desktop_names; + maybe_xdg_desktop_names = desktop_names; } - const session_desktop = try session.label.allocator.dupeZ(u8, xdg_session_desktop); - errdefer session.label.allocator.free(session_desktop); + const maybe_session_desktop = if (maybe_xdg_session_desktop) |xdg_session_desktop| try session.label.allocator.dupeZ(u8, xdg_session_desktop) else null; + errdefer if (maybe_session_desktop) |session_desktop| session.label.allocator.free(session_desktop); try session.addEnvironment(.{ .entry_ini = entry_ini, .name = entry.Name, - .xdg_session_desktop = session_desktop, - .xdg_desktop_names = xdg_desktop_names, + .xdg_session_desktop = maybe_session_desktop, + .xdg_desktop_names = maybe_xdg_desktop_names, .cmd = entry.Exec, .specifier = switch (display_server) { .wayland => lang.wayland, .x11 => lang.x11, + .custom => lang.custom, else => lang.other, }, .display_server = display_server, + .is_terminal = entry.Terminal orelse false, }); } } From 97efac0cd11cf44461089724408f9b3405c66bef Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Wed, 9 Jul 2025 21:38:45 +0200 Subject: [PATCH 12/15] Add Matrix space link to README Signed-off-by: AnErrupTion --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 57ca447..c81d6ef 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,8 @@ Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD. +Join us on Matrix over at [#ly:envs.net](https://matrix.to/#/#ly:envs.net)! + **Note**: Development happens on [Codeberg](https://codeberg.org/AnErrupTion/ly) with a mirror on [GitHub](https://github.com/fairyglade/ly). ## Dependencies From 1d4e32ba82829038b58c0b01904eb5ab6ad331df Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Thu, 10 Jul 2025 10:06:19 +0200 Subject: [PATCH 13/15] List all users in the system (fixes #373) Signed-off-by: AnErrupTion --- res/config.ini | 3 + res/lang/ar.ini | 1 + res/lang/cat.ini | 1 + res/lang/cs.ini | 1 + res/lang/de.ini | 1 + res/lang/en.ini | 1 + res/lang/es.ini | 1 + res/lang/fr.ini | 1 + res/lang/it.ini | 1 + res/lang/pl.ini | 1 + res/lang/pt.ini | 1 + res/lang/pt_BR.ini | 1 + res/lang/ro.ini | 1 + res/lang/ru.ini | 1 + res/lang/sr.ini | 1 + res/lang/sv.ini | 1 + res/lang/tr.ini | 1 + res/lang/uk.ini | 1 + res/lang/zh_CN.ini | 1 + src/UidRange.zig | 6 ++ src/config/Config.zig | 1 + src/config/Lang.zig | 1 + src/main.zig | 124 ++++++++++++++++++++++++++------ src/tui/components/UserList.zig | 45 ++++++++++++ 24 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 src/UidRange.zig create mode 100644 src/tui/components/UserList.zig diff --git a/res/config.ini b/res/config.ini index 8b4a4f9..866b6b2 100644 --- a/res/config.ini +++ b/res/config.ini @@ -184,6 +184,9 @@ load = true # You can also set environment variables in there, they'll persist until logout login_cmd = null +# Path for login.defs file (used for listing all local users on the system) +login_defs_path = /etc/login.defs + # Command executed when logging out # If null, no command will be executed # Important: the session will already be terminated when this command is executed, so diff --git a/res/lang/ar.ini b/res/lang/ar.ini index 9c2f9be..b06f253 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -41,6 +41,7 @@ err_pwnam = فشل في جلب معلومات المستخدم err_sleep = فشل في تنفيذ أمر sleep err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY) + err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم err_user_init = فشل في تهيئة بيانات المستخدم err_user_uid = فشل في تعيين معرّف المستخدم (UID) diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 7aa4261..d0ce33e 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -41,6 +41,7 @@ err_pwnam = error en obtenir la informació de l'usuari + err_user_gid = error en establir el GID de l'usuari err_user_init = error en inicialitzar usuari err_user_uid = error en establir l'UID de l'usuari diff --git a/res/lang/cs.ini b/res/lang/cs.ini index cca457a..7c87ea6 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -41,6 +41,7 @@ err_pwnam = nelze získat informace o uživateli + err_user_gid = nastavení GID uživatele selhalo err_user_init = inicializace uživatele selhala err_user_uid = nastavení UID uživateli selhalo diff --git a/res/lang/de.ini b/res/lang/de.ini index 32e00a6..44bbc53 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -41,6 +41,7 @@ err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen err_sleep = Sleep-Befehl fehlgeschlagen err_tty_ctrl = Fehler bei der TTY-Uebergabe + err_user_gid = Fehler beim Setzen der Gruppen-ID err_user_init = Nutzer-Initialisierung fehlgeschlagen err_user_uid = Setzen der Benutzer-ID fehlgeschlagen diff --git a/res/lang/en.ini b/res/lang/en.ini index d7076db..9554658 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -41,6 +41,7 @@ err_pwnam = failed to get user info err_sleep = failed to execute sleep command err_switch_tty = failed to switch tty err_tty_ctrl = tty control transfer failed +err_no_users = no users found err_user_gid = failed to set user GID err_user_init = failed to initialize user err_user_uid = failed to set user UID diff --git a/res/lang/es.ini b/res/lang/es.ini index fe3e6d9..2bd51a7 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -41,6 +41,7 @@ err_pwnam = error al obtener la información del usuario + err_user_gid = error al establecer el GID del usuario err_user_init = error al inicializar usuario err_user_uid = error al establecer el UID del usuario diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 6d46a1d..e024820 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -41,6 +41,7 @@ err_pwnam = échec de lecture des infos utilisateur err_sleep = échec de l'exécution de la commande de veille err_switch_tty = échec du changement de terminal err_tty_ctrl = échec du transfert de contrôle du terminal +err_no_users = aucun utilisateur trouvé err_user_gid = échec de modification du GID err_user_init = échec d'initialisation de l'utilisateur err_user_uid = échec de modification du UID diff --git a/res/lang/it.ini b/res/lang/it.ini index 4bff87e..3a7f5a8 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -41,6 +41,7 @@ err_pwnam = impossibile ottenere dati utente + err_user_gid = impossibile impostare GID utente err_user_init = impossibile inizializzare utente err_user_uid = impossible impostare UID utente diff --git a/res/lang/pl.ini b/res/lang/pl.ini index cfc5722..b3eee3d 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -41,6 +41,7 @@ err_pwnam = nie udało się uzyskać informacji o użytkowniku err_sleep = nie udało się wykonać polecenia sleep err_tty_ctrl = nie udało się przekazać kontroli tty + err_user_gid = nie udało się ustawić GID użytkownika err_user_init = nie udało się zainicjalizować użytkownika err_user_uid = nie udało się ustawić UID użytkownika diff --git a/res/lang/pt.ini b/res/lang/pt.ini index df1db13..587d3b4 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -41,6 +41,7 @@ err_pwnam = erro ao obter informação do utilizador + err_user_gid = erro ao definir o GID do utilizador err_user_init = erro ao iniciar o utilizador err_user_uid = erro ao definir o UID do utilizador diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index a7cee7b..2485522 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -41,6 +41,7 @@ err_pwnam = não foi possível obter informações do usuário + err_user_gid = não foi possível definir o GID do usuário err_user_init = não foi possível iniciar o usuário err_user_uid = não foi possível definir o UID do usuário diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 4289b7f..74c0461 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -49,6 +49,7 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator + login = utilizator logout = opreşte sesiunea diff --git a/res/lang/ru.ini b/res/lang/ru.ini index b9cfb1a..103f135 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -41,6 +41,7 @@ err_pwnam = не удалось получить информацию о пол + err_user_gid = не удалось установить GID пользователя err_user_init = не удалось инициализировать пользователя err_user_uid = не удалось установить UID пользователя diff --git a/res/lang/sr.ini b/res/lang/sr.ini index 61ec4c0..c63fe28 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -41,6 +41,7 @@ err_pwnam = neuspijesno skupljanje informacija o korisniku + err_user_gid = neuspijesno postavljanje korisničkog GID-a err_user_init = neuspijensa inicijalizacija korisnika err_user_uid = neuspijesno postavljanje UID-a korisnika diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 9ccd0f3..17359cc 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -41,6 +41,7 @@ err_pwnam = misslyckades att hämta användarinfo + err_user_gid = misslyckades att ställa in användar-GID err_user_init = misslyckades att initialisera användaren err_user_uid = misslyckades att ställa in användar-UID diff --git a/res/lang/tr.ini b/res/lang/tr.ini index d553495..535015c 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -41,6 +41,7 @@ err_pwnam = kullanici bilgileri alinamadi + err_user_gid = kullanici icin GID ayarlanamadi err_user_init = kullanici oturumu baslatilamadi err_user_uid = kullanici icin UID ayarlanamadi diff --git a/res/lang/uk.ini b/res/lang/uk.ini index ffae75c..9e6de2d 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -41,6 +41,7 @@ err_pwnam = не вдалося отримати дані користувача + err_user_gid = не вдалося змінити GID користувача err_user_init = не вдалося ініціалізувати користувача err_user_uid = не вдалося змінити UID користувача diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 6f7ba41..9afdc66 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -41,6 +41,7 @@ err_pwnam = 获取用户信息失败 + err_user_gid = 设置用户GID失败 err_user_init = 初始化用户失败 err_user_uid = 设置用户UID失败 diff --git a/src/UidRange.zig b/src/UidRange.zig new file mode 100644 index 0000000..13eb979 --- /dev/null +++ b/src/UidRange.zig @@ -0,0 +1,6 @@ +const std = @import("std"); + +// We set both values to 0 by default so that, in case they aren't present in +// the login.defs for some reason, then only the root username will be shown +uid_min: std.c.uid_t = 0, +uid_max: std.c.uid_t = 0, diff --git a/src/config/Config.zig b/src/config/Config.zig index 4a95b40..92e0552 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -50,6 +50,7 @@ input_len: u8 = 34, lang: []const u8 = "en", load: bool = true, login_cmd: ?[]const u8 = null, +login_defs_path: []const u8 = "/etc/login.defs", logout_cmd: ?[]const u8 = null, margin_box_h: u8 = 2, margin_box_v: u8 = 1, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index 3761fa7..7175e0f 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -46,6 +46,7 @@ err_pwnam: []const u8 = "failed to get user info", err_sleep: []const u8 = "failed to execute sleep command", err_switch_tty: []const u8 = "failed to switch tty", err_tty_ctrl: []const u8 = "tty control transfer failed", +err_no_users: []const u8 = "no users found", err_user_gid: []const u8 = "failed to set user GID", err_user_init: []const u8 = "failed to initialize user", err_user_uid: []const u8 = "failed to set user UID", diff --git a/src/main.zig b/src/main.zig index b52e779..0cc2c6e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,12 +18,15 @@ const TerminalBuffer = @import("tui/TerminalBuffer.zig"); const Session = @import("tui/components/Session.zig"); const Text = @import("tui/components/Text.zig"); 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 migrator = @import("config/migrator.zig"); const SharedError = @import("SharedError.zig"); +const UidRange = @import("UidRange.zig"); +const StringList = std.ArrayListUnmanaged([]const u8); const Ini = ini.Ini; const DisplayServer = enums.DisplayServer; const Entry = Environment.Entry; @@ -308,7 +311,21 @@ pub fn main() !void { try crawl(&session, lang, dir, .custom); } - var login = Text.init(allocator, &buffer, false, null); + 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 + // accounts *and* no root account...but at this point, if that's the + // case, you have bigger problems to deal with in the first place. :D + try info_line.addMessage(lang.err_no_users, config.error_bg, config.error_fg); + } + + var login = try UserList.init(allocator, &buffer, usernames); defer login.deinit(); var password = Text.init(allocator, &buffer, true, config.asterisk); @@ -320,9 +337,17 @@ pub fn main() !void { // Load last saved username and desktop selection, if any if (config.load) { if (save.user) |user| { - try login.text.appendSlice(login.allocator, user); - login.end = user.len; - login.cursor = login.end; + // 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)) { + login.label.current = i; + break; + } + } + active_input = .password; } @@ -338,15 +363,13 @@ pub fn main() !void { const coordinates = buffer.calculateComponentCoordinates(); info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null); session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center); - login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); + login.label.position(coordinates.x, coordinates.y + 4, coordinates.visible_length, config.text_in_center); password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); switch (active_input) { .info_line => info_line.label.handle(null, insert_mode), .session => session.label.handle(null, insert_mode), - .login => login.handle(null, insert_mode) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }, + .login => login.label.handle(null, insert_mode), .password => password.handle(null, insert_mode) catch { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); }, @@ -464,7 +487,7 @@ pub fn main() !void { const coordinates = buffer.calculateComponentCoordinates(); info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null); session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center); - login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length); + login.label.position(coordinates.x, coordinates.y + 4, coordinates.visible_length, config.text_in_center); password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length); resolution_changed = false; @@ -473,9 +496,7 @@ pub fn main() !void { switch (active_input) { .info_line => info_line.label.handle(null, insert_mode), .session => session.label.handle(null, insert_mode), - .login => login.handle(null, insert_mode) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }, + .login => login.label.handle(null, insert_mode), .password => password.handle(null, insert_mode) catch { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); }, @@ -571,7 +592,7 @@ pub fn main() !void { } session.label.draw(); - login.draw(); + login.label.draw(); password.draw(); } else { std.Thread.sleep(std.time.ns_per_ms * 10); @@ -660,10 +681,7 @@ pub fn main() !void { }, termbox.TB_KEY_CTRL_C => run = false, termbox.TB_KEY_CTRL_U => { - if (active_input == .login) { - login.clear(); - update = true; - } else if (active_input == .password) { + if (active_input == .password) { password.clear(); update = true; } @@ -726,7 +744,7 @@ pub fn main() !void { defer file.close(); const save_data = Save{ - .user = login.text.items, + .user = login.getCurrentUser(), .session_index = session.label.current, }; ini.writeFromStruct(save_data, file.writer(), null, .{}) catch break :save_last_settings; @@ -739,7 +757,7 @@ pub fn main() !void { defer shared_err.deinit(); { - const login_text = try allocator.dupeZ(u8, login.text.items); + const login_text = try allocator.dupeZ(u8, login.getCurrentUser()); defer allocator.free(login_text); const password_text = try allocator.dupeZ(u8, password.text.items); defer allocator.free(password_text); @@ -845,9 +863,7 @@ pub fn main() !void { switch (active_input) { .info_line => info_line.label.handle(&event, insert_mode), .session => session.label.handle(&event, insert_mode), - .login => login.handle(&event, insert_mode) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }, + .login => login.label.handle(&event, insert_mode), .password => password.handle(&event, insert_mode) catch { try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); }, @@ -934,6 +950,70 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa } } +fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8) !StringList { + const uid_range = try getUserIdRange(allocator, login_defs_path); + + var usernames: StringList = .empty; + var maybe_entry = interop.pwd.getpwent(); + + while (maybe_entry != null) { + const entry = maybe_entry.*; + + // We check if the UID is equal to 0 because we always want to add root + // as a username (even if you can't log into it) + if (entry.pw_uid >= uid_range.uid_min and entry.pw_uid <= uid_range.uid_max or entry.pw_uid == 0) { + const pw_name_slice = entry.pw_name[0..std.mem.len(entry.pw_name)]; + const username = try allocator.dupe(u8, pw_name_slice); + + try usernames.append(allocator, username); + } + + maybe_entry = interop.pwd.getpwent(); + } + + interop.pwd.endpwent(); + return usernames; +} + +// This is very bad parsing, but we only need to get 2 values... and the format +// of the file doesn't seem to be standard? So this should be fine... +fn getUserIdRange(allocator: std.mem.Allocator, login_defs_path: []const u8) !UidRange { + const login_defs_file = try std.fs.cwd().openFile(login_defs_path, .{}); + defer login_defs_file.close(); + + const login_defs_buffer = try login_defs_file.readToEndAlloc(allocator, std.math.maxInt(u16)); + defer allocator.free(login_defs_buffer); + + var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n'); + var uid_range = UidRange{}; + + while (iterator.next()) |line| { + const trimmed_line = std.mem.trim(u8, line, " \n\r\t"); + + if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) { + uid_range.uid_min = try parseValue(std.c.uid_t, "UID_MIN", trimmed_line); + } else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) { + uid_range.uid_max = try parseValue(std.c.uid_t, "UID_MAX", trimmed_line); + } + } + + return uid_range; +} + +fn parseValue(comptime T: type, name: []const u8, buffer: []const u8) !T { + var iterator = std.mem.splitAny(u8, buffer, " \t"); + var maybe_value: ?T = null; + + while (iterator.next()) |slice| { + // Skip the slice if it's empty (whitespace) or is the name of the + // property (e.g. UID_MIN or UID_MAX) + if (slice.len == 0 or std.mem.eql(u8, slice, name)) continue; + maybe_value = std.fmt.parseInt(T, slice, 10) catch continue; + } + + return maybe_value orelse error.ValueNotFound; +} + fn adjustBrightness(allocator: std.mem.Allocator, cmd: []const u8) !void { var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator); brightness.stdout_behavior = .Ignore; diff --git a/src/tui/components/UserList.zig b/src/tui/components/UserList.zig new file mode 100644 index 0000000..41eff39 --- /dev/null +++ b/src/tui/components/UserList.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const TerminalBuffer = @import("../TerminalBuffer.zig"); +const generic = @import("generic.zig"); + +const StringList = std.ArrayListUnmanaged([]const u8); +const Allocator = std.mem.Allocator; + +const UsernameText = generic.CyclableLabel([]const u8); + +const UserList = @This(); + +label: UsernameText, + +pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList) !UserList { + var userList = UserList{ + .label = UsernameText.init(allocator, buffer, drawItem), + }; + + for (usernames.items) |username| { + if (username.len == 0) continue; + + try userList.label.addItem(username); + } + + return userList; +} + +pub fn deinit(self: *UserList) void { + self.label.deinit(); +} + +pub fn getCurrentUser(self: UserList) []const u8 { + return self.label.list.items[self.label.current]; +} + +fn drawItem(label: *UsernameText, username: []const u8, _: usize, _: usize) bool { + const length = @min(username.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; + + label.buffer.drawLabel(username, x, label.y); + return true; +} From 5c3da103869f56bf21e6155e4876083bbb80b31a Mon Sep 17 00:00:00 2001 From: RadsammyT Date: Wed, 16 Jul 2025 22:03:51 -0400 Subject: [PATCH 14/15] fix: confined labels cutting off `drawConfinedLabel` didn't take into account the starting x axis when checking to break for exceeding `max_length`. This should fix the box title not appearing on terminals with larger column counts. --- src/tui/TerminalBuffer.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index 7d608c1..555f26b 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -220,7 +220,7 @@ pub fn drawConfinedLabel(self: TerminalBuffer, text: []const u8, x: usize, y: us var i: c_int = @intCast(x); while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) { - if (i >= max_length) break; + if (i - @as(c_int, @intCast(x)) >= max_length) break; _ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg); } } From c11194332ca4874b37559e9ec5276421578d6468 Mon Sep 17 00:00:00 2001 From: darallium Date: Thu, 19 Jun 2025 12:45:13 +0900 Subject: [PATCH 15/15] Add Japanese lang file --- res/lang/ja_JP.ini | 126 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 res/lang/ja_JP.ini diff --git a/res/lang/ja_JP.ini b/res/lang/ja_JP.ini new file mode 100644 index 0000000..c5f5596 --- /dev/null +++ b/res/lang/ja_JP.ini @@ -0,0 +1,126 @@ +authenticating=認証中... +brightness_down=明るさを下げる +brightness_up=明るさを上げる +capslock=CapsLock +err_alloc=メモリ割り当て失敗 +err_bounds=境界外インデックス +err_brightness_change=明るさの変更に失敗しました +err_chdir=ホームフォルダを開けませんでした +err_config=設定ファイルを解析できません +err_console_dev=コンソールへのアクセスに失敗しました +err_dgn_oob=ログメッセージ +err_domain=無効なドメイン +err_empty_password=空のパスワードは許可されていません +err_envlist=環境変数リストの取得に失敗しました +err_hostname=ホスト名の取得に失敗しました +err_mlock=パスワードメモリのロックに失敗しました +err_null=ヌルポインタ +err_numlock=NumLockの設定に失敗しました +err_pam=PAMトランザクション失敗 +err_pam_abort=PAMトランザクションが中断されました +err_pam_acct_expired=アカウントの有効期限が切れています +err_pam_auth=認証エラー +err_pam_authinfo_unavail=ユーザー情報の取得に失敗しました +err_pam_authok_reqd=トークンの有効期限が切れています +err_pam_buf=メモリバッファエラー +err_pam_cred_err=認証情報の設定に失敗しました +err_pam_cred_expired=認証情報の有効期限が切れています +err_pam_cred_insufficient=認証情報が不十分です +err_pam_cred_unavail=認証情報の取得に失敗しました +err_pam_maxtries=最大試行回数に到達しました +err_pam_perm_denied=アクセスが拒否されました +err_pam_session=セッションエラー +err_pam_sys=システムエラー +err_pam_user_unknown=不明なユーザー +err_path=パスの設定に失敗しました +err_perm_dir=カレントディレクトリの変更に失敗しました +err_perm_group=グループ権限のダウングレードに失敗しました +err_perm_user=ユーザー権限のダウングレードに失敗しました +err_pwnam=ユーザー情報の取得に失敗しました +err_sleep=スリープコマンドの実行に失敗しました +err_tty_ctrl=TTY制御の転送に失敗しました +err_user_gid=ユーザーGIDの設定に失敗しました +err_user_init=ユーザーの初期化に失敗しました +err_user_uid=ユーザーUIDの設定に失敗しました +err_xauth=xauthコマンドの実行に失敗しました +err_xcb_conn=XCB接続に失敗しました +err_xsessions_dir=セッションフォルダが見つかりませんでした +err_xsessions_open=セッションフォルダを開けませんでした +insert=挿入 +login=ログイン +logout=ログアウト済み +no_x11_support=X11サポートはコンパイル時に無効化されています +normal=通常 +numlock=NumLock +other=その他 +password=パスワード +restart=再起動 +shell=シェル +shutdown=シャットダウン +sleep=スリープ +wayland=Wayland +x11=X11 +xinitrc=xinitrc +authenticating=認証中... +brightness_down=明るさを下げる +brightness_up=明るさを上げる +capslock=CapsLock +err_alloc=メモリ割り当て失敗 +err_bounds=境界外インデックス +err_brightness_change=明るさの変更に失敗しました +err_chdir=ホームフォルダを開けませんでした +err_config=設定ファイルを解析できません +err_console_dev=コンソールへのアクセスに失敗しました +err_dgn_oob=ログメッセージ +err_domain=無効なドメイン +err_empty_password=空のパスワードは許可されていません +err_envlist=環境変数リストの取得に失敗しました +err_hostname=ホスト名の取得に失敗しました +err_mlock=パスワードメモリのロックに失敗しました +err_null=ヌルポインタ +err_numlock=NumLockの設定に失敗しました +err_pam=PAMトランザクション失敗 +err_pam_abort=PAMトランザクションが中断されました +err_pam_acct_expired=アカウントの有効期限が切れています +err_pam_auth=認証エラー +err_pam_authinfo_unavail=ユーザー情報の取得に失敗しました +err_pam_authok_reqd=トークンの有効期限が切れています +err_pam_buf=メモリバッファエラー +err_pam_cred_err=認証情報の設定に失敗しました +err_pam_cred_expired=認証情報の有効期限が切れています +err_pam_cred_insufficient=認証情報が不十分です +err_pam_cred_unavail=認証情報の取得に失敗しました +err_pam_maxtries=最大試行回数に到達しました +err_pam_perm_denied=アクセスが拒否されました +err_pam_session=セッションエラー +err_pam_sys=システムエラー +err_pam_user_unknown=不明なユーザー +err_path=パスの設定に失敗しました +err_perm_dir=カレントディレクトリの変更に失敗しました +err_perm_group=グループ権限のダウングレードに失敗しました +err_perm_user=ユーザー権限のダウングレードに失敗しました +err_pwnam=ユーザー情報の取得に失敗しました +err_sleep=スリープコマンドの実行に失敗しました +err_tty_ctrl=TTY制御の転送に失敗しました +err_user_gid=ユーザーGIDの設定に失敗しました +err_user_init=ユーザーの初期化に失敗しました +err_user_uid=ユーザーUIDの設定に失敗しました +err_xauth=xauthコマンドの実行に失敗しました +err_xcb_conn=XCB接続に失敗しました +err_xsessions_dir=セッションフォルダが見つかりませんでした +err_xsessions_open=セッションフォルダを開けませんでした +insert=挿入 +login=ログイン +logout=ログアウト済み +no_x11_support=X11サポートはコンパイル時に無効化されています +normal=通常 +numlock=NumLock +other=その他 +password=パスワード +restart=再起動 +shell=シェル +shutdown=シャットダウン +sleep=スリープ +wayland=Wayland +x11=X11 +xinitrc=xinitrc