Merge branch 'master' into refactor/termbox2-dependency

This commit is contained in:
jlucaso 2025-07-25 06:31:10 +02:00
commit 6933c4db02
35 changed files with 543 additions and 124 deletions

View File

@ -139,6 +139,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});
@ -171,6 +177,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();

View File

@ -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

View File

@ -100,20 +100,28 @@ colormix_col2 = 0x000000FF
# Color mixing animation third color id
colormix_col3 = 0x20000000
# Console path
console_dev = /dev/console
# 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
# DOOM animation top color (low intensity flames)
doom_top_color = 0x00FF0000
# DOOM animation fire height (1 thru 9)
doom_fire_height = 6
# DOOM animation middle color (medium intensity flames)
doom_middle_color = 0x00FFFF00
# DOOM animation fire spread (0 thru 4)
doom_fire_spread = 2
# DOOM animation bottom color (high intensity flames)
# DOOM animation custom top color (low intensity flames)
doom_top_color = 0x009F2707
# DOOM animation custom middle color (medium intensity flames)
doom_middle_color = 0x00C78F17
# DOOM animation custom bottom color (high intensity flames)
doom_bottom_color = 0x00FFFFFF
# Error background color id
@ -176,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
@ -248,7 +259,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
@ -263,5 +274,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

View File

@ -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)

View File

@ -2,17 +2,18 @@ authenticating = جاري المصادقة...
brightness_down = خفض السطوع
brightness_up = رفع السطوع
capslock = capslock
err_alloc = فشل في تخصيص الذاكرة
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 = لا يُسمح بكلمة مرور فارغة
err_envlist = فشل في جلب قائمة المتغيرات البيئية
err_hostname = فشل في جلب اسم المضيف (Hostname)
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
err_null = مؤشر فارغ (Null pointer)
err_numlock = فشل في ضبط Num Lock
@ -38,7 +39,9 @@ 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 = فشل في تهيئة بيانات المستخدم
err_user_uid = فشل في تعيين معرّف المستخدم (UID)

View File

@ -2,17 +2,18 @@ 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
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
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,8 @@ 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

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = alokace paměti selhala
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
err_hostname = nelze získat název hostitele
err_mlock = uzamčení paměti hesel selhalo
err_null = nulový ukazatel
@ -39,6 +40,8 @@ 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

View File

@ -2,17 +2,18 @@ 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
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
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,7 +39,9 @@ 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
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen

View File

@ -2,17 +2,18 @@ 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
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
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,7 +39,9 @@ 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_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

View File

@ -2,17 +2,18 @@ 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
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
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,8 @@ 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

View File

@ -2,17 +2,18 @@ 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é
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é
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,7 +39,9 @@ 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_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

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = impossibile allocare memoria
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
err_hostname = impossibile ottenere hostname
err_mlock = impossibile ottenere lock per la password in memoria
err_null = puntatore nullo
@ -39,6 +40,8 @@ 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

126
res/lang/ja_JP.ini Normal file
View File

@ -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

View File

@ -2,17 +2,18 @@ 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
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
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,7 +39,9 @@ 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
err_user_uid = nie udało się ustawić UID użytkownika

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = erro na atribuição de memória
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
err_hostname = erro ao obter o nome do host
err_mlock = erro de bloqueio de memória
err_null = ponteiro nulo
@ -39,6 +40,8 @@ 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

View File

@ -2,17 +2,18 @@
capslock = caixa alta
err_alloc = alocação de memória malsucedida
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
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,8 @@ 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

View File

@ -7,7 +7,8 @@ capslock = capslock
err_console_dev = nu s-a putut accesa consola
@ -47,6 +48,8 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
login = utilizator
logout = opreşte sesiunea

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = не удалось выделить память
err_bounds = за пределами индекса
err_chdir = не удалось открыть домашнюю папку
err_console_dev = не удалось получить доступ к консоли
err_dgn_oob = отладочное сообщение (log)
err_domain = неверный домен
err_hostname = не удалось получить имя хоста
err_mlock = сбой блокировки памяти
err_null = нулевой указатель
@ -39,6 +40,8 @@ err_perm_user = не удалось понизить права доступа
err_pwnam = не удалось получить информацию о пользователе
err_user_gid = не удалось установить GID пользователя
err_user_init = не удалось инициализировать пользователя
err_user_uid = не удалось установить UID пользователя

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = neuspijesna alokacija memorije
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
err_hostname = neuspijesno trazenje hostname-a
err_mlock = neuspijesno zakljucavanje memorije lozinke
err_null = null pokazivac
@ -39,6 +40,8 @@ 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

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = misslyckad minnesallokering
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
err_hostname = misslyckades att hämta värdnamn
err_mlock = misslyckades att låsa lösenordsminne
err_null = nullpekare
@ -39,6 +40,8 @@ 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

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = basarisiz bellek ayirma
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
err_hostname = ana bilgisayar adi alinamadi
err_mlock = parola bellegi kilitlenemedi
err_null = bos isaretci hatasi
@ -39,6 +40,8 @@ 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

View File

@ -2,17 +2,18 @@
capslock = capslock
err_alloc = невдале виділення пам'яті
err_bounds = поза межами індексу
err_chdir = не вдалося відкрити домашній каталог
err_console_dev = невдалий доступ до консолі
err_dgn_oob = повідомлення журналу (log)
err_domain = недійсний домен
err_hostname = не вдалося отримати ім'я хосту
err_mlock = збій блокування пам'яті
err_null = нульовий вказівник
@ -39,6 +40,8 @@ err_perm_user = не вдалося понизити права доступу
err_pwnam = не вдалося отримати дані користувача
err_user_gid = не вдалося змінити GID користувача
err_user_init = не вдалося ініціалізувати користувача
err_user_uid = не вдалося змінити UID користувача

View File

@ -2,17 +2,18 @@
capslock = 大写锁定
err_alloc = 内存分配失败
err_bounds = 索引越界
err_chdir = 无法打开home文件夹
err_console_dev = 无法访问控制台
err_dgn_oob = 日志消息
err_domain = 无效的域
err_hostname = 获取主机名失败
err_mlock = 锁定密码存储器失败
err_null = 空指针
@ -39,6 +40,8 @@ err_perm_user = 用户权限降级失败
err_pwnam = 获取用户信息失败
err_user_gid = 设置用户GID失败
err_user_init = 初始化用户失败
err_user_uid = 设置用户UID失败

View File

@ -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,

6
src/UidRange.zig Normal file
View File

@ -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,

View File

@ -7,21 +7,22 @@ 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, 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);
return .{
.allocator = allocator,
.terminal_buffer = terminal_buffer,
.buffer = buffer,
.fire = [_]Cell{
const levels =
[_]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 +36,15 @@ 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(HEIGHT_MAX, fire_height),
.spread = @min(SPREAD_MAX, fire_spread),
.fire = levels,
};
}
@ -57,20 +66,35 @@ 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 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);
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 + self.spread -| rand_spread;
const to_x = to % self.terminal_buffer.width;
const to_y = to / self.terminal_buffer.width;
// 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
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];
const to_cell = self.fire[level_buf_to];
from_cell.put(x, y);
to_cell.put(to_x, to_y);
}
// Draw bottom line (fire source)
const src_cell = self.fire[STEPS];
src_cell.put(x, self.terminal_buffer.height - 1);
}
}

View File

@ -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;
@ -177,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),
}
}
@ -193,15 +197,14 @@ 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",
.custom => "unspecified",
}, 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 +217,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);
}
@ -461,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 }));

View File

@ -28,8 +28,10 @@ cmatrix_max_codepoint: u16 = 0x7B,
colormix_col1: u32 = 0x00FF0000,
colormix_col2: u32 = 0x000000FF,
colormix_col3: u32 = 0x20000000,
console_dev: []const u8 = "/dev/console",
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,
doom_top_color: u32 = 0x00FF0000,
doom_middle_color: u32 = 0x00FFFF00,
doom_bottom_color: u32 = 0x00FFFFFF,
@ -48,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,

View File

@ -7,17 +7,18 @@ 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",
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",
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,7 +44,9 @@ 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_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",

View File

@ -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;

View File

@ -11,6 +11,7 @@ pub const DisplayServer = enum {
shell,
xinitrc,
x11,
custom,
};
pub const Input = enum {

View File

@ -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;
}
}

View File

@ -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;
@ -102,6 +105,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, &params, .{});
@ -289,10 +293,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| {
@ -300,7 +306,26 @@ pub fn main() !void {
}
}
var login = Text.init(allocator, &buffer, false, null);
var custom_session_dirs = std.mem.splitScalar(u8, config.custom_sessions, ':');
while (custom_session_dirs.next()) |dir| {
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
// 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);
@ -312,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;
}
@ -330,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);
},
@ -354,7 +385,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_top_color, config.doom_middle_color, config.doom_bottom_color, config.doom_fire_height, config.doom_fire_spread);
animation = doom.animation();
},
.matrix => {
@ -389,12 +420,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) {
@ -458,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;
@ -467,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);
},
@ -546,9 +573,10 @@ 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);
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;
};
@ -564,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);
@ -653,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;
}
@ -719,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;
@ -732,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);
@ -838,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);
},
@ -864,11 +887,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,44 +909,111 @@ 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,
});
}
}
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;

View File

@ -208,9 +208,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);
}
}
@ -219,10 +219,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) {
if (i >= max_length) break;
_ = termbox.tb_set_cell(@intCast(i + x), yc, codepoint, self.fg, self.bg);
var i: c_int = @intCast(x);
while (utf8.nextCodepoint()) |codepoint| : (i += termbox.tb_wcwidth(codepoint)) {
if (i - @as(c_int, @intCast(x)) >= max_length) break;
_ = termbox.tb_set_cell(i, yc, codepoint, self.fg, self.bg);
}
}
@ -236,7 +236,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);
}

View File

@ -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;
}