45 Commits

Author SHA1 Message Date
AnErrupTion
e0692885c5 Make delay floating point in dur format
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:34:47 +01:00
AnErrupTion
b6d3aa0e1c Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:25:57 +01:00
AnErrupTion
e6966a628c Fix wrong session index + save file corruption
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:25:01 +01:00
RacerBG
cab3f1bfb5 Added the Latest Changes to the Bulgarian Translation (#879)
Signed-off-by: RacerBG <racerbg@noreply.codeberg.org>

## What are the changes about?

N/A

## What existing issue does this resolve?

N/A

## Pre-requisites

- [ x ] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/879
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RacerBG <racerbg@noreply.codeberg.org>
Co-committed-by: RacerBG <racerbg@noreply.codeberg.org>
2025-12-06 13:39:52 +01:00
AnErrupTion
a9ff0a6d07 Only support dur format v7, set -1 color to black
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 10:05:41 +01:00
AnErrupTion
92beb24c80 Fix save file initialisation & incorrect saved session index
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-05 19:49:27 +01:00
hynak
7e18d906c4 [Feature] Add support for .dur file format and animations (closes #719) (#833)
Adds support for durdraw's .dur file format. Supports ascii, animations, and 16/256 color display.

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/833
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: hynak <hynak@noreply.codeberg.org>
Co-committed-by: hynak <hynak@noreply.codeberg.org>
2025-12-05 19:46:42 +01:00
AnErrupTion
3bfdc75a70 Add option to run command after inactivity delay (closes #747)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-03 20:39:27 +01:00
AnErrupTion
6cce221cbf Improve issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:37:03 +01:00
AnErrupTion
1c99574f73 Add option to run command before UI is initialised (closes #798)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:23:10 +01:00
AnErrupTion
b249dba092 Support multiple TTYs with systemd service (closes #102)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:11:39 +01:00
AnErrupTion
3365b33d6d Fix zig-clap dependency URL
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-01 22:09:41 +01:00
radsammyt
6cb53b6e38 Refactor active_input field-jumping logic (#873)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/873
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: radsammyt <radsammyt@noreply.codeberg.org>
Co-committed-by: radsammyt <radsammyt@noreply.codeberg.org>
2025-12-01 20:07:59 +01:00
AnErrupTion
d82fa82a87 Always add hostname last in the info line
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:50:39 +01:00
AnErrupTion
c2b3d794e8 Fix fallback UID range + add error if UID range not found
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:36:47 +01:00
AnErrupTion
e29bda3250 Add systemd-homed UID range
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:02:23 +01:00
AnErrupTion
4df2382698 Add possibility to disable auth_fails animation (closes #835)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 22:19:38 +01:00
AnErrupTion
3dfb86fe69 Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 21:59:09 +01:00
AnErrupTion
f9a001b160 Update zigini dependency & use same Git URL format everywhere
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 21:57:42 +01:00
RacerBG
5235ca47c5 Add Bulgarian Translation (#872)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/872
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RacerBG <racerbg@noreply.codeberg.org>
Co-committed-by: RacerBG <racerbg@noreply.codeberg.org>
2025-11-30 21:52:46 +01:00
AnErrupTion
a94abf2e69 Improve bug report template & add PR template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 12:08:10 +01:00
AnErrupTion
fe354a4809 Add fallback UID range options at compile-time
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:47:50 +01:00
AnErrupTion
8c964d9ce5 Don't crash when failing to crawl a session directory (closes #870)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:30:33 +01:00
AnErrupTion
392ea6ea63 Update French locale
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:18:29 +01:00
AnErrupTion
10854e643a Don't crash when failing to parse arguments
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:14:10 +01:00
ebits
1980b2e479 Feature: Added option for hibernate between sleep and brightness down (#867) (closes #866)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/867
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-11-28 19:05:17 +01:00
AnErrupTion
816be7449f Update Repology URL
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-19 22:19:12 +01:00
AnErrupTion
f0758d812e Fix potential out of bounds issue when automatically changing session
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-19 22:15:07 +01:00
AnErrupTion
3c0c84d067 Fix build error
I really should test PRs before merging them, shouldn't I?
2025-11-15 20:02:59 +01:00
notiant
cc07c4870a Add option to hide CapsLock and NumLock states (#864)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/864
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: notiant <codeberg.cizj4@simplelogin.com>
Co-committed-by: notiant <codeberg.cizj4@simplelogin.com>
2025-11-15 16:32:42 +01:00
RomanPro100
68ec85f412 Add missing Russian translations (#863)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/863
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RomanPro100 <romanpro100@disroot.org>
Co-committed-by: RomanPro100 <romanpro100@disroot.org>
2025-11-09 17:35:26 +01:00
Corey Newton
1537addd67 Change default session log directory to ~/.local/state (#859)
This addresses #808 and #823 conclusively for all parties.
- It requires no additional directory creation.
- It does not impact users who do not have a problem.
- It adds additional documentation detail in both the bug template and
the readme to help users who *are* having a problem.
- Fulfills the spirit of the [XDG Spec(https://specifications.freedesktop.org/basedir-spec/latest)
but not in practice as `$XDG_CONFIG_HOME` and `$HOME` and the associated
logic are not implemented. `~/.local/state` is the fallback location.

In particular, the spec indicates:
> It may contain: actions history (**logs**, history, …)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/859
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Corey Newton <corey.newton@zenoprax.com>
Co-committed-by: Corey Newton <corey.newton@zenoprax.com>
2025-11-05 21:57:10 +01:00
Piotr Ginał
2da3648179 Add missing Polish translations (#861)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/861
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Piotr Ginał <ginal.piotr11@gmail.com>
Co-committed-by: Piotr Ginał <ginal.piotr11@gmail.com>
2025-11-03 19:45:59 +01:00
ferreo
0cf752f3b8 [Feature] Add autologin support (#841) (closes #200)
This is the simplest change I could come up with to add working autologin, only bit I really dislike is the event but it seemed like the cleanest way without refactoring.

Co-authored-by: ferreo <harderthanfire@gmail.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/841
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ferreo <ferreo@noreply.codeberg.org>
Co-committed-by: ferreo <ferreo@noreply.codeberg.org>
2025-10-25 23:09:24 +02:00
AnErrupTion
15c78c838a Update screenshot (for real this time)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:51:51 +02:00
AnErrupTion
4a486bd876 Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:49:56 +02:00
AnErrupTion
38173d8557 Add edge margin to numlock & capslock
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:39:34 +02:00
AnErrupTion
95d1d9378c Add edge margin to top-right clock
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:53:58 +02:00
AnErrupTion
6f62106fcc Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:16:24 +02:00
AnErrupTion
4171e29995 Fix build error when runit service symlink already exists
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:15:43 +02:00
AnErrupTion
80c27224e9 Create xauth directory if it doesn't exist
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:15:16 +02:00
ebits
106f157a2c [Feature] Add edge margin option (#856) (closes #848)
Allows setting a balanced margin on all sides.

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/856
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-10-23 19:07:54 +02:00
AnErrupTion
ec16ad5dfc Remove mention of config.tty option in README (fixes #854)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-21 17:36:42 +02:00
AnErrupTion
5f22173b91 Mention only Ly v1.2.0 and above are supported in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 19:10:44 +02:00
AnErrupTion
4bc405f239 Start Ly v1.3.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 18:12:16 +02:00
44 changed files with 1197 additions and 150 deletions

View File

@@ -11,11 +11,15 @@ body:
options: options:
- label: I have looked for any other duplicate issues - label: I have looked for any other duplicate issues
required: true required: true
- label: I have reproduced the issue on a fresh install of my OS & Ly with default settings, except ones I will mention
required: true
- label: I have confirmed this issue also occurs on the latest development version
required: true
- type: input - type: input
id: version id: version
attributes: attributes:
label: Ly version label: Ly version
description: The output of `ly --version`. Please note that only Ly v1.1.0 and above are supported. description: The output of `ly --version`. Please note that only Ly v1.2.0 and above are supported.
placeholder: 1.1.0-dev.12+2b0301c placeholder: 1.1.0-dev.12+2b0301c
validations: validations:
required: true required: true
@@ -58,8 +62,9 @@ body:
attributes: attributes:
label: Relevant logs label: Relevant logs
description: | description: |
Please copy and paste (or attach) any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. Please copy and paste (or attach) any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. The log files (located as specified by `/etc/ly/config.ini`) usually contain relevant information about the problem:
Moreover, it is almost always a good idea to include your session log and your general log files (found at ~/ly-session.log and /var/log/ly.log respectively by default) as it usually contains relevant information about the problem. - The session log is located at `~/.local/state/ly-session.log` by default.
- The system log is located at `/var/log/ly.log` by default.
render: shell render: shell
- type: textarea - type: textarea
id: moreinfo id: moreinfo

11
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,11 @@
## What are the changes about?
_Replace this with a brief description of your changes_
## What existing issue does this resolve?
_Replace this with a reference to an existing issue, or N/A if there is none_
## Pre-requisites
- [ ] I have tested & confirmed the changes work locally

BIN
.github/screenshot.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -23,7 +23,7 @@ comptime {
} }
} }
const ly_version = std.SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }; const ly_version = std.SemanticVersion{ .major = 1, .minor = 3, .patch = 0 };
var dest_directory: []const u8 = undefined; var dest_directory: []const u8 = undefined;
var config_directory: []const u8 = undefined; var config_directory: []const u8 = undefined;
@@ -44,6 +44,8 @@ pub fn build(b: *std.Build) !void {
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support (default is on)") orelse true; const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support (default is on)") orelse true;
const default_tty = b.option(u8, "default_tty", "Set the TTY (default is 2)") orelse 2; const default_tty = b.option(u8, "default_tty", "Set the TTY (default is 2)") orelse 2;
const fallback_tty = b.option(u8, "fallback_tty", "Set the fallback TTY (default is 2). This value gets embedded into the binary") orelse 2; const fallback_tty = b.option(u8, "fallback_tty", "Set the fallback TTY (default is 2). This value gets embedded into the binary") orelse 2;
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary") orelse 1000;
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary") orelse 60000;
default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty}); default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty});
@@ -52,6 +54,8 @@ pub fn build(b: *std.Build) !void {
build_options.addOption([]const u8, "version", version_str); build_options.addOption([]const u8, "version", version_str);
build_options.addOption(u8, "tty", default_tty); build_options.addOption(u8, "tty", default_tty);
build_options.addOption(u8, "fallback_tty", fallback_tty); build_options.addOption(u8, "fallback_tty", fallback_tty);
build_options.addOption(std.posix.uid_t, "fallback_uid_min", fallback_uid_min);
build_options.addOption(std.posix.uid_t, "fallback_uid_max", fallback_uid_max);
build_options.addOption(bool, "enable_x11_support", enable_x11_support); build_options.addOption(bool, "enable_x11_support", enable_x11_support);
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
@@ -244,6 +248,7 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config:
defer pam_dir.close(); defer pam_dir.close();
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 }); try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 });
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .override_mode = 0o644 });
} }
} }
@@ -255,8 +260,8 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable; var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close(); defer service_dir.close();
const patched_service = try patchFile(allocator, "res/ly.service", patch_map); const patched_service = try patchFile(allocator, "res/ly@.service", patch_map);
try installText(patched_service, service_dir, service_path, "ly.service", .{ .mode = 0o644 }); try installText(patched_service, service_dir, service_path, "ly@.service", .{ .mode = 0o644 });
}, },
.openrc => { .openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" }); const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
@@ -283,7 +288,13 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map); const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 }); try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
try std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{}); std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
if (err == error.PathAlreadyExists) {
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
} else {
return err;
}
};
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{}); std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
}, },
.s6 => { .s6 => {
@@ -354,7 +365,7 @@ pub fn Uninstaller(uninstall_config: bool) type {
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found"); try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
switch (init_system) { switch (init_system) {
.systemd => try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly.service", "systemd service not found"), .systemd => try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
.openrc => try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found"), .openrc => try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found"),
.runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"), .runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"),
.s6 => { .s6 => {

View File

@@ -1,16 +1,16 @@
.{ .{
.name = .ly, .name = .ly,
.version = "1.2.0", .version = "1.3.0",
.fingerprint = 0xa148ffcc5dc2cb59, .fingerprint = 0xa148ffcc5dc2cb59,
.minimum_zig_version = "0.15.0", .minimum_zig_version = "0.15.0",
.dependencies = .{ .dependencies = .{
.clap = .{ .clap = .{
.url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.11.0.tar.gz", .url = "git+https://github.com/Hejsil/zig-clap#5289e0753cd274d65344bef1c114284c633536ea",
.hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e", .hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e",
}, },
.zigini = .{ .zigini = .{
.url = "https://github.com/AnErrupTion/zigini/archive/96ca1d9f1a7ec741f07ceb104dae2b3a7bdfd48a.tar.gz", .url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.15.0#9281f47702b57779e831d7618e158abb8eb4d4a2",
.hash = "zigini-0.3.2-BSkB7WJJAADybd5DGd9MLCp6ikGGUq9wicxsjv0HF1Qc", .hash = "zigini-0.3.3-36M0FRJJAADZVq5HPm-hYKMpFFTr0OgjbEYcK2ijKZ5n",
}, },
.termbox2 = .{ .termbox2 = .{
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#290ac6b8225aacfd16851224682b851b65fcb918", .url = "git+https://github.com/AnErrupTion/termbox2?ref=master#290ac6b8225aacfd16851224682b851b65fcb918",

View File

@@ -44,9 +44,9 @@ It is recommended to add a rule for Ly as it currently does not ship one.
# pkg install ca_root_nss libxcb git xorg xauth # pkg install ca_root_nss libxcb git xorg xauth
``` ```
## Packaging status ## Availability
[![Packaging status](https://repology.org/badge/vertical-allrepos/ly.svg?exclude_unsupported=1)](https://repology.org/project/ly/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/ly-display-manager.svg?exclude_unsupported=1)](https://repology.org/project/ly-display-manager/versions)
## Support ## Support
@@ -57,6 +57,11 @@ managers, all of which you can find in the sections below:
[X11 environments](#supported-x11-environments) [X11 environments](#supported-x11-environments)
Logs are defined by `/etc/ly/config.ini`:
- The session log is located at `~/.local/state/ly-session.log` by default.
- The system log is located at `/var/log/ly.log` by default.
## Manually building ## Manually building
The procedure for manually building Ly is pretty standard: The procedure for manually building Ly is pretty standard:
@@ -106,20 +111,19 @@ command:
Then, similarly to the previous command, you need to enable the Ly service: Then, similarly to the previous command, you need to enable the Ly service:
``` ```
# systemctl enable ly.service # systemctl enable ly@tty2.service
``` ```
**Important**: Because Ly runs in a TTY, you **must** disable the TTY service **Important**: Because Ly runs in a TTY, you **must** disable the TTY service
that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2 (the default TTY on which Ly spawns), you need to that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2, you need to execute the following command:
execute the following command:
``` ```
# systemctl disable getty@tty2.service # systemctl disable getty@tty2.service
``` ```
You can change the TTY Ly will run on by editing the `tty` option in the You can change the TTY Ly will run on by editing the corresponding
configuration file **and** change which TTY is used in the corresponding service file for your platform, or on systemd, by enabling the service on
service file.. different TTYs, as is done above.
### OpenRC ### OpenRC

View File

@@ -24,6 +24,7 @@ allow_empty_password = true
# matrix -> CMatrix # matrix -> CMatrix
# colormix -> Color mixing shader # colormix -> Color mixing shader
# gameoflife -> John Conway's Game of Life # gameoflife -> John Conway's Game of Life
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
animation = none animation = none
# Stop the animation after some time # Stop the animation after some time
@@ -39,6 +40,7 @@ animation_timeout_sec = 0
asterisk = * asterisk = *
# The number of failed authentications before a special animation is played... ;) # The number of failed authentications before a special animation is played... ;)
# If set to 0, the animation will never be played
auth_fails = 10 auth_fails = 10
# Identifier for battery whose charge to display at top left # Identifier for battery whose charge to display at top left
@@ -46,6 +48,30 @@ auth_fails = 10
# If set to null, battery status won't be shown # If set to null, battery status won't be shown
battery_id = null battery_id = null
# Automatic login configuration
# This feature allows Ly to automatically log in a user without password prompt.
# IMPORTANT: Both auto_login_user and auto_login_session must be set for this to work.
# Autologin only happens once at startup - it won't re-trigger after logout.
# PAM service name to use for automatic login
# The default service (ly-autologin) uses pam_permit to allow login without password
# The appropriate platform-specific PAM configuration (ly-autologin) will be used automatically
auto_login_service = ly-autologin
# Session name to launch automatically
# To find available session names, check the .desktop files in:
# - /usr/share/xsessions/ (for X11 sessions)
# - /usr/share/wayland-sessions/ (for Wayland sessions)
# Use the filename without .desktop extension, or the value of DesktopNames field
# Examples: "i3", "sway", "gnome", "plasma", "xfce"
# If null, automatic login is disabled
auto_login_session = null
# Username to automatically log in
# Must be a valid user on the system
# If null, automatic login is disabled
auto_login_user = null
# Background color id # Background color id
bg = 0x00000000 bg = 0x00000000
@@ -138,6 +164,18 @@ doom_middle_color = 0x00C78F17
# DOOM animation custom bottom color (high intensity flames) # DOOM animation custom bottom color (high intensity flames)
doom_bottom_color = 0x00FFFFFF doom_bottom_color = 0x00FFFFFF
# Dur file path
dur_file_path = $CONFIG_DIRECTORY/ly/example.dur
# Dur offset x direction
dur_x_offset = 0
# Dur offset y direction
dur_y_offset = 0
# Set margin to the edges of the DM (useful for curved monitors)
edge_margin = 0
# Error background color id # Error background color id
error_bg = 0x00000000 error_bg = 0x00000000
@@ -162,6 +200,8 @@ fg = 0x00FFFFFF
# TB_WHITE 0x0008 # TB_WHITE 0x0008
# If full color is off, the styling options still work. The colors are # If full color is off, the styling options still work. The colors are
# always 32-bit values with the styling in the most significant byte. # always 32-bit values with the styling in the most significant byte.
# Note: If using the dur_file animation option and the dur file's color range
# is saved as 256 with this option disabled, the file will not be drawn.
full_color = true full_color = true
# Game of Life entropy interval (0 = disabled, >0 = add entropy every N generations) # Game of Life entropy interval (0 = disabled, >0 = add entropy every N generations)
@@ -185,15 +225,31 @@ gameoflife_frame_delay = 6
# 0.7+ -> Dense, chaotic patterns # 0.7+ -> Dense, chaotic patterns
gameoflife_initial_density = 0.4 gameoflife_initial_density = 0.4
# Command executed when pressing hibernate key (can be null)
hibernate_cmd = null
# Specifies the key used for hibernate (F1-F12)
hibernate_key = F4
# Remove main box borders # Remove main box borders
hide_borders = false hide_borders = false
# Remove power management command hints # Remove power management command hints
hide_key_hints = false hide_key_hints = false
# Remove keyboard lock states from the top right corner
hide_keyboard_locks = false
# Remove version number from the top left corner # Remove version number from the top left corner
hide_version_string = false hide_version_string = false
# Command executed when no input is detected for a certain time
# If null, no command will be executed
inactivity_cmd = null
# Executes a command after a certain amount of seconds
inactivity_delay = 0
# Initial text to show on the info line # Initial text to show on the info line
# If set to null, the info line defaults to the hostname # If set to null, the info line defaults to the hostname
initial_info_text = null initial_info_text = null
@@ -258,7 +314,7 @@ service_name = ly
# Important: due to technical limitations, X11 and shell sessions aren't supported, which # Important: due to technical limitations, X11 and shell sessions aren't supported, which
# means you won't get any logs from those sessions. # means you won't get any logs from those sessions.
# If null, no session log will be created # If null, no session log will be created
session_log = ly-session.log session_log = .local/state/ly-session.log
# Setup command # Setup command
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
@@ -275,6 +331,10 @@ sleep_cmd = null
# Specifies the key used for sleep (F1-F12) # Specifies the key used for sleep (F1-F12)
sleep_key = F3 sleep_key = F3
# Command executed when starting Ly (before the TTY is taken control of)
# If null, no command will be executed
start_cmd = null
# Center the session name. # Center the session name.
text_in_center = false text_in_center = false

View File

@@ -4,19 +4,24 @@ brightness_up = رفع السطوع
capslock = capslock capslock = capslock
err_alloc = فشل في تخصيص الذاكرة err_alloc = فشل في تخصيص الذاكرة
err_bounds = out-of-bounds index err_bounds = out-of-bounds index
err_brightness_change = فشل في تغيير سطوع الشاشة err_brightness_change = فشل في تغيير سطوع الشاشة
err_chdir = فشل في فتح مجلد المنزل err_chdir = فشل في فتح مجلد المنزل
err_config = فشل في تفسير ملف الإعدادات err_config = فشل في تفسير ملف الإعدادات
err_dgn_oob = رسالة سجل (Log) err_dgn_oob = رسالة سجل (Log)
err_domain = اسم نطاق غير صالح err_domain = اسم نطاق غير صالح
err_empty_password = لا يُسمح بكلمة مرور فارغة err_empty_password = لا يُسمح بكلمة مرور فارغة
err_envlist = فشل في جلب قائمة المتغيرات البيئية err_envlist = فشل في جلب قائمة المتغيرات البيئية
err_hostname = فشل في جلب اسم المضيف (Hostname) err_hostname = فشل في جلب اسم المضيف (Hostname)
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock) err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
err_null = مؤشر فارغ (Null pointer) err_null = مؤشر فارغ (Null pointer)
err_numlock = فشل في ضبط Num Lock err_numlock = فشل في ضبط Num Lock
@@ -44,8 +49,10 @@ err_pwnam = فشل في جلب معلومات المستخدم
err_sleep = فشل في تنفيذ أمر sleep err_sleep = فشل في تنفيذ أمر sleep
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY) err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
err_user_init = فشل في تهيئة بيانات المستخدم err_user_init = فشل في تهيئة بيانات المستخدم
err_user_uid = فشل في تعيين معرّف المستخدم (UID) err_user_uid = فشل في تعيين معرّف المستخدم (UID)
@@ -53,6 +60,7 @@ err_xauth = فشل في تنفيذ أمر xauth
err_xcb_conn = فشل في الاتصال بمكتبة XCB err_xcb_conn = فشل في الاتصال بمكتبة XCB
err_xsessions_dir = فشل في العثور على مجلد Xsessions err_xsessions_dir = فشل في العثور على مجلد Xsessions
err_xsessions_open = فشل في فتح مجلد Xsessions err_xsessions_open = فشل في فتح مجلد Xsessions
insert = ادخال insert = ادخال
login = تسجيل الدخول login = تسجيل الدخول
logout = تم تسجيل خروجك logout = تم تسجيل خروجك

78
res/lang/bg.ini Normal file
View File

@@ -0,0 +1,78 @@
authenticating = удостоверяване...
brightness_down = намаляване на яркостта
brightness_up = увеличаване на яркостта
capslock = caps lock
custom = персонализирано
err_alloc = неуспешно заделяне на памет
err_args = неуспешен анализ на аргументите от командния ред
err_autologin_session = сесията за автоматично влизане не е намерена
err_bounds = индексът е извън границите
err_brightness_change = неуспешна промяна на яркостта
err_chdir = неуспешно отваряне на домашната папка
err_clock_too_long = низът на часовника е твърде дълъг
err_config = неуспешен анализ на конфигурационния файл
err_crawl = неуспешно обхождане на папките със сесии
err_dgn_oob = съобщение в дневника
err_domain = невалиден домейн
err_empty_password = не е позволена празна парола
err_envlist = неуспешно получаване на списъка с променливи на средата
err_get_active_tty = неуспешно откриване на активния TTY
err_hibernate = неуспешно изпълнение на командата за хибернация
err_hostname = неуспешно получаване на името на хоста
err_inactivity = неуспешно изпълнение на командата за неактивност
err_lock_state = неуспешно получаване на състоянието на заключване
err_log = неуспешно отваряне на файла с дневника
err_mlock = неуспешно заключване на паметта за паролата
err_null = нулев указател
err_numlock = неуспешно задаване на num lock
err_pam = неуспешна транзакция
err_pam_abort = прекратена транзакция
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_start = неуспешно изпълнение на командата за стартиране
err_battery = неуспешно зареждане на състоянието на батерията
err_switch_tty = неуспешна смяна на TTY
err_tty_ctrl = неуспешно прехвърляне на контрола над TTY
err_no_users = не са намерени потребители
err_uid_range = неуспешно динамично получаване на UID обхват
err_user_gid = неуспешно задаване на потребителския GID
err_user_init = неуспешна стартиране на потребителя
err_user_uid = неуспешно задаване на потребителския UID
err_xauth = неуспешна команда xauth
err_xcb_conn = неуспешна xcb връзка
err_xsessions_dir = папката със сесии не е намерена
err_xsessions_open = неуспешно отваряне на папката със сесии
hibernate = хибернация
insert = вмъкване
login = вход
logout = излизане
no_x11_support = поддръжката на x11 е изключена при компилирането
normal = нормално
numlock = num lock
other = друго
password = парола
restart = рестартиране
shell = обвивка
shutdown = изключване
sleep = заспиване
wayland = wayland
x11 = x11
xinitrc = xinitrc

View File

@@ -4,19 +4,24 @@ brightness_up = apujar brillantor
capslock = Bloq Majús capslock = Bloq Majús
err_alloc = assignació de memòria fallida err_alloc = assignació de memòria fallida
err_bounds = índex fora de límits err_bounds = índex fora de límits
err_brightness_change = error en canviar la brillantor err_brightness_change = error en canviar la brillantor
err_chdir = error en obrir la carpeta home err_chdir = error en obrir la carpeta home
err_dgn_oob = missatge de registre err_dgn_oob = missatge de registre
err_domain = domini invàlid err_domain = domini invàlid
err_envlist = error en obtenir l'envlist err_envlist = error en obtenir l'envlist
err_hostname = error en obtenir el nom de l'amfitrió err_hostname = error en obtenir el nom de l'amfitrió
err_mlock = error en bloquejar la memòria de clau err_mlock = error en bloquejar la memòria de clau
err_null = punter nul err_null = punter nul
err_numlock = error en establir el Bloq num err_numlock = error en establir el Bloq num
@@ -46,6 +51,8 @@ err_pwnam = error en obtenir la informació de l'usuari
err_user_gid = error en establir el GID de l'usuari err_user_gid = error en establir el GID de l'usuari
err_user_init = error en inicialitzar usuari err_user_init = error en inicialitzar usuari
err_user_uid = error en establir l'UID de l'usuari err_user_uid = error en establir l'UID de l'usuari
@@ -53,6 +60,7 @@ err_xauth = error en la comanda xauth
err_xcb_conn = error en la connexió xcb err_xcb_conn = error en la connexió xcb
err_xsessions_dir = error en trobar la carpeta de sessions err_xsessions_dir = error en trobar la carpeta de sessions
err_xsessions_open = error en obrir la carpeta de sessions err_xsessions_open = error en obrir la carpeta de sessions
insert = inserir insert = inserir
login = iniciar sessió login = iniciar sessió
logout = sessió tancada logout = sessió tancada

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = alokace paměti selhala err_alloc = alokace paměti selhala
err_bounds = index je mimo hranice pole err_bounds = index je mimo hranice pole
err_chdir = nelze otevřít domovský adresář err_chdir = nelze otevřít domovský adresář
err_dgn_oob = zpráva protokolu err_dgn_oob = zpráva protokolu
err_domain = neplatná doména err_domain = neplatná doména
err_hostname = nelze získat název hostitele err_hostname = nelze získat název hostitele
err_mlock = uzamčení paměti hesel selhalo err_mlock = uzamčení paměti hesel selhalo
err_null = nulový ukazatel err_null = nulový ukazatel
@@ -46,6 +51,8 @@ err_pwnam = nelze získat informace o uživateli
err_user_gid = nastavení GID uživatele selhalo err_user_gid = nastavení GID uživatele selhalo
err_user_init = inicializace uživatele selhala err_user_init = inicializace uživatele selhala
err_user_uid = nastavení UID uživateli selhalo err_user_uid = nastavení UID uživateli selhalo
@@ -54,6 +61,7 @@ err_user_uid = nastavení UID uživateli selhalo
err_xsessions_dir = nepodařilo se najít složku relací err_xsessions_dir = nepodařilo se najít složku relací
err_xsessions_open = nepodařilo se otevřít složku relací err_xsessions_open = nepodařilo se otevřít složku relací
login = uživatel login = uživatel
logout = odhlášen logout = odhlášen

View File

@@ -4,19 +4,24 @@ brightness_up = Helligkeit+
capslock = Feststelltaste capslock = Feststelltaste
err_alloc = Speicherzuweisung fehlgeschlagen err_alloc = Speicherzuweisung fehlgeschlagen
err_bounds = Index ausserhalb des Bereichs err_bounds = Index ausserhalb des Bereichs
err_brightness_change = Helligkeitsänderung fehlgeschlagen err_brightness_change = Helligkeitsänderung fehlgeschlagen
err_chdir = Fehler beim Oeffnen des Home-Ordners err_chdir = Fehler beim Oeffnen des Home-Ordners
err_config = Fehler beim Verarbeiten der Konfigurationsdatei err_config = Fehler beim Verarbeiten der Konfigurationsdatei
err_dgn_oob = Diagnose-Nachricht err_dgn_oob = Diagnose-Nachricht
err_domain = Ungueltige Domain err_domain = Ungueltige Domain
err_empty_password = Leeres Passwort nicht zugelassen err_empty_password = Leeres Passwort nicht zugelassen
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
err_hostname = Abrufen des Hostnames fehlgeschlagen err_hostname = Abrufen des Hostnames fehlgeschlagen
err_mlock = Sperren des Passwortspeichers fehlgeschlagen err_mlock = Sperren des Passwortspeichers fehlgeschlagen
err_null = Null Pointer err_null = Null Pointer
err_numlock = Numlock konnte nicht aktiviert werden err_numlock = Numlock konnte nicht aktiviert werden
@@ -44,8 +49,10 @@ err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
err_sleep = Sleep-Befehl fehlgeschlagen err_sleep = Sleep-Befehl fehlgeschlagen
err_tty_ctrl = Fehler bei der TTY-Uebergabe err_tty_ctrl = Fehler bei der TTY-Uebergabe
err_user_gid = Fehler beim Setzen der Gruppen-ID err_user_gid = Fehler beim Setzen der Gruppen-ID
err_user_init = Nutzer-Initialisierung fehlgeschlagen err_user_init = Nutzer-Initialisierung fehlgeschlagen
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
@@ -53,6 +60,7 @@ err_xauth = Xauth-Befehl fehlgeschlagen
err_xcb_conn = xcb-Verbindung fehlgeschlagen err_xcb_conn = xcb-Verbindung fehlgeschlagen
err_xsessions_dir = Fehler beim Finden des Sitzungsordners err_xsessions_dir = Fehler beim Finden des Sitzungsordners
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
insert = Einfügen insert = Einfügen
login = Nutzer login = Nutzer
logout = Abmelden logout = Abmelden

View File

@@ -4,17 +4,22 @@ brightness_up = increase brightness
capslock = capslock capslock = capslock
custom = custom custom = custom
err_alloc = failed memory allocation err_alloc = failed memory allocation
err_args = unable to parse command line arguments
err_autologin_session = autologin session not found
err_bounds = out-of-bounds index err_bounds = out-of-bounds index
err_brightness_change = failed to change brightness err_brightness_change = failed to change brightness
err_chdir = failed to open home folder err_chdir = failed to open home folder
err_clock_too_long = clock string too long err_clock_too_long = clock string too long
err_config = unable to parse config file err_config = unable to parse config file
err_crawl = failed to crawl session directories
err_dgn_oob = log message err_dgn_oob = log message
err_domain = invalid domain err_domain = invalid domain
err_empty_password = empty password not allowed err_empty_password = empty password not allowed
err_envlist = failed to get envlist err_envlist = failed to get envlist
err_get_active_tty = failed to get active tty err_get_active_tty = failed to get active tty
err_hibernate = failed to execute hibernate command
err_hostname = failed to get hostname err_hostname = failed to get hostname
err_inactivity = failed to execute inactivity command
err_lock_state = failed to get lock state err_lock_state = failed to get lock state
err_log = failed to open log file err_log = failed to open log file
err_mlock = failed to lock password memory err_mlock = failed to lock password memory
@@ -42,10 +47,12 @@ err_perm_group = failed to downgrade group permissions
err_perm_user = failed to downgrade user permissions err_perm_user = failed to downgrade user permissions
err_pwnam = failed to get user info err_pwnam = failed to get user info
err_sleep = failed to execute sleep command err_sleep = failed to execute sleep command
err_start = failed to execute start command
err_battery = failed to load battery status err_battery = failed to load battery status
err_switch_tty = failed to switch tty err_switch_tty = failed to switch tty
err_tty_ctrl = tty control transfer failed err_tty_ctrl = tty control transfer failed
err_no_users = no users found err_no_users = no users found
err_uid_range = failed to dynamically get uid range
err_user_gid = failed to set user GID err_user_gid = failed to set user GID
err_user_init = failed to initialize user err_user_init = failed to initialize user
err_user_uid = failed to set user UID err_user_uid = failed to set user UID
@@ -53,6 +60,7 @@ err_xauth = xauth command failed
err_xcb_conn = xcb connection failed err_xcb_conn = xcb connection failed
err_xsessions_dir = failed to find sessions folder err_xsessions_dir = failed to find sessions folder
err_xsessions_open = failed to open sessions folder err_xsessions_open = failed to open sessions folder
hibernate = hibernate
insert = insert insert = insert
login = login login = login
logout = logged out logout = logged out

View File

@@ -4,19 +4,24 @@ brightness_up = subir brillo
capslock = Bloq Mayús capslock = Bloq Mayús
err_alloc = asignación de memoria fallida err_alloc = asignación de memoria fallida
err_bounds = índice fuera de límites err_bounds = índice fuera de límites
err_chdir = error al abrir la carpeta home err_chdir = error al abrir la carpeta home
err_dgn_oob = mensaje de registro err_dgn_oob = mensaje de registro
err_domain = dominio inválido err_domain = dominio inválido
err_hostname = error al obtener el nombre de host err_hostname = error al obtener el nombre de host
err_mlock = error al bloquear la contraseña de memoria err_mlock = error al bloquear la contraseña de memoria
err_null = puntero nulo err_null = puntero nulo
@@ -46,6 +51,8 @@ err_pwnam = error al obtener la información del usuario
err_user_gid = error al establecer el GID del usuario err_user_gid = error al establecer el GID del usuario
err_user_init = error al inicializar usuario err_user_init = error al inicializar usuario
err_user_uid = error al establecer el UID del usuario err_user_uid = error al establecer el UID del usuario
@@ -53,6 +60,7 @@ err_user_uid = error al establecer el UID del usuario
err_xsessions_dir = error al buscar la carpeta de sesiones err_xsessions_dir = error al buscar la carpeta de sesiones
err_xsessions_open = error al abrir la carpeta de sesiones err_xsessions_open = error al abrir la carpeta de sesiones
insert = insertar insert = insertar
login = usuario login = usuario
logout = cerrar sesión logout = cerrar sesión

View File

@@ -4,17 +4,22 @@ brightness_up = augmenter la luminosité
capslock = verr.maj capslock = verr.maj
custom = customisé custom = customisé
err_alloc = échec d'allocation mémoire err_alloc = échec d'allocation mémoire
err_args = échec de l'analyse des arguments en lignes de commande
err_autologin_session = session de connexion automatique introuvable
err_bounds = indice hors-limite err_bounds = indice hors-limite
err_brightness_change = échec du changement de luminosité err_brightness_change = échec du changement de luminosité
err_chdir = échec de l'ouverture du répertoire home err_chdir = échec de l'ouverture du répertoire home
err_clock_too_long = chaîne de formattage de l'horloge trop longue err_clock_too_long = chaîne de formattage de l'horloge trop longue
err_config = échec de lecture du fichier de configuration err_config = échec de lecture du fichier de configuration
err_crawl = échec de la navigation des répertoires de session
err_dgn_oob = message err_dgn_oob = message
err_domain = domaine invalide err_domain = domaine invalide
err_empty_password = mot de passe vide non autorisé err_empty_password = mot de passe vide non autorisé
err_envlist = échec de lecture de la liste d'environnement err_envlist = échec de lecture de la liste d'environnement
err_get_active_tty = échec de lecture du terminal actif err_get_active_tty = échec de lecture du terminal actif
err_hibernate = échec de l'exécution de la commande de veille prolongée
err_hostname = échec de lecture du nom d'hôte err_hostname = échec de lecture du nom d'hôte
err_inactivity = échec de l'exécution de la commande d'inactivité
err_lock_state = échec de lecture de l'état de verrouillage err_lock_state = échec de lecture de l'état de verrouillage
err_log = échec de l'ouverture du fichier de journal err_log = échec de l'ouverture du fichier de journal
err_mlock = échec du verrouillage mémoire err_mlock = échec du verrouillage mémoire
@@ -42,10 +47,12 @@ err_perm_group = échec du déclassement des permissions de groupe
err_perm_user = échec du déclassement des permissions utilisateur err_perm_user = échec du déclassement des permissions utilisateur
err_pwnam = échec de lecture des infos utilisateur err_pwnam = échec de lecture des infos utilisateur
err_sleep = échec de l'exécution de la commande de veille err_sleep = échec de l'exécution de la commande de veille
err_start = échec de l'exécution de la commande de démarrage
err_battery = échec de lecture de l'état de la batterie err_battery = échec de lecture de l'état de la batterie
err_switch_tty = échec du changement de terminal err_switch_tty = échec du changement de terminal
err_tty_ctrl = échec du transfert de contrôle du terminal err_tty_ctrl = échec du transfert de contrôle du terminal
err_no_users = aucun utilisateur trouvé err_no_users = aucun utilisateur trouvé
err_uid_range = échec de récupération dynamique de la plage d'UID
err_user_gid = échec de modification du GID err_user_gid = échec de modification du GID
err_user_init = échec d'initialisation de l'utilisateur err_user_init = échec d'initialisation de l'utilisateur
err_user_uid = échec de modification du UID err_user_uid = échec de modification du UID
@@ -53,6 +60,7 @@ err_xauth = échec de la commande xauth
err_xcb_conn = échec de la connexion xcb err_xcb_conn = échec de la connexion xcb
err_xsessions_dir = échec de la recherche du dossier de sessions err_xsessions_dir = échec de la recherche du dossier de sessions
err_xsessions_open = échec de l'ouverture du dossier de sessions err_xsessions_open = échec de l'ouverture du dossier de sessions
hibernate = veille prolongée
insert = insertion insert = insertion
login = identifiant login = identifiant
logout = déconnecté logout = déconnecté

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = impossibile allocare memoria err_alloc = impossibile allocare memoria
err_bounds = indice fuori limite err_bounds = indice fuori limite
err_chdir = impossibile aprire home directory err_chdir = impossibile aprire home directory
err_dgn_oob = messaggio log err_dgn_oob = messaggio log
err_domain = dominio non valido err_domain = dominio non valido
err_hostname = impossibile ottenere hostname err_hostname = impossibile ottenere hostname
err_mlock = impossibile ottenere lock per la password in memoria err_mlock = impossibile ottenere lock per la password in memoria
err_null = puntatore nullo err_null = puntatore nullo
@@ -46,6 +51,8 @@ err_pwnam = impossibile ottenere dati utente
err_user_gid = impossibile impostare GID utente err_user_gid = impossibile impostare GID utente
err_user_init = impossibile inizializzare utente err_user_init = impossibile inizializzare utente
err_user_uid = impossible impostare UID utente err_user_uid = impossible impostare UID utente
@@ -54,6 +61,7 @@ err_user_uid = impossible impostare UID utente
err_xsessions_dir = impossibile localizzare cartella sessioni err_xsessions_dir = impossibile localizzare cartella sessioni
err_xsessions_open = impossibile aprire cartella sessioni err_xsessions_open = impossibile aprire cartella sessioni
login = username login = username
logout = scollegato logout = scollegato

View File

@@ -4,19 +4,24 @@ brightness_up = 明るさを上げる
capslock = CapsLock capslock = CapsLock
err_alloc = メモリ割り当て失敗 err_alloc = メモリ割り当て失敗
err_bounds = 境界外インデックス err_bounds = 境界外インデックス
err_brightness_change = 明るさの変更に失敗しました err_brightness_change = 明るさの変更に失敗しました
err_chdir = ホームフォルダを開けませんでした err_chdir = ホームフォルダを開けませんでした
err_config = 設定ファイルを解析できません err_config = 設定ファイルを解析できません
err_dgn_oob = ログメッセージ err_dgn_oob = ログメッセージ
err_domain = 無効なドメイン err_domain = 無効なドメイン
err_empty_password = 空のパスワードは許可されていません err_empty_password = 空のパスワードは許可されていません
err_envlist = 環境変数リストの取得に失敗しました err_envlist = 環境変数リストの取得に失敗しました
err_hostname = ホスト名の取得に失敗しました err_hostname = ホスト名の取得に失敗しました
err_mlock = パスワードメモリのロックに失敗しました err_mlock = パスワードメモリのロックに失敗しました
err_null = ヌルポインタ err_null = ヌルポインタ
err_numlock = NumLockの設定に失敗しました err_numlock = NumLockの設定に失敗しました
@@ -44,8 +49,10 @@ err_pwnam = ユーザー情報の取得に失敗しました
err_sleep = スリープコマンドの実行に失敗しました err_sleep = スリープコマンドの実行に失敗しました
err_tty_ctrl = TTY制御の転送に失敗しました err_tty_ctrl = TTY制御の転送に失敗しました
err_user_gid = ユーザーGIDの設定に失敗しました err_user_gid = ユーザーGIDの設定に失敗しました
err_user_init = ユーザーの初期化に失敗しました err_user_init = ユーザーの初期化に失敗しました
err_user_uid = ユーザーUIDの設定に失敗しました err_user_uid = ユーザーUIDの設定に失敗しました
@@ -53,6 +60,7 @@ err_xauth = xauthコマンドの実行に失敗しました
err_xcb_conn = XCB接続に失敗しました err_xcb_conn = XCB接続に失敗しました
err_xsessions_dir = セッションフォルダが見つかりませんでした err_xsessions_dir = セッションフォルダが見つかりませんでした
err_xsessions_open = セッションフォルダを開けませんでした err_xsessions_open = セッションフォルダを開けませんでした
insert = 挿入 insert = 挿入
login = ログイン login = ログイン
logout = ログアウト済み logout = ログアウト済み

View File

@@ -4,17 +4,22 @@ brightness_up = palielināt spilgtumu
capslock = caps lock capslock = caps lock
custom = pielāgots custom = pielāgots
err_alloc = neizdevās atmiņas piešķiršana err_alloc = neizdevās atmiņas piešķiršana
err_bounds = indekss ārpus robežām err_bounds = indekss ārpus robežām
err_brightness_change = neizdevās mainīt spilgtumu err_brightness_change = neizdevās mainīt spilgtumu
err_chdir = neizdevās atvērt mājas mapi err_chdir = neizdevās atvērt mājas mapi
err_clock_too_long = pulksteņa virkne pārāk gara err_clock_too_long = pulksteņa virkne pārāk gara
err_config = neizdevās parsēt konfigurācijas failu err_config = neizdevās parsēt konfigurācijas failu
err_dgn_oob = žurnāla ziņojums err_dgn_oob = žurnāla ziņojums
err_domain = nederīgs domēns err_domain = nederīgs domēns
err_empty_password = tukša parole nav atļauta err_empty_password = tukša parole nav atļauta
err_envlist = neizdevās iegūt vides mainīgo sarakstu err_envlist = neizdevās iegūt vides mainīgo sarakstu
err_get_active_tty = neizdevās iegūt aktīvo tty err_get_active_tty = neizdevās iegūt aktīvo tty
err_hostname = neizdevās iegūt hostname err_hostname = neizdevās iegūt hostname
err_lock_state = neizdevās iegūt bloķēšanas stāvokli err_lock_state = neizdevās iegūt bloķēšanas stāvokli
err_log = neizdevās atvērt žurnāla failu err_log = neizdevās atvērt žurnāla failu
err_mlock = neizdevās bloķēt paroles atmiņu err_mlock = neizdevās bloķēt paroles atmiņu
@@ -42,10 +47,12 @@ err_perm_group = neizdevās pazemināt grupas atļaujas
err_perm_user = neizdevās pazemināt lietotāja atļaujas err_perm_user = neizdevās pazemināt lietotāja atļaujas
err_pwnam = neizdevās iegūt lietotāja informāciju err_pwnam = neizdevās iegūt lietotāja informāciju
err_sleep = neizdevās izpildīt miega komandu err_sleep = neizdevās izpildīt miega komandu
err_battery = neizdevās ielādēt akumulatora stāvokli err_battery = neizdevās ielādēt akumulatora stāvokli
err_switch_tty = neizdevās pārslēgt tty err_switch_tty = neizdevās pārslēgt tty
err_tty_ctrl = tty vadības nodošana neizdevās err_tty_ctrl = tty vadības nodošana neizdevās
err_no_users = lietotāji nav atrasti err_no_users = lietotāji nav atrasti
err_user_gid = neizdevās iestatīt lietotāja GID err_user_gid = neizdevās iestatīt lietotāja GID
err_user_init = neizdevās inicializēt lietotāju err_user_init = neizdevās inicializēt lietotāju
err_user_uid = neizdevās iestatīt lietotāja UID err_user_uid = neizdevās iestatīt lietotāja UID
@@ -53,6 +60,7 @@ err_xauth = xauth komanda neizdevās
err_xcb_conn = xcb savienojums neizdevās err_xcb_conn = xcb savienojums neizdevās
err_xsessions_dir = neizdevās atrast sesiju mapi err_xsessions_dir = neizdevās atrast sesiju mapi
err_xsessions_open = neizdevās atvērt sesiju mapi err_xsessions_open = neizdevās atvērt sesiju mapi
insert = ievietot insert = ievietot
login = lietotājs login = lietotājs
logout = iziet logout = iziet
@@ -68,4 +76,3 @@ sleep = snauda
wayland = wayland wayland = wayland
x11 = x11 x11 = x11
xinitrc = xinitrc xinitrc = xinitrc

View File

@@ -2,21 +2,26 @@ authenticating = uwierzytelnianie...
brightness_down = zmniejsz jasność brightness_down = zmniejsz jasność
brightness_up = zwiększ jasność brightness_up = zwiększ jasność
capslock = capslock capslock = capslock
custom = własny
err_alloc = nieudana alokacja pamięci err_alloc = nieudana alokacja pamięci
err_autologin_session = nie znaleziono sesji autologowania
err_bounds = indeks poza zakresem err_bounds = indeks poza zakresem
err_brightness_change = nie udało się zmienić jasności err_brightness_change = nie udało się zmienić jasności
err_chdir = nie udało się otworzyć folderu domowego err_chdir = nie udało się otworzyć folderu domowego
err_clock_too_long = ciąg znaków zegara jest za długi
err_config = nie można przetworzyć pliku konfiguracyjnego err_config = nie można przetworzyć pliku konfiguracyjnego
err_dgn_oob = wiadomość loga err_dgn_oob = wiadomość loga
err_domain = niepoprawna domena err_domain = niepoprawna domena
err_empty_password = puste hasło jest niedozwolone err_empty_password = puste hasło jest niedozwolone
err_envlist = nie udało się pobrać listy zmiennych środowiskowych err_envlist = nie udało się pobrać listy zmiennych środowiskowych
err_get_active_tty = nie udało się uzyskać aktywnego tty
err_hostname = nie udało się uzyskać nazwy hosta err_hostname = nie udało się uzyskać nazwy hosta
err_lock_state = nie udało się uzyskać stanu blokady
err_log = nie udało się otworzyć pliku logu
err_mlock = nie udało się zablokować pamięci haseł err_mlock = nie udało się zablokować pamięci haseł
err_null = pusty wskaźnik err_null = pusty wskaźnik
err_numlock = nie udało się ustawić numlock err_numlock = nie udało się ustawić numlock
@@ -43,8 +48,10 @@ err_perm_user = nie udało się obniżyć uprawnień użytkownika
err_pwnam = nie udało się uzyskać informacji o użytkowniku err_pwnam = nie udało się uzyskać informacji o użytkowniku
err_sleep = nie udało się wykonać polecenia sleep err_sleep = nie udało się wykonać polecenia sleep
err_battery = nie udało się sprawdzić statusu baterii
err_switch_tty = nie można przełączyć tty
err_tty_ctrl = nie udało się przekazać kontroli tty err_tty_ctrl = nie udało się przekazać kontroli tty
err_no_users = nie znaleziono żadnego użytkownika
err_user_gid = nie udało się ustawić GID użytkownika err_user_gid = nie udało się ustawić GID użytkownika
err_user_init = nie udało się zainicjalizować użytkownika err_user_init = nie udało się zainicjalizować użytkownika
@@ -53,6 +60,7 @@ err_xauth = polecenie xauth nie powiodło się
err_xcb_conn = połączenie xcb nie powiodło się err_xcb_conn = połączenie xcb nie powiodło się
err_xsessions_dir = nie udało się znaleźć folderu sesji err_xsessions_dir = nie udało się znaleźć folderu sesji
err_xsessions_open = nie udało się otworzyć folderu sesji err_xsessions_open = nie udało się otworzyć folderu sesji
insert = wstaw insert = wstaw
login = login login = login
logout = wylogowano logout = wylogowano

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = erro na atribuição de memória err_alloc = erro na atribuição de memória
err_bounds = índice fora de limites err_bounds = índice fora de limites
err_chdir = erro ao abrir a pasta home err_chdir = erro ao abrir a pasta home
err_dgn_oob = mensagem de registo err_dgn_oob = mensagem de registo
err_domain = domínio inválido err_domain = domínio inválido
err_hostname = erro ao obter o nome do host err_hostname = erro ao obter o nome do host
err_mlock = erro de bloqueio de memória err_mlock = erro de bloqueio de memória
err_null = ponteiro nulo err_null = ponteiro nulo
@@ -46,6 +51,8 @@ err_pwnam = erro ao obter informação do utilizador
err_user_gid = erro ao definir o GID do utilizador err_user_gid = erro ao definir o GID do utilizador
err_user_init = erro ao iniciar o utilizador err_user_init = erro ao iniciar o utilizador
err_user_uid = erro ao definir o UID do utilizador err_user_uid = erro ao definir o UID do utilizador
@@ -54,6 +61,7 @@ err_user_uid = erro ao definir o UID do utilizador
err_xsessions_dir = erro ao localizar a pasta das sessões err_xsessions_dir = erro ao localizar a pasta das sessões
err_xsessions_open = erro ao abrir a pasta das sessões err_xsessions_open = erro ao abrir a pasta das sessões
login = iniciar sessão login = iniciar sessão
logout = terminar sessão logout = terminar sessão

View File

@@ -4,19 +4,24 @@
capslock = caixa alta capslock = caixa alta
err_alloc = alocação de memória malsucedida err_alloc = alocação de memória malsucedida
err_bounds = índice fora de limites err_bounds = índice fora de limites
err_chdir = não foi possível abrir o diretório home err_chdir = não foi possível abrir o diretório home
err_dgn_oob = mensagem de log err_dgn_oob = mensagem de log
err_domain = domínio inválido err_domain = domínio inválido
err_hostname = não foi possível obter o nome do host err_hostname = não foi possível obter o nome do host
err_mlock = bloqueio da memória de senha malsucedido err_mlock = bloqueio da memória de senha malsucedido
err_null = ponteiro nulo err_null = ponteiro nulo
@@ -46,6 +51,8 @@ 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_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_init = não foi possível iniciar o usuário
err_user_uid = não foi possível definir o UID do usuário err_user_uid = não foi possível definir o UID do usuário
@@ -54,6 +61,7 @@ err_user_uid = não foi possível definir o UID do usuário
err_xsessions_dir = não foi possível encontrar a pasta das sessões err_xsessions_dir = não foi possível encontrar a pasta das sessões
err_xsessions_open = não foi possível abrir a pasta das sessões err_xsessions_open = não foi possível abrir a pasta das sessões
login = conectar login = conectar
logout = desconectado logout = desconectado

View File

@@ -17,6 +17,11 @@ capslock = capslock
@@ -54,6 +59,9 @@ err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
login = utilizator login = utilizator
logout = opreşte sesiunea logout = opreşte sesiunea

View File

@@ -2,24 +2,29 @@ authenticating = аутентификация...
brightness_down = уменьшить яркость brightness_down = уменьшить яркость
brightness_up = увеличить яркость brightness_up = увеличить яркость
capslock = capslock capslock = capslock
custom = пользовательский
err_alloc = не удалось выделить память err_alloc = не удалось выделить память
err_autologin_session = не найдена сессия с автологином
err_bounds = за пределами индекса err_bounds = за пределами индекса
err_brightness_change = не удалось изменить яркость err_brightness_change = не удалось изменить яркость
err_chdir = не удалось открыть домашнюю папку err_chdir = не удалось открыть домашнюю папку
err_clock_too_long = строка часов слишком длинная
err_config = не удалось разобрать файл конфигурации err_config = не удалось разобрать файл конфигурации
err_dgn_oob = отладочное сообщение (log) err_dgn_oob = отладочное сообщение (log)
err_domain = неверный домен err_domain = неверный домен
err_empty_password = пустой пароль не допустим err_empty_password = пустой пароль не допустим
err_envlist = не удалось получить список переменных среды err_envlist = не удалось получить список переменных среды
err_get_active_tty = не удалось получить активный tty
err_hostname = не удалось получить имя хоста err_hostname = не удалось получить имя хоста
err_lock_state = не удалось получить состояние lock
err_log = не удалось открыть файл log
err_mlock = сбой блокировки памяти err_mlock = сбой блокировки памяти
err_null = нулевой указатель err_null = нулевой указатель
err_numlock = не удалось установить numlock
err_pam = pam транзакция не удалась err_pam = pam транзакция не удалась
err_pam_abort = pam транзакция прервана err_pam_abort = pam транзакция прервана
err_pam_acct_expired = срок действия аккаунта истёк err_pam_acct_expired = срок действия аккаунта истёк
@@ -43,9 +48,11 @@ err_perm_user = не удалось понизить права доступа
err_pwnam = не удалось получить информацию о пользователе err_pwnam = не удалось получить информацию о пользователе
err_sleep = не удалось выполнить команду sleep err_sleep = не удалось выполнить команду sleep
err_battery = не удалось получить статус батареи
err_switch_tty = не удалось переключить tty err_switch_tty = не удалось переключить tty
err_tty_ctrl = передача управления tty не удалась err_tty_ctrl = передача управления tty не удалась
err_no_users = пользователи не найдены err_no_users = пользователи не найдены
err_user_gid = не удалось установить GID пользователя err_user_gid = не удалось установить GID пользователя
err_user_init = не удалось инициализировать пользователя err_user_init = не удалось инициализировать пользователя
err_user_uid = не удалось установить UID пользователя err_user_uid = не удалось установить UID пользователя
@@ -54,10 +61,11 @@ err_xcb_conn = ошибка подключения xcb
err_xsessions_dir = не удалось найти сессионную папку err_xsessions_dir = не удалось найти сессионную папку
err_xsessions_open = не удалось открыть сессионную папку err_xsessions_open = не удалось открыть сессионную папку
insert = вставка
login = логин login = логин
logout = вышел из системы logout = вышел из системы
no_x11_support = поддержка x11 отключена во время компиляции no_x11_support = поддержка x11 отключена во время компиляции
normal = обычный
numlock = numlock numlock = numlock
other = прочие other = прочие
password = пароль password = пароль

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = neuspijesna alokacija memorije err_alloc = neuspijesna alokacija memorije
err_bounds = izvan granica indeksa err_bounds = izvan granica indeksa
err_chdir = neuspijesno otvaranje home foldera err_chdir = neuspijesno otvaranje home foldera
err_dgn_oob = log poruka err_dgn_oob = log poruka
err_domain = nevazeci domen err_domain = nevazeci domen
err_hostname = neuspijesno trazenje hostname-a err_hostname = neuspijesno trazenje hostname-a
err_mlock = neuspijesno zakljucavanje memorije lozinke err_mlock = neuspijesno zakljucavanje memorije lozinke
err_null = null pokazivac err_null = null pokazivac
@@ -46,6 +51,8 @@ err_pwnam = neuspijesno skupljanje informacija o korisniku
err_user_gid = neuspijesno postavljanje korisničkog GID-a err_user_gid = neuspijesno postavljanje korisničkog GID-a
err_user_init = neuspijensa inicijalizacija korisnika err_user_init = neuspijensa inicijalizacija korisnika
err_user_uid = neuspijesno postavljanje UID-a korisnika err_user_uid = neuspijesno postavljanje UID-a korisnika
@@ -54,6 +61,7 @@ err_user_uid = neuspijesno postavljanje UID-a korisnika
err_xsessions_dir = neuspijesno pronalazenje foldera sesija err_xsessions_dir = neuspijesno pronalazenje foldera sesija
err_xsessions_open = neuspijesno otvaranje foldera sesija err_xsessions_open = neuspijesno otvaranje foldera sesija
login = korisnik login = korisnik
logout = izlogovan logout = izlogovan

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = misslyckad minnesallokering err_alloc = misslyckad minnesallokering
err_bounds = utanför banan index err_bounds = utanför banan index
err_chdir = misslyckades att öppna hemkatalog err_chdir = misslyckades att öppna hemkatalog
err_dgn_oob = loggmeddelande err_dgn_oob = loggmeddelande
err_domain = okänd domän err_domain = okänd domän
err_hostname = misslyckades att hämta värdnamn err_hostname = misslyckades att hämta värdnamn
err_mlock = misslyckades att låsa lösenordsminne err_mlock = misslyckades att låsa lösenordsminne
err_null = nullpekare err_null = nullpekare
@@ -46,6 +51,8 @@ err_pwnam = misslyckades att hämta användarinfo
err_user_gid = misslyckades att ställa in användar-GID err_user_gid = misslyckades att ställa in användar-GID
err_user_init = misslyckades att initialisera användaren err_user_init = misslyckades att initialisera användaren
err_user_uid = misslyckades att ställa in användar-UID err_user_uid = misslyckades att ställa in användar-UID
@@ -54,6 +61,7 @@ err_user_uid = misslyckades att ställa in användar-UID
err_xsessions_dir = misslyckades att hitta sessionskatalog err_xsessions_dir = misslyckades att hitta sessionskatalog
err_xsessions_open = misslyckades att öppna sessionskatalog err_xsessions_open = misslyckades att öppna sessionskatalog
login = inloggning login = inloggning
logout = utloggad logout = utloggad

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = basarisiz bellek ayirma err_alloc = basarisiz bellek ayirma
err_bounds = sinirlarin disinda dizin err_bounds = sinirlarin disinda dizin
err_chdir = ev klasoru acilamadi err_chdir = ev klasoru acilamadi
err_dgn_oob = log mesaji err_dgn_oob = log mesaji
err_domain = gecersiz etki alani err_domain = gecersiz etki alani
err_hostname = ana bilgisayar adi alinamadi err_hostname = ana bilgisayar adi alinamadi
err_mlock = parola bellegi kilitlenemedi err_mlock = parola bellegi kilitlenemedi
err_null = bos isaretci hatasi err_null = bos isaretci hatasi
@@ -46,6 +51,8 @@ err_pwnam = kullanici bilgileri alinamadi
err_user_gid = kullanici icin GID ayarlanamadi err_user_gid = kullanici icin GID ayarlanamadi
err_user_init = kullanici oturumu baslatilamadi err_user_init = kullanici oturumu baslatilamadi
err_user_uid = kullanici icin UID ayarlanamadi err_user_uid = kullanici icin UID ayarlanamadi
@@ -54,6 +61,7 @@ err_user_uid = kullanici icin UID ayarlanamadi
err_xsessions_dir = oturumlar klasoru bulunamadi err_xsessions_dir = oturumlar klasoru bulunamadi
err_xsessions_open = oturumlar klasoru acilamadi err_xsessions_open = oturumlar klasoru acilamadi
login = kullanici login = kullanici
logout = oturumdan cikis yapildi logout = oturumdan cikis yapildi

View File

@@ -4,19 +4,24 @@
capslock = capslock capslock = capslock
err_alloc = невдале виділення пам'яті err_alloc = невдале виділення пам'яті
err_bounds = поза межами індексу err_bounds = поза межами індексу
err_chdir = не вдалося відкрити домашній каталог err_chdir = не вдалося відкрити домашній каталог
err_dgn_oob = повідомлення журналу (log) err_dgn_oob = повідомлення журналу (log)
err_domain = недійсний домен err_domain = недійсний домен
err_hostname = не вдалося отримати ім'я хосту err_hostname = не вдалося отримати ім'я хосту
err_mlock = збій блокування пам'яті err_mlock = збій блокування пам'яті
err_null = нульовий вказівник err_null = нульовий вказівник
@@ -46,6 +51,8 @@ err_pwnam = не вдалося отримати дані користувача
err_user_gid = не вдалося змінити GID користувача err_user_gid = не вдалося змінити GID користувача
err_user_init = не вдалося ініціалізувати користувача err_user_init = не вдалося ініціалізувати користувача
err_user_uid = не вдалося змінити UID користувача err_user_uid = не вдалося змінити UID користувача
@@ -54,6 +61,7 @@ err_user_uid = не вдалося змінити UID користувача
err_xsessions_dir = не вдалося знайти каталог сесій err_xsessions_dir = не вдалося знайти каталог сесій
err_xsessions_open = не вдалося відкрити каталог сесій err_xsessions_open = не вдалося відкрити каталог сесій
login = логін login = логін
logout = вийти logout = вийти

View File

@@ -4,19 +4,24 @@
capslock = 大写锁定 capslock = 大写锁定
err_alloc = 内存分配失败 err_alloc = 内存分配失败
err_bounds = 索引越界 err_bounds = 索引越界
err_chdir = 无法打开home文件夹 err_chdir = 无法打开home文件夹
err_dgn_oob = 日志消息 err_dgn_oob = 日志消息
err_domain = 无效的域 err_domain = 无效的域
err_hostname = 获取主机名失败 err_hostname = 获取主机名失败
err_mlock = 锁定密码存储器失败 err_mlock = 锁定密码存储器失败
err_null = 空指针 err_null = 空指针
@@ -46,6 +51,8 @@ err_pwnam = 获取用户信息失败
err_user_gid = 设置用户GID失败 err_user_gid = 设置用户GID失败
err_user_init = 初始化用户失败 err_user_init = 初始化用户失败
err_user_uid = 设置用户UID失败 err_user_uid = 设置用户UID失败
@@ -54,6 +61,7 @@ err_user_uid = 设置用户UID失败
err_xsessions_dir = 找不到会话文件夹 err_xsessions_dir = 找不到会话文件夹
err_xsessions_open = 无法打开会话文件夹 err_xsessions_open = 无法打开会话文件夹
login = 登录 login = 登录
logout = 注销 logout = 注销

View File

@@ -1,16 +1,16 @@
[Unit] [Unit]
Description=TUI display manager Description=TUI display manager
After=systemd-user-sessions.service plymouth-quit-wait.service After=systemd-user-sessions.service plymouth-quit-wait.service
After=getty@tty$DEFAULT_TTY.service After=getty@%I.service
Conflicts=getty@tty$DEFAULT_TTY.service Conflicts=getty@%I.service
[Service] [Service]
Type=idle Type=idle
ExecStart=$PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME ExecStart=$PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
StandardInput=tty StandardInput=tty
TTYPath=/dev/tty$DEFAULT_TTY TTYPath=/dev/%I
TTYReset=yes TTYReset=yes
TTYVHangup=yes TTYVHangup=yes
[Install] [Install]
Alias=display-manager.service WantedBy=multi-user.target

View File

@@ -0,0 +1,9 @@
#%PAM-1.0
# OpenPAM (used in FreeBSD) doesn't support prepending "-" for ignoring missing
# modules.
auth required pam_permit.so
auth include login
account include login
password include login
session include login

View File

@@ -0,0 +1,16 @@
#%PAM-1.0
auth required pam_permit.so
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include login
password include login
-password optional pam_gnome_keyring.so use_authtok
-session optional pam_systemd.so class=greeter
-session optional pam_elogind.so
session include login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_start

View File

@@ -16,6 +16,7 @@ pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
entry_ini: ?Ini(Entry) = null, entry_ini: ?Ini(Entry) = null,
name: []const u8 = "", name: []const u8 = "",
xdg_session_desktop: ?[]const u8 = null, xdg_session_desktop: ?[]const u8 = null,
xdg_session_desktop_owned: bool = false,
xdg_desktop_names: ?[]const u8 = null, xdg_desktop_names: ?[]const u8 = null,
cmd: ?[]const u8 = null, cmd: ?[]const u8 = null,
specifier: []const u8 = "", specifier: []const u8 = "",

408
src/animations/DurFile.zig Normal file
View File

@@ -0,0 +1,408 @@
const std = @import("std");
const Animation = @import("../tui/Animation.zig");
const Cell = @import("../tui/Cell.zig");
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
const Color = TerminalBuffer.Color;
const Styling = TerminalBuffer.Styling;
const Allocator = std.mem.Allocator;
const Json = std.json;
const eql = std.mem.eql;
const flate = std.compress.flate;
fn read_decompress_file(allocator: Allocator, file_path: []const u8) ![]u8 {
const file_buffer = std.fs.cwd().openFile(file_path, .{}) catch {
return error.FileNotFound;
};
defer file_buffer.close();
var file_reader_buffer: [4096]u8 = undefined;
var decompress_buffer: [flate.max_window_len]u8 = undefined;
var file_reader = file_buffer.reader(&file_reader_buffer);
var decompress: flate.Decompress = .init(&file_reader.interface, .gzip, &decompress_buffer);
const file_decompressed = decompress.reader.allocRemaining(allocator, .unlimited) catch {
return error.NotValidFile;
};
return file_decompressed;
}
const Frame = struct {
frameNumber: i32,
delay: f32,
contents: [][]u8,
colorMap: [][][]i32,
// allocator must be outside of struct as it will fail the json parser
pub fn deinit(self: *const Frame, allocator: Allocator) void {
for (self.contents) |con| {
allocator.free(con);
}
allocator.free(self.contents);
for (self.colorMap) |cm| {
for (cm) |int2| {
allocator.free(int2);
}
allocator.free(cm);
}
allocator.free(self.colorMap);
}
};
// https://github.com/cmang/durdraw/blob/0.29.0/durformat.md
const DurFormat = struct {
allocator: Allocator,
formatVersion: ?i64 = null,
colorFormat: ?[]const u8 = null,
encoding: ?[]const u8 = null,
framerate: ?f64 = null,
columns: ?i64 = null,
lines: ?i64 = null,
frames: std.ArrayList(Frame) = undefined,
pub fn valid(self: *DurFormat) bool {
if (self.formatVersion != null and
self.colorFormat != null and
self.encoding != null and
self.framerate != null and
self.columns != null and
self.lines != null and
self.frames.items.len >= 1)
{
// v8 may have breaking changes like changing the colormap xy direction
// (https://github.com/cmang/durdraw/issues/24)
if (self.formatVersion.? != 7) return false;
// Code currently only supports 16 and 256 color format only
if (!(eql(u8, "16", self.colorFormat.?) or eql(u8, "256", self.colorFormat.?)))
return false;
// Code currently supports only utf-8 encoding
if (!eql(u8, self.encoding.?, "utf-8")) return false;
// Sanity check on file
if (self.columns.? <= 0) return false;
if (self.lines.? <= 0) return false;
if (self.framerate.? < 0) return false;
return true;
}
return false;
}
fn parse_dur_from_json(self: *DurFormat, allocator: Allocator, dur_json_root: Json.Value) !void {
var dur_movie = if (dur_json_root.object.get("DurMovie")) |dm| dm.object else return error.NotValidFile;
// Depending on the version, a dur file can have different json object names (ie: columns vs sizeX)
self.formatVersion = if (dur_movie.get("formatVersion")) |x| x.integer else null;
self.colorFormat = if (dur_movie.get("colorFormat")) |x| try allocator.dupe(u8, x.string) else null;
self.encoding = if (dur_movie.get("encoding")) |x| try allocator.dupe(u8, x.string) else null;
self.framerate = if (dur_movie.get("framerate")) |x| x.float else null;
self.columns = if (dur_movie.get("columns")) |x| x.integer else if (dur_movie.get("sizeX")) |x| x.integer else null;
self.lines = if (dur_movie.get("lines")) |x| x.integer else if (dur_movie.get("sizeY")) |x| x.integer else null;
const frames = dur_movie.get("frames") orelse return error.NotValidFile;
self.frames = try .initCapacity(allocator, frames.array.items.len);
for (frames.array.items) |json_frame| {
var parsed_frame = try Json.parseFromValue(Frame, allocator, json_frame, .{});
defer parsed_frame.deinit();
const frame_val = parsed_frame.value;
// copy all fields to own the ptrs for deallocation, the parsed_frame has some other
// allocated memory making it difficult to deallocate without leaks
const frame: Frame = .{ .frameNumber = frame_val.frameNumber, .delay = frame_val.delay, .contents = try allocator.alloc([]u8, frame_val.contents.len), .colorMap = try allocator.alloc([][]i32, frame_val.colorMap.len) };
for (0..frame.contents.len) |i| {
frame.contents[i] = try allocator.dupe(u8, frame_val.contents[i]);
}
// colorMap is stored as an 3d array where:
// the outer (i) most array is the horizontal position of the color
// the middle (j) is the vertical position of the color
// the inner (0/1) is the foreground/background color
for (0..frame.colorMap.len) |i| {
frame.colorMap[i] = try allocator.alloc([]i32, frame_val.colorMap[i].len);
for (0..frame.colorMap[i].len) |j| {
frame.colorMap[i][j] = try allocator.alloc(i32, 2);
frame.colorMap[i][j][0] = frame_val.colorMap[i][j][0];
frame.colorMap[i][j][1] = frame_val.colorMap[i][j][1];
}
}
try self.frames.append(allocator, frame);
}
}
pub fn create_from_file(self: *DurFormat, allocator: Allocator, file_path: []const u8) !void {
const file_decompressed = try read_decompress_file(allocator, file_path);
defer allocator.free(file_decompressed);
const parsed = try Json.parseFromSlice(Json.Value, allocator, file_decompressed, .{});
defer parsed.deinit();
try parse_dur_from_json(self, allocator, parsed.value);
if (!self.valid()) {
return error.NotValidFile;
}
}
pub fn init(allocator: Allocator) DurFormat {
return .{ .allocator = allocator };
}
pub fn deinit(self: *DurFormat) void {
if (self.colorFormat) |str| self.allocator.free(str);
if (self.encoding) |str| self.allocator.free(str);
for (self.frames.items) |frame| {
frame.deinit(self.allocator);
}
self.frames.deinit(self.allocator);
}
};
const tb_color_16 = [16]u32{
Color.ECOL_BLACK,
Color.ECOL_RED,
Color.ECOL_GREEN,
Color.ECOL_YELLOW,
Color.ECOL_BLUE,
Color.ECOL_MAGENTA,
Color.ECOL_CYAN,
Color.ECOL_WHITE,
Color.ECOL_BLACK | Styling.BOLD,
Color.ECOL_RED | Styling.BOLD,
Color.ECOL_GREEN | Styling.BOLD,
Color.ECOL_YELLOW | Styling.BOLD,
Color.ECOL_BLUE | Styling.BOLD,
Color.ECOL_MAGENTA | Styling.BOLD,
Color.ECOL_CYAN | Styling.BOLD,
Color.ECOL_WHITE | Styling.BOLD,
};
// Using bold for bright colors allows for all 16 colors to be rendered on tty term
const rgb_color_16 = [16]u32{
Color.DEFAULT, // DEFAULT instead of TRUE_BLACK to not break compositors (the latter ignores transparency)
Color.TRUE_DIM_RED,
Color.TRUE_DIM_GREEN,
Color.TRUE_DIM_YELLOW,
Color.TRUE_DIM_BLUE,
Color.TRUE_DIM_MAGENTA,
Color.TRUE_DIM_CYAN,
Color.TRUE_DIM_WHITE,
Color.DEFAULT | Styling.BOLD,
Color.TRUE_RED | Styling.BOLD,
Color.TRUE_GREEN | Styling.BOLD,
Color.TRUE_YELLOW | Styling.BOLD,
Color.TRUE_BLUE | Styling.BOLD,
Color.TRUE_MAGENTA | Styling.BOLD,
Color.TRUE_CYAN | Styling.BOLD,
Color.TRUE_WHITE | Styling.BOLD,
};
// Made this table from looking at colormapping in dur source, not sure whats going on with the mapping logic
// Array indexes are dur colormappings which value maps to indexes in table above. Only needed for dur 16 color
const durcolor_table_to_color16 = [17]u32{
0, // 0 black
0, // 1 nothing?? dur source did not say why 1 is unused
4, // 2 blue
2, // 3 green
6, // 4 cyan
1, // 5 red
5, // 6 magenta
3, // 7 yellow
7, // 8 light gray
8, // 9 gray
12, // 10 bright blue
10, // 11 bright green
14, // 12 bright cyan
9, // 13 bright red
13, // 14 bright magenta
11, // 15 bright yellow
15, // 16 bright white
};
fn sixcube_to_channel(sixcube: u32) u32 {
// Although the range top for the extended range is 0xFF, 6 is not divisible into 0xFF,
// so we use 0xF0 instead with a scaler
const equal_divisions = 0xF0 / 6;
// Since the range is to 0xFF but 6 isn't divisible, we must add a scaler to get it to 0xFF at the last index (5)
const scaler = 0xFF - (equal_divisions * 5);
return if (sixcube > 0) (sixcube * equal_divisions) + scaler else 0;
}
fn convert_256_to_rgb(color_256: u32) u32 {
var rgb_color: u32 = 0;
// 0 - 15 is the standard color range, map to array table
if (color_256 < 16) {
rgb_color = rgb_color_16[color_256];
}
// 16 - 231 is the extended range
else if (color_256 < 232) {
// For extended term range we subtract by 16 to get it in a 0..(6x6x6) cube (range of 216)
// divide by 36 gets the depth of the cube (6x6x1)
// divide by 6 gets the width of the cube (6x1)
// divide by 1 gets the height of the cube (divide 1 for clarity for what we are doing)
// each channel can be 6 levels of brightness hence remander operation of 6
// finally bitshift to correct rgb channel (16 for red, 8 for green, 0 for blue)
rgb_color |= sixcube_to_channel(((color_256 - 16) / 36) % 6) << 16;
rgb_color |= sixcube_to_channel(((color_256 - 16) / 6) % 6) << 8;
rgb_color |= sixcube_to_channel(((color_256 - 16) / 1) % 6);
}
// 232 - 255 is the grayscale range
else {
// For grayscale we have a space of 232 - 255 (24)
// subtract by 232 to get it into the 0..23 range
// standard colors will contain white and black, so we do not use them in the grayscale range (0 is 0x08, 23 is 0xEE)
// this results in a skip of 0x08 for the first color and divisions of 0x0A
// example: term_col 232 = scaler + equal_divisions * (232 - 232) which becomes (scaler + 0x00) == 0x08
// example: term_col 255 = scaler + equal_divisions * (255 - 232) which becomes (scaler + 0xE6) == 0xEE
const scaler = 0x08;
// to get equal parts, the equation is:
// 0xEE = equal_divisions * 23 + scaler | top of range is 0xEE, 23 is last element value (255 minus 232)
// reordered to solve for equal_divisions:
const equal_divisions = (0xEE - scaler) / 23; // evals to 0x0A
const channel = scaler + equal_divisions * (color_256 - 232);
// gray is equal value of same channel color in rgb
rgb_color = channel | (channel << 8) | (channel << 16);
}
return rgb_color;
}
const DurFile = @This();
allocator: Allocator,
terminal_buffer: *TerminalBuffer,
frames: u64,
time_previous: i64,
x_offset: u32,
y_offset: u32,
full_color: bool,
dur_movie: DurFormat,
frame_width: u32,
frame_height: u32,
frame_time: u32,
is_color_format_16: bool,
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, log_writer: *std.io.Writer, file_path: []const u8, x_offset: u32, y_offset: u32, full_color: bool) !DurFile {
var dur_movie: DurFormat = .init(allocator);
// error state is recoverable when thrown to main and results in no background with Dummy in main
dur_movie.create_from_file(allocator, file_path) catch |err| switch (err) {
error.FileNotFound => {
try log_writer.print("error: dur_file was not found at: {s}\n", .{file_path});
return err;
},
error.NotValidFile => {
try log_writer.print("error: dur_file loaded was invalid or not a dur file!\n", .{});
return err;
},
else => return err,
};
// 4 bit mode with 256 color is unsupported
if (!full_color and eql(u8, dur_movie.colorFormat.?, "256")) {
try log_writer.print("error: dur_file can not be 256 color encoded when not using full_color option!\n", .{});
dur_movie.deinit();
return error.InvalidColorFormat;
}
const buf_width: u32 = @intCast(terminal_buffer.width);
const buf_height: u32 = @intCast(terminal_buffer.height);
const movie_width: u32 = @intCast(dur_movie.columns.?);
const movie_height: u32 = @intCast(dur_movie.lines.?);
// Clamp to prevent user from exceeding draw window
const x_offset_clamped = std.math.clamp(x_offset, 0, buf_width - 1);
const y_offset_clamped = std.math.clamp(y_offset, 0, buf_height - 1);
// Ensure if user offsets and frame goes offscreen, it will not overflow draw
const frame_width = if ((movie_width + x_offset_clamped) < buf_width) movie_width else buf_width - x_offset_clamped;
const frame_height = if ((movie_height + y_offset_clamped) < buf_height) movie_height else buf_height - y_offset_clamped;
// Convert dur fps to frames per ms
const frame_time: u32 = @intFromFloat(1000 / dur_movie.framerate.?);
return .{
.allocator = allocator,
.terminal_buffer = terminal_buffer,
.frames = 0,
.time_previous = std.time.milliTimestamp(),
.x_offset = x_offset_clamped,
.y_offset = y_offset_clamped,
.full_color = full_color,
.dur_movie = dur_movie,
.frame_width = frame_width,
.frame_height = frame_height,
.frame_time = frame_time,
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
};
}
pub fn animation(self: *DurFile) Animation {
return Animation.init(self, deinit, realloc, draw);
}
fn deinit(self: *DurFile) void {
self.dur_movie.deinit();
}
fn realloc(_: *DurFile) anyerror!void {}
fn draw(self: *DurFile) void {
const current_frame = self.dur_movie.frames.items[self.frames];
for (0..self.frame_height) |y| {
var iter = std.unicode.Utf8View.initUnchecked(current_frame.contents[y]).iterator();
for (0..self.frame_width) |x| {
const codepoint: u21 = iter.nextCodepoint().?;
const color_map = current_frame.colorMap[x][y];
var color_map_0: u32 = @intCast(if (color_map[0] == -1) 0 else color_map[0]);
var color_map_1: u32 = @intCast(if (color_map[1] == -1) 0 else color_map[1]);
if (self.is_color_format_16) {
color_map_0 = durcolor_table_to_color16[color_map_0];
color_map_1 = durcolor_table_to_color16[color_map_1 + 1]; // Add 1, dur source stores it like this for some reason
}
const fg_color = if (self.full_color) convert_256_to_rgb(color_map_0) else tb_color_16[color_map_0];
const bg_color = if (self.full_color) convert_256_to_rgb(color_map_1) else tb_color_16[color_map_1];
const cell = Cell{ .ch = @intCast(codepoint), .fg = fg_color, .bg = bg_color };
cell.put(x + self.x_offset, y + self.y_offset);
}
}
const time_current = std.time.milliTimestamp();
const delta_time = time_current - self.time_previous;
// Convert delay from sec to ms
const delay_time: u32 = @intFromFloat(current_frame.delay * 1000);
if (delta_time > (self.frame_time + delay_time)) {
self.time_previous = time_current;
const frame_count = self.dur_movie.frames.items.len;
self.frames = (self.frames + 1) % frame_count;
}
}

View File

@@ -342,7 +342,7 @@ fn createXauthFile(pwd: []const u8, buffer: []u8) ![]const u8 {
dir.close(); dir.close();
// xauth_dir is a directory, use it to store Xauthority // xauth_dir is a directory, use it to store Xauthority
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{xauth_dir}); xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config/ly", .{pwd});
} else { } else {
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{xdg_cfg_home.?}); xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{xdg_cfg_home.?});
} }
@@ -365,6 +365,9 @@ fn createXauthFile(pwd: []const u8, buffer: []u8) ![]const u8 {
const trimmed_xauth_dir = xauth_dir[0 .. i + 1]; const trimmed_xauth_dir = xauth_dir[0 .. i + 1];
const xauthority: []u8 = try std.fmt.bufPrint(buffer, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file }); const xauthority: []u8 = try std.fmt.bufPrint(buffer, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
std.fs.makeDirAbsolute(trimmed_xauth_dir) catch {};
const file = try std.fs.createFileAbsolute(xauthority, .{}); const file = try std.fs.createFileAbsolute(xauthority, .{});
file.close(); file.close();

View File

@@ -12,6 +12,9 @@ animation_timeout_sec: u12 = 0,
asterisk: ?u32 = '*', asterisk: ?u32 = '*',
auth_fails: u64 = 10, auth_fails: u64 = 10,
battery_id: ?[]const u8 = null, battery_id: ?[]const u8 = null,
auto_login_service: [:0]const u8 = "ly-autologin",
auto_login_session: ?[]const u8 = null,
auto_login_user: ?[]const u8 = null,
bg: u32 = 0x00000000, bg: u32 = 0x00000000,
bigclock: Bigclock = .none, bigclock: Bigclock = .none,
bigclock_12hr: bool = false, bigclock_12hr: bool = false,
@@ -39,6 +42,10 @@ doom_fire_spread: u8 = 2,
doom_top_color: u32 = 0x00FF0000, doom_top_color: u32 = 0x00FF0000,
doom_middle_color: u32 = 0x00FFFF00, doom_middle_color: u32 = 0x00FFFF00,
doom_bottom_color: u32 = 0x00FFFFFF, doom_bottom_color: u32 = 0x00FFFFFF,
dur_file_path: []const u8 = build_options.config_directory ++ "/ly/example.dur",
dur_x_offset: u32 = 0,
dur_y_offset: u32 = 0,
edge_margin: u8 = 0,
error_bg: u32 = 0x00000000, error_bg: u32 = 0x00000000,
error_fg: u32 = 0x01FF0000, error_fg: u32 = 0x01FF0000,
fg: u32 = 0x00FFFFFF, fg: u32 = 0x00FFFFFF,
@@ -47,9 +54,14 @@ gameoflife_fg: u32 = 0x0000FF00,
gameoflife_entropy_interval: usize = 10, gameoflife_entropy_interval: usize = 10,
gameoflife_frame_delay: usize = 6, gameoflife_frame_delay: usize = 6,
gameoflife_initial_density: f32 = 0.4, gameoflife_initial_density: f32 = 0.4,
hibernate_cmd: ?[]const u8 = null,
hibernate_key: []const u8 = "F4",
hide_borders: bool = false, hide_borders: bool = false,
hide_key_hints: bool = false, hide_key_hints: bool = false,
hide_keyboard_locks: bool = false,
hide_version_string: bool = false, hide_version_string: bool = false,
inactivity_cmd: ?[]const u8 = null,
inactivity_delay: u16 = 0,
initial_info_text: ?[]const u8 = null, initial_info_text: ?[]const u8 = null,
input_len: u8 = 34, input_len: u8 = 34,
lang: []const u8 = "en", lang: []const u8 = "en",
@@ -72,6 +84,7 @@ shutdown_cmd: []const u8 = "/sbin/shutdown -a now",
shutdown_key: []const u8 = "F1", shutdown_key: []const u8 = "F1",
sleep_cmd: ?[]const u8 = null, sleep_cmd: ?[]const u8 = null,
sleep_key: []const u8 = "F3", sleep_key: []const u8 = "F3",
start_cmd: ?[]const u8 = null,
text_in_center: bool = false, text_in_center: bool = false,
vi_default_mode: ViMode = .normal, vi_default_mode: ViMode = .normal,
vi_mode: bool = false, vi_mode: bool = false,

View File

@@ -9,17 +9,22 @@ brightness_up: []const u8 = "increase brightness",
capslock: []const u8 = "capslock", capslock: []const u8 = "capslock",
custom: []const u8 = "custom", custom: []const u8 = "custom",
err_alloc: []const u8 = "failed memory allocation", err_alloc: []const u8 = "failed memory allocation",
err_args: []const u8 = "unable to parse command line arguments",
err_autologin_session: []const u8 = "autologin session not found",
err_bounds: []const u8 = "out-of-bounds index", err_bounds: []const u8 = "out-of-bounds index",
err_brightness_change: []const u8 = "failed to change brightness", err_brightness_change: []const u8 = "failed to change brightness",
err_chdir: []const u8 = "failed to open home folder", err_chdir: []const u8 = "failed to open home folder",
err_clock_too_long: []const u8 = "clock string too long", err_clock_too_long: []const u8 = "clock string too long",
err_config: []const u8 = "unable to parse config file", err_config: []const u8 = "unable to parse config file",
err_crawl: []const u8 = "failed to crawl session directories",
err_dgn_oob: []const u8 = "log message", err_dgn_oob: []const u8 = "log message",
err_domain: []const u8 = "invalid domain", err_domain: []const u8 = "invalid domain",
err_empty_password: []const u8 = "empty password not allowed", err_empty_password: []const u8 = "empty password not allowed",
err_envlist: []const u8 = "failed to get envlist", err_envlist: []const u8 = "failed to get envlist",
err_get_active_tty: []const u8 = "failed to get active tty", err_get_active_tty: []const u8 = "failed to get active tty",
err_hibernate: []const u8 = "failed to execute hibernate command",
err_hostname: []const u8 = "failed to get hostname", err_hostname: []const u8 = "failed to get hostname",
err_inactivity: []const u8 = "failed to execute inactivity command",
err_lock_state: []const u8 = "failed to get lock state", err_lock_state: []const u8 = "failed to get lock state",
err_log: []const u8 = "failed to open log file", err_log: []const u8 = "failed to open log file",
err_mlock: []const u8 = "failed to lock password memory", err_mlock: []const u8 = "failed to lock password memory",
@@ -47,10 +52,12 @@ err_perm_group: []const u8 = "failed to downgrade group permissions",
err_perm_user: []const u8 = "failed to downgrade user permissions", err_perm_user: []const u8 = "failed to downgrade user permissions",
err_pwnam: []const u8 = "failed to get user info", err_pwnam: []const u8 = "failed to get user info",
err_sleep: []const u8 = "failed to execute sleep command", err_sleep: []const u8 = "failed to execute sleep command",
err_start: []const u8 = "failed to execute start command",
err_battery: []const u8 = "failed to load battery status", err_battery: []const u8 = "failed to load battery status",
err_switch_tty: []const u8 = "failed to switch tty", err_switch_tty: []const u8 = "failed to switch tty",
err_tty_ctrl: []const u8 = "tty control transfer failed", err_tty_ctrl: []const u8 = "tty control transfer failed",
err_no_users: []const u8 = "no users found", err_no_users: []const u8 = "no users found",
err_uid_range: []const u8 = "failed to dynamically get uid range",
err_user_gid: []const u8 = "failed to set user GID", err_user_gid: []const u8 = "failed to set user GID",
err_user_init: []const u8 = "failed to initialize user", err_user_init: []const u8 = "failed to initialize user",
err_user_uid: []const u8 = "failed to set user UID", err_user_uid: []const u8 = "failed to set user UID",
@@ -58,6 +65,7 @@ err_xauth: []const u8 = "xauth command failed",
err_xcb_conn: []const u8 = "xcb connection failed", err_xcb_conn: []const u8 = "xcb connection failed",
err_xsessions_dir: []const u8 = "failed to find sessions folder", err_xsessions_dir: []const u8 = "failed to find sessions folder",
err_xsessions_open: []const u8 = "failed to open sessions folder", err_xsessions_open: []const u8 = "failed to open sessions folder",
hibernate: []const u8 = "hibernate",
insert: []const u8 = "insert", insert: []const u8 = "insert",
login: []const u8 = "login", login: []const u8 = "login",
logout: []const u8 = "logged out", logout: []const u8 = "logged out",

View File

@@ -5,6 +5,8 @@ const SavedUsers = @This();
const User = struct { const User = struct {
username: []const u8, username: []const u8,
session_index: usize, session_index: usize,
first_run: bool,
allocated_username: bool,
}; };
user_list: std.ArrayList(User), user_list: std.ArrayList(User),
@@ -18,5 +20,9 @@ pub fn init() SavedUsers {
} }
pub fn deinit(self: *SavedUsers, allocator: std.mem.Allocator) void { pub fn deinit(self: *SavedUsers, allocator: std.mem.Allocator) void {
for (self.user_list.items) |user| {
if (user.allocated_username) allocator.free(user.username);
}
self.user_list.deinit(allocator); self.user_list.deinit(allocator);
} }

View File

@@ -238,7 +238,12 @@ pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, save_ini: *ini.Ini(Ol
if (std.mem.eql(u8, user, username)) saved_users.last_username_index = i; if (std.mem.eql(u8, user, username)) saved_users.last_username_index = i;
} }
try saved_users.user_list.append(allocator, .{ .username = username, .session_index = save.session_index orelse 0 }); try saved_users.user_list.append(allocator, .{
.username = username,
.session_index = save.session_index orelse 0,
.first_run = false,
.allocated_username = false,
});
} }
return true; return true;

View File

@@ -1,9 +1,11 @@
const std = @import("std");
pub const Animation = enum { pub const Animation = enum {
none, none,
doom, doom,
matrix, matrix,
colormix, colormix,
gameoflife, gameoflife,
dur_file,
}; };
pub const DisplayServer = enum { pub const DisplayServer = enum {
@@ -19,6 +21,26 @@ pub const Input = enum {
session, session,
login, login,
password, password,
/// Moves the current Input forwards by one entry. If `reverse`, then the Input
/// moves backwards. If `wrap` is true, then the entry will wrap back around
pub fn move(self: *Input, reverse: bool, wrap: bool) void {
const maxNum = @typeInfo(Input).@"enum".fields.len - 1;
const selfNum = @intFromEnum(self.*);
if (reverse) {
if (wrap) {
self.* = @enumFromInt(selfNum -% 1);
} else if (selfNum != 0) {
self.* = @enumFromInt(selfNum - 1);
}
} else {
if (wrap) {
self.* = @enumFromInt(selfNum +% 1);
} else if (selfNum != maxNum) {
self.* = @enumFromInt(selfNum + 1);
}
}
}
}; };
pub const ViMode = enum { pub const ViMode = enum {

View File

@@ -81,6 +81,9 @@ fn PlatformStruct() type {
pub const vt_activate = vt.VT_ACTIVATE; pub const vt_activate = vt.VT_ACTIVATE;
pub const vt_waitactive = vt.VT_WAITACTIVE; pub const vt_waitactive = vt.VT_WAITACTIVE;
const SYSTEMD_HOMED_UID_MIN = 60001;
const SYSTEMD_HOMED_UID_MAX = 60513;
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void { pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
const status = grp.initgroups(username, @intCast(entry.gid)); const status = grp.initgroups(username, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed; if (status != 0) return error.GroupInitializationFailed;
@@ -168,17 +171,35 @@ fn PlatformStruct() type {
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n'); var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
var uid_range = UidRange{}; var uid_range = UidRange{};
var nameFound = false;
while (iterator.next()) |line| { while (iterator.next()) |line| {
const trimmed_line = std.mem.trim(u8, line, " \n\r\t"); const trimmed_line = std.mem.trim(u8, line, " \n\r\t");
if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) { if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) {
uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line); uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line);
nameFound = true;
} else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) { } else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) {
uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line); uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line);
nameFound = true;
} }
} }
if (!nameFound) return error.UidNameNotFound;
// This code assumes the OS has a login.defs file with UID_MIN
// and UID_MAX values defined in it, which should be the case
// for most systemd-based Linux distributions out there.
// This should be a good enough safeguard for now, as there's
// no reliable (and clean) way to check for systemd support
if (uid_range.uid_min > SYSTEMD_HOMED_UID_MIN) {
uid_range.uid_min = SYSTEMD_HOMED_UID_MIN;
}
if (uid_range.uid_max < SYSTEMD_HOMED_UID_MAX) {
uid_range.uid_max = SYSTEMD_HOMED_UID_MAX;
}
return uid_range; return uid_range;
} }
@@ -226,6 +247,9 @@ fn PlatformStruct() type {
pub const vt_activate = consio.VT_ACTIVATE; pub const vt_activate = consio.VT_ACTIVATE;
pub const vt_waitactive = consio.VT_WAITACTIVE; pub const vt_waitactive = consio.VT_WAITACTIVE;
const FREEBSD_UID_MIN = 1000;
const FREEBSD_UID_MAX = 32000;
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void { pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
// FreeBSD has initgroups() in unistd // FreeBSD has initgroups() in unistd
const status = unistd.initgroups(username, @intCast(entry.gid)); const status = unistd.initgroups(username, @intCast(entry.gid));
@@ -244,8 +268,8 @@ fn PlatformStruct() type {
return .{ return .{
// Hardcoded default values chosen from // Hardcoded default values chosen from
// /usr/src/usr.sbin/pw/pw_conf.c // /usr/src/usr.sbin/pw/pw_conf.c
.uid_min = 1000, .uid_min = FREEBSD_UID_MIN,
.uid_max = 32000, .uid_max = FREEBSD_UID_MAX,
}; };
} }
}, },

View File

@@ -13,6 +13,7 @@ const Doom = @import("animations/Doom.zig");
const Dummy = @import("animations/Dummy.zig"); const Dummy = @import("animations/Dummy.zig");
const Matrix = @import("animations/Matrix.zig"); const Matrix = @import("animations/Matrix.zig");
const GameOfLife = @import("animations/GameOfLife.zig"); const GameOfLife = @import("animations/GameOfLife.zig");
const DurFile = @import("animations/DurFile.zig");
const Animation = @import("tui/Animation.zig"); const Animation = @import("tui/Animation.zig");
const TerminalBuffer = @import("tui/TerminalBuffer.zig"); const TerminalBuffer = @import("tui/TerminalBuffer.zig");
const Session = @import("tui/components/Session.zig"); const Session = @import("tui/components/Session.zig");
@@ -26,6 +27,7 @@ const SavedUsers = @import("config/SavedUsers.zig");
const migrator = @import("config/migrator.zig"); const migrator = @import("config/migrator.zig");
const SharedError = @import("SharedError.zig"); const SharedError = @import("SharedError.zig");
const LogFile = @import("LogFile.zig"); const LogFile = @import("LogFile.zig");
const UidRange = @import("UidRange.zig");
const StringList = std.ArrayListUnmanaged([]const u8); const StringList = std.ArrayListUnmanaged([]const u8);
const Ini = ini.Ini; const Ini = ini.Ini;
@@ -97,7 +99,7 @@ pub fn main() !void {
defer _ = gpa.deinit(); defer _ = gpa.deinit();
// Allows stopping an animation after some time // Allows stopping an animation after some time
const time_start = try interop.getTimeOfDay(); const animation_time_start = try interop.getTimeOfDay();
var animation_timed_out: bool = false; var animation_timed_out: bool = false;
const allocator = gpa.allocator(); const allocator = gpa.allocator();
@@ -110,17 +112,20 @@ pub fn main() !void {
); );
var diag = clap.Diagnostic{}; var diag = clap.Diagnostic{};
var res = clap.parse(clap.Help, &params, clap.parsers.default, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| { var arg_parse_error: anyerror = undefined;
var maybe_res = clap.parse(clap.Help, &params, clap.parsers.default, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| parse_error: {
arg_parse_error = err;
diag.report(stderr, err) catch {}; diag.report(stderr, err) catch {};
try stderr.flush(); try stderr.flush();
return err; break :parse_error null;
}; };
defer res.deinit(); defer if (maybe_res) |*res| res.deinit();
var config: Config = undefined; var config: Config = undefined;
var lang: Lang = undefined; var lang: Lang = undefined;
var old_save_file_exists = false; var old_save_file_exists = false;
var maybe_config_load_error: ?anyerror = null; var maybe_config_load_error: ?anyerror = null;
var start_cmd_exit_code: u8 = 0;
var can_get_lock_state = true; var can_get_lock_state = true;
var can_draw_clock = true; var can_draw_clock = true;
var can_draw_battery = true; var can_draw_battery = true;
@@ -128,17 +133,19 @@ pub fn main() !void {
var saved_users = SavedUsers.init(); var saved_users = SavedUsers.init();
defer saved_users.deinit(allocator); defer saved_users.deinit(allocator);
if (res.args.help != 0) { if (maybe_res) |*res| {
try clap.help(stderr, clap.Help, &params, .{}); if (res.args.help != 0) {
try clap.help(stderr, clap.Help, &params, .{});
_ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is located at " ++ build_options.config_directory ++ "/ly/config.ini.\n"); _ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is located at " ++ build_options.config_directory ++ "/ly/config.ini.\n");
try stderr.flush(); try stderr.flush();
std.process.exit(0); std.process.exit(0);
} }
if (res.args.version != 0) { if (res.args.version != 0) {
_ = try stderr.write("Ly version " ++ build_options.version ++ "\n"); _ = try stderr.write("Ly version " ++ build_options.version ++ "\n");
try stderr.flush(); try stderr.flush();
std.process.exit(0); std.process.exit(0);
}
} }
// Load configuration file // Load configuration file
@@ -161,7 +168,8 @@ pub fn main() !void {
const comment_characters = "#"; const comment_characters = "#";
if (res.args.config) |s| { if (maybe_res != null and maybe_res.?.args.config != null) {
const s = maybe_res.?.args.config.?;
const trailing_slash = if (s[s.len - 1] != '/') "/" else ""; const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash }); const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
@@ -214,7 +222,8 @@ pub fn main() !void {
migrator.lateConfigFieldHandler(&config); migrator.lateConfigFieldHandler(&config);
} }
var usernames = try getAllUsernames(allocator, config.login_defs_path); var maybe_uid_range_error: ?anyerror = null;
var usernames = try getAllUsernames(allocator, config.login_defs_path, &maybe_uid_range_error);
defer { defer {
for (usernames.items) |username| allocator.free(username); for (usernames.items) |username| allocator.free(username);
usernames.deinit(allocator); usernames.deinit(allocator);
@@ -246,18 +255,23 @@ pub fn main() !void {
const session_index = std.fmt.parseInt(usize, session_index_str, 10) catch continue; const session_index = std.fmt.parseInt(usize, session_index_str, 10) catch continue;
try saved_users.user_list.append(allocator, .{ try saved_users.user_list.append(allocator, .{
.username = username, .username = try allocator.dupe(u8, username),
.session_index = session_index, .session_index = session_index,
.first_run = false,
.allocated_username = true,
}); });
} }
}
// If no save file previously existed, fill it up with all usernames // If no save file previously existed, fill it up with all usernames
if (saved_users.user_list.items.len > 0) break :read_save_file; // TODO: Add new username with existing save file
if (config.save and saved_users.user_list.items.len == 0) {
for (usernames.items) |user| { for (usernames.items) |user| {
try saved_users.user_list.append(allocator, .{ try saved_users.user_list.append(allocator, .{
.username = user, .username = user,
.session_index = 0, .session_index = 0,
.first_run = true,
.allocated_username = false,
}); });
} }
} }
@@ -275,6 +289,19 @@ pub fn main() !void {
restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd); restart_cmd = try temporary_allocator.dupe(u8, config.restart_cmd);
commands_allocated = true; commands_allocated = true;
if (config.start_cmd) |start_cmd| {
var start = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", start_cmd }, allocator);
start.stdout_behavior = .Ignore;
start.stderr_behavior = .Ignore;
handle_start_cmd: {
const process_result = start.spawnAndWait() catch {
break :handle_start_cmd;
};
start_cmd_exit_code = process_result.Exited;
}
}
// Initialize termbox // Initialize termbox
try log_writer.writeAll("initializing termbox2\n"); try log_writer.writeAll("initializing termbox2\n");
_ = termbox.tb_init(); _ = termbox.tb_init();
@@ -330,6 +357,25 @@ pub fn main() !void {
var info_line = InfoLine.init(allocator, &buffer); var info_line = InfoLine.init(allocator, &buffer);
defer info_line.deinit(); defer info_line.deinit();
if (maybe_res == null) {
var longest = diag.name.longest();
if (longest.kind == .positional)
longest.name = diag.arg;
try info_line.addMessage(lang.err_args, config.error_bg, config.error_fg);
try log_writer.print("unable to parse argument '{s}{s}': {s}\n", .{ longest.kind.prefix(), longest.name, @errorName(arg_parse_error) });
}
if (maybe_uid_range_error) |err| {
try info_line.addMessage(lang.err_uid_range, config.error_bg, config.error_fg);
try log_writer.print("failed to get uid range: {s}; falling back to default\n", .{@errorName(err)});
}
if (start_cmd_exit_code != 0) {
try info_line.addMessage(lang.err_start, config.error_bg, config.error_fg);
try log_writer.print("failed to execute start command: exit code {d}\n", .{start_cmd_exit_code});
}
if (maybe_config_load_error) |err| { if (maybe_config_load_error) |err| {
// We can't localize this since the config failed to load so we'd fallback to the default language anyway // We can't localize this since the config failed to load so we'd fallback to the default language anyway
try info_line.addMessage("unable to parse config file", config.error_bg, config.error_fg); try info_line.addMessage("unable to parse config file", config.error_bg, config.error_fg);
@@ -367,6 +413,9 @@ pub fn main() !void {
var session = Session.init(allocator, &buffer, &login); var session = Session.init(allocator, &buffer, &login);
defer session.deinit(); defer session.deinit();
login = try UserList.init(allocator, &buffer, usernames, &saved_users, &session);
defer login.deinit();
addOtherEnvironment(&session, lang, .shell, null) catch |err| { addOtherEnvironment(&session, lang, .shell, null) catch |err| {
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
try log_writer.print("failed to add shell environment: {s}\n", .{@errorName(err)}); try log_writer.print("failed to add shell environment: {s}\n", .{@errorName(err)});
@@ -384,35 +433,37 @@ pub fn main() !void {
try log_writer.writeAll("x11 support disabled at compile-time\n"); try log_writer.writeAll("x11 support disabled at compile-time\n");
} }
if (config.initial_info_text) |text| { var has_crawl_error = false;
try info_line.addMessage(text, config.bg, config.fg);
} else get_host_name: {
// Initialize information line with host name
var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostname = std.posix.gethostname(&name_buf) catch |err| {
try info_line.addMessage(lang.err_hostname, config.error_bg, config.error_fg);
try log_writer.print("failed to get hostname: {s}\n", .{@errorName(err)});
break :get_host_name;
};
try info_line.addMessage(hostname, config.bg, config.fg);
}
// Crawl session directories (Wayland, X11 and custom respectively) // Crawl session directories (Wayland, X11 and custom respectively)
var wayland_session_dirs = std.mem.splitScalar(u8, config.waylandsessions, ':'); var wayland_session_dirs = std.mem.splitScalar(u8, config.waylandsessions, ':');
while (wayland_session_dirs.next()) |dir| { while (wayland_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .wayland); crawl(&session, lang, dir, .wayland) catch |err| {
has_crawl_error = true;
try log_writer.print("failed to crawl wayland session directory '{s}': {s}\n", .{ dir, @errorName(err) });
};
} }
if (build_options.enable_x11_support) { if (build_options.enable_x11_support) {
var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':'); var x_session_dirs = std.mem.splitScalar(u8, config.xsessions, ':');
while (x_session_dirs.next()) |dir| { while (x_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .x11); crawl(&session, lang, dir, .x11) catch |err| {
has_crawl_error = true;
try log_writer.print("failed to crawl x11 session directory '{s}': {s}\n", .{ dir, @errorName(err) });
};
} }
} }
var custom_session_dirs = std.mem.splitScalar(u8, config.custom_sessions, ':'); var custom_session_dirs = std.mem.splitScalar(u8, config.custom_sessions, ':');
while (custom_session_dirs.next()) |dir| { while (custom_session_dirs.next()) |dir| {
try crawl(&session, lang, dir, .custom); crawl(&session, lang, dir, .custom) catch |err| {
has_crawl_error = true;
try log_writer.print("failed to crawl custom session directory '{s}': {s}\n", .{ dir, @errorName(err) });
};
}
if (has_crawl_error) {
try info_line.addMessage(lang.err_crawl, config.error_bg, config.error_fg);
} }
if (usernames.items.len == 0) { if (usernames.items.len == 0) {
@@ -424,17 +475,43 @@ pub fn main() !void {
try log_writer.writeAll("no users found\n"); try log_writer.writeAll("no users found\n");
} }
login = try UserList.init(allocator, &buffer, usernames, &saved_users, &session);
defer login.deinit();
var password = Text.init(allocator, &buffer, true, config.asterisk); var password = Text.init(allocator, &buffer, true, config.asterisk);
defer password.deinit(); defer password.deinit();
var active_input = config.default_input; var active_input = config.default_input;
var insert_mode = !config.vi_mode or config.vi_default_mode == .insert; var insert_mode = !config.vi_mode or config.vi_default_mode == .insert;
var is_autologin = false;
check_autologin: {
const auto_user = config.auto_login_user orelse break :check_autologin;
const auto_session = config.auto_login_session orelse break :check_autologin;
if (!isValidUsername(auto_user, usernames)) {
try info_line.addMessage(lang.err_pam_user_unknown, config.error_bg, config.error_fg);
try log_writer.print("autologin failed: username '{s}' not found\n", .{auto_user});
break :check_autologin;
}
const session_index = findSessionByName(&session, auto_session) orelse {
try log_writer.print("autologin failed: session '{s}' not found\n", .{auto_session});
try info_line.addMessage(lang.err_autologin_session, config.error_bg, config.error_fg);
break :check_autologin;
};
try log_writer.print("attempting autologin for user '{s}' with session '{s}'\n", .{ auto_user, auto_session });
session.label.current = session_index;
for (login.label.list.items, 0..) |username, i| {
if (std.mem.eql(u8, username.name, auto_user)) {
login.label.current = i;
break;
}
}
is_autologin = true;
}
// Load last saved username and desktop selection, if any // Load last saved username and desktop selection, if any
if (config.save) { // Skip if autologin is active to prevent overriding autologin session
if (config.save and !is_autologin) {
if (saved_users.last_username_index) |index| load_last_user: { if (saved_users.last_username_index) |index| load_last_user: {
// If the saved index isn't valid, bail out // If the saved index isn't valid, bail out
if (index >= saved_users.user_list.items.len) break :load_last_user; if (index >= saved_users.user_list.items.len) break :load_last_user;
@@ -501,6 +578,10 @@ pub fn main() !void {
var game_of_life = try GameOfLife.init(allocator, &buffer, config.gameoflife_fg, config.gameoflife_entropy_interval, config.gameoflife_frame_delay, config.gameoflife_initial_density); var game_of_life = try GameOfLife.init(allocator, &buffer, config.gameoflife_fg, config.gameoflife_entropy_interval, config.gameoflife_frame_delay, config.gameoflife_initial_density);
animation = game_of_life.animation(); animation = game_of_life.animation();
}, },
.dur_file => {
var dur = try DurFile.init(allocator, &buffer, log_writer, config.dur_file_path, config.dur_x_offset, config.dur_y_offset, config.full_color);
animation = dur.animation();
},
} }
defer animation.deinit(); defer animation.deinit();
@@ -511,6 +592,8 @@ pub fn main() !void {
const restart_len = try TerminalBuffer.strWidth(lang.restart); const restart_len = try TerminalBuffer.strWidth(lang.restart);
const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10); const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
const sleep_len = try TerminalBuffer.strWidth(lang.sleep); const sleep_len = try TerminalBuffer.strWidth(lang.sleep);
const hibernate_key = try std.fmt.parseInt(u8, config.hibernate_key[1..], 10);
const hibernate_len = try TerminalBuffer.strWidth(lang.hibernate);
const brightness_down_key = if (config.brightness_down_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null; const brightness_down_key = if (config.brightness_down_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
const brightness_down_len = try TerminalBuffer.strWidth(lang.brightness_down); const brightness_down_len = try TerminalBuffer.strWidth(lang.brightness_down);
const brightness_up_key = if (config.brightness_up_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null; const brightness_up_key = if (config.brightness_up_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
@@ -521,6 +604,8 @@ pub fn main() !void {
var update = true; var update = true;
var resolution_changed = false; var resolution_changed = false;
var auth_fails: u64 = 0; var auth_fails: u64 = 0;
var inactivity_time_start = try interop.getTimeOfDay();
var inactivity_cmd_ran = false;
// Switch to selected TTY // Switch to selected TTY
const active_tty = interop.getActiveTty(allocator) catch |err| no_tty_found: { const active_tty = interop.getActiveTty(allocator) catch |err| no_tty_found: {
@@ -533,6 +618,19 @@ pub fn main() !void {
try log_writer.print("failed to switch tty: {s}\n", .{@errorName(err)}); try log_writer.print("failed to switch tty: {s}\n", .{@errorName(err)});
}; };
if (config.initial_info_text) |text| {
try info_line.addMessage(text, config.bg, config.fg);
} else get_host_name: {
// Initialize information line with host name
var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostname = std.posix.gethostname(&name_buf) catch |err| {
try info_line.addMessage(lang.err_hostname, config.error_bg, config.error_fg);
try log_writer.print("failed to get hostname: {s}\n", .{@errorName(err)});
break :get_host_name;
};
try info_line.addMessage(hostname, config.bg, config.fg);
}
while (run) { while (run) {
// If there's no input or there's an animation, a resolution change needs to be checked // If there's no input or there's an animation, a resolution change needs to be checked
if (!update or animate) { if (!update or animate) {
@@ -562,7 +660,7 @@ pub fn main() !void {
if (update) { if (update) {
// If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally // If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally
if (auth_fails >= config.auth_fails) { if (config.auth_fails > 0 and auth_fails >= config.auth_fails) {
std.Thread.sleep(std.time.ns_per_ms * 10); std.Thread.sleep(std.time.ns_per_ms * 10);
update = buffer.cascade(); update = buffer.cascade();
@@ -577,12 +675,12 @@ pub fn main() !void {
_ = termbox.tb_clear(); _ = termbox.tb_clear();
var length: usize = 0; var length: usize = config.edge_margin;
if (!animation_timed_out) animation.draw(); if (!animation_timed_out) animation.draw();
if (!config.hide_version_string) { if (!config.hide_version_string) {
buffer.drawLabel(ly_version_str, 0, buffer.height - 1); buffer.drawLabel(ly_version_str, config.edge_margin, buffer.height - 1 - config.edge_margin);
} }
if (config.battery_id) |id| draw_battery: { if (config.battery_id) |id| draw_battery: {
@@ -598,8 +696,11 @@ pub fn main() !void {
var battery_buf: [16:0]u8 = undefined; var battery_buf: [16:0]u8 = undefined;
const battery_str = std.fmt.bufPrintZ(&battery_buf, "BAT: {d}%", .{battery_percentage}) catch break :draw_battery; const battery_str = std.fmt.bufPrintZ(&battery_buf, "BAT: {d}%", .{battery_percentage}) catch break :draw_battery;
const battery_y: usize = if (config.hide_key_hints) 0 else 1; var battery_y: usize = config.edge_margin;
buffer.drawLabel(battery_str, 0, battery_y); if (!config.hide_key_hints) {
battery_y += 1;
}
buffer.drawLabel(battery_str, config.edge_margin, battery_y);
can_draw_battery = true; can_draw_battery = true;
} }
@@ -660,7 +761,7 @@ pub fn main() !void {
break :draw_clock; break :draw_clock;
} }
buffer.drawLabel(clock_str, buffer.width - @min(buffer.width, clock_str.len), 0); buffer.drawLabel(clock_str, buffer.width - @min(buffer.width, clock_str.len) - config.edge_margin, config.edge_margin);
} }
const label_x = buffer.box_x + buffer.margin_box_h; const label_x = buffer.box_x + buffer.margin_box_h;
@@ -672,44 +773,53 @@ pub fn main() !void {
info_line.label.draw(); info_line.label.draw();
if (!config.hide_key_hints) { if (!config.hide_key_hints) {
buffer.drawLabel(config.shutdown_key, length, 0); buffer.drawLabel(config.shutdown_key, length, config.edge_margin);
length += config.shutdown_key.len + 1; length += config.shutdown_key.len + 1;
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.shutdown, length, 0); buffer.drawLabel(lang.shutdown, length, config.edge_margin);
length += shutdown_len + 1; length += shutdown_len + 1;
buffer.drawLabel(config.restart_key, length, 0); buffer.drawLabel(config.restart_key, length, config.edge_margin);
length += config.restart_key.len + 1; length += config.restart_key.len + 1;
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.restart, length, 0); buffer.drawLabel(lang.restart, length, config.edge_margin);
length += restart_len + 1; length += restart_len + 1;
if (config.sleep_cmd != null) { if (config.sleep_cmd != null) {
buffer.drawLabel(config.sleep_key, length, 0); buffer.drawLabel(config.sleep_key, length, config.edge_margin);
length += config.sleep_key.len + 1; length += config.sleep_key.len + 1;
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.sleep, length, 0); buffer.drawLabel(lang.sleep, length, config.edge_margin);
length += sleep_len + 1; length += sleep_len + 1;
} }
if (config.brightness_down_key) |key| { if (config.hibernate_cmd != null) {
buffer.drawLabel(key, length, 0); buffer.drawLabel(config.hibernate_key, length, config.edge_margin);
length += key.len + 1; length += config.hibernate_key.len + 1;
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.brightness_down, length, 0); buffer.drawLabel(lang.hibernate, length, config.edge_margin);
length += hibernate_len + 1;
}
if (config.brightness_down_key) |key| {
buffer.drawLabel(key, length, config.edge_margin);
length += key.len + 1;
buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.brightness_down, length, config.edge_margin);
length += brightness_down_len + 1; length += brightness_down_len + 1;
} }
if (config.brightness_up_key) |key| { if (config.brightness_up_key) |key| {
buffer.drawLabel(key, length, 0); buffer.drawLabel(key, length, config.edge_margin);
length += key.len + 1; length += key.len + 1;
buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(" ", length - 1, config.edge_margin);
buffer.drawLabel(lang.brightness_up, length, 0); buffer.drawLabel(lang.brightness_up, length, config.edge_margin);
length += brightness_up_len + 1; length += brightness_up_len + 1;
} }
} }
@@ -723,7 +833,7 @@ pub fn main() !void {
buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height); buffer.drawLabel(label_txt, buffer.box_x, buffer.box_y + buffer.box_height);
} }
if (can_get_lock_state) draw_lock_state: { if (!config.hide_keyboard_locks and can_get_lock_state) draw_lock_state: {
const lock_state = interop.getLockState() catch |err| { const lock_state = interop.getLockState() catch |err| {
try info_line.addMessage(lang.err_lock_state, config.error_bg, config.error_fg); try info_line.addMessage(lang.err_lock_state, config.error_bg, config.error_fg);
can_get_lock_state = false; can_get_lock_state = false;
@@ -731,8 +841,10 @@ pub fn main() !void {
break :draw_lock_state; break :draw_lock_state;
}; };
var lock_state_x = buffer.width - @min(buffer.width, lang.numlock.len); var lock_state_x = buffer.width - @min(buffer.width, lang.numlock.len) - config.edge_margin;
const lock_state_y: usize = if (config.clock != null) 1 else 0; var lock_state_y: usize = config.edge_margin;
if (config.clock != null) lock_state_y += 1;
if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y); if (lock_state.numlock) buffer.drawLabel(lang.numlock, lock_state_x, lock_state_y);
@@ -758,7 +870,7 @@ pub fn main() !void {
// Check how long we've been running so we can turn off the animation // Check how long we've been running so we can turn off the animation
const time = try interop.getTimeOfDay(); const time = try interop.getTimeOfDay();
if (config.animation_timeout_sec > 0 and time.seconds - time_start.seconds > config.animation_timeout_sec) { if (config.animation_timeout_sec > 0 and time.seconds - animation_time_start.seconds > config.animation_timeout_sec) {
animation_timed_out = true; animation_timed_out = true;
animation.deinit(); animation.deinit();
} }
@@ -766,17 +878,56 @@ pub fn main() !void {
const time = try interop.getTimeOfDay(); const time = try interop.getTimeOfDay();
timeout = @intCast((60 - @rem(time.seconds, 60)) * 1000 - @divTrunc(time.microseconds, 1000) + 1); timeout = @intCast((60 - @rem(time.seconds, 60)) * 1000 - @divTrunc(time.microseconds, 1000) + 1);
} else if (config.clock != null or auth_fails >= config.auth_fails) { } else if (config.clock != null or (config.auth_fails > 0 and auth_fails >= config.auth_fails)) {
const time = try interop.getTimeOfDay(); const time = try interop.getTimeOfDay();
timeout = @intCast(1000 - @divTrunc(time.microseconds, 1000) + 1); timeout = @intCast(1000 - @divTrunc(time.microseconds, 1000) + 1);
} }
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout); if (config.inactivity_cmd) |inactivity_cmd| {
const time = try interop.getTimeOfDay();
update = timeout != -1; if (!inactivity_cmd_ran and time.seconds - inactivity_time_start.seconds > config.inactivity_delay) {
var inactivity = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", inactivity_cmd }, allocator);
inactivity.stdout_behavior = .Ignore;
inactivity.stderr_behavior = .Ignore;
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue; handle_inactivity_cmd: {
const process_result = inactivity.spawnAndWait() catch {
break :handle_inactivity_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_inactivity, config.error_bg, config.error_fg);
try log_writer.print("failed to execute inactivity command: exit code {d}\n", .{process_result.Exited});
}
}
inactivity_cmd_ran = true;
}
}
// Skip event polling if autologin is set, use simulated Enter key press instead
if (is_autologin) {
event = termbox.tb_event{
.type = termbox.TB_EVENT_KEY,
.key = termbox.TB_KEY_ENTER,
.ch = 0,
.w = 0,
.h = 0,
.x = 0,
.y = 0,
.mod = 0,
};
} else {
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
update = timeout != -1;
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
}
// Input of some kind was detected, so reset the inactivity timer
inactivity_time_start = try interop.getTimeOfDay();
switch (event.key) { switch (event.key) {
termbox.TB_KEY_ESC => { termbox.TB_KEY_ESC => {
@@ -809,6 +960,22 @@ pub fn main() !void {
} }
} }
} }
} else if (pressed_key == hibernate_key) {
if (config.hibernate_cmd) |hibernate_cmd| {
var hibernate = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", hibernate_cmd }, allocator);
hibernate.stdout_behavior = .Ignore;
hibernate.stderr_behavior = .Ignore;
handle_hibernate_cmd: {
const process_result = hibernate.spawnAndWait() catch {
break :handle_hibernate_cmd;
};
if (process_result.Exited != 0) {
try info_line.addMessage(lang.err_hibernate, config.error_bg, config.error_fg);
try log_writer.print("failed to execute hibernate command: exit code {d}\n", .{process_result.Exited});
}
}
}
} else if (brightness_down_key != null and pressed_key == brightness_down_key.?) { } else if (brightness_down_key != null and pressed_key == brightness_down_key.?) {
adjustBrightness(allocator, config.brightness_down_cmd) catch |err| { adjustBrightness(allocator, config.brightness_down_cmd) catch |err| {
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg); try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
@@ -827,37 +994,19 @@ pub fn main() !void {
update = true; update = true;
}, },
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => { termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
active_input = switch (active_input) { active_input.move(true, false);
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true; update = true;
}, },
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => { termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
active_input = switch (active_input) { active_input.move(false, false);
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true; update = true;
}, },
termbox.TB_KEY_TAB => { termbox.TB_KEY_TAB => {
active_input = switch (active_input) { active_input.move(false, true);
.info_line => .session,
.session => .login,
.login => .password,
.password => .info_line,
};
update = true; update = true;
}, },
termbox.TB_KEY_BACK_TAB => { termbox.TB_KEY_BACK_TAB => {
active_input = switch (active_input) { active_input.move(true, true);
.info_line => .password,
.session => .info_line,
.login => .session,
.password => .login,
};
update = true; update = true;
}, },
termbox.TB_KEY_ENTER => authenticate: { termbox.TB_KEY_ENTER => authenticate: {
@@ -922,9 +1071,14 @@ pub fn main() !void {
session_pid = try std.posix.fork(); session_pid = try std.posix.fork();
if (session_pid == 0) { if (session_pid == 0) {
const current_environment = session.label.list.items[session.label.current].environment; const current_environment = session.label.list.items[session.label.current].environment;
// Use auto_login_service for autologin, otherwise use configured service
const service_name = if (is_autologin) config.auto_login_service else config.service_name;
const password_text = if (is_autologin) "" else password.text.items;
const auth_options = auth.AuthOptions{ const auth_options = auth.AuthOptions{
.tty = active_tty, .tty = active_tty,
.service_name = config.service_name, .service_name = service_name,
.path = config.path, .path = config.path,
.session_log = config.session_log, .session_log = config.session_log,
.xauth_cmd = config.xauth_cmd, .xauth_cmd = config.xauth_cmd,
@@ -944,7 +1098,7 @@ pub fn main() !void {
try log_file.reinit(); try log_file.reinit();
auth.authenticate(allocator, &log_file, auth_options, current_environment, login.getCurrentUsername(), password.text.items) catch |err| { auth.authenticate(allocator, &log_file, auth_options, current_environment, login.getCurrentUsername(), password_text) catch |err| {
shared_err.writeError(err); shared_err.writeError(err);
log_file.deinit(); log_file.deinit();
@@ -987,13 +1141,14 @@ pub fn main() !void {
} }
password.clear(); password.clear();
is_autologin = false;
try info_line.addMessage(lang.logout, config.bg, config.fg); try info_line.addMessage(lang.logout, config.bg, config.fg);
try log_writer.writeAll("logged out\n"); try log_writer.writeAll("logged out\n");
} }
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios);
if (auth_fails < config.auth_fails) { if (config.auth_fails == 0 or auth_fails < config.auth_fails) {
_ = termbox.tb_clear(); _ = termbox.tb_clear();
try ttyClearScreen(); try ttyClearScreen();
@@ -1008,20 +1163,12 @@ pub fn main() !void {
if (!insert_mode) { if (!insert_mode) {
switch (event.ch) { switch (event.ch) {
'k' => { 'k' => {
active_input = switch (active_input) { active_input.move(true, false);
.session, .info_line => .info_line,
.login => .session,
.password => .login,
};
update = true; update = true;
continue; continue;
}, },
'j' => { 'j' => {
active_input = switch (active_input) { active_input.move(false, false);
.info_line => .session,
.session => .login,
.login, .password => .password,
};
update = true; update = true;
continue; continue;
}, },
@@ -1086,7 +1233,9 @@ fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplaySer
} }
fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: DisplayServer) !void { fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: DisplayServer) !void {
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return; if (!std.fs.path.isAbsolute(path)) return error.PathNotAbsolute;
var iterable_directory = try std.fs.openDirAbsolute(path, .{ .iterate = true });
defer iterable_directory.close(); defer iterable_directory.close();
var iterator = iterable_directory.iterate(); var iterator = iterable_directory.iterate();
@@ -1105,6 +1254,7 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
const entry = entry_ini.data.@"Desktop Entry"; const entry = entry_ini.data.@"Desktop Entry";
var maybe_xdg_session_desktop: ?[]const u8 = null; var maybe_xdg_session_desktop: ?[]const u8 = null;
var maybe_xdg_desktop_names: ?[]const u8 = null; var maybe_xdg_desktop_names: ?[]const u8 = null;
var xdg_session_desktop_owned = false;
// Prepare the XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP environment // Prepare the XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP environment
// variables here // variables here
@@ -1118,13 +1268,18 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
} else if (display_server != .custom) { } else if (display_server != .custom) {
// If DesktopNames is empty, and this isn't a custom session entry, // If DesktopNames is empty, and this isn't a custom session entry,
// we'll take the name of the session file // we'll take the name of the session file
maybe_xdg_session_desktop = std.fs.path.stem(item.name); const stem = std.fs.path.stem(item.name);
if (stem.len > 0) {
maybe_xdg_session_desktop = try session.label.allocator.dupe(u8, stem);
xdg_session_desktop_owned = true;
}
} }
try session.addEnvironment(.{ try session.addEnvironment(.{
.entry_ini = entry_ini, .entry_ini = entry_ini,
.name = entry.Name, .name = entry.Name,
.xdg_session_desktop = maybe_xdg_session_desktop, .xdg_session_desktop = maybe_xdg_session_desktop,
.xdg_session_desktop_owned = xdg_session_desktop_owned,
.xdg_desktop_names = maybe_xdg_desktop_names, .xdg_desktop_names = maybe_xdg_desktop_names,
.cmd = entry.Exec, .cmd = entry.Exec,
.specifier = switch (display_server) { .specifier = switch (display_server) {
@@ -1139,8 +1294,34 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa
} }
} }
fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8) !StringList { fn isValidUsername(username: []const u8, usernames: StringList) bool {
const uid_range = try interop.getUserIdRange(allocator, login_defs_path); for (usernames.items) |valid_username| {
if (std.mem.eql(u8, username, valid_username)) return true;
}
return false;
}
fn findSessionByName(session: *Session, name: []const u8) ?usize {
for (session.label.list.items, 0..) |env, i| {
if (env.environment.xdg_session_desktop) |session_desktop| {
if (session_desktop.len > 0 and std.ascii.eqlIgnoreCase(session_desktop, name)) return i;
}
if (env.environment.xdg_desktop_names) |session_desktop_name| {
if (std.ascii.eqlIgnoreCase(session_desktop_name, name)) return i;
}
if (std.ascii.eqlIgnoreCase(env.environment.name, name)) return i;
}
return null;
}
fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, uid_range_error: *?anyerror) !StringList {
const uid_range = interop.getUserIdRange(allocator, login_defs_path) catch |err| no_uid_range: {
uid_range_error.* = err;
break :no_uid_range UidRange{
.uid_min = build_options.fallback_uid_min,
.uid_max = build_options.fallback_uid_max,
};
};
var usernames: StringList = .empty; var usernames: StringList = .empty;
var maybe_entry = interop.getNextUsernameEntry(); var maybe_entry = interop.getNextUsernameEntry();

View File

@@ -38,6 +38,13 @@ pub const Color = struct {
pub const TRUE_MAGENTA = 0x00FF00FF; pub const TRUE_MAGENTA = 0x00FF00FF;
pub const TRUE_CYAN = 0x0000FFFF; pub const TRUE_CYAN = 0x0000FFFF;
pub const TRUE_WHITE = 0x00FFFFFF; pub const TRUE_WHITE = 0x00FFFFFF;
pub const TRUE_DIM_RED = 0x00800000;
pub const TRUE_DIM_GREEN = 0x00008000;
pub const TRUE_DIM_YELLOW = 0x00808000;
pub const TRUE_DIM_BLUE = 0x00000080;
pub const TRUE_DIM_MAGENTA = 0x00800080;
pub const TRUE_DIM_CYAN = 0x00008080;
pub const TRUE_DIM_WHITE = 0x00C0C0C0;
pub const ECOL_BLACK = 1; pub const ECOL_BLACK = 1;
pub const ECOL_RED = 2; pub const ECOL_RED = 2;
pub const ECOL_GREEN = 3; pub const ECOL_GREEN = 3;

View File

@@ -17,28 +17,39 @@ const EnvironmentLabel = generic.CyclableLabel(Env, *UserList);
const Session = @This(); const Session = @This();
label: EnvironmentLabel, label: EnvironmentLabel,
user_list: *UserList,
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, user_list: *UserList) Session { pub fn init(allocator: Allocator, buffer: *TerminalBuffer, user_list: *UserList) Session {
return .{ return .{
.label = EnvironmentLabel.init(allocator, buffer, drawItem, sessionChanged, user_list), .label = EnvironmentLabel.init(allocator, buffer, drawItem, sessionChanged, user_list),
.user_list = user_list,
}; };
} }
pub fn deinit(self: *Session) void { pub fn deinit(self: *Session) void {
for (self.label.list.items) |*env| { for (self.label.list.items) |*env| {
if (env.environment.entry_ini) |*entry_ini| entry_ini.deinit(); if (env.environment.entry_ini) |*entry_ini| entry_ini.deinit();
if (env.environment.xdg_session_desktop_owned) {
self.label.allocator.free(env.environment.xdg_session_desktop.?);
}
} }
self.label.deinit(); self.label.deinit();
} }
pub fn addEnvironment(self: *Session, environment: Environment) !void { pub fn addEnvironment(self: *Session, environment: Environment) !void {
try self.label.addItem(.{ .environment = environment, .index = self.label.list.items.len }); const env = Env{ .environment = environment, .index = self.label.list.items.len };
try self.label.addItem(env);
sessionChanged(env, self.user_list);
} }
fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void { fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
if (maybe_user_list) |user_list| { if (maybe_user_list) |user_list| {
user_list.label.list.items[user_list.label.current].session_index.* = env.index; const user = user_list.label.list.items[user_list.label.current];
if (!user.first_run) return;
user.session_index.* = env.index;
} }
} }

View File

@@ -11,6 +11,7 @@ pub const User = struct {
name: []const u8, name: []const u8,
session_index: *usize, session_index: *usize,
allocated_index: bool, allocated_index: bool,
first_run: bool,
}; };
const UserLabel = generic.CyclableLabel(User, *Session); const UserLabel = generic.CyclableLabel(User, *Session);
@@ -27,9 +28,11 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList
if (username.len == 0) continue; if (username.len == 0) continue;
var maybe_session_index: ?*usize = null; var maybe_session_index: ?*usize = null;
var first_run = true;
for (saved_users.user_list.items) |*saved_user| { for (saved_users.user_list.items) |*saved_user| {
if (std.mem.eql(u8, username, saved_user.username)) { if (std.mem.eql(u8, username, saved_user.username)) {
maybe_session_index = &saved_user.session_index; maybe_session_index = &saved_user.session_index;
first_run = saved_user.first_run;
break; break;
} }
} }
@@ -45,6 +48,7 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList
.name = username, .name = username,
.session_index = maybe_session_index.?, .session_index = maybe_session_index.?,
.allocated_index = allocated_index, .allocated_index = allocated_index,
.first_run = first_run,
}); });
} }
@@ -67,6 +71,7 @@ pub fn getCurrentUsername(self: UserList) []const u8 {
fn usernameChanged(user: User, maybe_session: ?*Session) void { fn usernameChanged(user: User, maybe_session: ?*Session) void {
if (maybe_session) |session| { if (maybe_session) |session| {
if (user.session_index.* >= session.label.list.items.len) return;
session.label.current = user.session_index.*; session.label.current = user.session_index.*;
} }
} }